Merge "Fix multi-listener load balancers"
This commit is contained in:
commit
37799137a3
@ -29,9 +29,7 @@ communication is limited to fail-over protocols.)
|
||||
Versioning
|
||||
----------
|
||||
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,
|
||||
any reference to a *:version* variable should be replaced with the literal
|
||||
string 'v0.5'.)
|
||||
purposes of this document, the initial version of this API shall be 1.0.
|
||||
|
||||
Response codes
|
||||
--------------
|
||||
@ -74,136 +72,6 @@ a secure way (ex. memory filesystem).
|
||||
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
|
||||
----------------
|
||||
* **URL:** /info
|
||||
@ -249,7 +117,7 @@ version string prepended to it.
|
||||
Get amphora details
|
||||
-------------------
|
||||
|
||||
* **URL:** /*:version*/details
|
||||
* **URL:** /1.0/details
|
||||
* **Method:** GET
|
||||
* **URL params:** none
|
||||
* **Data params:** none
|
||||
@ -372,7 +240,7 @@ health of the amphora, currently-configured topology and role, etc.
|
||||
Get interface
|
||||
-------------
|
||||
|
||||
* **URL:** /*:version*/interface/*:ip*
|
||||
* **URL:** /1.0/interface/*:ip*
|
||||
* **Method:** GET
|
||||
* **URL params:**
|
||||
|
||||
@ -408,7 +276,7 @@ Get interface
|
||||
::
|
||||
|
||||
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:
|
||||
{
|
||||
@ -422,7 +290,7 @@ Get interface
|
||||
::
|
||||
|
||||
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:
|
||||
{
|
||||
@ -435,7 +303,7 @@ Get interface
|
||||
::
|
||||
|
||||
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:
|
||||
{
|
||||
@ -446,7 +314,7 @@ Get interface
|
||||
Get all listeners' statuses
|
||||
---------------------------
|
||||
|
||||
* **URL:** /*:version*/listeners
|
||||
* **URL:** /1.0/listeners
|
||||
* **Method:** GET
|
||||
* **URL params:** none
|
||||
* **Data params:** none
|
||||
@ -492,91 +360,14 @@ a valid haproxy configuration).
|
||||
'type': 'TERMINATED_HTTPS',
|
||||
}]
|
||||
|
||||
Get a listener's status
|
||||
-----------------------
|
||||
Start or Stop a load balancer
|
||||
-----------------------------
|
||||
|
||||
* **URL:** /*:version*/listeners/*:listener*
|
||||
* **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*
|
||||
* **URL:** /1.0/loadbalancer/*:object_id*/*:action*
|
||||
* **Method:** PUT
|
||||
* **URL params:**
|
||||
|
||||
* *:listener* = Listener UUID
|
||||
* *:object_id* = Object UUID
|
||||
* *:action* = One of: start, stop, reload
|
||||
|
||||
* **Data params:** none
|
||||
@ -612,7 +403,7 @@ Start or Stop a listener
|
||||
|
||||
| OK
|
||||
| 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:**
|
||||
|
||||
@ -621,12 +412,12 @@ Start or Stop a listener
|
||||
::
|
||||
|
||||
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:
|
||||
{
|
||||
'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:
|
||||
@ -634,7 +425,7 @@ Start or Stop a listener
|
||||
::
|
||||
|
||||
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:
|
||||
{
|
||||
@ -647,12 +438,12 @@ Start or Stop a listener
|
||||
::
|
||||
|
||||
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:
|
||||
{
|
||||
'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:
|
||||
@ -660,7 +451,7 @@ Start or Stop a listener
|
||||
::
|
||||
|
||||
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:
|
||||
{
|
||||
@ -680,7 +471,7 @@ Start or Stop a listener
|
||||
Delete a listener
|
||||
-----------------
|
||||
|
||||
* **URL:** /*:version*/listeners/*:listener*
|
||||
* **URL:** /1.0/listeners/*:listener*
|
||||
* **Method:** DELETE
|
||||
* **URL params:**
|
||||
|
||||
@ -724,7 +515,7 @@ Delete a listener
|
||||
::
|
||||
|
||||
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:
|
||||
{
|
||||
@ -736,7 +527,7 @@ Delete a listener
|
||||
::
|
||||
|
||||
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:
|
||||
{
|
||||
@ -756,11 +547,11 @@ Delete a listener
|
||||
Upload SSL certificate PEM file
|
||||
-------------------------------
|
||||
|
||||
* **URL:** /*:version*/listeners/*:listener*/certificates/*:filename.pem*
|
||||
* **URL:** /1.0/loadbalancer/*:loadbalancer_id*/certificates/*:filename.pem*
|
||||
* **Method:** PUT
|
||||
* **URL params:**
|
||||
|
||||
* *:listener* = Listener UUID
|
||||
* *:loadbalancer_id* = Load balancer UUID
|
||||
* *:filename* = PEM filename (see notes below for naming convention)
|
||||
|
||||
* **Data params:** Certificate data. (PEM file should be a concatenation of
|
||||
@ -812,7 +603,7 @@ explicitly restarted
|
||||
::
|
||||
|
||||
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
|
||||
described above)
|
||||
|
||||
@ -826,7 +617,7 @@ explicitly restarted
|
||||
::
|
||||
|
||||
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)
|
||||
|
||||
JSON Response:
|
||||
@ -839,7 +630,7 @@ explicitly restarted
|
||||
::
|
||||
|
||||
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)
|
||||
|
||||
JSON Response:
|
||||
@ -852,7 +643,7 @@ explicitly restarted
|
||||
::
|
||||
|
||||
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.)
|
||||
|
||||
JSON Response:
|
||||
@ -865,15 +656,14 @@ explicitly restarted
|
||||
::
|
||||
|
||||
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:
|
||||
{
|
||||
'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:
|
||||
|
||||
::
|
||||
@ -883,15 +673,14 @@ explicitly restarted
|
||||
'message': 'Topology transition in progress',
|
||||
}
|
||||
|
||||
|
||||
Get SSL certificate md5sum
|
||||
--------------------------
|
||||
|
||||
* **URL:** /*:version*/listeners/*:listener*/certificates/*:filename.pem*
|
||||
* **URL:** /1.0/loadbalancer/*:loadbalancer_id*/certificates/*:filename.pem*
|
||||
* **Method:** GET
|
||||
* **URL params:**
|
||||
|
||||
* *:listener* = Listener UUID
|
||||
* *:loadbalancer_id* = Load balancer UUID
|
||||
* *:filename* = PEM filename (see notes below for naming convention)
|
||||
|
||||
* **Data params:** none
|
||||
@ -937,7 +726,7 @@ disclosing it over the wire from the amphora is a security risk.
|
||||
JSON Response:
|
||||
{
|
||||
'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:
|
||||
@ -953,11 +742,11 @@ disclosing it over the wire from the amphora is a security risk.
|
||||
Delete SSL certificate PEM file
|
||||
-------------------------------
|
||||
|
||||
* **URL:** /*:version*/listeners/*:listener*/certificates/*:filename.pem*
|
||||
* **URL:** /1.0/loadbalancer/*:loadbalancer_id*/certificates/*:filename.pem*
|
||||
* **Method:** DELETE
|
||||
* **URL params:**
|
||||
|
||||
* *:listener* = Listener UUID
|
||||
* *:loadbalancer_id* = Load balancer UUID
|
||||
* *:filename* = PEM filename (see notes below for naming convention)
|
||||
|
||||
* **Data params:** none
|
||||
@ -988,7 +777,7 @@ Delete SSL certificate PEM file
|
||||
::
|
||||
|
||||
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:
|
||||
{
|
||||
@ -1000,7 +789,7 @@ Delete SSL certificate PEM file
|
||||
::
|
||||
|
||||
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:
|
||||
{
|
||||
@ -1017,14 +806,14 @@ Delete SSL certificate PEM file
|
||||
'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
|
||||
* **URL params:**
|
||||
|
||||
* *:listener* = Listener UUID
|
||||
* *:loadbalancer_id* = Load Balancer UUID
|
||||
* *:amphora_id* = Amphora UUID
|
||||
|
||||
* **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:
|
||||
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.)
|
||||
|
||||
JSON Response:
|
||||
@ -1098,14 +887,14 @@ out of the haproxy daemon status interface for tracking health and stats).
|
||||
'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
|
||||
* **URL params:**
|
||||
|
||||
* *:listener* = Listener UUID
|
||||
* *:loadbalancer_id* = Load balancer UUID
|
||||
|
||||
* **Data params:** none
|
||||
* **Success Response:**
|
||||
@ -1122,7 +911,7 @@ Get listener haproxy configuration
|
||||
|
||||
* **Response:**
|
||||
|
||||
| # Config file for 7e9f91eb-b3e6-4e3b-a1a7-d6f7fdc1de7c
|
||||
| # Config file for 85e2111b-29c4-44be-94f3-e72045805801
|
||||
| (cut for brevity)
|
||||
|
||||
* **Implied actions:** none
|
||||
@ -1134,11 +923,11 @@ Get listener haproxy configuration
|
||||
::
|
||||
|
||||
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:
|
||||
|
||||
# Config file for 7e9f91eb-b3e6-4e3b-a1a7-d6f7fdc1de7c
|
||||
# Config file for 85e2111b-29c4-44be-94f3-e72045805801
|
||||
(cut for brevity)
|
||||
|
||||
* Error code 404:
|
||||
@ -1147,15 +936,14 @@ Get listener haproxy configuration
|
||||
|
||||
JSON Response:
|
||||
{
|
||||
'message': 'Listener Not Found',
|
||||
'details': 'No listener with UUID: 04bff5c3-5862-4a13-b9e3-9b440d0ed50a',
|
||||
'message': 'Loadbalancer Not Found',
|
||||
'details': 'No loadbalancer with UUID: 04bff5c3-5862-4a13-b9e3-9b440d0ed50a',
|
||||
}
|
||||
|
||||
|
||||
Plug VIP
|
||||
--------
|
||||
|
||||
* **URL:** /*:version*/plug/vip/*:ip*
|
||||
* **URL:** /1.0/plug/vip/*:ip*
|
||||
* **Method:** Post
|
||||
* **URL params:**
|
||||
|
||||
@ -1210,7 +998,7 @@ Plug VIP
|
||||
::
|
||||
|
||||
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:
|
||||
{
|
||||
@ -1225,10 +1013,6 @@ Plug VIP
|
||||
'details': 'VIP 203.0.113.2 plugged on interface eth1'
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
* Error code 400:
|
||||
|
||||
::
|
||||
@ -1251,7 +1035,7 @@ Plug VIP
|
||||
Plug Network
|
||||
------------
|
||||
|
||||
* **URL:** /*:version*/plug/network/
|
||||
* **URL:** /1.0/plug/network/
|
||||
* **Method:** POST
|
||||
* **URL params:** none
|
||||
|
||||
@ -1292,7 +1076,7 @@ Plug Network
|
||||
::
|
||||
|
||||
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:
|
||||
{
|
||||
@ -1319,7 +1103,7 @@ Plug Network
|
||||
Upload SSL server certificate PEM file for Controller Communication
|
||||
-------------------------------------------------------------------
|
||||
|
||||
* **URL:** /*:version*/certificate
|
||||
* **URL:** /1.0/certificate
|
||||
* **Method:** PUT
|
||||
|
||||
* **Data params:** Certificate data. (PEM file should be a concatenation of
|
||||
@ -1362,7 +1146,7 @@ not be available for some time.
|
||||
::
|
||||
|
||||
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
|
||||
described above)
|
||||
|
||||
@ -1376,7 +1160,7 @@ not be available for some time.
|
||||
::
|
||||
|
||||
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)
|
||||
|
||||
JSON Response:
|
||||
@ -1389,7 +1173,7 @@ not be available for some time.
|
||||
::
|
||||
|
||||
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)
|
||||
|
||||
JSON Response:
|
||||
@ -1402,7 +1186,7 @@ not be available for some time.
|
||||
::
|
||||
|
||||
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.)
|
||||
|
||||
JSON Response:
|
||||
@ -1414,7 +1198,7 @@ not be available for some time.
|
||||
Upload keepalived configuration
|
||||
-------------------------------
|
||||
|
||||
* **URL:** /*:version*/vrrp/upload
|
||||
* **URL:** /1.0/vrrp/upload
|
||||
* **Method:** PUT
|
||||
* **URL params:** none
|
||||
* **Data params:** none
|
||||
@ -1441,7 +1225,7 @@ OK
|
||||
::
|
||||
|
||||
PUT URI:
|
||||
https://octavia-haproxy-img-00328.local/v0.5/vrrp/upload
|
||||
https://octavia-haproxy-img-00328.local/1.0/vrrp/upload
|
||||
|
||||
JSON Response:
|
||||
{
|
||||
@ -1452,7 +1236,7 @@ OK
|
||||
Start, Stop, or Reload keepalived
|
||||
---------------------------------
|
||||
|
||||
* **URL:** /*:version*/vrrp/*:action*
|
||||
* **URL:** /1.0/vrrp/*:action*
|
||||
* **Method:** PUT
|
||||
* **URL params:**
|
||||
|
||||
@ -1489,7 +1273,7 @@ Start, Stop, or Reload keepalived
|
||||
::
|
||||
|
||||
PUT URL:
|
||||
https://octavia-haproxy-img-00328.local/v0.5/vrrp/start
|
||||
https://octavia-haproxy-img-00328.local/1.0/vrrp/start
|
||||
|
||||
JSON Response:
|
||||
{
|
||||
@ -1502,7 +1286,7 @@ Start, Stop, or Reload keepalived
|
||||
::
|
||||
|
||||
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:
|
||||
{
|
||||
@ -1515,7 +1299,7 @@ Start, Stop, or Reload keepalived
|
||||
::
|
||||
|
||||
PUT URL:
|
||||
https://octavia-haproxy-img-00328.local/v0.5/vrrp/stop
|
||||
https://octavia-haproxy-img-00328.local/1.0/vrrp/stop
|
||||
|
||||
JSON Response:
|
||||
{
|
||||
@ -1526,7 +1310,7 @@ Start, Stop, or Reload keepalived
|
||||
Update the amphora agent configuration
|
||||
--------------------------------------
|
||||
|
||||
* **URL:** /*:version*/config
|
||||
* **URL:** /1.0/config
|
||||
* **Method:** PUT
|
||||
|
||||
* **Data params:** A amphora-agent configuration file
|
||||
@ -1561,7 +1345,7 @@ will be updated.
|
||||
::
|
||||
|
||||
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.)
|
||||
|
||||
JSON Response:
|
||||
|
@ -12,4 +12,4 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
VERSION = '0.5'
|
||||
VERSION = '1.0'
|
||||
|
@ -46,7 +46,7 @@ class AmphoraInfo(object):
|
||||
return webob.Response(json=body)
|
||||
|
||||
def compile_amphora_details(self, extend_udp_driver=None):
|
||||
haproxy_listener_list = util.get_listeners()
|
||||
haproxy_listener_list = sorted(util.get_listeners())
|
||||
extend_body = {}
|
||||
udp_listener_list = []
|
||||
if extend_udp_driver:
|
||||
@ -87,8 +87,8 @@ class AmphoraInfo(object):
|
||||
'load': self._load(),
|
||||
'topology': consts.TOPOLOGY_SINGLE,
|
||||
'topology_status': consts.TOPOLOGY_STATUS_OK,
|
||||
'listeners': list(
|
||||
set(haproxy_listener_list + udp_listener_list))
|
||||
'listeners': sorted(list(
|
||||
set(haproxy_listener_list + udp_listener_list)))
|
||||
if udp_listener_list else haproxy_listener_list,
|
||||
'packages': {}}
|
||||
if extend_body:
|
||||
@ -101,10 +101,10 @@ class AmphoraInfo(object):
|
||||
version = subprocess.check_output(cmd.split())
|
||||
return version
|
||||
|
||||
def _count_haproxy_processes(self, listener_list):
|
||||
def _count_haproxy_processes(self, lb_list):
|
||||
num = 0
|
||||
for listener_id in listener_list:
|
||||
if util.is_listener_running(listener_id):
|
||||
for lb_id in lb_list:
|
||||
if util.is_lb_running(lb_id):
|
||||
# optional check if it's still running
|
||||
num += 1
|
||||
return num
|
||||
|
@ -22,7 +22,7 @@ from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
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.common import constants as consts
|
||||
|
||||
@ -43,7 +43,7 @@ check_script_template = j2_env.get_template(consts.CHECK_SCRIPT_CONF)
|
||||
class Keepalived(object):
|
||||
|
||||
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()):
|
||||
os.makedirs(util.keepalived_dir())
|
||||
|
@ -25,10 +25,9 @@ from oslo_log import log as logging
|
||||
import webob
|
||||
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 util
|
||||
from octavia.amphorae.backends.utils import keepalivedlvs_query
|
||||
from octavia.common import constants as consts
|
||||
|
||||
BUFFER = 100
|
||||
@ -51,7 +50,7 @@ class KeepalivedLvs(udp_listener_base.UdpListenerApiServerBase):
|
||||
_SUBSCRIBED_AMP_COMPILE = ['keepalived', 'ipvsadm']
|
||||
|
||||
def upload_udp_listener_config(self, listener_id):
|
||||
stream = listener.Wrapped(flask.request.stream)
|
||||
stream = loadbalancer.Wrapped(flask.request.stream)
|
||||
NEED_CHECK = True
|
||||
|
||||
if not os.path.exists(util.keepalived_lvs_dir()):
|
||||
@ -256,45 +255,6 @@ class KeepalivedLvs(udp_listener_base.UdpListenerApiServerBase):
|
||||
})
|
||||
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):
|
||||
try:
|
||||
self._check_udp_listener_exists(listener_id)
|
||||
|
@ -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 osutils
|
||||
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 utils as octavia_utils
|
||||
|
||||
@ -54,10 +53,6 @@ SYSVINIT_TEMPLATE = JINJA_ENV.get_template(SYSVINIT_CONF)
|
||||
SYSTEMD_TEMPLATE = JINJA_ENV.get_template(SYSTEMD_CONF)
|
||||
|
||||
|
||||
class ParsingError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
# Wrap a stream so we can compute the md5 while reading
|
||||
class Wrapped(object):
|
||||
def __init__(self, stream_):
|
||||
@ -77,37 +72,37 @@ class Wrapped(object):
|
||||
return getattr(self.stream, attr)
|
||||
|
||||
|
||||
class Listener(object):
|
||||
class Loadbalancer(object):
|
||||
|
||||
def __init__(self):
|
||||
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
|
||||
|
||||
:param listener_id: the id of the listener
|
||||
"""
|
||||
self._check_listener_exists(listener_id)
|
||||
with open(util.config_path(listener_id), 'r') as file:
|
||||
self._check_lb_exists(lb_id)
|
||||
with open(util.config_path(lb_id), 'r') as file:
|
||||
cfg = file.read()
|
||||
resp = webob.Response(cfg, content_type='text/plain')
|
||||
resp.headers['ETag'] = hashlib.md5(six.b(cfg)).hexdigest() # nosec
|
||||
return resp
|
||||
|
||||
def upload_haproxy_config(self, amphora_id, listener_id):
|
||||
def upload_haproxy_config(self, amphora_id, lb_id):
|
||||
"""Upload the haproxy config
|
||||
|
||||
: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)
|
||||
# We have to hash here because HAProxy has a string length limitation
|
||||
# in the configuration file "peer <peername>" lines
|
||||
peer_name = octavia_utils.base64_sha1_string(amphora_id).rstrip('=')
|
||||
if not os.path.exists(util.haproxy_dir(listener_id)):
|
||||
os.makedirs(util.haproxy_dir(listener_id))
|
||||
if not os.path.exists(util.haproxy_dir(lb_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
|
||||
# mode 00600
|
||||
mode = stat.S_IRUSR | stat.S_IWUSR
|
||||
@ -148,7 +143,7 @@ class Listener(object):
|
||||
status=400)
|
||||
|
||||
# file ok - move it
|
||||
os.rename(name, util.config_path(listener_id))
|
||||
os.rename(name, util.config_path(lb_id))
|
||||
|
||||
try:
|
||||
|
||||
@ -156,7 +151,7 @@ class Listener(object):
|
||||
|
||||
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:
|
||||
template = SYSTEMD_TEMPLATE
|
||||
@ -194,9 +189,9 @@ class Listener(object):
|
||||
|
||||
text = template.render(
|
||||
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_cfg=util.config_path(listener_id),
|
||||
haproxy_cfg=util.config_path(lb_id),
|
||||
haproxy_user_group_cfg=consts.HAPROXY_USER_GROUP_CFG,
|
||||
respawn_count=util.CONF.haproxy_amphora.respawn_count,
|
||||
respawn_interval=(util.CONF.haproxy_amphora.
|
||||
@ -212,25 +207,25 @@ class Listener(object):
|
||||
# Make sure the new service is enabled on boot
|
||||
if init_system == consts.INIT_SYSTEMD:
|
||||
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:
|
||||
try:
|
||||
subprocess.check_output(init_enable_cmd.split(),
|
||||
stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as e:
|
||||
LOG.error("Failed to enable haproxy-%(list)s service: "
|
||||
"%(err)s %(out)s", {'list': listener_id, 'err': e,
|
||||
LOG.error("Failed to enable haproxy-%(lb_id)s service: "
|
||||
"%(err)s %(out)s", {'lb_id': lb_id, 'err': e,
|
||||
'out': e.output})
|
||||
return webob.Response(json=dict(
|
||||
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.headers['ETag'] = stream.get_md5()
|
||||
|
||||
return res
|
||||
|
||||
def start_stop_listener(self, listener_id, action):
|
||||
def start_stop_lb(self, lb_id, action):
|
||||
action = action.lower()
|
||||
if action not in [consts.AMP_ACTION_START,
|
||||
consts.AMP_ACTION_STOP,
|
||||
@ -239,30 +234,30 @@ class Listener(object):
|
||||
message='Invalid Request',
|
||||
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
|
||||
# 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
|
||||
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
|
||||
# so start it if haproxy is not already running
|
||||
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
|
||||
|
||||
cmd = ("/usr/sbin/service haproxy-{listener_id} {action}".format(
|
||||
listener_id=listener_id, action=action))
|
||||
cmd = ("/usr/sbin/service haproxy-{lb_id} {action}".format(
|
||||
lb_id=lb_id, action=action))
|
||||
|
||||
try:
|
||||
subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as e:
|
||||
if b'Job is already running' not in e.output:
|
||||
LOG.debug(
|
||||
"Failed to %(action)s haproxy-%(list)s service: %(err)s "
|
||||
"%(out)s", {'action': action, 'list': listener_id,
|
||||
"Failed to %(action)s haproxy-%(lb_id)s service: %(err)s "
|
||||
"%(out)s", {'action': action, 'lb_id': lb_id,
|
||||
'err': e, 'out': e.output})
|
||||
return webob.Response(json=dict(
|
||||
message="Error {0}ing haproxy".format(action),
|
||||
@ -271,40 +266,40 @@ class Listener(object):
|
||||
consts.AMP_ACTION_RELOAD]:
|
||||
return webob.Response(json=dict(
|
||||
message='OK',
|
||||
details='Listener {listener_id} {action}ed'.format(
|
||||
listener_id=listener_id, action=action)), status=202)
|
||||
details='Listener {lb_id} {action}ed'.format(
|
||||
lb_id=lb_id, action=action)), status=202)
|
||||
|
||||
details = (
|
||||
'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),
|
||||
status=202)
|
||||
|
||||
def delete_listener(self, listener_id):
|
||||
def delete_lb(self, lb_id):
|
||||
try:
|
||||
self._check_listener_exists(listener_id)
|
||||
self._check_lb_exists(lb_id)
|
||||
except exceptions.HTTPException:
|
||||
return webob.Response(json={'message': 'OK'})
|
||||
|
||||
# check if that haproxy is still running and if stop it
|
||||
if os.path.exists(util.pid_path(listener_id)) and os.path.exists(
|
||||
os.path.join('/proc', util.get_haproxy_pid(listener_id))):
|
||||
cmd = "/usr/sbin/service haproxy-{0} stop".format(listener_id)
|
||||
if os.path.exists(util.pid_path(lb_id)) and os.path.exists(
|
||||
os.path.join('/proc', util.get_haproxy_pid(lb_id))):
|
||||
cmd = "/usr/sbin/service haproxy-{0} stop".format(lb_id)
|
||||
try:
|
||||
subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as e:
|
||||
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(
|
||||
message="Error stopping haproxy",
|
||||
details=e.output), status=500)
|
||||
|
||||
# parse config and delete stats socket
|
||||
try:
|
||||
cfg = self._parse_haproxy_file(listener_id)
|
||||
os.remove(cfg['stats_socket'])
|
||||
stats_socket = util.parse_haproxy_file(lb_id)[0]
|
||||
os.remove(stats_socket)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@ -313,22 +308,22 @@ class Listener(object):
|
||||
# on this amphora and not write the file if VRRP is not in use
|
||||
if os.path.exists(util.keepalived_check_script_path()):
|
||||
self.vrrp_check_script_update(
|
||||
listener_id, action=consts.AMP_ACTION_STOP)
|
||||
lb_id, action=consts.AMP_ACTION_STOP)
|
||||
|
||||
# delete the ssl files
|
||||
try:
|
||||
shutil.rmtree(self._cert_dir(listener_id))
|
||||
shutil.rmtree(self._cert_dir(lb_id))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# disable the service
|
||||
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:
|
||||
util.run_systemctl_command(
|
||||
consts.DISABLE, "haproxy-{list}".format(
|
||||
list=listener_id))
|
||||
consts.DISABLE, "haproxy-{lb_id}".format(
|
||||
lb_id=lb_id))
|
||||
elif init_system == consts.INIT_SYSVINIT:
|
||||
init_disable_cmd = "insserv -r {file}".format(file=init_path)
|
||||
elif init_system != consts.INIT_UPSTART:
|
||||
@ -339,15 +334,15 @@ class Listener(object):
|
||||
subprocess.check_output(init_disable_cmd.split(),
|
||||
stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as e:
|
||||
LOG.error("Failed to disable haproxy-%(list)s service: "
|
||||
"%(err)s %(out)s", {'list': listener_id, 'err': e,
|
||||
LOG.error("Failed to disable haproxy-%(lb_id)s service: "
|
||||
"%(err)s %(out)s", {'lb_id': lb_id, 'err': e,
|
||||
'out': e.output})
|
||||
return webob.Response(json=dict(
|
||||
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
|
||||
shutil.rmtree(util.haproxy_dir(listener_id))
|
||||
shutil.rmtree(util.haproxy_dir(lb_id))
|
||||
if os.path.exists(init_path):
|
||||
os.remove(init_path)
|
||||
|
||||
@ -364,68 +359,29 @@ class Listener(object):
|
||||
"""
|
||||
listeners = list()
|
||||
|
||||
for listener in util.get_listeners():
|
||||
status = self._check_listener_status(listener)
|
||||
listener_type = ''
|
||||
for lb in util.get_loadbalancers():
|
||||
stats_socket, listeners_on_lb = util.parse_haproxy_file(lb)
|
||||
|
||||
if status == consts.ACTIVE:
|
||||
listener_type = self._parse_haproxy_file(listener)['mode']
|
||||
|
||||
listeners.append({
|
||||
'status': status,
|
||||
'uuid': listener,
|
||||
'type': listener_type,
|
||||
})
|
||||
for listener_id, listener in listeners_on_lb.items():
|
||||
listeners.append({
|
||||
'status': consts.ACTIVE,
|
||||
'uuid': listener_id,
|
||||
'type': listener['mode'],
|
||||
})
|
||||
|
||||
if other_listeners:
|
||||
listeners = listeners + other_listeners
|
||||
return webob.Response(json=listeners, content_type='application/json')
|
||||
|
||||
def get_listener_status(self, listener_id):
|
||||
"""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):
|
||||
def upload_certificate(self, lb_id, filename):
|
||||
self._check_ssl_filename_format(filename)
|
||||
|
||||
# create directory if not already there
|
||||
if not os.path.exists(self._cert_dir(listener_id)):
|
||||
os.makedirs(self._cert_dir(listener_id))
|
||||
if not os.path.exists(self._cert_dir(lb_id)):
|
||||
os.makedirs(self._cert_dir(lb_id))
|
||||
|
||||
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
|
||||
# mode 00600
|
||||
mode = stat.S_IRUSR | stat.S_IWUSR
|
||||
@ -439,10 +395,10 @@ class Listener(object):
|
||||
resp.headers['ETag'] = stream.get_md5()
|
||||
return resp
|
||||
|
||||
def get_certificate_md5(self, listener_id, filename):
|
||||
def get_certificate_md5(self, lb_id, 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)
|
||||
if not path_exists:
|
||||
return webob.Response(json=dict(
|
||||
@ -457,60 +413,34 @@ class Listener(object):
|
||||
resp.headers['ETag'] = md5
|
||||
return resp
|
||||
|
||||
def delete_certificate(self, listener_id, filename):
|
||||
def delete_certificate(self, lb_id, filename):
|
||||
self._check_ssl_filename_format(filename)
|
||||
if os.path.exists(self._cert_file_path(listener_id, filename)):
|
||||
os.remove(self._cert_file_path(listener_id, filename))
|
||||
if os.path.exists(self._cert_file_path(lb_id, filename)):
|
||||
os.remove(self._cert_file_path(lb_id, filename))
|
||||
return webob.Response(json=dict(message='OK'))
|
||||
|
||||
def _check_listener_status(self, listener_id):
|
||||
if os.path.exists(util.pid_path(listener_id)):
|
||||
def _get_listeners_on_lb(self, lb_id):
|
||||
if os.path.exists(util.pid_path(lb_id)):
|
||||
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
|
||||
with open(util.config_path(listener_id), 'r') as file:
|
||||
with open(util.config_path(lb_id), 'r') as file:
|
||||
cfg = file.read()
|
||||
m = re.search('frontend {}'.format(listener_id), cfg)
|
||||
if m:
|
||||
return consts.ACTIVE
|
||||
return consts.OFFLINE
|
||||
m = re.findall('^frontend (.*)$', cfg, re.MULTILINE)
|
||||
return m or []
|
||||
else: # pid file but no process...
|
||||
return consts.ERROR
|
||||
return []
|
||||
else:
|
||||
return consts.OFFLINE
|
||||
return []
|
||||
|
||||
def _parse_haproxy_file(self, listener_id):
|
||||
with open(util.config_path(listener_id), 'r') as file:
|
||||
cfg = file.read()
|
||||
|
||||
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)):
|
||||
def _check_lb_exists(self, lb_id):
|
||||
# check if we know about that lb
|
||||
if lb_id not in util.get_loadbalancers():
|
||||
raise exceptions.HTTPException(
|
||||
response=webob.Response(json=dict(
|
||||
message='Listener Not Found',
|
||||
details="No listener with UUID: {0}".format(
|
||||
listener_id)), status=404))
|
||||
message='Loadbalancer Not Found',
|
||||
details="No loadbalancer with UUID: {0}".format(
|
||||
lb_id)), status=404))
|
||||
|
||||
def _check_ssl_filename_format(self, filename):
|
||||
# check if the format is (xxx.)*xxx.pem
|
||||
@ -519,20 +449,19 @@ class Listener(object):
|
||||
response=webob.Response(json=dict(
|
||||
message='Filename has wrong format'), status=400))
|
||||
|
||||
def _cert_dir(self, listener_id):
|
||||
return os.path.join(util.CONF.haproxy_amphora.base_cert_dir,
|
||||
listener_id)
|
||||
def _cert_dir(self, lb_id):
|
||||
return os.path.join(util.CONF.haproxy_amphora.base_cert_dir, lb_id)
|
||||
|
||||
def _cert_file_path(self, listener_id, filename):
|
||||
return os.path.join(self._cert_dir(listener_id), filename)
|
||||
def _cert_file_path(self, lb_id, filename):
|
||||
return os.path.join(self._cert_dir(lb_id), filename)
|
||||
|
||||
def vrrp_check_script_update(self, listener_id, action):
|
||||
listener_ids = util.get_listeners()
|
||||
def vrrp_check_script_update(self, lb_id, action):
|
||||
lb_ids = util.get_loadbalancers()
|
||||
if action == consts.AMP_ACTION_STOP:
|
||||
listener_ids.remove(listener_id)
|
||||
lb_ids.remove(lb_id)
|
||||
args = []
|
||||
for lid in listener_ids:
|
||||
args.append(util.haproxy_sock_path(lid))
|
||||
for lbid in lb_ids:
|
||||
args.append(util.haproxy_sock_path(lbid))
|
||||
|
||||
if not os.path.exists(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:
|
||||
text_file.write(cmd)
|
||||
|
||||
def _check_haproxy_status(self, listener_id):
|
||||
if os.path.exists(util.pid_path(listener_id)):
|
||||
def _check_haproxy_status(self, lb_id):
|
||||
if os.path.exists(util.pid_path(lb_id)):
|
||||
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.OFFLINE
|
@ -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 certificate_update
|
||||
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 plug
|
||||
from octavia.amphorae.backends.agent.api_server import udp_listener_base
|
||||
@ -56,7 +56,7 @@ class Server(object):
|
||||
self.app = flask.Flask(__name__)
|
||||
self._osutils = osutils.BaseOS.get_os_util()
|
||||
self._keepalived = keepalived.Keepalived()
|
||||
self._listener = listener.Listener()
|
||||
self._loadbalancer = loadbalancer.Loadbalancer()
|
||||
self._udp_listener = (udp_listener_base.UdpListenerApiServerBase.
|
||||
get_server_driver())
|
||||
self._plug = plug.Plug(self._osutils)
|
||||
@ -64,8 +64,10 @@ class Server(object):
|
||||
|
||||
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 +
|
||||
'/listeners/<amphora_id>/<listener_id>/haproxy',
|
||||
'/loadbalancer/<amphora_id>/<lb_id>/haproxy',
|
||||
view_func=self.upload_haproxy_config,
|
||||
methods=['PUT'])
|
||||
self.app.add_url_rule(rule=PATH_PREFIX +
|
||||
@ -74,7 +76,7 @@ class Server(object):
|
||||
view_func=self.upload_udp_listener_config,
|
||||
methods=['PUT'])
|
||||
self.app.add_url_rule(rule=PATH_PREFIX +
|
||||
'/listeners/<listener_id>/haproxy',
|
||||
'/loadbalancer/<lb_id>/haproxy',
|
||||
view_func=self.get_haproxy_config,
|
||||
methods=['GET'])
|
||||
self.app.add_url_rule(rule=PATH_PREFIX +
|
||||
@ -82,11 +84,11 @@ class Server(object):
|
||||
view_func=self.get_udp_listener_config,
|
||||
methods=['GET'])
|
||||
self.app.add_url_rule(rule=PATH_PREFIX +
|
||||
'/listeners/<listener_id>/<action>',
|
||||
view_func=self.start_stop_listener,
|
||||
'/loadbalancer/<object_id>/<action>',
|
||||
view_func=self.start_stop_lb_object,
|
||||
methods=['PUT'])
|
||||
self.app.add_url_rule(rule=PATH_PREFIX + '/listeners/<listener_id>',
|
||||
view_func=self.delete_listener,
|
||||
self.app.add_url_rule(rule=PATH_PREFIX + '/listeners/<object_id>',
|
||||
view_func=self.delete_lb_object,
|
||||
methods=['DELETE'])
|
||||
self.app.add_url_rule(rule=PATH_PREFIX + '/config',
|
||||
view_func=self.upload_config,
|
||||
@ -100,18 +102,15 @@ class Server(object):
|
||||
self.app.add_url_rule(rule=PATH_PREFIX + '/listeners',
|
||||
view_func=self.get_all_listeners_status,
|
||||
methods=['GET'])
|
||||
self.app.add_url_rule(rule=PATH_PREFIX + '/listeners/<listener_id>',
|
||||
view_func=self.get_listener_status,
|
||||
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>',
|
||||
view_func=self.upload_certificate,
|
||||
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>',
|
||||
view_func=self.get_certificate_md5,
|
||||
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>',
|
||||
view_func=self.delete_certificate,
|
||||
methods=['DELETE'])
|
||||
@ -133,30 +132,30 @@ class Server(object):
|
||||
view_func=self.get_interface,
|
||||
methods=['GET'])
|
||||
|
||||
def upload_haproxy_config(self, amphora_id, listener_id):
|
||||
return self._listener.upload_haproxy_config(amphora_id, listener_id)
|
||||
def upload_haproxy_config(self, amphora_id, lb_id):
|
||||
return self._loadbalancer.upload_haproxy_config(amphora_id, lb_id)
|
||||
|
||||
def upload_udp_listener_config(self, amphora_id, listener_id):
|
||||
return self._udp_listener.upload_udp_listener_config(listener_id)
|
||||
|
||||
def get_haproxy_config(self, listener_id):
|
||||
return self._listener.get_haproxy_config(listener_id)
|
||||
def get_haproxy_config(self, lb_id):
|
||||
return self._loadbalancer.get_haproxy_config(lb_id)
|
||||
|
||||
def get_udp_listener_config(self, listener_id):
|
||||
return self._udp_listener.get_udp_listener_config(listener_id)
|
||||
|
||||
def start_stop_listener(self, listener_id, action):
|
||||
protocol = util.get_listener_protocol(listener_id)
|
||||
def start_stop_lb_object(self, object_id, action):
|
||||
protocol = util.get_protocol_for_lb_object(object_id)
|
||||
if protocol == 'UDP':
|
||||
return self._udp_listener.manage_udp_listener(
|
||||
listener_id, action)
|
||||
return self._listener.start_stop_listener(listener_id, action)
|
||||
listener_id=object_id, action=action)
|
||||
return self._loadbalancer.start_stop_lb(lb_id=object_id, action=action)
|
||||
|
||||
def delete_listener(self, listener_id):
|
||||
protocol = util.get_listener_protocol(listener_id)
|
||||
def delete_lb_object(self, object_id):
|
||||
protocol = util.get_protocol_for_lb_object(object_id)
|
||||
if protocol == 'UDP':
|
||||
return self._udp_listener.delete_udp_listener(listener_id)
|
||||
return self._listener.delete_listener(listener_id)
|
||||
return self._udp_listener.delete_udp_listener(object_id)
|
||||
return self._loadbalancer.delete_lb(object_id)
|
||||
|
||||
def get_details(self):
|
||||
return self._amphora_info.compile_amphora_details(
|
||||
@ -168,23 +167,17 @@ class Server(object):
|
||||
|
||||
def get_all_listeners_status(self):
|
||||
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)
|
||||
|
||||
def get_listener_status(self, listener_id):
|
||||
protocol = util.get_listener_protocol(listener_id)
|
||||
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, lb_id, filename):
|
||||
return self._loadbalancer.upload_certificate(lb_id, filename)
|
||||
|
||||
def upload_certificate(self, listener_id, filename):
|
||||
return self._listener.upload_certificate(listener_id, filename)
|
||||
def get_certificate_md5(self, lb_id, filename):
|
||||
return self._loadbalancer.get_certificate_md5(lb_id, filename)
|
||||
|
||||
def get_certificate_md5(self, listener_id, filename):
|
||||
return self._listener.get_certificate_md5(listener_id, filename)
|
||||
|
||||
def delete_certificate(self, listener_id, filename):
|
||||
return self._listener.delete_certificate(listener_id, filename)
|
||||
def delete_certificate(self, lb_id, filename):
|
||||
return self._loadbalancer.delete_certificate(lb_id, filename)
|
||||
|
||||
def plug_vip(self, vip):
|
||||
# Catch any issues with the subnet info json
|
||||
@ -251,3 +244,6 @@ class Server(object):
|
||||
details=str(e)), status=500)
|
||||
|
||||
return webob.Response(json={'message': 'OK'}, status=202)
|
||||
|
||||
def version_discovery(self):
|
||||
return webob.Response(json={'api_version': api_server.VERSION})
|
||||
|
@ -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
|
||||
def delete_udp_listener(self, listener_id):
|
||||
"""Delete a UDP Listener from a amphora
|
||||
|
@ -28,21 +28,31 @@ from octavia.common import constants as consts
|
||||
CONF = cfg.CONF
|
||||
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):
|
||||
pass
|
||||
|
||||
|
||||
def init_path(listener_id, init_system):
|
||||
def init_path(lb_id, init_system):
|
||||
if init_system == consts.INIT_SYSTEMD:
|
||||
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:
|
||||
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:
|
||||
return os.path.join(consts.SYSVINIT_DIR,
|
||||
'haproxy-{0}'.format(listener_id))
|
||||
'haproxy-{0}'.format(lb_id))
|
||||
raise UnknownInitError()
|
||||
|
||||
|
||||
@ -91,20 +101,20 @@ def keepalived_lvs_cfg_path(listener_id):
|
||||
str(listener_id))
|
||||
|
||||
|
||||
def haproxy_dir(listener_id):
|
||||
return os.path.join(CONF.haproxy_amphora.base_path, listener_id)
|
||||
def haproxy_dir(lb_id):
|
||||
return os.path.join(CONF.haproxy_amphora.base_path, lb_id)
|
||||
|
||||
|
||||
def pid_path(listener_id):
|
||||
return os.path.join(haproxy_dir(listener_id), listener_id + '.pid')
|
||||
def pid_path(lb_id):
|
||||
return os.path.join(haproxy_dir(lb_id), lb_id + '.pid')
|
||||
|
||||
|
||||
def config_path(listener_id):
|
||||
return os.path.join(haproxy_dir(listener_id), 'haproxy.cfg')
|
||||
def config_path(lb_id):
|
||||
return os.path.join(haproxy_dir(lb_id), 'haproxy.cfg')
|
||||
|
||||
|
||||
def get_haproxy_pid(listener_id):
|
||||
with open(pid_path(listener_id), 'r') as f:
|
||||
def get_haproxy_pid(lb_id):
|
||||
with open(pid_path(lb_id), 'r') as f:
|
||||
return f.readline().rstrip()
|
||||
|
||||
|
||||
@ -114,8 +124,8 @@ def get_keepalivedlvs_pid(listener_id):
|
||||
return f.readline().rstrip()
|
||||
|
||||
|
||||
def haproxy_sock_path(listener_id):
|
||||
return os.path.join(CONF.haproxy_amphora.base_path, listener_id + '.sock')
|
||||
def haproxy_sock_path(lb_id):
|
||||
return os.path.join(CONF.haproxy_amphora.base_path, lb_id + '.sock')
|
||||
|
||||
|
||||
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', ...]
|
||||
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):
|
||||
return [f for f in os.listdir(CONF.haproxy_amphora.base_path)
|
||||
if os.path.exists(config_path(f))]
|
||||
return []
|
||||
|
||||
|
||||
def is_listener_running(listener_id):
|
||||
return os.path.exists(pid_path(listener_id)) and os.path.exists(
|
||||
os.path.join('/proc', get_haproxy_pid(listener_id)))
|
||||
def is_lb_running(lb_id):
|
||||
return os.path.exists(pid_path(lb_id)) and os.path.exists(
|
||||
os.path.join('/proc', get_haproxy_pid(lb_id)))
|
||||
|
||||
|
||||
def get_udp_listeners():
|
||||
@ -251,7 +273,7 @@ def run_systemctl_command(command, service):
|
||||
'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.
|
||||
|
||||
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.
|
||||
: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
|
||||
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 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
|
||||
|
@ -43,18 +43,19 @@ SEQ = 0
|
||||
# incompatible changes.
|
||||
#
|
||||
# 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):
|
||||
stat_sock_files = {}
|
||||
if hadir is None:
|
||||
hadir = CONF.haproxy_amphora.base_path
|
||||
listener_ids = util.get_listeners()
|
||||
for listener_id in listener_ids:
|
||||
sock_file = listener_id + ".sock"
|
||||
stat_sock_files[listener_id] = os.path.join(hadir, sock_file)
|
||||
lb_ids = util.get_loadbalancers()
|
||||
for lb_id in lb_ids:
|
||||
sock_file = lb_id + ".sock"
|
||||
stat_sock_files[lb_id] = os.path.join(hadir, sock_file)
|
||||
return stat_sock_files
|
||||
|
||||
|
||||
@ -115,39 +116,55 @@ def get_stats(stat_sock_file):
|
||||
|
||||
|
||||
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
|
||||
msg = {'id': CONF.amphora_agent.amphora_id,
|
||||
'seq': SEQ, "listeners": {},
|
||||
'seq': SEQ, 'listeners': {}, 'pools': {},
|
||||
'ver': MSG_VER}
|
||||
SEQ += 1
|
||||
stat_sock_files = list_sock_stat_files()
|
||||
for listener_id, stat_sock_file in stat_sock_files.items():
|
||||
listener_dict = {'pools': {},
|
||||
'status': 'DOWN',
|
||||
'stats': {
|
||||
'tx': 0,
|
||||
'rx': 0,
|
||||
'conns': 0,
|
||||
'totconns': 0,
|
||||
'ereq': 0}}
|
||||
msg['listeners'][listener_id] = listener_dict
|
||||
if util.is_listener_running(listener_id):
|
||||
# TODO(rm_work) There should only be one of these in the new config system
|
||||
for lb_id, stat_sock_file in stat_sock_files.items():
|
||||
if util.is_lb_running(lb_id):
|
||||
(stats, pool_status) = get_stats(stat_sock_file)
|
||||
listener_dict = msg['listeners'][listener_id]
|
||||
for row in stats:
|
||||
if row['svname'] == 'FRONTEND':
|
||||
listener_dict['stats']['tx'] = int(row['bout'])
|
||||
listener_dict['stats']['rx'] = int(row['bin'])
|
||||
listener_dict['stats']['conns'] = int(row['scur'])
|
||||
listener_dict['stats']['totconns'] = int(row['stot'])
|
||||
listener_dict['stats']['ereq'] = int(row['ereq'])
|
||||
listener_dict['status'] = row['status']
|
||||
for oid, pool in pool_status.items():
|
||||
if oid != listener_id:
|
||||
pool_id = oid
|
||||
pools = listener_dict['pools']
|
||||
pools[pool_id] = {"status": pool['status'],
|
||||
"members": pool['members']}
|
||||
listener_id = row['pxname']
|
||||
msg['listeners'][listener_id] = {
|
||||
'status': row['status'],
|
||||
'stats': {'tx': int(row['bout']),
|
||||
'rx': int(row['bin']),
|
||||
'conns': int(row['scur']),
|
||||
'totconns': int(row['stot']),
|
||||
'ereq': int(row['ereq'])}}
|
||||
for pool_id, pool in pool_status.items():
|
||||
msg['pools'][pool_id] = {"status": pool['status'],
|
||||
"members": pool['members']}
|
||||
|
||||
# UDP listener part
|
||||
udp_listener_ids = util.get_udp_listeners()
|
||||
|
@ -124,7 +124,9 @@ class HAProxyQuery(object):
|
||||
final_results[line['pxname']] = dict(members={})
|
||||
|
||||
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']
|
||||
else:
|
||||
final_results[line['pxname']]['members'][line['svname']] = (
|
||||
|
@ -22,17 +22,19 @@ import six
|
||||
class AmphoraLoadBalancerDriver(object):
|
||||
|
||||
@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.
|
||||
|
||||
:param listeners: List of listeners to update.
|
||||
:type listener: list
|
||||
:param amphora_id: The ID of the amphora to update
|
||||
:type amphora_id: string
|
||||
:param loadbalancer: List of listeners to update.
|
||||
:type loadbalancer: list(octavia.db.models.Listener)
|
||||
:param amphora: The index of the specific amphora to update
|
||||
:type amphora: octavia.db.models.Amphora
|
||||
:param timeout_dict: Dictionary of timeout values for calls to the
|
||||
amphora. May contain: req_conn_timeout,
|
||||
req_read_timeout, conn_max_retries,
|
||||
conn_retry_interval
|
||||
:type timeout_dict: dict
|
||||
:returns: None
|
||||
|
||||
Builds a new configuration, pushes it to the amphora, and reloads
|
||||
@ -40,14 +42,12 @@ class AmphoraLoadBalancerDriver(object):
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def update(self, listener, vip):
|
||||
def update(self, loadbalancer):
|
||||
"""Update the amphora with a new configuration.
|
||||
|
||||
: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 loadbalancer: loadbalancer object, need to use its
|
||||
vip.ip_address property
|
||||
:type loadbalancer: octavia.db.models.LoadBalancer
|
||||
:returns: None
|
||||
|
||||
At this moment, we just build the basic structure for testing, will
|
||||
@ -55,31 +55,13 @@ class AmphoraLoadBalancerDriver(object):
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def stop(self, listener, vip):
|
||||
"""Stop the listener on the vip.
|
||||
def start(self, loadbalancer, amphora):
|
||||
"""Start the listeners on the amphora.
|
||||
|
||||
: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
|
||||
: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 loadbalancer: loadbalancer object to start listeners
|
||||
:type loadbalancer: octavia.db.models.LoadBalancer
|
||||
: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)
|
||||
|
||||
At this moment, we just build the basic structure for testing, will
|
||||
@ -87,14 +69,12 @@ class AmphoraLoadBalancerDriver(object):
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete(self, listener, vip):
|
||||
def delete(self, listener):
|
||||
"""Delete 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
|
||||
:type listener: octavia.db.models.Listener
|
||||
:returns: return a value list (listener, vip, status flag--delete)
|
||||
|
||||
At this moment, we just build the basic structure for testing, will
|
||||
@ -106,7 +86,7 @@ class AmphoraLoadBalancerDriver(object):
|
||||
"""Returns information about the amphora.
|
||||
|
||||
: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')
|
||||
|
||||
At this moment, we just build the basic structure for testing, will
|
||||
@ -122,7 +102,7 @@ class AmphoraLoadBalancerDriver(object):
|
||||
"""Return ceilometer ready diagnostic data.
|
||||
|
||||
: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
|
||||
t_diagnostics')
|
||||
|
||||
@ -138,7 +118,7 @@ class AmphoraLoadBalancerDriver(object):
|
||||
"""Finalize the amphora before any listeners are configured.
|
||||
|
||||
:param amphora: amphora object, need to use its id property
|
||||
:type amphora: object
|
||||
:type amphora: octavia.db.models.Amphora
|
||||
:returns: None
|
||||
|
||||
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):
|
||||
"""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
|
||||
and plugged in the network driver.
|
||||
:type load_balancer: octavia.common.data_models.LoadBalancer
|
||||
@ -168,7 +150,7 @@ class AmphoraLoadBalancerDriver(object):
|
||||
"""Called after amphora added to network
|
||||
|
||||
: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
|
||||
:type port: octavia.network.data_models.Port
|
||||
|
||||
@ -182,7 +164,7 @@ class AmphoraLoadBalancerDriver(object):
|
||||
"""Start health checks.
|
||||
|
||||
:param health_mixin: health mixin object
|
||||
:type amphora: object
|
||||
:type health_mixin: HealthMixin
|
||||
|
||||
Starts listener process and calls HealthMixin to update
|
||||
databases information.
|
||||
@ -199,7 +181,7 @@ class AmphoraLoadBalancerDriver(object):
|
||||
"""Upload cert info to the amphora.
|
||||
|
||||
: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
|
||||
:type pem_file: file object
|
||||
|
||||
@ -210,7 +192,7 @@ class AmphoraLoadBalancerDriver(object):
|
||||
"""Upload and update the amphora agent configuration.
|
||||
|
||||
: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.
|
||||
:type agent_config: string
|
||||
"""
|
||||
|
@ -32,7 +32,8 @@ from octavia.amphorae.drivers.haproxy import exceptions as exc
|
||||
from octavia.amphorae.drivers.keepalived import vrrp_rest_driver
|
||||
from octavia.common.config import cfg
|
||||
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.tls_utils import cert_parser
|
||||
from octavia.common import utils
|
||||
@ -51,14 +52,23 @@ class HaproxyAmphoraLoadBalancerDriver(
|
||||
|
||||
def __init__(self):
|
||||
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(
|
||||
namespace='octavia.cert_manager',
|
||||
name=CONF.certificates.cert_manager,
|
||||
invoke_on_load=True,
|
||||
).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_crt_dir=CONF.haproxy_amphora.base_cert_dir,
|
||||
haproxy_template=CONF.haproxy_amphora.haproxy_template,
|
||||
@ -72,21 +82,39 @@ class HaproxyAmphoraLoadBalancerDriver(
|
||||
|
||||
: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,
|
||||
amphorae, timeout_dict=None):
|
||||
This will query the amphora for version discovery and populate
|
||||
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.
|
||||
|
||||
:param listeners: List of listeners to update.
|
||||
:type listener: list
|
||||
:param amphora_index: The index of the amphora to update
|
||||
:type amphora_index: integer
|
||||
:param amphorae: List of amphorae
|
||||
:type amphorae: list
|
||||
:param loadbalancer: The load balancer to update
|
||||
:type loadbalancer: object
|
||||
:param amphora: The amphora to update
|
||||
:type amphora: object
|
||||
:param timeout_dict: Dictionary of timeout values for calls to the
|
||||
amphora. May contain: req_conn_timeout,
|
||||
req_read_timeout, conn_max_retries,
|
||||
@ -96,46 +124,82 @@ class HaproxyAmphoraLoadBalancerDriver(
|
||||
Updates the configuration of the listeners on a single amphora.
|
||||
"""
|
||||
# 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.')
|
||||
return
|
||||
amp = amphorae[amphora_index]
|
||||
if amp is None or amp.status == consts.DELETED:
|
||||
if amphora is None or amphora.status == consts.DELETED:
|
||||
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
|
||||
for listener in listeners:
|
||||
has_tcp = False
|
||||
for listener in loadbalancer.listeners:
|
||||
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':
|
||||
# Generate Keepalived LVS configuration from listener object
|
||||
config = self.udp_jinja.build_config(listener=listener)
|
||||
self.client.upload_udp_config(amp, listener.id, config,
|
||||
timeout_dict=timeout_dict)
|
||||
self.client.reload_listener(amp, listener.id,
|
||||
timeout_dict=timeout_dict)
|
||||
self.clients[amphora.api_version].upload_udp_config(
|
||||
amphora, listener.id, config, timeout_dict=timeout_dict)
|
||||
self.clients[amphora.api_version].reload_listener(
|
||||
amphora, listener.id, timeout_dict=timeout_dict)
|
||||
else:
|
||||
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)
|
||||
has_tcp = True
|
||||
if split_config:
|
||||
obj_id = listener.id
|
||||
else:
|
||||
obj_id = loadbalancer.id
|
||||
|
||||
# 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,
|
||||
timeout_dict=timeout_dict)
|
||||
self.client.reload_listener(amp, listener.id,
|
||||
timeout_dict=timeout_dict)
|
||||
certs = self._process_tls_certificates(
|
||||
listener, amphora, obj_id)
|
||||
client_ca_filename = self._process_secret(
|
||||
listener, listener.client_ca_tls_certificate_id,
|
||||
amphora, obj_id)
|
||||
crl_filename = self._process_secret(
|
||||
listener, listener.client_crl_container_id,
|
||||
amphora, obj_id)
|
||||
pool_tls_certs = self._process_listener_pool_certs(
|
||||
listener, amphora, obj_id)
|
||||
|
||||
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):
|
||||
LOG.debug("Amphora %s keepalivedlvs, updating "
|
||||
@ -146,69 +210,132 @@ class HaproxyAmphoraLoadBalancerDriver(
|
||||
for amp in listener.load_balancer.amphorae:
|
||||
if amp.status != consts.DELETED:
|
||||
# Generate Keepalived LVS configuration from listener object
|
||||
self._populate_amphora_api_version(amp)
|
||||
config = self.udp_jinja.build_config(listener=listener)
|
||||
self.client.upload_udp_config(amp, listener.id, config)
|
||||
self.client.reload_listener(amp, listener.id)
|
||||
self.clients[amp.api_version].upload_udp_config(
|
||||
amp, listener.id, config)
|
||||
self.clients[amp.api_version].reload_listener(
|
||||
amp, listener.id)
|
||||
|
||||
def update(self, listener, vip):
|
||||
if listener.protocol == 'UDP':
|
||||
self._udp_update(listener, vip)
|
||||
else:
|
||||
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 update(self, loadbalancer):
|
||||
for amphora in loadbalancer.amphorae:
|
||||
if amphora.status != consts.DELETED:
|
||||
self.update_amphora_listeners(loadbalancer, amphora)
|
||||
|
||||
def upload_cert_amp(self, amp, pem):
|
||||
LOG.debug("Amphora %s updating cert in REST driver "
|
||||
"with amphora id %s,",
|
||||
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:
|
||||
for amp in listener.load_balancer.amphorae:
|
||||
if amp.status != consts.DELETED:
|
||||
func(amp, listener.id, *args)
|
||||
amphorae = loadbalancer.amphorae
|
||||
else:
|
||||
if amphora.status != consts.DELETED:
|
||||
func(amphora, listener.id, *args)
|
||||
amphorae = [amphora]
|
||||
|
||||
def stop(self, listener, vip):
|
||||
self._apply(self.client.stop_listener, listener)
|
||||
for amp in 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)
|
||||
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):
|
||||
self._apply(self.client.start_listener, listener, amphora)
|
||||
def start(self, loadbalancer, amphora=None):
|
||||
self._apply('start_listener', loadbalancer, amphora)
|
||||
|
||||
def delete(self, listener, vip):
|
||||
self._apply(self.client.delete_listener, listener)
|
||||
def delete(self, 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):
|
||||
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):
|
||||
pass
|
||||
@ -218,6 +345,7 @@ class HaproxyAmphoraLoadBalancerDriver(
|
||||
|
||||
def post_vip_plug(self, amphora, load_balancer, amphorae_network_config):
|
||||
if amphora.status != consts.DELETED:
|
||||
self._populate_amphora_api_version(amphora)
|
||||
subnet = amphorae_network_config.get(amphora.id).vip_subnet
|
||||
# NOTE(blogan): using the vrrp port here because that
|
||||
# is what the allowed address pairs network driver sets
|
||||
@ -242,9 +370,8 @@ class HaproxyAmphoraLoadBalancerDriver(
|
||||
'mtu': port.network.mtu,
|
||||
'host_routes': host_routes}
|
||||
try:
|
||||
self.client.plug_vip(amphora,
|
||||
load_balancer.vip.ip_address,
|
||||
net_info)
|
||||
self.clients[amphora.api_version].plug_vip(
|
||||
amphora, load_balancer.vip.ip_address, net_info)
|
||||
except exc.Conflict:
|
||||
LOG.warning('VIP with MAC %(mac)s already exists on amphora, '
|
||||
'skipping post_vip_plug',
|
||||
@ -264,13 +391,14 @@ class HaproxyAmphoraLoadBalancerDriver(
|
||||
'fixed_ips': fixed_ips,
|
||||
'mtu': port.network.mtu}
|
||||
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:
|
||||
LOG.warning('Network with MAC %(mac)s already exists on amphora, '
|
||||
'skipping post_network_plug',
|
||||
{'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.
|
||||
|
||||
Converts and uploads PEM data to the Amphora API
|
||||
@ -290,15 +418,15 @@ class HaproxyAmphoraLoadBalancerDriver(
|
||||
sni_certs = data['sni_certs']
|
||||
certs.extend(sni_certs)
|
||||
|
||||
for cert in certs:
|
||||
pem = cert_parser.build_pem(cert)
|
||||
md5 = hashlib.md5(pem).hexdigest() # nosec
|
||||
name = '{id}.pem'.format(id=cert.id)
|
||||
self._apply(self._upload_cert, listener, None, pem, md5, name)
|
||||
|
||||
if amphora and obj_id:
|
||||
for cert in certs:
|
||||
pem = cert_parser.build_pem(cert)
|
||||
md5 = hashlib.md5(pem).hexdigest() # nosec
|
||||
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}
|
||||
|
||||
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.
|
||||
|
||||
:returns: The filename of the secret in the amp.
|
||||
@ -314,10 +442,14 @@ class HaproxyAmphoraLoadBalancerDriver(
|
||||
md5 = hashlib.md5(secret).hexdigest() # nosec
|
||||
id = hashlib.sha1(secret).hexdigest() # nosec
|
||||
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
|
||||
|
||||
def _process_listener_pool_certs(self, listener):
|
||||
def _process_listener_pool_certs(self, listener, amphora=None,
|
||||
obj_id=None):
|
||||
# {'POOL-ID': {
|
||||
# 'client_cert': client_full_filename,
|
||||
# 'ca_cert': ca_cert_full_filename,
|
||||
@ -325,19 +457,20 @@ class HaproxyAmphoraLoadBalancerDriver(
|
||||
pool_certs_dict = dict()
|
||||
for pool in listener.pools:
|
||||
if pool.id not in pool_certs_dict:
|
||||
pool_certs_dict[pool.id] = self._process_pool_certs(listener,
|
||||
pool)
|
||||
pool_certs_dict[pool.id] = self._process_pool_certs(
|
||||
listener, pool, amphora, obj_id)
|
||||
for l7policy in listener.l7policies:
|
||||
if (l7policy.redirect_pool and
|
||||
l7policy.redirect_pool.id not in pool_certs_dict):
|
||||
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
|
||||
|
||||
def _process_pool_certs(self, listener, pool):
|
||||
def _process_pool_certs(self, listener, pool, amphora=None, obj_id=None):
|
||||
pool_cert_dict = dict()
|
||||
|
||||
# Handle the cleint cert(s) and key
|
||||
# Handle the client cert(s) and key
|
||||
if pool.tls_certificate_id:
|
||||
data = cert_parser.load_certificates_data(self.cert_manager, pool)
|
||||
pem = cert_parser.build_pem(data)
|
||||
@ -347,15 +480,18 @@ class HaproxyAmphoraLoadBalancerDriver(
|
||||
pass
|
||||
md5 = hashlib.md5(pem).hexdigest() # nosec
|
||||
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(
|
||||
CONF.haproxy_amphora.base_cert_dir, listener.id, name)
|
||||
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(
|
||||
CONF.haproxy_amphora.base_cert_dir, listener.id, name)
|
||||
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(
|
||||
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):
|
||||
try:
|
||||
if self.client.get_cert_md5sum(
|
||||
if self.clients[amp.api_version].get_cert_md5sum(
|
||||
amp, listener_id, name, ignore=(404,)) == md5:
|
||||
return
|
||||
except exc.NotFound:
|
||||
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,
|
||||
timeout_dict=None):
|
||||
@ -389,8 +526,9 @@ class HaproxyAmphoraLoadBalancerDriver(
|
||||
new values.
|
||||
"""
|
||||
try:
|
||||
self.client.update_agent_config(amphora, agent_config,
|
||||
timeout_dict=timeout_dict)
|
||||
self._populate_amphora_api_version(amphora)
|
||||
self.clients[amphora.api_version].update_agent_config(
|
||||
amphora, agent_config, timeout_dict=timeout_dict)
|
||||
except exc.NotFound:
|
||||
LOG.debug('Amphora {} does not support the update_agent_config '
|
||||
'API.'.format(amphora.id))
|
||||
@ -411,10 +549,9 @@ class CustomHostNameCheckingAdapter(requests.adapters.HTTPAdapter):
|
||||
self).init_poolmanager(*pool_args, **pool_kwargs)
|
||||
|
||||
|
||||
class AmphoraAPIClient(object):
|
||||
class AmphoraAPIClientBase(object):
|
||||
def __init__(self):
|
||||
super(AmphoraAPIClient, self).__init__()
|
||||
self.secure = False
|
||||
super(AmphoraAPIClientBase, self).__init__()
|
||||
|
||||
self.get = functools.partial(self.request, 'get')
|
||||
self.post = functools.partial(self.request, 'post')
|
||||
@ -422,38 +559,29 @@ class AmphoraAPIClient(object):
|
||||
self.delete = functools.partial(self.request, 'delete')
|
||||
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.cert = CONF.haproxy_amphora.client_cert
|
||||
self.ssl_adapter = CustomHostNameCheckingAdapter()
|
||||
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):
|
||||
ip = '[{ip}%{interface}]'.format(
|
||||
ip=ip,
|
||||
interface=CONF.haproxy_amphora.lb_network_interface)
|
||||
elif utils.is_ipv6(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,
|
||||
port=CONF.haproxy_amphora.bind_port,
|
||||
version=API_VERSION)
|
||||
port=CONF.haproxy_amphora.bind_port)
|
||||
|
||||
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
|
||||
if timeout_dict is None:
|
||||
timeout_dict = {}
|
||||
@ -468,7 +596,7 @@ class AmphoraAPIClient(object):
|
||||
|
||||
LOG.debug("request url %s", path)
|
||||
_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)
|
||||
reqargs = {
|
||||
'verify': CONF.haproxy_amphora.server_ca,
|
||||
@ -481,7 +609,7 @@ class AmphoraAPIClient(object):
|
||||
self.ssl_adapter.uuid = amp.id
|
||||
exception = None
|
||||
# Keep retrying
|
||||
for a in six.moves.xrange(conn_max_retries):
|
||||
for dummy in six.moves.xrange(conn_max_retries):
|
||||
try:
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings(
|
||||
@ -497,6 +625,8 @@ class AmphoraAPIClient(object):
|
||||
# amphora is not yet up, in which case retry.
|
||||
# Otherwise return the response quickly.
|
||||
if r.status_code == 404:
|
||||
if not retry_404:
|
||||
raise exc.NotFound()
|
||||
LOG.debug('Got a 404 (content-type: %(content_type)s) -- '
|
||||
'connection data: %(content)s',
|
||||
{'content_type': content_type,
|
||||
@ -524,6 +654,32 @@ class AmphoraAPIClient(object):
|
||||
'exception': exception})
|
||||
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):
|
||||
r = self.put(
|
||||
amp,
|
||||
@ -532,14 +688,6 @@ class AmphoraAPIClient(object):
|
||||
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, listener_id, timeout_dict=None):
|
||||
r = self.put(amp, 'listeners/{listener_id}/{action}'.format(
|
||||
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):
|
||||
r = self.put(amp, 'config', timeout_dict, data=agent_config)
|
||||
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)
|
||||
|
@ -45,13 +45,14 @@ class KeepalivedAmphoraDriverMixin(driver_base.VRRPDriverMixin):
|
||||
lambda amp: amp.status == constants.AMPHORA_ALLOCATED,
|
||||
loadbalancer.amphorae):
|
||||
|
||||
self._populate_amphora_api_version(amp)
|
||||
# Get the VIP subnet prefix for the amphora
|
||||
vip_cidr = amphorae_network_config[amp.id].vip_subnet.cidr
|
||||
|
||||
# Generate Keepalived configuration from loadbalancer object
|
||||
config = templater.build_keepalived_config(
|
||||
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):
|
||||
"""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,
|
||||
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):
|
||||
"""Start the VRRP services of all amphorae of the loadbalancer
|
||||
@ -80,7 +82,8 @@ class KeepalivedAmphoraDriverMixin(driver_base.VRRPDriverMixin):
|
||||
loadbalancer.amphorae):
|
||||
|
||||
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):
|
||||
"""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,
|
||||
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):
|
||||
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']
|
||||
|
@ -37,44 +37,41 @@ class NoopManager(object):
|
||||
super(NoopManager, self).__init__()
|
||||
self.amphoraconfig = {}
|
||||
|
||||
def update_amphora_listeners(self, listeners, amphora_index,
|
||||
amphorae, timeout_dict):
|
||||
amphora_id = amphorae[amphora_index].id
|
||||
for listener in listeners:
|
||||
def update_amphora_listeners(self, loadbalancer, amphora, timeout_dict):
|
||||
amphora_id = amphora.id
|
||||
for listener in loadbalancer.listeners:
|
||||
LOG.debug("Amphora noop driver update_amphora_listeners, "
|
||||
"listener %s, amphora %s, timeouts %s", listener.id,
|
||||
amphora_id, timeout_dict)
|
||||
self.amphoraconfig[(listener.id, amphora_id)] = (
|
||||
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",
|
||||
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__,
|
||||
listener.protocol_port, vip.ip_address)
|
||||
self.amphoraconfig[(listener.protocol_port,
|
||||
vip.ip_address)] = (listener, vip, 'stop')
|
||||
tuple(l.protocol_port for l in loadbalancer.listeners),
|
||||
loadbalancer.vip.ip_address)
|
||||
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):
|
||||
LOG.debug("Amphora %s no-op, start listener %s, vip %s, amp %s",
|
||||
self.__class__.__name__,
|
||||
listener.protocol_port, vip.ip_address, amphora)
|
||||
self.amphoraconfig[(listener.protocol_port,
|
||||
vip.ip_address, amphora)] = (listener, vip,
|
||||
amphora, 'start')
|
||||
def start(self, loadbalancer, amphora=None):
|
||||
LOG.debug("Amphora %s no-op, start listeners, lb %s, amp %s",
|
||||
self.__class__.__name__, loadbalancer.id, amphora)
|
||||
self.amphoraconfig[
|
||||
(loadbalancer.id, amphora.id)] = (loadbalancer, amphora,
|
||||
'start')
|
||||
|
||||
def delete(self, listener, vip):
|
||||
def delete(self, listener):
|
||||
LOG.debug("Amphora %s no-op, delete listener %s, vip %s",
|
||||
self.__class__.__name__,
|
||||
listener.protocol_port, vip.ip_address)
|
||||
listener.protocol_port,
|
||||
listener.load_balancer.vip.ip_address)
|
||||
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):
|
||||
LOG.debug("Amphora %s no-op, info amphora %s",
|
||||
@ -124,27 +121,22 @@ class NoopAmphoraLoadBalancerDriver(
|
||||
super(NoopAmphoraLoadBalancerDriver, self).__init__()
|
||||
self.driver = NoopManager()
|
||||
|
||||
def update_amphora_listeners(self, listeners, amphora_index,
|
||||
amphorae, timeout_dict):
|
||||
def update_amphora_listeners(self, loadbalancer, amphora, timeout_dict):
|
||||
|
||||
self.driver.update_amphora_listeners(listeners, amphora_index,
|
||||
amphorae, timeout_dict)
|
||||
self.driver.update_amphora_listeners(loadbalancer, amphora,
|
||||
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)
|
||||
|
||||
def delete(self, listener, vip):
|
||||
|
||||
self.driver.delete(listener, vip)
|
||||
self.driver.delete(listener)
|
||||
|
||||
def get_info(self, amphora):
|
||||
|
||||
|
@ -122,3 +122,6 @@ def main():
|
||||
process.join()
|
||||
except KeyboardInterrupt:
|
||||
process_cleanup()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
@ -39,3 +39,6 @@ def main():
|
||||
workers=CONF.controller_worker.workers, args=(CONF,))
|
||||
oslo_config_glue.setup(sm, CONF, reload_method="mutate")
|
||||
sm.run()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
499
octavia/common/jinja/haproxy/combined_listeners/jinja_cfg.py
Normal file
499
octavia/common/jinja/haproxy/combined_listeners/jinja_cfg.py
Normal 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)
|
@ -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 %}
|
@ -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 %}
|
@ -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 %}
|
@ -110,7 +110,7 @@ class UpdateHealthDb(update_base.HealthUpdateBase):
|
||||
:type map: string
|
||||
:returns: null
|
||||
|
||||
The input health data structure is shown as below::
|
||||
The input v1 health data structure is shown as below::
|
||||
|
||||
health = {
|
||||
"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()
|
||||
|
||||
@ -134,10 +161,15 @@ class UpdateHealthDb(update_base.HealthUpdateBase):
|
||||
ignore_listener_count = False
|
||||
|
||||
if db_lb:
|
||||
expected_listener_count = len(db_lb.get('listeners', {}))
|
||||
expected_listener_count = 0
|
||||
if 'PENDING' in db_lb['provisioning_status']:
|
||||
ignore_listener_count = True
|
||||
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
|
||||
# UDP special for backward compatibility.
|
||||
if 'ver' not in health:
|
||||
@ -231,6 +263,8 @@ class UpdateHealthDb(update_base.HealthUpdateBase):
|
||||
else:
|
||||
lb_status = constants.ONLINE
|
||||
|
||||
health_msg_version = health.get('ver', 0)
|
||||
|
||||
for listener_id in db_lb.get('listeners', {}):
|
||||
db_op_status = db_lb['listeners'][listener_id]['operating_status']
|
||||
listener_status = None
|
||||
@ -267,11 +301,36 @@ class UpdateHealthDb(update_base.HealthUpdateBase):
|
||||
if not listener:
|
||||
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', {}):
|
||||
# If we saw this pool already on another listener
|
||||
# skip it.
|
||||
# If we saw this pool already, skip it.
|
||||
if db_pool_id in processed_pools:
|
||||
continue
|
||||
db_pool_dict = db_lb['pools'][db_pool_id]
|
||||
@ -416,7 +475,7 @@ class UpdateStatsDb(update_base.StatsUpdateBase, stats.StatsMixin):
|
||||
:type map: string
|
||||
:returns: null
|
||||
|
||||
Example::
|
||||
Example V1 message::
|
||||
|
||||
health = {
|
||||
"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()
|
||||
|
||||
|
@ -235,13 +235,14 @@ class ControllerWorker(base_taskflow.BaseTaskFlowEngine):
|
||||
raise db_exceptions.NoResultFound
|
||||
|
||||
load_balancer = listener.load_balancer
|
||||
listeners = load_balancer.listeners
|
||||
|
||||
create_listener_tf = self._taskflow_load(self._listener_flows.
|
||||
get_create_listener_flow(),
|
||||
store={constants.LOADBALANCER:
|
||||
load_balancer,
|
||||
constants.LISTENERS:
|
||||
[listener]})
|
||||
listeners})
|
||||
with tf_logging.DynamicLoggingListener(create_listener_tf,
|
||||
log=LOG):
|
||||
create_listener_tf.run()
|
||||
|
@ -470,7 +470,7 @@ class AmphoraFlows(object):
|
||||
update_amps_subflow.add(
|
||||
amphora_driver_tasks.AmpListenersUpdate(
|
||||
name=constants.AMP_LISTENER_UPDATE + '-' + str(amp_index),
|
||||
requires=(constants.LISTENERS, constants.AMPHORAE),
|
||||
requires=(constants.LOADBALANCER, constants.AMPHORAE),
|
||||
inject={constants.AMPHORA_INDEX: amp_index,
|
||||
constants.TIMEOUT_DICT: timeout_dict}))
|
||||
amp_index += 1
|
||||
@ -514,8 +514,7 @@ class AmphoraFlows(object):
|
||||
requires=constants.AMPHORA))
|
||||
|
||||
failover_amphora_flow.add(amphora_driver_tasks.ListenersStart(
|
||||
requires=(constants.LOADBALANCER, constants.LISTENERS,
|
||||
constants.AMPHORA)))
|
||||
requires=(constants.LOADBALANCER, constants.AMPHORA)))
|
||||
failover_amphora_flow.add(
|
||||
database_tasks.DisableAmphoraHealthMonitoring(
|
||||
rebind={constants.AMPHORA: constants.FAILED_AMPHORA},
|
||||
|
@ -37,7 +37,7 @@ class HealthMonitorFlows(object):
|
||||
create_hm_flow.add(database_tasks.MarkHealthMonitorPendingCreateInDB(
|
||||
requires=constants.HEALTH_MON))
|
||||
create_hm_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
create_hm_flow.add(database_tasks.MarkHealthMonitorActiveInDB(
|
||||
requires=constants.HEALTH_MON))
|
||||
create_hm_flow.add(database_tasks.MarkPoolActiveInDB(
|
||||
@ -63,7 +63,7 @@ class HealthMonitorFlows(object):
|
||||
DeleteModelObject(rebind={constants.OBJECT:
|
||||
constants.HEALTH_MON}))
|
||||
delete_hm_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
delete_hm_flow.add(database_tasks.DeleteHealthMonitorInDB(
|
||||
requires=constants.HEALTH_MON))
|
||||
delete_hm_flow.add(database_tasks.DecrementHealthMonitorQuota(
|
||||
@ -92,7 +92,7 @@ class HealthMonitorFlows(object):
|
||||
update_hm_flow.add(database_tasks.MarkHealthMonitorPendingUpdateInDB(
|
||||
requires=constants.HEALTH_MON))
|
||||
update_hm_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
update_hm_flow.add(database_tasks.UpdateHealthMonInDB(
|
||||
requires=[constants.HEALTH_MON, constants.UPDATE_DICT]))
|
||||
update_hm_flow.add(database_tasks.MarkHealthMonitorActiveInDB(
|
||||
|
@ -37,7 +37,7 @@ class L7PolicyFlows(object):
|
||||
create_l7policy_flow.add(database_tasks.MarkL7PolicyPendingCreateInDB(
|
||||
requires=constants.L7POLICY))
|
||||
create_l7policy_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
create_l7policy_flow.add(database_tasks.MarkL7PolicyActiveInDB(
|
||||
requires=constants.L7POLICY))
|
||||
create_l7policy_flow.add(database_tasks.MarkLBAndListenersActiveInDB(
|
||||
@ -60,7 +60,7 @@ class L7PolicyFlows(object):
|
||||
delete_l7policy_flow.add(model_tasks.DeleteModelObject(
|
||||
rebind={constants.OBJECT: constants.L7POLICY}))
|
||||
delete_l7policy_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
delete_l7policy_flow.add(database_tasks.DeleteL7PolicyInDB(
|
||||
requires=constants.L7POLICY))
|
||||
delete_l7policy_flow.add(database_tasks.MarkLBAndListenersActiveInDB(
|
||||
@ -81,7 +81,7 @@ class L7PolicyFlows(object):
|
||||
update_l7policy_flow.add(database_tasks.MarkL7PolicyPendingUpdateInDB(
|
||||
requires=constants.L7POLICY))
|
||||
update_l7policy_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
update_l7policy_flow.add(database_tasks.UpdateL7PolicyInDB(
|
||||
requires=[constants.L7POLICY, constants.UPDATE_DICT]))
|
||||
update_l7policy_flow.add(database_tasks.MarkL7PolicyActiveInDB(
|
||||
|
@ -37,7 +37,7 @@ class L7RuleFlows(object):
|
||||
create_l7rule_flow.add(database_tasks.MarkL7RulePendingCreateInDB(
|
||||
requires=constants.L7RULE))
|
||||
create_l7rule_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
create_l7rule_flow.add(database_tasks.MarkL7RuleActiveInDB(
|
||||
requires=constants.L7RULE))
|
||||
create_l7rule_flow.add(database_tasks.MarkL7PolicyActiveInDB(
|
||||
@ -62,7 +62,7 @@ class L7RuleFlows(object):
|
||||
delete_l7rule_flow.add(model_tasks.DeleteModelObject(
|
||||
rebind={constants.OBJECT: constants.L7RULE}))
|
||||
delete_l7rule_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
delete_l7rule_flow.add(database_tasks.DeleteL7RuleInDB(
|
||||
requires=constants.L7RULE))
|
||||
delete_l7rule_flow.add(database_tasks.MarkL7PolicyActiveInDB(
|
||||
@ -85,7 +85,7 @@ class L7RuleFlows(object):
|
||||
update_l7rule_flow.add(database_tasks.MarkL7RulePendingUpdateInDB(
|
||||
requires=constants.L7RULE))
|
||||
update_l7rule_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
update_l7rule_flow.add(database_tasks.UpdateL7RuleInDB(
|
||||
requires=[constants.L7RULE, constants.UPDATE_DICT]))
|
||||
update_l7rule_flow.add(database_tasks.MarkL7RuleActiveInDB(
|
||||
|
@ -33,7 +33,7 @@ class ListenerFlows(object):
|
||||
create_listener_flow.add(lifecycle_tasks.ListenersToErrorOnRevertTask(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
create_listener_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
create_listener_flow.add(network_tasks.UpdateVIP(
|
||||
requires=constants.LOADBALANCER))
|
||||
create_listener_flow.add(database_tasks.
|
||||
@ -57,7 +57,7 @@ class ListenerFlows(object):
|
||||
requires=constants.LOADBALANCER_ID,
|
||||
provides=constants.LOADBALANCER))
|
||||
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(
|
||||
requires=constants.LOADBALANCER))
|
||||
return create_all_listeners_flow
|
||||
@ -71,7 +71,7 @@ class ListenerFlows(object):
|
||||
delete_listener_flow.add(lifecycle_tasks.ListenerToErrorOnRevertTask(
|
||||
requires=constants.LISTENER))
|
||||
delete_listener_flow.add(amphora_driver_tasks.ListenerDelete(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENER]))
|
||||
requires=constants.LISTENER))
|
||||
delete_listener_flow.add(network_tasks.UpdateVIPForDelete(
|
||||
requires=constants.LOADBALANCER))
|
||||
delete_listener_flow.add(database_tasks.DeleteListenerInDB(
|
||||
@ -115,7 +115,7 @@ class ListenerFlows(object):
|
||||
update_listener_flow.add(lifecycle_tasks.ListenersToErrorOnRevertTask(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
update_listener_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
update_listener_flow.add(database_tasks.UpdateListenerInDB(
|
||||
requires=[constants.LISTENER, constants.UPDATE_DICT]))
|
||||
update_listener_flow.add(database_tasks.
|
||||
|
@ -332,7 +332,7 @@ class LoadBalancerFlows(object):
|
||||
update_LB_flow.add(network_tasks.ApplyQos(
|
||||
requires=(constants.LOADBALANCER, constants.UPDATE_DICT)))
|
||||
update_LB_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
update_LB_flow.add(database_tasks.UpdateLoadbalancerInDB(
|
||||
requires=[constants.LOADBALANCER, constants.UPDATE_DICT]))
|
||||
update_LB_flow.add(database_tasks.MarkLBActiveInDB(
|
||||
|
@ -48,7 +48,7 @@ class MemberFlows(object):
|
||||
requires=(constants.LOADBALANCER, constants.ADDED_PORTS)
|
||||
))
|
||||
create_member_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=(constants.LOADBALANCER, constants.LISTENERS)))
|
||||
requires=constants.LOADBALANCER))
|
||||
create_member_flow.add(database_tasks.MarkMemberActiveInDB(
|
||||
requires=constants.MEMBER))
|
||||
create_member_flow.add(database_tasks.MarkPoolActiveInDB(
|
||||
@ -79,7 +79,7 @@ class MemberFlows(object):
|
||||
delete_member_flow.add(database_tasks.DeleteMemberInDB(
|
||||
requires=constants.MEMBER))
|
||||
delete_member_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
delete_member_flow.add(database_tasks.DecrementMemberQuota(
|
||||
requires=constants.MEMBER))
|
||||
delete_member_flow.add(database_tasks.MarkPoolActiveInDB(
|
||||
@ -105,7 +105,7 @@ class MemberFlows(object):
|
||||
update_member_flow.add(database_tasks.MarkMemberPendingUpdateInDB(
|
||||
requires=constants.MEMBER))
|
||||
update_member_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
update_member_flow.add(database_tasks.UpdateMemberInDB(
|
||||
requires=[constants.MEMBER, constants.UPDATE_DICT]))
|
||||
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)
|
||||
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
|
||||
batch_update_members_flow.add(unordered_members_active_flow)
|
||||
|
@ -37,7 +37,7 @@ class PoolFlows(object):
|
||||
create_pool_flow.add(database_tasks.MarkPoolPendingCreateInDB(
|
||||
requires=constants.POOL))
|
||||
create_pool_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
create_pool_flow.add(database_tasks.MarkPoolActiveInDB(
|
||||
requires=constants.POOL))
|
||||
create_pool_flow.add(database_tasks.MarkLBAndListenersActiveInDB(
|
||||
@ -62,7 +62,7 @@ class PoolFlows(object):
|
||||
delete_pool_flow.add(model_tasks.DeleteModelObject(
|
||||
rebind={constants.OBJECT: constants.POOL}))
|
||||
delete_pool_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
delete_pool_flow.add(database_tasks.DeletePoolInDB(
|
||||
requires=constants.POOL))
|
||||
delete_pool_flow.add(database_tasks.DecrementPoolQuota(
|
||||
@ -116,7 +116,7 @@ class PoolFlows(object):
|
||||
update_pool_flow.add(database_tasks.MarkPoolPendingUpdateInDB(
|
||||
requires=constants.POOL))
|
||||
update_pool_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
update_pool_flow.add(database_tasks.UpdatePoolInDB(
|
||||
requires=[constants.POOL, constants.UPDATE_DICT]))
|
||||
update_pool_flow.add(database_tasks.MarkPoolActiveInDB(
|
||||
|
@ -52,13 +52,13 @@ class BaseAmphoraTask(task.Task):
|
||||
class AmpListenersUpdate(BaseAmphoraTask):
|
||||
"""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
|
||||
# in a failover flow with both amps failing. Skip it and let
|
||||
# health manager fix it.
|
||||
try:
|
||||
self.amphora_driver.update_amphora_listeners(
|
||||
listeners, amphora_index, amphorae, timeout_dict)
|
||||
loadbalancer, amphorae[amphora_index], timeout_dict)
|
||||
except Exception as e:
|
||||
amphora_id = amphorae[amphora_index].id
|
||||
LOG.error('Failed to update listeners on amphora %s. Skipping '
|
||||
@ -71,11 +71,9 @@ class AmpListenersUpdate(BaseAmphoraTask):
|
||||
class ListenersUpdate(BaseAmphoraTask):
|
||||
"""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."""
|
||||
for listener in listeners:
|
||||
listener.load_balancer = loadbalancer
|
||||
self.amphora_driver.update(listener, loadbalancer.vip)
|
||||
self.amphora_driver.update(loadbalancer)
|
||||
|
||||
def revert(self, loadbalancer, *args, **kwargs):
|
||||
"""Handle failed listeners updates."""
|
||||
@ -86,61 +84,30 @@ class ListenersUpdate(BaseAmphoraTask):
|
||||
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):
|
||||
"""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."""
|
||||
for listener in listeners:
|
||||
self.amphora_driver.start(listener, loadbalancer.vip, amphora)
|
||||
LOG.debug("Started the listeners on the vip")
|
||||
if loadbalancer.listeners:
|
||||
self.amphora_driver.start(loadbalancer, amphora)
|
||||
LOG.debug("Started the listeners on the vip")
|
||||
|
||||
def revert(self, listeners, *args, **kwargs):
|
||||
def revert(self, loadbalancer, *args, **kwargs):
|
||||
"""Handle failed 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)
|
||||
|
||||
|
||||
class ListenerDelete(BaseAmphoraTask):
|
||||
"""Task to delete the listener on the vip."""
|
||||
|
||||
def execute(self, loadbalancer, listener):
|
||||
def execute(self, listener):
|
||||
"""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")
|
||||
|
||||
def revert(self, listener, *args, **kwargs):
|
||||
|
@ -235,13 +235,14 @@ class ControllerWorker(base_taskflow.BaseTaskFlowEngine):
|
||||
raise db_exceptions.NoResultFound
|
||||
|
||||
load_balancer = listener.load_balancer
|
||||
listeners = load_balancer.listeners
|
||||
|
||||
create_listener_tf = self._taskflow_load(self._listener_flows.
|
||||
get_create_listener_flow(),
|
||||
store={constants.LOADBALANCER:
|
||||
load_balancer,
|
||||
constants.LISTENERS:
|
||||
[listener]})
|
||||
listeners})
|
||||
with tf_logging.DynamicLoggingListener(create_listener_tf,
|
||||
log=LOG):
|
||||
create_listener_tf.run()
|
||||
|
@ -470,7 +470,7 @@ class AmphoraFlows(object):
|
||||
update_amps_subflow.add(
|
||||
amphora_driver_tasks.AmpListenersUpdate(
|
||||
name=constants.AMP_LISTENER_UPDATE + '-' + str(amp_index),
|
||||
requires=(constants.LISTENERS, constants.AMPHORAE),
|
||||
requires=(constants.LOADBALANCER, constants.AMPHORAE),
|
||||
inject={constants.AMPHORA_INDEX: amp_index,
|
||||
constants.TIMEOUT_DICT: timeout_dict}))
|
||||
amp_index += 1
|
||||
@ -514,8 +514,7 @@ class AmphoraFlows(object):
|
||||
requires=constants.AMPHORA))
|
||||
|
||||
failover_amphora_flow.add(amphora_driver_tasks.ListenersStart(
|
||||
requires=(constants.LOADBALANCER, constants.LISTENERS,
|
||||
constants.AMPHORA)))
|
||||
requires=(constants.LOADBALANCER, constants.AMPHORA)))
|
||||
failover_amphora_flow.add(
|
||||
database_tasks.DisableAmphoraHealthMonitoring(
|
||||
rebind={constants.AMPHORA: constants.FAILED_AMPHORA},
|
||||
|
@ -37,7 +37,7 @@ class HealthMonitorFlows(object):
|
||||
create_hm_flow.add(database_tasks.MarkHealthMonitorPendingCreateInDB(
|
||||
requires=constants.HEALTH_MON))
|
||||
create_hm_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
create_hm_flow.add(database_tasks.MarkHealthMonitorActiveInDB(
|
||||
requires=constants.HEALTH_MON))
|
||||
create_hm_flow.add(database_tasks.MarkPoolActiveInDB(
|
||||
@ -63,7 +63,7 @@ class HealthMonitorFlows(object):
|
||||
DeleteModelObject(rebind={constants.OBJECT:
|
||||
constants.HEALTH_MON}))
|
||||
delete_hm_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
delete_hm_flow.add(database_tasks.DeleteHealthMonitorInDB(
|
||||
requires=constants.HEALTH_MON))
|
||||
delete_hm_flow.add(database_tasks.DecrementHealthMonitorQuota(
|
||||
@ -92,7 +92,7 @@ class HealthMonitorFlows(object):
|
||||
update_hm_flow.add(database_tasks.MarkHealthMonitorPendingUpdateInDB(
|
||||
requires=constants.HEALTH_MON))
|
||||
update_hm_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
update_hm_flow.add(database_tasks.UpdateHealthMonInDB(
|
||||
requires=[constants.HEALTH_MON, constants.UPDATE_DICT]))
|
||||
update_hm_flow.add(database_tasks.MarkHealthMonitorActiveInDB(
|
||||
|
@ -37,7 +37,7 @@ class L7PolicyFlows(object):
|
||||
create_l7policy_flow.add(database_tasks.MarkL7PolicyPendingCreateInDB(
|
||||
requires=constants.L7POLICY))
|
||||
create_l7policy_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
create_l7policy_flow.add(database_tasks.MarkL7PolicyActiveInDB(
|
||||
requires=constants.L7POLICY))
|
||||
create_l7policy_flow.add(database_tasks.MarkLBAndListenersActiveInDB(
|
||||
@ -60,7 +60,7 @@ class L7PolicyFlows(object):
|
||||
delete_l7policy_flow.add(model_tasks.DeleteModelObject(
|
||||
rebind={constants.OBJECT: constants.L7POLICY}))
|
||||
delete_l7policy_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
delete_l7policy_flow.add(database_tasks.DeleteL7PolicyInDB(
|
||||
requires=constants.L7POLICY))
|
||||
delete_l7policy_flow.add(database_tasks.MarkLBAndListenersActiveInDB(
|
||||
@ -81,7 +81,7 @@ class L7PolicyFlows(object):
|
||||
update_l7policy_flow.add(database_tasks.MarkL7PolicyPendingUpdateInDB(
|
||||
requires=constants.L7POLICY))
|
||||
update_l7policy_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
update_l7policy_flow.add(database_tasks.UpdateL7PolicyInDB(
|
||||
requires=[constants.L7POLICY, constants.UPDATE_DICT]))
|
||||
update_l7policy_flow.add(database_tasks.MarkL7PolicyActiveInDB(
|
||||
|
@ -37,7 +37,7 @@ class L7RuleFlows(object):
|
||||
create_l7rule_flow.add(database_tasks.MarkL7RulePendingCreateInDB(
|
||||
requires=constants.L7RULE))
|
||||
create_l7rule_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
create_l7rule_flow.add(database_tasks.MarkL7RuleActiveInDB(
|
||||
requires=constants.L7RULE))
|
||||
create_l7rule_flow.add(database_tasks.MarkL7PolicyActiveInDB(
|
||||
@ -62,7 +62,7 @@ class L7RuleFlows(object):
|
||||
delete_l7rule_flow.add(model_tasks.DeleteModelObject(
|
||||
rebind={constants.OBJECT: constants.L7RULE}))
|
||||
delete_l7rule_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
delete_l7rule_flow.add(database_tasks.DeleteL7RuleInDB(
|
||||
requires=constants.L7RULE))
|
||||
delete_l7rule_flow.add(database_tasks.MarkL7PolicyActiveInDB(
|
||||
@ -85,7 +85,7 @@ class L7RuleFlows(object):
|
||||
update_l7rule_flow.add(database_tasks.MarkL7RulePendingUpdateInDB(
|
||||
requires=constants.L7RULE))
|
||||
update_l7rule_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
update_l7rule_flow.add(database_tasks.UpdateL7RuleInDB(
|
||||
requires=[constants.L7RULE, constants.UPDATE_DICT]))
|
||||
update_l7rule_flow.add(database_tasks.MarkL7RuleActiveInDB(
|
||||
|
@ -33,7 +33,7 @@ class ListenerFlows(object):
|
||||
create_listener_flow.add(lifecycle_tasks.ListenersToErrorOnRevertTask(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
create_listener_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
create_listener_flow.add(network_tasks.UpdateVIP(
|
||||
requires=constants.LOADBALANCER))
|
||||
create_listener_flow.add(database_tasks.
|
||||
@ -57,7 +57,7 @@ class ListenerFlows(object):
|
||||
requires=constants.LOADBALANCER_ID,
|
||||
provides=constants.LOADBALANCER))
|
||||
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(
|
||||
requires=constants.LOADBALANCER))
|
||||
return create_all_listeners_flow
|
||||
@ -71,7 +71,7 @@ class ListenerFlows(object):
|
||||
delete_listener_flow.add(lifecycle_tasks.ListenerToErrorOnRevertTask(
|
||||
requires=constants.LISTENER))
|
||||
delete_listener_flow.add(amphora_driver_tasks.ListenerDelete(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENER]))
|
||||
requires=constants.LISTENER))
|
||||
delete_listener_flow.add(network_tasks.UpdateVIPForDelete(
|
||||
requires=constants.LOADBALANCER))
|
||||
delete_listener_flow.add(database_tasks.DeleteListenerInDB(
|
||||
@ -115,7 +115,7 @@ class ListenerFlows(object):
|
||||
update_listener_flow.add(lifecycle_tasks.ListenersToErrorOnRevertTask(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
update_listener_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
update_listener_flow.add(database_tasks.UpdateListenerInDB(
|
||||
requires=[constants.LISTENER, constants.UPDATE_DICT]))
|
||||
update_listener_flow.add(database_tasks.
|
||||
|
@ -332,7 +332,7 @@ class LoadBalancerFlows(object):
|
||||
update_LB_flow.add(network_tasks.ApplyQos(
|
||||
requires=(constants.LOADBALANCER, constants.UPDATE_DICT)))
|
||||
update_LB_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
update_LB_flow.add(database_tasks.UpdateLoadbalancerInDB(
|
||||
requires=[constants.LOADBALANCER, constants.UPDATE_DICT]))
|
||||
update_LB_flow.add(database_tasks.MarkLBActiveInDB(
|
||||
|
@ -48,7 +48,7 @@ class MemberFlows(object):
|
||||
requires=(constants.LOADBALANCER, constants.ADDED_PORTS)
|
||||
))
|
||||
create_member_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=(constants.LOADBALANCER, constants.LISTENERS)))
|
||||
requires=constants.LOADBALANCER))
|
||||
create_member_flow.add(database_tasks.MarkMemberActiveInDB(
|
||||
requires=constants.MEMBER))
|
||||
create_member_flow.add(database_tasks.MarkPoolActiveInDB(
|
||||
@ -79,7 +79,7 @@ class MemberFlows(object):
|
||||
delete_member_flow.add(database_tasks.DeleteMemberInDB(
|
||||
requires=constants.MEMBER))
|
||||
delete_member_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
delete_member_flow.add(database_tasks.DecrementMemberQuota(
|
||||
requires=constants.MEMBER))
|
||||
delete_member_flow.add(database_tasks.MarkPoolActiveInDB(
|
||||
@ -105,7 +105,7 @@ class MemberFlows(object):
|
||||
update_member_flow.add(database_tasks.MarkMemberPendingUpdateInDB(
|
||||
requires=constants.MEMBER))
|
||||
update_member_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
update_member_flow.add(database_tasks.UpdateMemberInDB(
|
||||
requires=[constants.MEMBER, constants.UPDATE_DICT]))
|
||||
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)
|
||||
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
|
||||
batch_update_members_flow.add(unordered_members_active_flow)
|
||||
|
@ -37,7 +37,7 @@ class PoolFlows(object):
|
||||
create_pool_flow.add(database_tasks.MarkPoolPendingCreateInDB(
|
||||
requires=constants.POOL))
|
||||
create_pool_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
create_pool_flow.add(database_tasks.MarkPoolActiveInDB(
|
||||
requires=constants.POOL))
|
||||
create_pool_flow.add(database_tasks.MarkLBAndListenersActiveInDB(
|
||||
@ -62,7 +62,7 @@ class PoolFlows(object):
|
||||
delete_pool_flow.add(model_tasks.DeleteModelObject(
|
||||
rebind={constants.OBJECT: constants.POOL}))
|
||||
delete_pool_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
delete_pool_flow.add(database_tasks.DeletePoolInDB(
|
||||
requires=constants.POOL))
|
||||
delete_pool_flow.add(database_tasks.DecrementPoolQuota(
|
||||
@ -116,7 +116,7 @@ class PoolFlows(object):
|
||||
update_pool_flow.add(database_tasks.MarkPoolPendingUpdateInDB(
|
||||
requires=constants.POOL))
|
||||
update_pool_flow.add(amphora_driver_tasks.ListenersUpdate(
|
||||
requires=[constants.LOADBALANCER, constants.LISTENERS]))
|
||||
requires=constants.LOADBALANCER))
|
||||
update_pool_flow.add(database_tasks.UpdatePoolInDB(
|
||||
requires=[constants.POOL, constants.UPDATE_DICT]))
|
||||
update_pool_flow.add(database_tasks.MarkPoolActiveInDB(
|
||||
|
@ -52,13 +52,13 @@ class BaseAmphoraTask(task.Task):
|
||||
class AmpListenersUpdate(BaseAmphoraTask):
|
||||
"""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
|
||||
# in a failover flow with both amps failing. Skip it and let
|
||||
# health manager fix it.
|
||||
try:
|
||||
self.amphora_driver.update_amphora_listeners(
|
||||
listeners, amphora_index, amphorae, timeout_dict)
|
||||
loadbalancer, amphorae[amphora_index], timeout_dict)
|
||||
except Exception as e:
|
||||
amphora_id = amphorae[amphora_index].id
|
||||
LOG.error('Failed to update listeners on amphora %s. Skipping '
|
||||
@ -71,11 +71,9 @@ class AmpListenersUpdate(BaseAmphoraTask):
|
||||
class ListenersUpdate(BaseAmphoraTask):
|
||||
"""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."""
|
||||
for listener in listeners:
|
||||
listener.load_balancer = loadbalancer
|
||||
self.amphora_driver.update(listener, loadbalancer.vip)
|
||||
self.amphora_driver.update(loadbalancer)
|
||||
|
||||
def revert(self, loadbalancer, *args, **kwargs):
|
||||
"""Handle failed listeners updates."""
|
||||
@ -86,61 +84,30 @@ class ListenersUpdate(BaseAmphoraTask):
|
||||
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):
|
||||
"""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."""
|
||||
for listener in listeners:
|
||||
self.amphora_driver.start(listener, loadbalancer.vip, amphora)
|
||||
LOG.debug("Started the listeners on the vip")
|
||||
if loadbalancer.listeners:
|
||||
self.amphora_driver.start(loadbalancer, amphora)
|
||||
LOG.debug("Started the listeners on the vip")
|
||||
|
||||
def revert(self, listeners, *args, **kwargs):
|
||||
def revert(self, loadbalancer, *args, **kwargs):
|
||||
"""Handle failed 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)
|
||||
|
||||
|
||||
class ListenerDelete(BaseAmphoraTask):
|
||||
"""Task to delete the listener on the vip."""
|
||||
|
||||
def execute(self, loadbalancer, listener):
|
||||
def execute(self, listener):
|
||||
"""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")
|
||||
|
||||
def revert(self, listener, *args, **kwargs):
|
||||
|
@ -1253,6 +1253,7 @@ class AmphoraRepository(BaseRepository):
|
||||
"load_balancer.operating_status AS lb_op_status, "
|
||||
"listener.id AS list_id, "
|
||||
"listener.operating_status AS list_op_status, "
|
||||
"listener.enabled AS list_enabled, "
|
||||
"listener.protocol AS list_protocol, "
|
||||
"pool.id AS pool_id, "
|
||||
"pool.operating_status AS pool_op_status, "
|
||||
@ -1278,7 +1279,8 @@ class AmphoraRepository(BaseRepository):
|
||||
lb['operating_status'] = row['lb_op_status']
|
||||
if row['list_id'] and row['list_id'] not in listeners:
|
||||
listener = {'operating_status': row['list_op_status'],
|
||||
'protocol': row['list_protocol']}
|
||||
'protocol': row['list_protocol'],
|
||||
'enabled': row['list_enabled']}
|
||||
listeners[row['list_id']] = listener
|
||||
if row['pool_id']:
|
||||
if row['pool_id'] in pools and row['member_id']:
|
||||
|
@ -18,7 +18,6 @@ import subprocess
|
||||
|
||||
import flask
|
||||
import mock
|
||||
from werkzeug import exceptions
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
@ -315,67 +314,6 @@ class KeepalivedLvsTestCase(base.TestCase):
|
||||
'start')
|
||||
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.'
|
||||
'get_udp_listeners', return_value=[LISTENER_ID])
|
||||
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
|
||||
|
@ -118,11 +118,11 @@ class TestServerTestCase(base.TestCase):
|
||||
mock_distro_id.return_value = distro
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.put('/' + api_server.VERSION +
|
||||
'/listeners/amp_123/123/haproxy',
|
||||
'/loadbalancer/amp_123/123/haproxy',
|
||||
data='test')
|
||||
elif distro == consts.CENTOS:
|
||||
rv = self.centos_app.put('/' + api_server.VERSION +
|
||||
'/listeners/amp_123/123/haproxy',
|
||||
'/loadbalancer/amp_123/123/haproxy',
|
||||
data='test')
|
||||
mode = stat.S_IRUSR | stat.S_IWUSR
|
||||
mock_open.assert_called_with(file_name, flags, mode)
|
||||
@ -159,11 +159,11 @@ class TestServerTestCase(base.TestCase):
|
||||
mock_distro_id.return_value = distro
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.put('/' + api_server.VERSION +
|
||||
'/listeners/amp_123/123/haproxy',
|
||||
'/loadbalancer/amp_123/123/haproxy',
|
||||
data='test')
|
||||
elif distro == consts.CENTOS:
|
||||
rv = self.centos_app.put('/' + api_server.VERSION +
|
||||
'/listeners/amp_123/123/haproxy',
|
||||
'/loadbalancer/amp_123/123/haproxy',
|
||||
data='test')
|
||||
self.assertEqual(500, rv.status_code)
|
||||
|
||||
@ -188,11 +188,11 @@ class TestServerTestCase(base.TestCase):
|
||||
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.put('/' + api_server.VERSION +
|
||||
'/listeners/amp_123/123/haproxy',
|
||||
'/loadbalancer/amp_123/123/haproxy',
|
||||
data='test')
|
||||
elif distro == consts.CENTOS:
|
||||
rv = self.centos_app.put('/' + api_server.VERSION +
|
||||
'/listeners/amp_123/123/haproxy',
|
||||
'/loadbalancer/amp_123/123/haproxy',
|
||||
data='test')
|
||||
|
||||
self.assertEqual(202, rv.status_code)
|
||||
@ -220,11 +220,11 @@ class TestServerTestCase(base.TestCase):
|
||||
mock_distro_id.return_value = distro
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.put('/' + api_server.VERSION +
|
||||
'/listeners/amp_123/123/haproxy',
|
||||
'/loadbalancer/amp_123/123/haproxy',
|
||||
data='test')
|
||||
elif distro == consts.CENTOS:
|
||||
rv = self.centos_app.put('/' + api_server.VERSION +
|
||||
'/listeners/amp_123/123/haproxy',
|
||||
'/loadbalancer/amp_123/123/haproxy',
|
||||
data='test')
|
||||
self.assertEqual(400, rv.status_code)
|
||||
self.assertEqual(
|
||||
@ -255,11 +255,11 @@ class TestServerTestCase(base.TestCase):
|
||||
mock_distro_id.return_value = distro
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.put('/' + api_server.VERSION +
|
||||
'/listeners/amp_123/123/haproxy',
|
||||
'/loadbalancer/amp_123/123/haproxy',
|
||||
data='test')
|
||||
elif distro == consts.CENTOS:
|
||||
rv = self.ubuntu_app.put('/' + api_server.VERSION +
|
||||
'/listeners/amp_123/123/haproxy',
|
||||
'/loadbalancer/amp_123/123/haproxy',
|
||||
data='test')
|
||||
self.assertEqual(500, rv.status_code)
|
||||
|
||||
@ -269,45 +269,49 @@ class TestServerTestCase(base.TestCase):
|
||||
def test_centos_start(self):
|
||||
self._test_start(consts.CENTOS)
|
||||
|
||||
@mock.patch('os.listdir')
|
||||
@mock.patch('os.path.exists')
|
||||
@mock.patch('octavia.amphorae.backends.agent.api_server.listener.Listener.'
|
||||
'vrrp_check_script_update')
|
||||
@mock.patch('octavia.amphorae.backends.agent.api_server.loadbalancer.'
|
||||
'Loadbalancer.vrrp_check_script_update')
|
||||
@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])
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.put('/' + api_server.VERSION +
|
||||
'/listeners/123/error')
|
||||
'/loadbalancer/123/error')
|
||||
elif distro == consts.CENTOS:
|
||||
rv = self.centos_app.put('/' + api_server.VERSION +
|
||||
'/listeners/123/error')
|
||||
'/loadbalancer/123/error')
|
||||
self.assertEqual(400, rv.status_code)
|
||||
self.assertEqual(
|
||||
{'message': 'Invalid Request',
|
||||
'details': 'Unknown action: error', },
|
||||
jsonutils.loads(rv.data.decode('utf-8')))
|
||||
|
||||
mock_exists.reset_mock()
|
||||
mock_exists.return_value = False
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.put('/' + api_server.VERSION +
|
||||
'/listeners/123/start')
|
||||
'/loadbalancer/123/start')
|
||||
elif distro == consts.CENTOS:
|
||||
rv = self.centos_app.put('/' + api_server.VERSION +
|
||||
'/listeners/123/start')
|
||||
'/loadbalancer/123/start')
|
||||
self.assertEqual(404, rv.status_code)
|
||||
self.assertEqual(
|
||||
{'message': 'Listener Not Found',
|
||||
'details': 'No listener with UUID: 123'},
|
||||
{'message': 'Loadbalancer Not Found',
|
||||
'details': 'No loadbalancer with UUID: 123'},
|
||||
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_listdir.return_value = ['123']
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.put('/' + api_server.VERSION +
|
||||
'/listeners/123/start')
|
||||
'/loadbalancer/123/start')
|
||||
elif distro == consts.CENTOS:
|
||||
rv = self.centos_app.put('/' + api_server.VERSION +
|
||||
'/listeners/123/start')
|
||||
'/loadbalancer/123/start')
|
||||
self.assertEqual(202, rv.status_code)
|
||||
self.assertEqual(
|
||||
{'message': 'OK',
|
||||
@ -322,10 +326,10 @@ class TestServerTestCase(base.TestCase):
|
||||
7, 'test', RANDOM_ERROR)
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.put('/' + api_server.VERSION +
|
||||
'/listeners/123/start')
|
||||
'/loadbalancer/123/start')
|
||||
elif distro == consts.CENTOS:
|
||||
rv = self.centos_app.put('/' + api_server.VERSION +
|
||||
'/listeners/123/start')
|
||||
'/loadbalancer/123/start')
|
||||
self.assertEqual(500, rv.status_code)
|
||||
self.assertEqual(
|
||||
{
|
||||
@ -341,26 +345,28 @@ class TestServerTestCase(base.TestCase):
|
||||
def test_centos_reload(self):
|
||||
self._test_reload(consts.CENTOS)
|
||||
|
||||
@mock.patch('os.listdir')
|
||||
@mock.patch('os.path.exists')
|
||||
@mock.patch('octavia.amphorae.backends.agent.api_server.listener.Listener.'
|
||||
'vrrp_check_script_update')
|
||||
@mock.patch('octavia.amphorae.backends.agent.api_server.listener.Listener.'
|
||||
'_check_haproxy_status')
|
||||
@mock.patch('octavia.amphorae.backends.agent.api_server.loadbalancer.'
|
||||
'Loadbalancer.vrrp_check_script_update')
|
||||
@mock.patch('octavia.amphorae.backends.agent.api_server.loadbalancer.'
|
||||
'Loadbalancer._check_haproxy_status')
|
||||
@mock.patch('subprocess.check_output')
|
||||
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])
|
||||
|
||||
# Process running so reload
|
||||
mock_exists.return_value = True
|
||||
mock_listdir.return_value = ['123']
|
||||
mock_haproxy_status.return_value = consts.ACTIVE
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.put('/' + api_server.VERSION +
|
||||
'/listeners/123/reload')
|
||||
'/loadbalancer/123/reload')
|
||||
elif distro == consts.CENTOS:
|
||||
rv = self.centos_app.put('/' + api_server.VERSION +
|
||||
'/listeners/123/reload')
|
||||
'/loadbalancer/123/reload')
|
||||
self.assertEqual(202, rv.status_code)
|
||||
self.assertEqual(
|
||||
{'message': 'OK',
|
||||
@ -374,10 +380,10 @@ class TestServerTestCase(base.TestCase):
|
||||
mock_haproxy_status.return_value = consts.OFFLINE
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.put('/' + api_server.VERSION +
|
||||
'/listeners/123/reload')
|
||||
'/loadbalancer/123/reload')
|
||||
elif distro == consts.CENTOS:
|
||||
rv = self.centos_app.put('/' + api_server.VERSION +
|
||||
'/listeners/123/reload')
|
||||
'/loadbalancer/123/reload')
|
||||
self.assertEqual(202, rv.status_code)
|
||||
self.assertEqual(
|
||||
{'message': 'OK',
|
||||
@ -411,13 +417,13 @@ class TestServerTestCase(base.TestCase):
|
||||
|
||||
self.assertEqual(200, rv.status_code)
|
||||
self.assertEqual(dict(
|
||||
api_version='0.5',
|
||||
api_version='1.0',
|
||||
haproxy_version='9.9.99-9',
|
||||
hostname='test-host'),
|
||||
jsonutils.loads(rv.data.decode('utf-8')))
|
||||
|
||||
@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.'
|
||||
'get_os_init_system', return_value=consts.INIT_SYSTEMD)
|
||||
def test_delete_ubuntu_listener_systemd(self, mock_init_system,
|
||||
@ -426,7 +432,7 @@ class TestServerTestCase(base.TestCase):
|
||||
mock_init_system)
|
||||
|
||||
@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.'
|
||||
'get_os_init_system', return_value=consts.INIT_SYSTEMD)
|
||||
def test_delete_centos_listener_systemd(self, mock_init_system,
|
||||
@ -435,7 +441,7 @@ class TestServerTestCase(base.TestCase):
|
||||
mock_init_system)
|
||||
|
||||
@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.'
|
||||
'get_os_init_system', return_value=consts.INIT_SYSVINIT)
|
||||
def test_delete_ubuntu_listener_sysvinit(self, mock_init_system,
|
||||
@ -444,7 +450,7 @@ class TestServerTestCase(base.TestCase):
|
||||
mock_init_system)
|
||||
|
||||
@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.'
|
||||
'get_os_init_system', return_value=consts.INIT_UPSTART)
|
||||
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,
|
||||
mock_init_system)
|
||||
|
||||
@mock.patch('os.listdir')
|
||||
@mock.patch('os.path.exists')
|
||||
@mock.patch('subprocess.check_output')
|
||||
@mock.patch('octavia.amphorae.backends.agent.api_server.listener.Listener.'
|
||||
'vrrp_check_script_update')
|
||||
@mock.patch('octavia.amphorae.backends.agent.api_server.loadbalancer.'
|
||||
'Loadbalancer.vrrp_check_script_update')
|
||||
@mock.patch('octavia.amphorae.backends.agent.api_server.util.' +
|
||||
'get_haproxy_pid')
|
||||
@mock.patch('shutil.rmtree')
|
||||
@mock.patch('os.remove')
|
||||
def _test_delete_listener(self, init_system, distro, mock_init_system,
|
||||
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])
|
||||
# no listener
|
||||
mock_exists.return_value = False
|
||||
mock_listdir.return_value = ['123']
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.delete('/' + api_server.VERSION +
|
||||
'/listeners/123')
|
||||
@ -474,10 +482,10 @@ class TestServerTestCase(base.TestCase):
|
||||
'/listeners/123')
|
||||
self.assertEqual(200, rv.status_code)
|
||||
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
|
||||
mock_exists.side_effect = [True, False, False, False]
|
||||
mock_exists.side_effect = [True, True, False, False, False]
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.delete('/' + api_server.VERSION +
|
||||
'/listeners/123')
|
||||
@ -504,7 +512,7 @@ class TestServerTestCase(base.TestCase):
|
||||
mock_exists.assert_any_call('/var/lib/octavia/123/123.pid')
|
||||
|
||||
# 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:
|
||||
rv = self.ubuntu_app.delete('/' + api_server.VERSION +
|
||||
'/listeners/123')
|
||||
@ -531,7 +539,7 @@ class TestServerTestCase(base.TestCase):
|
||||
mock_exists.assert_any_call('/var/lib/octavia/123/123.pid')
|
||||
|
||||
# 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:
|
||||
rv = self.ubuntu_app.delete('/' + api_server.VERSION +
|
||||
'/listeners/123')
|
||||
@ -555,7 +563,7 @@ class TestServerTestCase(base.TestCase):
|
||||
self.assertIn(init_system, consts.VALID_INIT_SYSTEMS)
|
||||
|
||||
# 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:
|
||||
rv = self.ubuntu_app.delete('/' + api_server.VERSION +
|
||||
'/listeners/123')
|
||||
@ -579,7 +587,7 @@ class TestServerTestCase(base.TestCase):
|
||||
self.assertIn(init_system, consts.VALID_INIT_SYSTEMS)
|
||||
|
||||
# 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'
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.delete('/' + api_server.VERSION +
|
||||
@ -609,7 +617,7 @@ class TestServerTestCase(base.TestCase):
|
||||
self.assertIn(init_system, consts.VALID_INIT_SYSTEMS)
|
||||
|
||||
# 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'
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.delete('/' + api_server.VERSION +
|
||||
@ -639,7 +647,7 @@ class TestServerTestCase(base.TestCase):
|
||||
self.assertIn(init_system, consts.VALID_INIT_SYSTEMS)
|
||||
|
||||
# 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(
|
||||
7, 'test', RANDOM_ERROR)
|
||||
if distro == consts.UBUNTU:
|
||||
@ -661,8 +669,9 @@ class TestServerTestCase(base.TestCase):
|
||||
def test_centos_get_haproxy(self):
|
||||
self._test_get_haproxy(consts.CENTOS)
|
||||
|
||||
@mock.patch('os.listdir')
|
||||
@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])
|
||||
|
||||
@ -670,23 +679,24 @@ class TestServerTestCase(base.TestCase):
|
||||
mock_exists.side_effect = [False]
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.get('/' + api_server.VERSION +
|
||||
'/listeners/123/haproxy')
|
||||
'/loadbalancer/123/haproxy')
|
||||
elif distro == consts.CENTOS:
|
||||
rv = self.centos_app.get('/' + api_server.VERSION +
|
||||
'/listeners/123/haproxy')
|
||||
'/loadbalancer/123/haproxy')
|
||||
self.assertEqual(404, rv.status_code)
|
||||
|
||||
mock_exists.side_effect = [True]
|
||||
mock_exists.side_effect = [True, True]
|
||||
|
||||
path = util.config_path('123')
|
||||
self.useFixture(test_utils.OpenFixture(path, CONTENT))
|
||||
|
||||
mock_listdir.return_value = ['123']
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.get('/' + api_server.VERSION +
|
||||
'/listeners/123/haproxy')
|
||||
'/loadbalancer/123/haproxy')
|
||||
elif distro == consts.CENTOS:
|
||||
rv = self.centos_app.get('/' + api_server.VERSION +
|
||||
'/listeners/123/haproxy')
|
||||
'/loadbalancer/123/haproxy')
|
||||
self.assertEqual(200, rv.status_code)
|
||||
self.assertEqual(six.b(CONTENT), rv.data)
|
||||
self.assertEqual('text/plain; charset=utf-8',
|
||||
@ -699,17 +709,17 @@ class TestServerTestCase(base.TestCase):
|
||||
self._test_get_all_listeners(consts.CENTOS)
|
||||
|
||||
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
|
||||
'get_listeners')
|
||||
@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')
|
||||
'get_loadbalancers')
|
||||
@mock.patch('octavia.amphorae.backends.agent.api_server.loadbalancer.'
|
||||
'Loadbalancer._check_haproxy_status')
|
||||
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
|
||||
'parse_haproxy_file')
|
||||
def _test_get_all_listeners(self, distro, mock_parse, mock_status,
|
||||
mock_listener):
|
||||
mock_lbs):
|
||||
self.assertIn(distro, [consts.UBUNTU, consts.CENTOS])
|
||||
|
||||
# no listeners
|
||||
mock_listener.side_effect = [[]]
|
||||
mock_lbs.side_effect = [[]]
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.get('/' + api_server.VERSION + '/listeners')
|
||||
elif distro == consts.CENTOS:
|
||||
@ -719,8 +729,8 @@ class TestServerTestCase(base.TestCase):
|
||||
self.assertFalse(jsonutils.loads(rv.data.decode('utf-8')))
|
||||
|
||||
# one listener ACTIVE
|
||||
mock_listener.side_effect = [['123']]
|
||||
mock_parse.side_effect = [{'mode': 'test'}]
|
||||
mock_lbs.side_effect = [['123']]
|
||||
mock_parse.side_effect = [['fake_socket', {'123': {'mode': 'test'}}]]
|
||||
mock_status.side_effect = [consts.ACTIVE]
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.get('/' + api_server.VERSION + '/listeners')
|
||||
@ -732,10 +742,11 @@ class TestServerTestCase(base.TestCase):
|
||||
[{'status': consts.ACTIVE, 'type': 'test', 'uuid': '123'}],
|
||||
jsonutils.loads(rv.data.decode('utf-8')))
|
||||
|
||||
# two listener one ACTIVE, one ERROR
|
||||
mock_listener.side_effect = [['123', '456']]
|
||||
mock_parse.side_effect = [{'mode': 'test'}, {'mode': 'http'}]
|
||||
mock_status.side_effect = [consts.ACTIVE, consts.ERROR]
|
||||
# two listeners, two modes
|
||||
mock_lbs.side_effect = [['123', '456']]
|
||||
mock_parse.side_effect = [['fake_socket', {'123': {'mode': 'test'}}],
|
||||
['fake_socket', {'456': {'mode': 'http'}}]]
|
||||
mock_status.return_value = consts.ACTIVE
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.get('/' + api_server.VERSION + '/listeners')
|
||||
elif distro == consts.CENTOS:
|
||||
@ -744,86 +755,7 @@ class TestServerTestCase(base.TestCase):
|
||||
self.assertEqual(200, rv.status_code)
|
||||
self.assertEqual(
|
||||
[{'status': consts.ACTIVE, 'type': 'test', 'uuid': '123'},
|
||||
{'status': consts.ERROR, 'type': '', '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'}])]),
|
||||
{'status': consts.ACTIVE, 'type': 'http', 'uuid': '456'}],
|
||||
jsonutils.loads(rv.data.decode('utf-8')))
|
||||
|
||||
def test_ubuntu_delete_cert(self):
|
||||
@ -838,11 +770,13 @@ class TestServerTestCase(base.TestCase):
|
||||
self.assertIn(distro, [consts.UBUNTU, consts.CENTOS])
|
||||
mock_exists.side_effect = [False]
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.delete('/' + api_server.VERSION +
|
||||
'/listeners/123/certificates/test.pem')
|
||||
rv = self.ubuntu_app.delete(
|
||||
'/' + api_server.VERSION +
|
||||
'/loadbalancer/123/certificates/test.pem')
|
||||
elif distro == consts.CENTOS:
|
||||
rv = self.centos_app.delete('/' + api_server.VERSION +
|
||||
'/listeners/123/certificates/test.pem')
|
||||
rv = self.centos_app.delete(
|
||||
'/' + api_server.VERSION +
|
||||
'/loadbalancer/123/certificates/test.pem')
|
||||
self.assertEqual(200, rv.status_code)
|
||||
self.assertEqual(OK, jsonutils.loads(rv.data.decode('utf-8')))
|
||||
mock_exists.assert_called_once_with(
|
||||
@ -851,20 +785,24 @@ class TestServerTestCase(base.TestCase):
|
||||
# wrong file name
|
||||
mock_exists.side_effect = [True]
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.delete('/' + api_server.VERSION +
|
||||
'/listeners/123/certificates/test.bla')
|
||||
rv = self.ubuntu_app.delete(
|
||||
'/' + api_server.VERSION +
|
||||
'/loadbalancer/123/certificates/test.bla')
|
||||
elif distro == consts.CENTOS:
|
||||
rv = self.centos_app.delete('/' + api_server.VERSION +
|
||||
'/listeners/123/certificates/test.bla')
|
||||
rv = self.centos_app.delete(
|
||||
'/' + api_server.VERSION +
|
||||
'/loadbalancer/123/certificates/test.bla')
|
||||
self.assertEqual(400, rv.status_code)
|
||||
|
||||
mock_exists.side_effect = [True]
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.delete('/' + api_server.VERSION +
|
||||
'/listeners/123/certificates/test.pem')
|
||||
rv = self.ubuntu_app.delete(
|
||||
'/' + api_server.VERSION +
|
||||
'/loadbalancer/123/certificates/test.pem')
|
||||
elif distro == consts.CENTOS:
|
||||
rv = self.centos_app.delete('/' + api_server.VERSION +
|
||||
'/listeners/123/certificates/test.pem')
|
||||
rv = self.centos_app.delete(
|
||||
'/' + api_server.VERSION +
|
||||
'/loadbalancer/123/certificates/test.pem')
|
||||
self.assertEqual(200, rv.status_code)
|
||||
self.assertEqual(OK, jsonutils.loads(rv.data.decode('utf-8')))
|
||||
mock_remove.assert_called_once_with(
|
||||
@ -886,10 +824,10 @@ class TestServerTestCase(base.TestCase):
|
||||
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.get('/' + api_server.VERSION +
|
||||
'/listeners/123/certificates/test.pem')
|
||||
'/loadbalancer/123/certificates/test.pem')
|
||||
elif distro == consts.CENTOS:
|
||||
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(dict(
|
||||
details='No certificate with filename: test.pem',
|
||||
@ -901,29 +839,29 @@ class TestServerTestCase(base.TestCase):
|
||||
mock_exists.side_effect = [True]
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.put('/' + api_server.VERSION +
|
||||
'/listeners/123/certificates/test.bla',
|
||||
'/loadbalancer/123/certificates/test.bla',
|
||||
data='TestTest')
|
||||
elif distro == consts.CENTOS:
|
||||
rv = self.centos_app.put('/' + api_server.VERSION +
|
||||
'/listeners/123/certificates/test.bla',
|
||||
'/loadbalancer/123/certificates/test.bla',
|
||||
data='TestTest')
|
||||
self.assertEqual(400, rv.status_code)
|
||||
|
||||
mock_exists.return_value = True
|
||||
mock_exists.side_effect = None
|
||||
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')
|
||||
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')
|
||||
self.useFixture(test_utils.OpenFixture(path, CONTENT))
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.get('/' + api_server.VERSION +
|
||||
'/listeners/123/certificates/test.pem')
|
||||
'/loadbalancer/123/certificates/test.pem')
|
||||
elif distro == consts.CENTOS:
|
||||
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(dict(md5sum=hashlib.md5(six.b(CONTENT)).hexdigest()),
|
||||
jsonutils.loads(rv.data.decode('utf-8')))
|
||||
@ -942,20 +880,20 @@ class TestServerTestCase(base.TestCase):
|
||||
# wrong file name
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.put('/' + api_server.VERSION +
|
||||
'/listeners/123/certificates/test.bla',
|
||||
'/loadbalancer/123/certificates/test.bla',
|
||||
data='TestTest')
|
||||
elif distro == consts.CENTOS:
|
||||
rv = self.centos_app.put('/' + api_server.VERSION +
|
||||
'/listeners/123/certificates/test.bla',
|
||||
'/loadbalancer/123/certificates/test.bla',
|
||||
data='TestTest')
|
||||
self.assertEqual(400, rv.status_code)
|
||||
|
||||
mock_exists.return_value = True
|
||||
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')
|
||||
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')
|
||||
|
||||
m = self.useFixture(test_utils.OpenFixture(path)).mock_open
|
||||
@ -963,11 +901,11 @@ class TestServerTestCase(base.TestCase):
|
||||
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.put('/' + api_server.VERSION +
|
||||
'/listeners/123/certificates/'
|
||||
'/loadbalancer/123/certificates/'
|
||||
'test.pem', data='TestTest')
|
||||
elif distro == consts.CENTOS:
|
||||
rv = self.centos_app.put('/' + api_server.VERSION +
|
||||
'/listeners/123/certificates/'
|
||||
'/loadbalancer/123/certificates/'
|
||||
'test.pem', data='TestTest')
|
||||
self.assertEqual(200, rv.status_code)
|
||||
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):
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.put('/' + api_server.VERSION +
|
||||
'/listeners/123/certificates/'
|
||||
'/loadbalancer/123/certificates/'
|
||||
'test.pem', data='TestTest')
|
||||
elif distro == consts.CENTOS:
|
||||
rv = self.centos_app.put('/' + api_server.VERSION +
|
||||
'/listeners/123/certificates/'
|
||||
'/loadbalancer/123/certificates/'
|
||||
'test.pem', data='TestTest')
|
||||
self.assertEqual(200, rv.status_code)
|
||||
self.assertEqual(OK, jsonutils.loads(rv.data.decode('utf-8')))
|
||||
@ -2626,7 +2564,7 @@ class TestServerTestCase(base.TestCase):
|
||||
haproxy_count = random.randrange(0, 100)
|
||||
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,
|
||||
'total': cpu_total, 'user': cpu_user},
|
||||
'disk': {'available': disk_available,
|
||||
@ -2699,3 +2637,11 @@ class TestServerTestCase(base.TestCase):
|
||||
rv = self.centos_app.put('/' + api_server.VERSION +
|
||||
'/config', data='TestTest')
|
||||
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')))
|
||||
|
@ -3274,7 +3274,8 @@ class AmphoraRepositoryTest(BaseRepositoryTest):
|
||||
enabled=True, peer_port=1025, default_pool_id=pool.id)
|
||||
|
||||
listener_ref = {listener.id: {'operating_status': constants.ONLINE,
|
||||
'protocol': constants.PROTOCOL_HTTP}}
|
||||
'protocol': constants.PROTOCOL_HTTP,
|
||||
'enabled': 1}}
|
||||
lb_ref['listeners'] = listener_ref
|
||||
|
||||
# Test with an LB, pool, and listener (no members)
|
||||
|
@ -19,13 +19,18 @@ from oslo_utils import uuidutils
|
||||
|
||||
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 util
|
||||
from octavia.common.jinja.haproxy.combined_listeners import jinja_cfg
|
||||
from octavia.tests.common import utils as test_utils
|
||||
import octavia.tests.unit.base as base
|
||||
from octavia.tests.unit.common.sample_configs import sample_configs_combined
|
||||
|
||||
|
||||
class TestAmphoraInfo(base.TestCase):
|
||||
|
||||
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)
|
||||
KEEPALIVED_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_3 = uuidutils.generate_uuid()
|
||||
FAKE_LISTENER_ID_4 = uuidutils.generate_uuid()
|
||||
LB_ID_1 = uuidutils.generate_uuid()
|
||||
|
||||
def setUp(self):
|
||||
super(TestAmphoraInfo, self).setUp()
|
||||
@ -40,6 +46,23 @@ class TestAmphoraInfo(base.TestCase):
|
||||
self.amp_info = amphora_info.AmphoraInfo(self.osutils_mock)
|
||||
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):
|
||||
if package_name == 'ipvsadm':
|
||||
return self.IPVSADM_VERSION
|
||||
@ -138,8 +161,8 @@ class TestAmphoraInfo(base.TestCase):
|
||||
u'haproxy_count': 5,
|
||||
u'haproxy_version': self.HAPROXY_VERSION,
|
||||
u'hostname': u'FAKE_HOST',
|
||||
u'listeners': [self.FAKE_LISTENER_ID_1,
|
||||
self.FAKE_LISTENER_ID_2],
|
||||
u'listeners': sorted([self.FAKE_LISTENER_ID_1,
|
||||
self.FAKE_LISTENER_ID_2]),
|
||||
u'load': [u'0.09', u'0.11', u'0.10'],
|
||||
u'memory': {u'buffers': 344792,
|
||||
u'cached': 4271856,
|
||||
@ -163,8 +186,7 @@ class TestAmphoraInfo(base.TestCase):
|
||||
'get_udp_listeners',
|
||||
return_value=[FAKE_LISTENER_ID_3, FAKE_LISTENER_ID_4])
|
||||
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
|
||||
'get_listeners', return_value=[FAKE_LISTENER_ID_1,
|
||||
FAKE_LISTENER_ID_2])
|
||||
'get_loadbalancers')
|
||||
@mock.patch('octavia.amphorae.backends.agent.api_server.'
|
||||
'amphora_info.AmphoraInfo._get_meminfo')
|
||||
@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,
|
||||
m_pkg_version, m_load, m_get_nets,
|
||||
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,
|
||||
'MemTotal': 21692784, 'Cached': 4271856,
|
||||
'Slab': 534384, 'MemFree': 12685624,
|
||||
@ -205,6 +227,7 @@ class TestAmphoraInfo(base.TestCase):
|
||||
self.udp_driver.get_subscribed_amp_compile_info.return_value = [
|
||||
'keepalived', 'ipvsadm']
|
||||
self.udp_driver.is_listener_running.side_effect = [True, False]
|
||||
mock_get_lb.return_value = [self.LB_ID_1]
|
||||
original_version = api_server.VERSION
|
||||
api_server.VERSION = self.API_VERSION
|
||||
expected_dict = {u'active': True,
|
||||
@ -221,10 +244,10 @@ class TestAmphoraInfo(base.TestCase):
|
||||
u'ipvsadm_version': self.IPVSADM_VERSION,
|
||||
u'udp_listener_process_count': 1,
|
||||
u'hostname': u'FAKE_HOST',
|
||||
u'listeners': list(set([self.FAKE_LISTENER_ID_1,
|
||||
self.FAKE_LISTENER_ID_2,
|
||||
self.FAKE_LISTENER_ID_3,
|
||||
self.FAKE_LISTENER_ID_4])),
|
||||
u'listeners': sorted(list(set(
|
||||
[self.FAKE_LISTENER_ID_3,
|
||||
self.FAKE_LISTENER_ID_4,
|
||||
'sample_listener_id_1']))),
|
||||
u'load': [u'0.09', u'0.11', u'0.10'],
|
||||
u'memory': {u'buffers': 344792,
|
||||
u'cached': 4271856,
|
||||
@ -245,7 +268,7 @@ class TestAmphoraInfo(base.TestCase):
|
||||
api_server.VERSION = original_version
|
||||
|
||||
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
|
||||
'is_listener_running')
|
||||
'is_lb_running')
|
||||
def test__count_haproxy_process(self, mock_is_running):
|
||||
|
||||
# Test no listeners passed in
|
||||
|
@ -17,7 +17,7 @@ import mock
|
||||
from octavia.amphorae.backends.agent.api_server import haproxy_compatibility
|
||||
from octavia.common import constants
|
||||
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):
|
||||
@ -30,7 +30,7 @@ class HAProxyCompatTestCase(base.TestCase):
|
||||
" user nobody\n"
|
||||
" log /dev/log local0\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"
|
||||
" maxconn {maxconn}\n\n"
|
||||
"defaults\n"
|
||||
@ -47,11 +47,11 @@ class HAProxyCompatTestCase(base.TestCase):
|
||||
" maxconn {maxconn}\n"
|
||||
" bind 10.0.0.2:80\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(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
self.backend_without_external = (
|
||||
"backend sample_pool_id_1\n"
|
||||
"backend sample_pool_id_1:sample_listener_id_1\n"
|
||||
" mode http\n"
|
||||
" balance roundrobin\n"
|
||||
" cookie SRV insert indirect nocache\n"
|
||||
@ -69,7 +69,7 @@ class HAProxyCompatTestCase(base.TestCase):
|
||||
"sample_member_id_2\n").format(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
self.backend_with_external = (
|
||||
"backend sample_pool_id_1\n"
|
||||
"backend sample_pool_id_1:sample_listener_id_1\n"
|
||||
" mode http\n"
|
||||
" balance roundrobin\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):
|
||||
# Test 1.6 version path, no change to config expected
|
||||
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)
|
||||
result_config = haproxy_compatibility.process_cfg_for_version_compat(
|
||||
test_config)
|
||||
@ -111,7 +111,7 @@ class HAProxyCompatTestCase(base.TestCase):
|
||||
|
||||
# Test 1.5 version path, external-check should be removed
|
||||
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)
|
||||
result_config = haproxy_compatibility.process_cfg_for_version_compat(
|
||||
test_config)
|
||||
|
@ -13,7 +13,6 @@
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
from werkzeug import exceptions
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
@ -29,13 +28,6 @@ class KeepalivedLvsTestCase(base.TestCase):
|
||||
super(KeepalivedLvsTestCase, self).setUp()
|
||||
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('os.path.exists')
|
||||
def test_delete_udp_listener_not_exist(self, m_exist, m_webob):
|
||||
|
@ -17,141 +17,35 @@ import subprocess
|
||||
import mock
|
||||
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.common import constants as consts
|
||||
from octavia.common.jinja.haproxy import jinja_cfg
|
||||
from octavia.tests.common import utils as test_utils
|
||||
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()
|
||||
LB_ID1 = uuidutils.generate_uuid()
|
||||
|
||||
|
||||
class ListenerTestCase(base.TestCase):
|
||||
def setUp(self):
|
||||
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.return_value = "ubuntu"
|
||||
self.test_listener = listener.Listener()
|
||||
|
||||
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))
|
||||
self.test_loadbalancer = loadbalancer.Loadbalancer()
|
||||
|
||||
@mock.patch('os.makedirs')
|
||||
@mock.patch('os.path.exists')
|
||||
@mock.patch('os.listdir')
|
||||
@mock.patch('os.path.join')
|
||||
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
|
||||
'get_listeners')
|
||||
'get_loadbalancers')
|
||||
@mock.patch('octavia.amphorae.backends.agent.api_server.util'
|
||||
'.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_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_exists.return_value = False
|
||||
cmd = 'haproxy-vrrp-check ' + ' '.join(['listener.sock']) + '; exit $?'
|
||||
@ -159,17 +53,17 @@ class ListenerTestCase(base.TestCase):
|
||||
path = agent_util.keepalived_dir()
|
||||
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.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',
|
||||
'listener.sock']) + '; exit '
|
||||
'$?')
|
||||
|
||||
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.write.assert_called_once_with(cmd)
|
||||
|
||||
@ -181,29 +75,29 @@ class ListenerTestCase(base.TestCase):
|
||||
mock_exists.side_effect = [True, True]
|
||||
self.assertEqual(
|
||||
consts.ACTIVE,
|
||||
self.test_listener._check_haproxy_status(LISTENER_ID1))
|
||||
self.test_loadbalancer._check_haproxy_status(LISTENER_ID1))
|
||||
|
||||
mock_exists.side_effect = [True, False]
|
||||
self.assertEqual(
|
||||
consts.OFFLINE,
|
||||
self.test_listener._check_haproxy_status(LISTENER_ID1))
|
||||
self.test_loadbalancer._check_haproxy_status(LISTENER_ID1))
|
||||
|
||||
mock_exists.side_effect = [False]
|
||||
self.assertEqual(
|
||||
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.'
|
||||
'_check_haproxy_status')
|
||||
@mock.patch('octavia.amphorae.backends.agent.api_server.listener.Listener.'
|
||||
'vrrp_check_script_update')
|
||||
@mock.patch('octavia.amphorae.backends.agent.api_server.loadbalancer.'
|
||||
'Loadbalancer._check_haproxy_status')
|
||||
@mock.patch('octavia.amphorae.backends.agent.api_server.loadbalancer.'
|
||||
'Loadbalancer.vrrp_check_script_update')
|
||||
@mock.patch('os.path.exists')
|
||||
@mock.patch('octavia.amphorae.backends.agent.api_server.listener.Listener.'
|
||||
'_check_listener_exists')
|
||||
@mock.patch('octavia.amphorae.backends.agent.api_server.loadbalancer.'
|
||||
'Loadbalancer._check_lb_exists')
|
||||
@mock.patch('subprocess.check_output')
|
||||
def test_start_stop_listener(self, mock_check_output, mock_list_exists,
|
||||
mock_path_exists, mock_vrrp_update,
|
||||
mock_check_status):
|
||||
def test_start_stop_lb(self, mock_check_output, mock_lb_exists,
|
||||
mock_path_exists, mock_vrrp_update,
|
||||
mock_check_status):
|
||||
listener_id = uuidutils.generate_uuid()
|
||||
|
||||
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(consts.AMP_ACTION_START)
|
||||
|
||||
result = self.test_listener.start_stop_listener(
|
||||
result = self.test_loadbalancer.start_stop_lb(
|
||||
listener_id, consts.AMP_ACTION_START)
|
||||
|
||||
mock_check_output.assert_called_once_with(ref_command_split,
|
||||
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()
|
||||
self.assertEqual(202, result.status_code)
|
||||
self.assertEqual('OK', result.json['message'])
|
||||
@ -228,7 +122,7 @@ class ListenerTestCase(base.TestCase):
|
||||
self.assertEqual(ref_details, result.json['details'])
|
||||
|
||||
# Happy path - VRRP - RELOAD
|
||||
mock_list_exists.reset_mock()
|
||||
mock_lb_exists.reset_mock()
|
||||
mock_vrrp_update.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(consts.AMP_ACTION_RELOAD)
|
||||
|
||||
result = self.test_listener.start_stop_listener(
|
||||
result = self.test_loadbalancer.start_stop_lb(
|
||||
listener_id, consts.AMP_ACTION_RELOAD)
|
||||
|
||||
mock_check_output.assert_called_once_with(ref_command_split,
|
||||
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,
|
||||
consts.AMP_ACTION_RELOAD)
|
||||
self.assertEqual(202, result.status_code)
|
||||
@ -251,7 +145,7 @@ class ListenerTestCase(base.TestCase):
|
||||
self.assertEqual(ref_details, result.json['details'])
|
||||
|
||||
# Happy path - VRRP - RELOAD - OFFLINE
|
||||
mock_list_exists.reset_mock()
|
||||
mock_lb_exists.reset_mock()
|
||||
mock_vrrp_update.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(consts.AMP_ACTION_START)
|
||||
|
||||
result = self.test_listener.start_stop_listener(
|
||||
result = self.test_loadbalancer.start_stop_lb(
|
||||
listener_id, consts.AMP_ACTION_RELOAD)
|
||||
|
||||
mock_check_output.assert_called_once_with(ref_command_split,
|
||||
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,
|
||||
consts.AMP_ACTION_RELOAD)
|
||||
self.assertEqual(202, result.status_code)
|
||||
@ -274,7 +168,7 @@ class ListenerTestCase(base.TestCase):
|
||||
self.assertEqual(ref_details, result.json['details'])
|
||||
|
||||
# Unhappy path - Not already running
|
||||
mock_list_exists.reset_mock()
|
||||
mock_lb_exists.reset_mock()
|
||||
mock_vrrp_update.reset_mock()
|
||||
mock_check_output.reset_mock()
|
||||
|
||||
@ -285,12 +179,12 @@ class ListenerTestCase(base.TestCase):
|
||||
mock_check_output.side_effect = subprocess.CalledProcessError(
|
||||
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)
|
||||
|
||||
mock_check_output.assert_called_once_with(ref_command_split,
|
||||
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()
|
||||
self.assertEqual(500, result.status_code)
|
||||
self.assertEqual('Error {}ing haproxy'.format(consts.AMP_ACTION_START),
|
||||
@ -298,7 +192,7 @@ class ListenerTestCase(base.TestCase):
|
||||
self.assertEqual('bogus', result.json['details'])
|
||||
|
||||
# Unhappy path - Already running
|
||||
mock_list_exists.reset_mock()
|
||||
mock_lb_exists.reset_mock()
|
||||
mock_vrrp_update.reset_mock()
|
||||
mock_check_output.reset_mock()
|
||||
|
||||
@ -309,12 +203,12 @@ class ListenerTestCase(base.TestCase):
|
||||
mock_check_output.side_effect = subprocess.CalledProcessError(
|
||||
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)
|
||||
|
||||
mock_check_output.assert_called_once_with(ref_command_split,
|
||||
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()
|
||||
self.assertEqual(202, result.status_code)
|
||||
self.assertEqual('OK', result.json['message'])
|
||||
@ -324,14 +218,62 @@ class ListenerTestCase(base.TestCase):
|
||||
|
||||
# Invalid action
|
||||
mock_check_output.reset_mock()
|
||||
mock_list_exists.reset_mock()
|
||||
mock_lb_exists.reset_mock()
|
||||
mock_path_exists.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('Invalid Request', result.json['message'])
|
||||
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_vrrp_update.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')])
|
@ -22,11 +22,15 @@ from oslo_utils import uuidutils
|
||||
|
||||
from octavia.amphorae.backends.agent.api_server import util
|
||||
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
|
||||
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
|
||||
LISTENER_ID1 = uuidutils.generate_uuid()
|
||||
|
||||
|
||||
class TestUtil(base.TestCase):
|
||||
@ -34,6 +38,9 @@ class TestUtil(base.TestCase):
|
||||
super(TestUtil, self).setUp()
|
||||
self.CONF = self.useFixture(oslo_fixture.Config(cfg.CONF))
|
||||
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):
|
||||
fake_path = '/fake/path'
|
||||
@ -171,7 +178,7 @@ class TestUtil(base.TestCase):
|
||||
mock_cfg_path.return_value = '/there'
|
||||
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_path_exists.assert_called_once_with('/there')
|
||||
@ -180,7 +187,7 @@ class TestUtil(base.TestCase):
|
||||
|
||||
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_lvs_path.assert_called_once_with('2')
|
||||
@ -189,8 +196,97 @@ class TestUtil(base.TestCase):
|
||||
mock_cfg_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_lvs_path.assert_called_once_with('3')
|
||||
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)
|
||||
|
@ -52,7 +52,7 @@ SAMPLE_STATS = ({'': '', 'status': 'OPEN', 'lastchg': '',
|
||||
'rate': '0', 'req_rate': '0', 'check_status': '',
|
||||
'econ': '', 'comp_out': '0', 'wredis': '', 'dresp': '0',
|
||||
'ereq': '0', 'tracked': '', 'comp_in': '0',
|
||||
'pxname': '490b6ae7-21aa-43f1-b82a-68ddcd2ca2fb',
|
||||
'pxname': LISTENER_ID1,
|
||||
'dreq': '0', 'hrsp_5xx': '0', 'last_chk': '',
|
||||
'check_code': '', 'sid': '0', 'bout': '0', 'hrsp_1xx': '0',
|
||||
'qlimit': '', 'hrsp_other': '0', 'bin': '0', 'rtime': '',
|
||||
@ -107,25 +107,16 @@ SAMPLE_STATS = ({'': '', 'status': 'OPEN', 'lastchg': '',
|
||||
SAMPLE_STATS_MSG = {
|
||||
'listeners': {
|
||||
LISTENER_ID1: {
|
||||
'pools': {
|
||||
'432fc8b3-d446-48d4-bb64-13beb90e22bc': {
|
||||
'members': {
|
||||
'302e33d9-dee1-4de9-98d5-36329a06fb58': 'DOWN'},
|
||||
'status': 'UP'}},
|
||||
'stats': {
|
||||
'totconns': 0, 'conns': 0,
|
||||
'tx': 0, 'rx': 0, 'ereq': 0},
|
||||
'status': 'OPEN'},
|
||||
LISTENER_ID2: {
|
||||
'pools': {
|
||||
'432fc8b3-d446-48d4-bb64-13beb90e22bc': {
|
||||
'members': {
|
||||
'302e33d9-dee1-4de9-98d5-36329a06fb58': 'DOWN'},
|
||||
'status': 'UP'}},
|
||||
'stats': {
|
||||
'totconns': 0, 'conns': 0,
|
||||
'tx': 0, 'rx': 0, 'ereq': 0},
|
||||
'status': 'OPEN'}
|
||||
},
|
||||
'pools': {'432fc8b3-d446-48d4-bb64-13beb90e22bc': {
|
||||
'members': {'302e33d9-dee1-4de9-98d5-36329a06fb58': 'DOWN'},
|
||||
'status': 'UP'}, '432fc8b3-d446-48d4-bb64-13beb90e22bc': {
|
||||
'members': {'302e33d9-dee1-4de9-98d5-36329a06fb58': 'DOWN'},
|
||||
'status': 'UP'},
|
||||
},
|
||||
'id': None,
|
||||
'seq': 0,
|
||||
@ -141,7 +132,7 @@ class TestHealthDaemon(base.TestCase):
|
||||
conf.config(group="haproxy_amphora", base_path=BASE_PATH)
|
||||
|
||||
@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):
|
||||
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()
|
||||
|
||||
@mock.patch('octavia.amphorae.backends.agent.api_server.'
|
||||
'util.is_listener_running')
|
||||
'util.is_lb_running')
|
||||
@mock.patch('octavia.amphorae.backends.health_daemon.'
|
||||
'health_daemon.get_stats')
|
||||
@mock.patch('octavia.amphorae.backends.health_daemon.'
|
||||
@ -318,7 +309,7 @@ class TestHealthDaemon(base.TestCase):
|
||||
mock_get_stats.assert_any_call('TEST2')
|
||||
|
||||
@mock.patch('octavia.amphorae.backends.agent.api_server.'
|
||||
'util.is_listener_running')
|
||||
'util.is_lb_running')
|
||||
@mock.patch('octavia.amphorae.backends.health_daemon.'
|
||||
'health_daemon.get_stats')
|
||||
@mock.patch('octavia.amphorae.backends.health_daemon.'
|
||||
@ -336,25 +327,6 @@ class TestHealthDaemon(base.TestCase):
|
||||
|
||||
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."
|
||||
"get_udp_listener_pool_status")
|
||||
@mock.patch("octavia.amphorae.backends.utils.keepalivedlvs_query."
|
||||
@ -411,7 +383,7 @@ class TestHealthDaemon(base.TestCase):
|
||||
'status': constants.DOWN,
|
||||
'pools': {},
|
||||
'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}
|
||||
msg = health_daemon.build_stats_message()
|
||||
self.assertEqual(expected, msg)
|
||||
|
@ -29,22 +29,29 @@ STATS_SOCKET_SAMPLE = (
|
||||
"_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,"
|
||||
"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"
|
||||
",,1,3,1,,0,,2,0,,0,L4TOUT,,30001,0,0,0,0,0,0,0,,,,0,0,,,,,-1,,,0,0,0,0,\n"
|
||||
"http-servers,id-34824,0,0,0,0,,0,0,0,,0,,0,0,0,0,DOWN,1,1,0,1,1,567,567,"
|
||||
",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,BACKEND,0,0,0,0,200,0,0,0,0,0,,0,0,0,0,DOWN,0,0,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,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,,"
|
||||
"1,5,1,,0,,2,0,,0,L4TOUT,,30000,,,,,,,0,,,,0,0,,,,,-1,,,0,0,0,0,\n"
|
||||
"tcp-servers,id-34836,0,0,0,0,,0,0,0,,0,,0,0,0,0,UP,1,1,0,1,1,552,552,,"
|
||||
"1,5,2,,0,,2,0,,0,L4TOUT,,30001,,,,,,,0,,,,0,0,,,,,-1,,,0,0,0,0,\n"
|
||||
"tcp-servers,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,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,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,"
|
||||
"http-servers:listener-id,id-34821,0,0,0,0,,0,0,0,,0,,0,0,0,0,DOWN,1,1,0,"
|
||||
"1,1,575,575,,1,3,1,,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,"
|
||||
"1,1,567,567,,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,BACKEND,0,0,0,0,200,0,0,0,0,0,,0,0,0,0,DOWN,0,0,"
|
||||
"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,"
|
||||
"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,"
|
||||
"560,560,,1,5,1,,0,,2,0,,0,L4TOUT,,30000,,,,,,,0,,,,0,0,,,,,-1,,,0,0,0,0,"
|
||||
"\n"
|
||||
"tcp-servers:listener-id,id-34836,0,0,0,0,,0,0,0,,0,,0,0,0,0,UP,1,1,0,1,1,"
|
||||
"552,552,,1,5,2,,0,,2,0,,0,L4TOUT,,30001,,,,,,,0,,,,0,0,,,,,-1,,,0,0,0,0,"
|
||||
"\n"
|
||||
"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 = (
|
||||
@ -94,17 +101,19 @@ class QueryTestCase(base.TestCase):
|
||||
self.q._query = query_mock
|
||||
query_mock.return_value = STATS_SOCKET_SAMPLE
|
||||
self.assertEqual(
|
||||
{'tcp-servers': {
|
||||
{'tcp-servers:listener-id': {
|
||||
'status': constants.UP,
|
||||
'uuid': 'tcp-servers',
|
||||
'listener_uuid': 'listener-id',
|
||||
'pool_uuid': 'tcp-servers',
|
||||
'members':
|
||||
{'id-34833': constants.UP,
|
||||
'id-34836': constants.UP,
|
||||
'id-34839': constants.DRAIN,
|
||||
'id-34842': constants.MAINT}},
|
||||
'http-servers': {
|
||||
'http-servers:listener-id': {
|
||||
'status': constants.DOWN,
|
||||
'uuid': 'http-servers',
|
||||
'listener_uuid': 'listener-id',
|
||||
'pool_uuid': 'http-servers',
|
||||
'members':
|
||||
{'id-34821': constants.DOWN,
|
||||
'id-34824': constants.DOWN}}},
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -20,18 +20,25 @@ from octavia.amphorae.drivers.keepalived import vrrp_rest_driver
|
||||
from octavia.common import constants
|
||||
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):
|
||||
|
||||
def setUp(self):
|
||||
self.keepalived_mixin = vrrp_rest_driver.KeepalivedAmphoraDriverMixin()
|
||||
self.keepalived_mixin.client = mock.MagicMock()
|
||||
self.client = self.keepalived_mixin.client
|
||||
self.keepalived_mixin.clients = {
|
||||
'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.lb_mock = mock.MagicMock()
|
||||
self.amphora_mock = mock.MagicMock()
|
||||
self.amphora_mock.id = uuidutils.generate_uuid()
|
||||
self.amphora_mock.status = constants.AMPHORA_ALLOCATED
|
||||
self.amphora_mock.api_version = API_VERSION
|
||||
self.lb_mock.amphorae = [self.amphora_mock]
|
||||
self.amphorae_network_config = {}
|
||||
vip_subnet = mock.MagicMock()
|
||||
@ -49,7 +56,7 @@ class TestVRRPRestDriver(base.TestCase):
|
||||
self.keepalived_mixin.update_vrrp_conf(self.lb_mock,
|
||||
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.FAKE_CONFIG)
|
||||
|
||||
@ -57,16 +64,19 @@ class TestVRRPRestDriver(base.TestCase):
|
||||
|
||||
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):
|
||||
|
||||
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):
|
||||
|
||||
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)
|
||||
|
@ -12,6 +12,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from octavia.amphorae.drivers.noop_driver import driver
|
||||
@ -54,6 +55,7 @@ class TestNoopAmphoraLoadBalancerDriver(base.TestCase):
|
||||
self.load_balancer = data_models.LoadBalancer(
|
||||
id=FAKE_UUID_1, amphorae=[self.amphora], vip=self.vip,
|
||||
listeners=[self.listener])
|
||||
self.listener.load_balancer = self.load_balancer
|
||||
self.network = network_models.Network(id=self.FAKE_UUID_1)
|
||||
self.port = network_models.Port(id=uuidutils.generate_uuid())
|
||||
self.amphorae_net_configs = {
|
||||
@ -70,8 +72,7 @@ class TestNoopAmphoraLoadBalancerDriver(base.TestCase):
|
||||
constants.CONN_RETRY_INTERVAL: 4}
|
||||
|
||||
def test_update_amphora_listeners(self):
|
||||
amphorae = [self.amphora]
|
||||
self.driver.update_amphora_listeners([self.listener], 0, amphorae,
|
||||
self.driver.update_amphora_listeners(self.load_balancer, self.amphora,
|
||||
self.timeout_dict)
|
||||
self.assertEqual((self.listener, self.amphora.id, self.timeout_dict,
|
||||
'update_amp'),
|
||||
@ -80,28 +81,22 @@ class TestNoopAmphoraLoadBalancerDriver(base.TestCase):
|
||||
self.amphora.id)])
|
||||
|
||||
def test_update(self):
|
||||
self.driver.update(self.listener, self.vip)
|
||||
self.assertEqual((self.listener, self.vip, 'active'),
|
||||
self.driver.update(self.load_balancer)
|
||||
self.assertEqual(([self.listener], self.vip, 'active'),
|
||||
self.driver.driver.amphoraconfig[(
|
||||
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.listener.protocol_port,),
|
||||
self.vip.ip_address)])
|
||||
|
||||
def test_start(self):
|
||||
self.driver.start(self.listener, self.vip, amphora='amp1')
|
||||
self.assertEqual((self.listener, self.vip, 'amp1', 'start'),
|
||||
mock_amphora = mock.MagicMock()
|
||||
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.listener.protocol_port,
|
||||
self.vip.ip_address, 'amp1')])
|
||||
self.load_balancer.id, '321')])
|
||||
|
||||
def test_delete(self):
|
||||
self.driver.delete(self.listener, self.vip)
|
||||
self.driver.delete(self.listener)
|
||||
self.assertEqual((self.listener, self.vip, 'delete'),
|
||||
self.driver.driver.amphoraconfig[(
|
||||
self.listener.protocol_port,
|
||||
|
@ -134,7 +134,8 @@ class TestBarbicanManager(base.TestCase):
|
||||
self.assertIsInstance(data, cert.Cert)
|
||||
self.assertEqual(sample.X509_CERT_KEY, data.get_private_key())
|
||||
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())
|
||||
|
||||
def test_delete_cert_legacy(self):
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -17,9 +17,9 @@ import copy
|
||||
import os
|
||||
|
||||
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.common.sample_configs import sample_configs
|
||||
from octavia.tests.unit.common.sample_configs import sample_configs_split
|
||||
|
||||
|
||||
class TestHaproxyCfg(base.TestCase):
|
||||
@ -70,20 +70,19 @@ class TestHaproxyCfg(base.TestCase):
|
||||
"weight 13 check inter 30s fall 3 rise 2 cookie "
|
||||
"sample_member_id_2\n\n").format(
|
||||
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',
|
||||
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,
|
||||
client_ca_cert=True,
|
||||
client_crl_cert=True),
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(
|
||||
proto='TERMINATED_HTTPS', tls=True, sni=True,
|
||||
client_ca_cert=True, client_crl_cert=True),
|
||||
tls_tupe, client_ca_filename='client_ca.pem',
|
||||
client_crl='SHA_ID.pem')
|
||||
self.assertEqual(
|
||||
sample_configs.sample_base_expected_config(
|
||||
sample_configs_split.sample_base_expected_config(
|
||||
frontend=fe, backend=be),
|
||||
rendered_obj)
|
||||
|
||||
@ -121,16 +120,16 @@ class TestHaproxyCfg(base.TestCase):
|
||||
"cookie sample_member_id_2\n\n").format(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple(
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(
|
||||
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',
|
||||
certificate='ImAalsdkfjCert',
|
||||
private_key='ImAsdlfksdjPrivateKey',
|
||||
primary_cn="FakeCN"))
|
||||
self.assertEqual(
|
||||
sample_configs.sample_base_expected_config(
|
||||
sample_configs_split.sample_base_expected_config(
|
||||
frontend=fe, backend=be),
|
||||
rendered_obj)
|
||||
|
||||
@ -154,10 +153,10 @@ class TestHaproxyCfg(base.TestCase):
|
||||
"cookie sample_member_id_2\n\n").format(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple())
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple())
|
||||
self.assertEqual(
|
||||
sample_configs.sample_base_expected_config(backend=be),
|
||||
sample_configs_split.sample_base_expected_config(backend=be),
|
||||
rendered_obj)
|
||||
|
||||
def test_render_template_member_backup(self):
|
||||
@ -182,11 +181,11 @@ class TestHaproxyCfg(base.TestCase):
|
||||
"cookie sample_member_id_2 backup\n\n").format(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple(monitor_ip_port=True,
|
||||
backup_member=True))
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(
|
||||
monitor_ip_port=True, backup_member=True))
|
||||
self.assertEqual(
|
||||
sample_configs.sample_base_expected_config(backend=be),
|
||||
sample_configs_split.sample_base_expected_config(backend=be),
|
||||
rendered_obj)
|
||||
|
||||
def test_render_template_custom_timeouts(self):
|
||||
@ -219,13 +218,13 @@ class TestHaproxyCfg(base.TestCase):
|
||||
"sample_member_id_2\n\n").format(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple(timeout_member_connect=1,
|
||||
timeout_client_data=2,
|
||||
timeout_member_data=3))
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(
|
||||
timeout_member_connect=1, timeout_client_data=2,
|
||||
timeout_member_data=3))
|
||||
self.assertEqual(
|
||||
sample_configs.sample_base_expected_config(frontend=fe,
|
||||
backend=be),
|
||||
sample_configs_split.sample_base_expected_config(
|
||||
frontend=fe, backend=be),
|
||||
rendered_obj)
|
||||
|
||||
def test_render_template_null_timeouts(self):
|
||||
@ -258,13 +257,13 @@ class TestHaproxyCfg(base.TestCase):
|
||||
"sample_member_id_2\n\n").format(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple(timeout_member_connect=None,
|
||||
timeout_client_data=None,
|
||||
timeout_member_data=None))
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(
|
||||
timeout_member_connect=None, timeout_client_data=None,
|
||||
timeout_member_data=None))
|
||||
self.assertEqual(
|
||||
sample_configs.sample_base_expected_config(frontend=fe,
|
||||
backend=be),
|
||||
sample_configs_split.sample_base_expected_config(
|
||||
frontend=fe, backend=be),
|
||||
rendered_obj)
|
||||
|
||||
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(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple(monitor_ip_port=True))
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(monitor_ip_port=True))
|
||||
self.assertEqual(
|
||||
sample_configs.sample_base_expected_config(backend=be),
|
||||
sample_configs_split.sample_base_expected_config(backend=be),
|
||||
rendered_obj)
|
||||
|
||||
def test_render_template_https_real_monitor(self):
|
||||
@ -326,9 +325,9 @@ class TestHaproxyCfg(base.TestCase):
|
||||
"cookie sample_member_id_2\n\n").format(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple(proto='HTTPS'))
|
||||
self.assertEqual(sample_configs.sample_base_expected_config(
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(proto='HTTPS'))
|
||||
self.assertEqual(sample_configs_split.sample_base_expected_config(
|
||||
frontend=fe, backend=be), rendered_obj)
|
||||
|
||||
def test_render_template_https_hello_monitor(self):
|
||||
@ -361,10 +360,10 @@ class TestHaproxyCfg(base.TestCase):
|
||||
"cookie sample_member_id_2\n\n").format(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple(
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(
|
||||
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)
|
||||
|
||||
def test_render_template_no_monitor_http(self):
|
||||
@ -382,9 +381,10 @@ class TestHaproxyCfg(base.TestCase):
|
||||
"cookie sample_member_id_2\n\n").format(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple(proto='HTTP', monitor=False))
|
||||
self.assertEqual(sample_configs.sample_base_expected_config(
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(
|
||||
proto='HTTP', monitor=False))
|
||||
self.assertEqual(sample_configs_split.sample_base_expected_config(
|
||||
backend=be), rendered_obj)
|
||||
|
||||
def test_render_template_disabled_member(self):
|
||||
@ -402,10 +402,10 @@ class TestHaproxyCfg(base.TestCase):
|
||||
"cookie sample_member_id_2 disabled\n\n").format(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple(proto='HTTP', monitor=False,
|
||||
disabled_member=True))
|
||||
self.assertEqual(sample_configs.sample_base_expected_config(
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(
|
||||
proto='HTTP', monitor=False, disabled_member=True))
|
||||
self.assertEqual(sample_configs_split.sample_base_expected_config(
|
||||
backend=be), rendered_obj)
|
||||
|
||||
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(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple(proto='HTTP',
|
||||
monitor_proto='PING'))
|
||||
self.assertEqual(sample_configs.sample_base_expected_config(
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(
|
||||
proto='HTTP', monitor_proto='PING'))
|
||||
self.assertEqual(sample_configs_split.sample_base_expected_config(
|
||||
backend=be, global_opts=go), rendered_obj)
|
||||
|
||||
def test_render_template_no_monitor_https(self):
|
||||
@ -462,9 +462,10 @@ class TestHaproxyCfg(base.TestCase):
|
||||
"cookie sample_member_id_2\n\n").format(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple(proto='HTTPS', monitor=False))
|
||||
self.assertEqual(sample_configs.sample_base_expected_config(
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(
|
||||
proto='HTTPS', monitor=False))
|
||||
self.assertEqual(sample_configs_split.sample_base_expected_config(
|
||||
frontend=fe, backend=be), rendered_obj)
|
||||
|
||||
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(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple(proto='HTTP',
|
||||
monitor_proto='HTTP',
|
||||
hm_host_http_check=True))
|
||||
self.assertEqual(sample_configs.sample_base_expected_config(
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(
|
||||
proto='HTTP', monitor_proto='HTTP', hm_host_http_check=True))
|
||||
self.assertEqual(sample_configs_split.sample_base_expected_config(
|
||||
backend=be), rendered_obj)
|
||||
|
||||
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 "
|
||||
"weight 13\n\n").format(maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple(proto='HTTPS', monitor=False,
|
||||
persistence=False))
|
||||
self.assertEqual(sample_configs.sample_base_expected_config(
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(
|
||||
proto='HTTPS', monitor=False, persistence=False))
|
||||
self.assertEqual(sample_configs_split.sample_base_expected_config(
|
||||
frontend=fe, backend=be), rendered_obj)
|
||||
|
||||
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 "
|
||||
"weight 13\n\n").format(maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple(proto='HTTP', monitor=False,
|
||||
persistence=False))
|
||||
self.assertEqual(sample_configs.sample_base_expected_config(
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(
|
||||
proto='HTTP', monitor=False, persistence=False))
|
||||
self.assertEqual(sample_configs_split.sample_base_expected_config(
|
||||
backend=be), rendered_obj)
|
||||
|
||||
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(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple(
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(
|
||||
persistence_type='SOURCE_IP'))
|
||||
self.assertEqual(
|
||||
sample_configs.sample_base_expected_config(backend=be),
|
||||
sample_configs_split.sample_base_expected_config(backend=be),
|
||||
rendered_obj)
|
||||
|
||||
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(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple(
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(
|
||||
persistence_type='APP_COOKIE',
|
||||
persistence_cookie='JSESSIONID'))
|
||||
self.assertEqual(
|
||||
sample_configs.sample_base_expected_config(backend=be),
|
||||
sample_configs_split.sample_base_expected_config(backend=be),
|
||||
rendered_obj)
|
||||
|
||||
def test_render_template_unlimited_connections(self):
|
||||
@ -622,9 +622,10 @@ class TestHaproxyCfg(base.TestCase):
|
||||
"cookie sample_member_id_2\n\n").format(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple(proto='HTTPS', monitor=False))
|
||||
self.assertEqual(sample_configs.sample_base_expected_config(
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(
|
||||
proto='HTTPS', monitor=False))
|
||||
self.assertEqual(sample_configs_split.sample_base_expected_config(
|
||||
frontend=fe, backend=be), rendered_obj)
|
||||
|
||||
def test_render_template_limited_connections(self):
|
||||
@ -652,10 +653,10 @@ class TestHaproxyCfg(base.TestCase):
|
||||
"cookie sample_member_id_2\n\n")
|
||||
g_opts = " maxconn 2014\n\n"
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple(proto='HTTPS', monitor=False,
|
||||
connection_limit=2014))
|
||||
self.assertEqual(sample_configs.sample_base_expected_config(
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(
|
||||
proto='HTTPS', monitor=False, connection_limit=2014))
|
||||
self.assertEqual(sample_configs_split.sample_base_expected_config(
|
||||
frontend=fe, backend=be, global_opts=g_opts), rendered_obj)
|
||||
|
||||
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(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple(l7=True))
|
||||
self.assertEqual(sample_configs.sample_base_expected_config(
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(l7=True))
|
||||
self.assertEqual(sample_configs_split.sample_base_expected_config(
|
||||
frontend=fe, backend=be), rendered_obj)
|
||||
|
||||
def test_render_template_http_xff(self):
|
||||
@ -746,11 +747,11 @@ class TestHaproxyCfg(base.TestCase):
|
||||
"cookie sample_member_id_2\n\n").format(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple(
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(
|
||||
insert_headers={'X-Forwarded-For': 'true'}))
|
||||
self.assertEqual(
|
||||
sample_configs.sample_base_expected_config(backend=be),
|
||||
sample_configs_split.sample_base_expected_config(backend=be),
|
||||
rendered_obj)
|
||||
|
||||
def test_render_template_http_xff_xfport(self):
|
||||
@ -775,12 +776,12 @@ class TestHaproxyCfg(base.TestCase):
|
||||
"cookie sample_member_id_2\n\n").format(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple(
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(
|
||||
insert_headers={'X-Forwarded-For': 'true',
|
||||
'X-Forwarded-Port': 'true'}))
|
||||
self.assertEqual(
|
||||
sample_configs.sample_base_expected_config(backend=be),
|
||||
sample_configs_split.sample_base_expected_config(backend=be),
|
||||
rendered_obj)
|
||||
|
||||
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(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple(
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(
|
||||
be_proto='PROXY'))
|
||||
self.assertEqual(
|
||||
sample_configs.sample_base_expected_config(backend=be),
|
||||
sample_configs_split.sample_base_expected_config(backend=be),
|
||||
rendered_obj)
|
||||
|
||||
def test_render_template_pool_cert(self):
|
||||
@ -831,15 +832,15 @@ class TestHaproxyCfg(base.TestCase):
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN,
|
||||
opts="ssl crt %s verify none sni ssl_fc_sni" % cert_file_path)
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple(
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(
|
||||
pool_cert=True, tls_enabled=True),
|
||||
pool_tls_certs={
|
||||
'sample_pool_id_1':
|
||||
{'client_cert': cert_file_path,
|
||||
'ca_cert': None, 'crl': None}})
|
||||
self.assertEqual(
|
||||
sample_configs.sample_base_expected_config(backend=be),
|
||||
sample_configs_split.sample_base_expected_config(backend=be),
|
||||
rendered_obj)
|
||||
|
||||
def test_render_template_with_full_pool_cert(self):
|
||||
@ -870,8 +871,8 @@ class TestHaproxyCfg(base.TestCase):
|
||||
"crl-file %s" % pool_crl,
|
||||
"verify required sni ssl_fc_sni"))
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple(
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(
|
||||
pool_cert=True, pool_ca_cert=True, pool_crl=True,
|
||||
tls_enabled=True),
|
||||
pool_tls_certs={
|
||||
@ -880,107 +881,109 @@ class TestHaproxyCfg(base.TestCase):
|
||||
'ca_cert': pool_ca_cert,
|
||||
'crl': pool_crl}})
|
||||
self.assertEqual(
|
||||
sample_configs.sample_base_expected_config(backend=be),
|
||||
sample_configs_split.sample_base_expected_config(backend=be),
|
||||
rendered_obj)
|
||||
|
||||
def test_transform_session_persistence(self):
|
||||
in_persistence = sample_configs.sample_session_persistence_tuple()
|
||||
ret = self.jinja_cfg._transform_session_persistence(in_persistence, {})
|
||||
self.assertEqual(sample_configs.RET_PERSISTENCE, ret)
|
||||
in_persistence = (
|
||||
sample_configs_split.sample_session_persistence_tuple())
|
||||
ret = self.jinja_cfg._transform_session_persistence(
|
||||
in_persistence, {})
|
||||
self.assertEqual(sample_configs_split.RET_PERSISTENCE, ret)
|
||||
|
||||
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, {})
|
||||
self.assertEqual(sample_configs.RET_MONITOR_1, ret)
|
||||
self.assertEqual(sample_configs_split.RET_MONITOR_1, ret)
|
||||
|
||||
def test_transform_member(self):
|
||||
in_member = sample_configs.sample_member_tuple('sample_member_id_1',
|
||||
'10.0.0.99')
|
||||
in_member = sample_configs_split.sample_member_tuple(
|
||||
'sample_member_id_1', '10.0.0.99')
|
||||
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):
|
||||
in_pool = sample_configs.sample_pool_tuple()
|
||||
in_pool = sample_configs_split.sample_pool_tuple()
|
||||
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):
|
||||
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, {})
|
||||
self.assertEqual(sample_configs.RET_POOL_2, ret)
|
||||
self.assertEqual(sample_configs_split.RET_POOL_2, ret)
|
||||
|
||||
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(
|
||||
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
|
||||
self.assertEqual(expected_config, ret)
|
||||
|
||||
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,
|
||||
'test_listener_id', 'pool_cert.pem')
|
||||
ret = self.jinja_cfg._transform_pool(
|
||||
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
|
||||
self.assertEqual(expected_config, ret)
|
||||
|
||||
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, {},
|
||||
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):
|
||||
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, {},
|
||||
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):
|
||||
in_amphora = sample_configs.sample_amphora_tuple()
|
||||
in_listener = sample_configs.sample_listener_tuple()
|
||||
in_amphora = sample_configs_split.sample_amphora_tuple()
|
||||
in_listener = sample_configs_split.sample_listener_tuple()
|
||||
ret = self.jinja_cfg._transform_loadbalancer(
|
||||
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):
|
||||
in_amphora = sample_configs.sample_amphora_tuple()
|
||||
in_amphora = sample_configs_split.sample_amphora_tuple()
|
||||
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):
|
||||
in_amphora = sample_configs.sample_amphora_tuple()
|
||||
in_listener = sample_configs.sample_listener_tuple(l7=True)
|
||||
in_amphora = sample_configs_split.sample_amphora_tuple()
|
||||
in_listener = sample_configs_split.sample_listener_tuple(l7=True)
|
||||
ret = self.jinja_cfg._transform_loadbalancer(
|
||||
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):
|
||||
in_l7policy = sample_configs.sample_l7policy_tuple(
|
||||
in_l7policy = sample_configs_split.sample_l7policy_tuple(
|
||||
'sample_l7policy_id_1')
|
||||
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):
|
||||
in_l7policy = sample_configs.sample_l7policy_tuple(
|
||||
in_l7policy = sample_configs_split.sample_l7policy_tuple(
|
||||
'sample_l7policy_id_2', sample_policy=2)
|
||||
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
|
||||
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)
|
||||
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):
|
||||
in_l7policy = sample_configs.sample_l7policy_tuple(
|
||||
in_l7policy = sample_configs_split.sample_l7policy_tuple(
|
||||
'sample_l7policy_id_6', sample_policy=6)
|
||||
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):
|
||||
self.assertEqual(self.jinja_cfg._escape_haproxy_config_string(
|
||||
@ -1034,11 +1037,12 @@ class TestHaproxyCfg(base.TestCase):
|
||||
" option splice-response\n"
|
||||
" option http-keep-alive\n\n")
|
||||
rendered_obj = j_cfg.render_loadbalancer_obj(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple()
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple()
|
||||
)
|
||||
self.assertEqual(
|
||||
sample_configs.sample_base_expected_config(defaults=defaults),
|
||||
sample_configs_split.sample_base_expected_config(
|
||||
defaults=defaults),
|
||||
rendered_obj)
|
||||
|
||||
def test_http_reuse(self):
|
||||
@ -1065,12 +1069,12 @@ class TestHaproxyCfg(base.TestCase):
|
||||
"cookie sample_member_id_2 send-proxy\n\n").format(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
rendered_obj = j_cfg.build_config(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple(be_proto='PROXY'),
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(be_proto='PROXY'),
|
||||
tls_cert=None,
|
||||
haproxy_versions=("1", "8", "1"))
|
||||
self.assertEqual(
|
||||
sample_configs.sample_base_expected_config(backend=be),
|
||||
sample_configs_split.sample_base_expected_config(backend=be),
|
||||
rendered_obj)
|
||||
|
||||
# Without http-reuse
|
||||
@ -1091,12 +1095,12 @@ class TestHaproxyCfg(base.TestCase):
|
||||
"cookie sample_member_id_2 send-proxy\n\n").format(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
rendered_obj = j_cfg.build_config(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs.sample_listener_tuple(be_proto='PROXY'),
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(be_proto='PROXY'),
|
||||
tls_cert=None,
|
||||
haproxy_versions=("1", "5", "18"))
|
||||
self.assertEqual(
|
||||
sample_configs.sample_base_expected_config(backend=be),
|
||||
sample_configs_split.sample_base_expected_config(backend=be),
|
||||
rendered_obj)
|
||||
|
||||
def test_ssl_types_l7rules(self):
|
||||
@ -1172,15 +1176,15 @@ class TestHaproxyCfg(base.TestCase):
|
||||
" timeout server 50000\n"
|
||||
" 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")
|
||||
sample_listener = sample_configs.sample_listener_tuple(
|
||||
sample_listener = sample_configs_split.sample_listener_tuple(
|
||||
proto=constants.PROTOCOL_TERMINATED_HTTPS, l7=True,
|
||||
ssl_type_l7=True)
|
||||
rendered_obj = j_cfg.build_config(
|
||||
sample_configs.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_listener,
|
||||
tls_cert=None,
|
||||
haproxy_versions=("1", "5", "18"))
|
||||
self.assertEqual(
|
||||
sample_configs.sample_base_expected_config(
|
||||
sample_configs_split.sample_base_expected_config(
|
||||
frontend=fe, backend=be),
|
||||
rendered_obj)
|
@ -16,7 +16,7 @@
|
||||
from octavia.common import constants
|
||||
from octavia.common.jinja.lvs import jinja_cfg
|
||||
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 fixture as oslo_fixture
|
||||
@ -76,7 +76,7 @@ class TestLvsCfg(base.TestCase):
|
||||
" }\n\n"
|
||||
"}\n\n")
|
||||
rendered_obj = self.udp_jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs.sample_listener_tuple(
|
||||
sample_configs_combined.sample_listener_tuple(
|
||||
proto=constants.PROTOCOL_UDP,
|
||||
persistence_type=constants.SESSION_PERSISTENCE_SOURCE_IP,
|
||||
persistence_timeout=33,
|
||||
@ -124,7 +124,7 @@ class TestLvsCfg(base.TestCase):
|
||||
" }\n\n"
|
||||
"}\n\n")
|
||||
|
||||
listener = sample_configs.sample_listener_tuple(
|
||||
listener = sample_configs_combined.sample_listener_tuple(
|
||||
proto=constants.PROTOCOL_UDP,
|
||||
monitor_proto=constants.HEALTH_MONITOR_UDP_CONNECT,
|
||||
connection_limit=98,
|
||||
@ -172,7 +172,7 @@ class TestLvsCfg(base.TestCase):
|
||||
"}\n\n")
|
||||
|
||||
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_proto=constants.HEALTH_MONITOR_UDP_CONNECT,
|
||||
persistence=False,
|
||||
@ -185,56 +185,57 @@ class TestLvsCfg(base.TestCase):
|
||||
"net_namespace amphora-haproxy\n\n\n")
|
||||
|
||||
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,
|
||||
persistence=False, alloc_default_pool=False))
|
||||
self.assertEqual(exp, rendered_obj)
|
||||
|
||||
def test_udp_transform_session_persistence(self):
|
||||
persistence_src_ip = sample_configs.sample_session_persistence_tuple(
|
||||
persistence_type=constants.SESSION_PERSISTENCE_SOURCE_IP,
|
||||
persistence_cookie=None,
|
||||
persistence_timeout=33,
|
||||
persistence_granularity='255.0.0.0'
|
||||
)
|
||||
exp = sample_configs.UDP_SOURCE_IP_BODY
|
||||
persistence_src_ip = (
|
||||
sample_configs_combined.sample_session_persistence_tuple(
|
||||
persistence_type=constants.SESSION_PERSISTENCE_SOURCE_IP,
|
||||
persistence_cookie=None,
|
||||
persistence_timeout=33,
|
||||
persistence_granularity='255.0.0.0'
|
||||
))
|
||||
exp = sample_configs_combined.UDP_SOURCE_IP_BODY
|
||||
ret = self.udp_jinja_cfg._transform_session_persistence(
|
||||
persistence_src_ip)
|
||||
self.assertEqual(exp, ret)
|
||||
|
||||
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
|
||||
)
|
||||
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):
|
||||
in_member = sample_configs.sample_member_tuple('member_id_1',
|
||||
'192.0.2.10')
|
||||
in_member = sample_configs_combined.sample_member_tuple(
|
||||
'member_id_1', '192.0.2.10')
|
||||
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):
|
||||
in_pool = sample_configs.sample_pool_tuple(
|
||||
in_pool = sample_configs_combined.sample_pool_tuple(
|
||||
proto=constants.PROTOCOL_UDP,
|
||||
persistence_type=constants.SESSION_PERSISTENCE_SOURCE_IP,
|
||||
persistence_timeout=33, persistence_granularity='255.0.0.0',
|
||||
)
|
||||
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,
|
||||
persistence_type=constants.SESSION_PERSISTENCE_SOURCE_IP,
|
||||
persistence_timeout=33, persistence_granularity='255.0.0.0',
|
||||
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)
|
||||
self.assertEqual(sample_configs.RET_UDP_POOL, ret)
|
||||
self.assertEqual(sample_configs_combined.RET_UDP_POOL, ret)
|
||||
|
||||
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,
|
||||
persistence_type=constants.SESSION_PERSISTENCE_SOURCE_IP,
|
||||
persistence_timeout=33,
|
||||
@ -243,9 +244,9 @@ class TestLvsCfg(base.TestCase):
|
||||
connection_limit=98
|
||||
)
|
||||
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,
|
||||
persistence_type=constants.SESSION_PERSISTENCE_SOURCE_IP,
|
||||
persistence_timeout=33,
|
||||
@ -254,5 +255,5 @@ class TestLvsCfg(base.TestCase):
|
||||
connection_limit=-1)
|
||||
|
||||
ret = self.udp_jinja_cfg._transform_listener(in_listener)
|
||||
sample_configs.RET_UDP_LISTENER.pop('connection_limit')
|
||||
self.assertEqual(sample_configs.RET_UDP_LISTENER, ret)
|
||||
sample_configs_combined.RET_UDP_LISTENER.pop('connection_limit')
|
||||
self.assertEqual(sample_configs_combined.RET_UDP_LISTENER, ret)
|
||||
|
1076
octavia/tests/unit/common/sample_configs/sample_configs_combined.py
Normal file
1076
octavia/tests/unit/common/sample_configs/sample_configs_combined.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -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_port_id='1234', ha_port_id='1234', role=None,
|
||||
status='ACTIVE', vrrp_interface=None,
|
||||
vrrp_priority=None):
|
||||
vrrp_priority=None, api_version='0.5'):
|
||||
in_amphora = collections.namedtuple(
|
||||
'amphora', 'id, lb_network_ip, vrrp_ip, ha_ip, vrrp_port_id, '
|
||||
'ha_port_id, role, status, vrrp_interface,'
|
||||
'vrrp_priority')
|
||||
'vrrp_priority, api_version')
|
||||
return in_amphora(
|
||||
id=id,
|
||||
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,
|
||||
status=status,
|
||||
vrrp_interface=vrrp_interface,
|
||||
vrrp_priority=vrrp_priority)
|
||||
vrrp_priority=vrrp_priority,
|
||||
api_version=api_version)
|
||||
|
||||
|
||||
RET_PERSISTENCE = {
|
||||
@ -510,7 +511,7 @@ def sample_listener_loadbalancer_tuple(proto=None, topology=None,
|
||||
topology = constants.TOPOLOGY_SINGLE
|
||||
in_lb = collections.namedtuple(
|
||||
'load_balancer', 'id, name, protocol, vip, amphorae, topology, '
|
||||
'enabled, project_id')
|
||||
'listeners, enabled, project_id')
|
||||
return in_lb(
|
||||
id='sample_loadbalancer_id_1',
|
||||
name='test-lb',
|
||||
@ -524,6 +525,45 @@ def sample_listener_loadbalancer_tuple(proto=None, topology=None,
|
||||
role=constants.ROLE_BACKUP)]
|
||||
if more_amp else [sample_amphora_tuple()],
|
||||
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,
|
||||
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,
|
||||
ssl_type_l7=False, pool_cert=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
|
||||
if be_proto is None:
|
||||
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,
|
||||
hm_host_http_check=hm_host_http_check)]
|
||||
l7policies = []
|
||||
return in_listener(
|
||||
id='sample_listener_id_1',
|
||||
listener = in_listener(
|
||||
id=id,
|
||||
project_id='12345',
|
||||
protocol_port=port,
|
||||
protocol=proto,
|
||||
@ -689,6 +730,9 @@ def sample_listener_tuple(proto=None, monitor=True, alloc_default_pool=True,
|
||||
constants.CLIENT_AUTH_NONE),
|
||||
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):
|
@ -22,7 +22,7 @@ import octavia.common.exceptions as exceptions
|
||||
import octavia.common.tls_utils.cert_parser as cert_parser
|
||||
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_configs
|
||||
from octavia.tests.unit.common.sample_configs import sample_configs_combined
|
||||
|
||||
|
||||
class TestTLSParseUtils(base.TestCase):
|
||||
@ -144,8 +144,8 @@ class TestTLSParseUtils(base.TestCase):
|
||||
|
||||
@mock.patch('oslo_context.context.RequestContext')
|
||||
def test_load_certificates(self, mock_oslo):
|
||||
listener = sample_configs.sample_listener_tuple(tls=True, sni=True,
|
||||
client_ca_cert=True)
|
||||
listener = sample_configs_combined.sample_listener_tuple(
|
||||
tls=True, sni=True, client_ca_cert=True)
|
||||
client = mock.MagicMock()
|
||||
context = mock.Mock()
|
||||
context.project_id = '12345'
|
||||
@ -165,8 +165,8 @@ class TestTLSParseUtils(base.TestCase):
|
||||
client.assert_has_calls(calls_cert_mngr)
|
||||
|
||||
# Test asking for nothing
|
||||
listener = sample_configs.sample_listener_tuple(tls=False, sni=False,
|
||||
client_ca_cert=False)
|
||||
listener = sample_configs_combined.sample_listener_tuple(
|
||||
tls=False, sni=False, client_ca_cert=False)
|
||||
client = mock.MagicMock()
|
||||
with mock.patch.object(cert_parser,
|
||||
'_map_cert_tls_container') as mock_map:
|
||||
@ -211,7 +211,7 @@ class TestTLSParseUtils(base.TestCase):
|
||||
|
||||
def test_build_pem(self):
|
||||
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',
|
||||
intermediates=[b'imainter', b'imainter2'])
|
||||
self.assertEqual(expected, cert_parser.build_pem(tls_tuple))
|
||||
|
@ -136,7 +136,8 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
if listener:
|
||||
listener_ref = {'listener-id-1': {
|
||||
constants.OPERATING_STATUS: 'bogus',
|
||||
'protocol': listener_protocol}}
|
||||
'protocol': listener_protocol,
|
||||
'enabled': True}}
|
||||
lb_ref['listeners'] = listener_ref
|
||||
|
||||
return lb_ref
|
||||
@ -145,6 +146,7 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
|
||||
health = {
|
||||
"id": self.FAKE_UUID_1,
|
||||
"ver": 1,
|
||||
"listeners": {},
|
||||
"recv_time": time.time()
|
||||
}
|
||||
@ -161,6 +163,7 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
|
||||
health = {
|
||||
"id": self.FAKE_UUID_1,
|
||||
"ver": 1,
|
||||
"listeners": {},
|
||||
"recv_time": time.time()
|
||||
}
|
||||
@ -178,6 +181,7 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
|
||||
health = {
|
||||
"id": self.FAKE_UUID_1,
|
||||
"ver": 1,
|
||||
"listeners": {},
|
||||
"recv_time": time.time()
|
||||
}
|
||||
@ -194,6 +198,7 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
hb_interval = cfg.CONF.health_manager.heartbeat_interval
|
||||
health = {
|
||||
"id": self.FAKE_UUID_1,
|
||||
"ver": 1,
|
||||
"listeners": {},
|
||||
"recv_time": time.time() - hb_interval - 1 # extra -1 for buffer
|
||||
}
|
||||
@ -211,6 +216,7 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
|
||||
health = {
|
||||
"id": self.FAKE_UUID_1,
|
||||
"ver": 1,
|
||||
"listeners": {
|
||||
"listener-id-1": {"status": constants.OPEN, "pools": {
|
||||
"pool-id-1": {"status": constants.UP,
|
||||
@ -233,9 +239,9 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
self.session_mock.rollback.assert_called_once()
|
||||
|
||||
def test_update_health_online(self):
|
||||
|
||||
health = {
|
||||
"id": self.FAKE_UUID_1,
|
||||
"ver": 1,
|
||||
"listeners": {
|
||||
"listener-id-1": {"status": constants.OPEN, "pools": {
|
||||
"pool-id-1": {"status": constants.UP,
|
||||
@ -281,10 +287,53 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
self.hm.update_health(health, '192.0.2.1')
|
||||
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):
|
||||
|
||||
health = {
|
||||
"id": self.FAKE_UUID_1,
|
||||
"ver": 1,
|
||||
"listeners": {
|
||||
"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):
|
||||
health = {
|
||||
"id": self.FAKE_UUID_1,
|
||||
"ver": 1,
|
||||
"listeners": {
|
||||
"listener-id-1": {"status": constants.OPEN, "pools": {
|
||||
"pool-id-1": {"status": constants.DOWN,
|
||||
@ -333,7 +383,8 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
|
||||
lb_ref['listeners']['listener-id-2'] = {
|
||||
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.hm.update_health(health, '192.0.2.1')
|
||||
@ -359,6 +410,7 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
|
||||
health = {
|
||||
"id": self.FAKE_UUID_1,
|
||||
"ver": 1,
|
||||
"listeners": {
|
||||
"listener-id-1": {"status": constants.OPEN, "pools": {
|
||||
"pool-id-1": {"status": constants.UP,
|
||||
@ -396,10 +448,54 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
self.session_mock, member_id,
|
||||
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):
|
||||
|
||||
health = {
|
||||
"id": self.FAKE_UUID_1,
|
||||
"ver": 1,
|
||||
"listeners": {
|
||||
"listener-id-1": {"status": constants.OPEN, "pools": {
|
||||
"pool-id-5": {"status": constants.UP,
|
||||
@ -436,6 +532,7 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
|
||||
health = {
|
||||
"id": self.FAKE_UUID_1,
|
||||
"ver": 1,
|
||||
"listeners": {
|
||||
"listener-id-1": {
|
||||
"status": constants.OPEN,
|
||||
@ -475,6 +572,7 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
|
||||
health = {
|
||||
"id": self.FAKE_UUID_1,
|
||||
"ver": 1,
|
||||
"listeners": {
|
||||
"listener-id-1": {
|
||||
"status": constants.OPEN,
|
||||
@ -514,6 +612,7 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
|
||||
health = {
|
||||
"id": self.FAKE_UUID_1,
|
||||
"ver": 1,
|
||||
"listeners": {
|
||||
"listener-id-1": {
|
||||
"status": constants.OPEN,
|
||||
@ -547,6 +646,7 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
|
||||
health = {
|
||||
"id": self.FAKE_UUID_1,
|
||||
"ver": 1,
|
||||
"listeners": {
|
||||
"listener-id-1": {"status": constants.OPEN, "pools": {
|
||||
"pool-id-1": {"status": constants.UP,
|
||||
@ -589,6 +689,7 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
|
||||
health = {
|
||||
"id": self.FAKE_UUID_1,
|
||||
"ver": 1,
|
||||
"listeners": {
|
||||
"listener-id-1": {"status": constants.OPEN, "pools": {
|
||||
"pool-id-1": {"status": constants.UP,
|
||||
@ -626,6 +727,7 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
|
||||
health = {
|
||||
"id": self.FAKE_UUID_1,
|
||||
"ver": 1,
|
||||
"listeners": {
|
||||
"listener-id-1": {"status": constants.OPEN, "pools": {
|
||||
"pool-id-1": {"status": constants.UP,
|
||||
@ -668,6 +770,7 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
|
||||
health = {
|
||||
"id": self.FAKE_UUID_1,
|
||||
"ver": 1,
|
||||
"listeners": {
|
||||
"listener-id-1": {"status": constants.OPEN, "pools": {
|
||||
"pool-id-1": {"status": constants.UP,
|
||||
@ -710,6 +813,7 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
|
||||
health = {
|
||||
"id": self.FAKE_UUID_1,
|
||||
"ver": 1,
|
||||
"listeners": {
|
||||
"listener-id-1": {
|
||||
"status": constants.OPEN,
|
||||
@ -755,6 +859,7 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
|
||||
health = {
|
||||
"id": self.FAKE_UUID_1,
|
||||
"ver": 1,
|
||||
"listeners": {
|
||||
"listener-id-1": {"status": constants.FULL, "pools": {
|
||||
"pool-id-1": {"status": constants.UP,
|
||||
@ -804,6 +909,7 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
|
||||
health = {
|
||||
"id": self.FAKE_UUID_1,
|
||||
"ver": 1,
|
||||
"listeners": {
|
||||
"listener-id-1": {"status": constants.OPEN, "pools": {
|
||||
"pool-id-1": {"status": constants.DOWN,
|
||||
@ -847,6 +953,7 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
|
||||
health = {
|
||||
"id": self.FAKE_UUID_1,
|
||||
"ver": 1,
|
||||
"listeners": {
|
||||
"listener-id-1": {"status": constants.FULL, "pools": {
|
||||
"pool-id-1": {"status": constants.DOWN,
|
||||
@ -895,7 +1002,8 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
|
||||
lb_ref['listeners']['listener-id-%s' % i] = {
|
||||
constants.OPERATING_STATUS: 'bogus',
|
||||
'protocol': constants.PROTOCOL_TCP}
|
||||
'protocol': constants.PROTOCOL_TCP,
|
||||
'enabled': True}
|
||||
|
||||
if i == 3:
|
||||
members_dict = {'member-id-3': {
|
||||
@ -936,6 +1044,7 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
|
||||
health = {
|
||||
"id": self.FAKE_UUID_1,
|
||||
"ver": 1,
|
||||
"listeners": {
|
||||
"listener-id-1": {"status": constants.OPEN, "pools": {
|
||||
"pool-id-1": {"status": constants.UP,
|
||||
@ -1000,6 +1109,7 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
def test_update_health_no_status_change(self):
|
||||
health = {
|
||||
"id": self.FAKE_UUID_1,
|
||||
"ver": 1,
|
||||
"listeners": {
|
||||
"listener-id-1": {
|
||||
"status": constants.OPEN, "pools": {
|
||||
@ -1036,6 +1146,7 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
def test_update_health_lb_admin_down(self):
|
||||
health = {
|
||||
"id": self.FAKE_UUID_1,
|
||||
"ver": 1,
|
||||
"listeners": {},
|
||||
"recv_time": time.time()}
|
||||
|
||||
@ -1118,10 +1229,12 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
listener_protocol=constants.PROTOCOL_UDP)
|
||||
lb_ref['listeners']['listener-id-2'] = {
|
||||
constants.OPERATING_STATUS: 'bogus',
|
||||
'protocol': constants.PROTOCOL_UDP}
|
||||
'protocol': constants.PROTOCOL_UDP,
|
||||
'enabled': True}
|
||||
lb_ref['listeners']['listener-id-3'] = {
|
||||
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.hm.update_health(health, '192.0.2.1')
|
||||
@ -1132,6 +1245,7 @@ class TestUpdateHealthDb(base.TestCase):
|
||||
def test_update_health_no_db_lb(self):
|
||||
health = {
|
||||
"id": self.FAKE_UUID_1,
|
||||
"ver": 1,
|
||||
"listeners": {},
|
||||
"recv_time": time.time()
|
||||
}
|
||||
@ -1288,6 +1402,7 @@ class TestUpdateStatsDb(base.TestCase):
|
||||
|
||||
health = {
|
||||
"id": self.amphora_id,
|
||||
"ver": 1,
|
||||
"listeners": {
|
||||
self.listener_id: {
|
||||
"status": constants.OPEN,
|
||||
|
@ -128,9 +128,10 @@ class TestLoadBalancerFlows(base.TestCase):
|
||||
self.assertIsInstance(lb_flow, flow.Flow)
|
||||
|
||||
self.assertIn(constants.LOADBALANCER, lb_flow.requires)
|
||||
self.assertIn(constants.UPDATE_DICT, lb_flow.requires)
|
||||
|
||||
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):
|
||||
amp_flow = self.LBFlow.get_post_lb_amp_association_flow(
|
||||
|
@ -93,15 +93,15 @@ class TestAmphoraDriverTasks(base.TestCase):
|
||||
constants.CONN_RETRY_INTERVAL: 4}
|
||||
|
||||
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)
|
||||
|
||||
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')
|
||||
|
||||
amp_list_update_obj.execute([_listener_mock], 0,
|
||||
amp_list_update_obj.execute(_load_balancer_mock, 0,
|
||||
[_amphora_mock], timeout_dict)
|
||||
|
||||
mock_amphora_repo_update.assert_called_once_with(
|
||||
@ -117,9 +117,9 @@ class TestAmphoraDriverTasks(base.TestCase):
|
||||
mock_amphora_repo_update):
|
||||
|
||||
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
|
||||
amp = listener_update_obj.revert(_load_balancer_mock)
|
||||
@ -152,12 +152,9 @@ class TestAmphoraDriverTasks(base.TestCase):
|
||||
data_models.Listener(id='listener2')]
|
||||
vip = data_models.Vip(ip_address='10.0.0.1')
|
||||
lb = data_models.LoadBalancer(id='lb1', listeners=listeners, vip=vip)
|
||||
listeners_update_obj.execute(lb, listeners)
|
||||
mock_driver.update.assert_has_calls([mock.call(listeners[0], vip),
|
||||
mock.call(listeners[1], vip)])
|
||||
self.assertEqual(2, mock_driver.update.call_count)
|
||||
self.assertIsNotNone(listeners[0].load_balancer)
|
||||
self.assertIsNotNone(listeners[1].load_balancer)
|
||||
listeners_update_obj.execute(lb)
|
||||
mock_driver.update.assert_called_once_with(lb)
|
||||
self.assertEqual(1, mock_driver.update.call_count)
|
||||
|
||||
# Test the revert
|
||||
amp = listeners_update_obj.revert(lb)
|
||||
@ -171,69 +168,37 @@ class TestAmphoraDriverTasks(base.TestCase):
|
||||
self.assertEqual(2, repo.ListenerRepository.update.call_count)
|
||||
self.assertIsNone(amp)
|
||||
|
||||
def test_listener_stop(self,
|
||||
mock_driver,
|
||||
mock_generate_uuid,
|
||||
mock_log,
|
||||
mock_get_session,
|
||||
mock_listener_repo_get,
|
||||
mock_listener_repo_update,
|
||||
mock_amphora_repo_update):
|
||||
@mock.patch('octavia.controller.worker.task_utils.TaskUtils.'
|
||||
'mark_listener_prov_status_error')
|
||||
def test_listeners_start(self,
|
||||
mock_prov_status_error,
|
||||
mock_driver,
|
||||
mock_generate_uuid,
|
||||
mock_log,
|
||||
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()
|
||||
listener_stop_obj.execute(_load_balancer_mock, _listener_mock)
|
||||
# Test no listeners
|
||||
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
|
||||
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)
|
||||
|
||||
# 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)
|
||||
# Test revert
|
||||
mock_lb.listeners = [mock_listener]
|
||||
listeners_start_obj.revert(mock_lb)
|
||||
mock_prov_status_error.assert_called_once_with('12345')
|
||||
|
||||
def test_listener_delete(self,
|
||||
mock_driver,
|
||||
@ -245,9 +210,9 @@ class TestAmphoraDriverTasks(base.TestCase):
|
||||
mock_amphora_repo_update):
|
||||
|
||||
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
|
||||
amp = listener_delete_obj.revert(_listener_mock)
|
||||
|
@ -49,6 +49,7 @@ _health_mon_mock = mock.MagicMock()
|
||||
_vip_mock = mock.MagicMock()
|
||||
_listener_mock = mock.MagicMock()
|
||||
_load_balancer_mock = mock.MagicMock()
|
||||
_load_balancer_mock.listeners = [_listener_mock]
|
||||
_member_mock = mock.MagicMock()
|
||||
_pool_mock = mock.MagicMock()
|
||||
_l7policy_mock = mock.MagicMock()
|
||||
@ -324,7 +325,7 @@ class TestControllerWorker(base.TestCase):
|
||||
store={constants.LOADBALANCER:
|
||||
_load_balancer_mock,
|
||||
constants.LISTENERS:
|
||||
[_listener_mock]}))
|
||||
_load_balancer_mock.listeners}))
|
||||
|
||||
_flow_mock.run.assert_called_once_with()
|
||||
self.assertEqual(2, mock_listener_repo_get.call_count)
|
||||
|
@ -128,9 +128,10 @@ class TestLoadBalancerFlows(base.TestCase):
|
||||
self.assertIsInstance(lb_flow, flow.Flow)
|
||||
|
||||
self.assertIn(constants.LOADBALANCER, lb_flow.requires)
|
||||
self.assertIn(constants.UPDATE_DICT, lb_flow.requires)
|
||||
|
||||
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):
|
||||
amp_flow = self.LBFlow.get_post_lb_amp_association_flow(
|
||||
|
@ -93,15 +93,15 @@ class TestAmphoraDriverTasks(base.TestCase):
|
||||
constants.CONN_RETRY_INTERVAL: 4}
|
||||
|
||||
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)
|
||||
|
||||
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')
|
||||
|
||||
amp_list_update_obj.execute([_listener_mock], 0,
|
||||
amp_list_update_obj.execute(_load_balancer_mock, 0,
|
||||
[_amphora_mock], timeout_dict)
|
||||
|
||||
mock_amphora_repo_update.assert_called_once_with(
|
||||
@ -117,9 +117,9 @@ class TestAmphoraDriverTasks(base.TestCase):
|
||||
mock_amphora_repo_update):
|
||||
|
||||
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
|
||||
amp = listener_update_obj.revert(_load_balancer_mock)
|
||||
@ -152,12 +152,9 @@ class TestAmphoraDriverTasks(base.TestCase):
|
||||
data_models.Listener(id='listener2')]
|
||||
vip = data_models.Vip(ip_address='10.0.0.1')
|
||||
lb = data_models.LoadBalancer(id='lb1', listeners=listeners, vip=vip)
|
||||
listeners_update_obj.execute(lb, listeners)
|
||||
mock_driver.update.assert_has_calls([mock.call(listeners[0], vip),
|
||||
mock.call(listeners[1], vip)])
|
||||
self.assertEqual(2, mock_driver.update.call_count)
|
||||
self.assertIsNotNone(listeners[0].load_balancer)
|
||||
self.assertIsNotNone(listeners[1].load_balancer)
|
||||
listeners_update_obj.execute(lb)
|
||||
mock_driver.update.assert_called_once_with(lb)
|
||||
self.assertEqual(1, mock_driver.update.call_count)
|
||||
|
||||
# Test the revert
|
||||
amp = listeners_update_obj.revert(lb)
|
||||
@ -171,69 +168,37 @@ class TestAmphoraDriverTasks(base.TestCase):
|
||||
self.assertEqual(2, repo.ListenerRepository.update.call_count)
|
||||
self.assertIsNone(amp)
|
||||
|
||||
def test_listener_stop(self,
|
||||
mock_driver,
|
||||
mock_generate_uuid,
|
||||
mock_log,
|
||||
mock_get_session,
|
||||
mock_listener_repo_get,
|
||||
mock_listener_repo_update,
|
||||
mock_amphora_repo_update):
|
||||
@mock.patch('octavia.controller.worker.task_utils.TaskUtils.'
|
||||
'mark_listener_prov_status_error')
|
||||
def test_listeners_start(self,
|
||||
mock_prov_status_error,
|
||||
mock_driver,
|
||||
mock_generate_uuid,
|
||||
mock_log,
|
||||
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()
|
||||
listener_stop_obj.execute(_load_balancer_mock, _listener_mock)
|
||||
# Test no listeners
|
||||
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
|
||||
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)
|
||||
|
||||
# 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)
|
||||
# Test revert
|
||||
mock_lb.listeners = [mock_listener]
|
||||
listeners_start_obj.revert(mock_lb)
|
||||
mock_prov_status_error.assert_called_once_with('12345')
|
||||
|
||||
def test_listener_delete(self,
|
||||
mock_driver,
|
||||
@ -245,9 +210,9 @@ class TestAmphoraDriverTasks(base.TestCase):
|
||||
mock_amphora_repo_update):
|
||||
|
||||
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
|
||||
amp = listener_delete_obj.revert(_listener_mock)
|
||||
|
@ -49,6 +49,7 @@ _health_mon_mock = mock.MagicMock()
|
||||
_vip_mock = mock.MagicMock()
|
||||
_listener_mock = mock.MagicMock()
|
||||
_load_balancer_mock = mock.MagicMock()
|
||||
_load_balancer_mock.listeners = [_listener_mock]
|
||||
_member_mock = mock.MagicMock()
|
||||
_pool_mock = mock.MagicMock()
|
||||
_l7policy_mock = mock.MagicMock()
|
||||
@ -324,7 +325,7 @@ class TestControllerWorker(base.TestCase):
|
||||
store={constants.LOADBALANCER:
|
||||
_load_balancer_mock,
|
||||
constants.LISTENERS:
|
||||
[_listener_mock]}))
|
||||
_load_balancer_mock.listeners}))
|
||||
|
||||
_flow_mock.run.assert_called_once_with()
|
||||
self.assertEqual(2, mock_listener_repo_get.call_count)
|
||||
|
@ -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.
|
Loading…
Reference in New Issue
Block a user