Fix multi-listener load balancers
Load balancers with multiple listeners, running on an amphora image with HAProxy 1.8 or newer can experience excessive memory usage that may lead to an ERROR provisioning_status. This patch resolves this issue by consolidating the listeners into a single haproxy process inside the amphora. Story: 2005412 Task: 34744 Co-Authored-By: Adam Harwell <flux.adam@gmail.com> Change-Id: Idaccbcfa0126f1e26fbb3ad770c65c9266cfad5bchanges/68/668068/33
parent
31428bf25a
commit
06ce4777c3
|
@ -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
|
||||
|