Merge "Fix flavors support when using spares pool"
This commit is contained in:
commit
04c041df26
@ -29,9 +29,9 @@ 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.1. (So,
|
||||
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.1'.)
|
||||
string 'v0.5'.)
|
||||
|
||||
Response codes
|
||||
--------------
|
||||
@ -408,7 +408,7 @@ Get interface
|
||||
::
|
||||
|
||||
GET URL:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/interface/10.0.0.1
|
||||
https://octavia-haproxy-img-00328.local/v0.5/interface/10.0.0.1
|
||||
|
||||
JSON Response:
|
||||
{
|
||||
@ -422,7 +422,7 @@ Get interface
|
||||
::
|
||||
|
||||
GET URL:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/interface/10.5.0.1
|
||||
https://octavia-haproxy-img-00328.local/v0.5/interface/10.5.0.1
|
||||
|
||||
JSON Response:
|
||||
{
|
||||
@ -435,7 +435,7 @@ Get interface
|
||||
::
|
||||
|
||||
GET URL:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/interface/10.6.0.1.1
|
||||
https://octavia-haproxy-img-00328.local/v0.5/interface/10.6.0.1.1
|
||||
|
||||
JSON Response:
|
||||
{
|
||||
@ -621,7 +621,7 @@ Start or Stop a listener
|
||||
::
|
||||
|
||||
PUT URL:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/start
|
||||
https://octavia-haproxy-img-00328.local/v0.5/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/start
|
||||
|
||||
JSON Response:
|
||||
{
|
||||
@ -634,7 +634,7 @@ Start or Stop a listener
|
||||
::
|
||||
|
||||
PUT URL:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/BAD_TEST_DATA
|
||||
https://octavia-haproxy-img-00328.local/v0.5/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/BAD_TEST_DATA
|
||||
|
||||
JSON Response:
|
||||
{
|
||||
@ -647,7 +647,7 @@ Start or Stop a listener
|
||||
::
|
||||
|
||||
PUT URL:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/stop
|
||||
https://octavia-haproxy-img-00328.local/v0.5/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/stop
|
||||
|
||||
JSON Response:
|
||||
{
|
||||
@ -660,7 +660,7 @@ Start or Stop a listener
|
||||
::
|
||||
|
||||
PUT URL:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/stop
|
||||
https://octavia-haproxy-img-00328.local/v0.5/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/stop
|
||||
|
||||
Response:
|
||||
{
|
||||
@ -724,7 +724,7 @@ Delete a listener
|
||||
::
|
||||
|
||||
DELETE URL:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a
|
||||
https://octavia-haproxy-img-00328.local/v0.5/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a
|
||||
|
||||
JSON Response:
|
||||
{
|
||||
@ -736,7 +736,7 @@ Delete a listener
|
||||
::
|
||||
|
||||
DELETE URL:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a
|
||||
https://octavia-haproxy-img-00328.local/v0.5/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a
|
||||
|
||||
JSON Response:
|
||||
{
|
||||
@ -812,7 +812,7 @@ explicitly restarted
|
||||
::
|
||||
|
||||
PUT URI:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/certificates/www.example.com.pem
|
||||
https://octavia-haproxy-img-00328.local/v0.5/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/certificates/www.example.com.pem
|
||||
(Put data should contain the certificate information, concatenated as
|
||||
described above)
|
||||
|
||||
@ -826,7 +826,7 @@ explicitly restarted
|
||||
::
|
||||
|
||||
PUT URI:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/certificates/www.example.com.pem
|
||||
https://octavia-haproxy-img-00328.local/v0.5/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/certificates/www.example.com.pem
|
||||
(If PUT data does not contain a certificate)
|
||||
|
||||
JSON Response:
|
||||
@ -839,7 +839,7 @@ explicitly restarted
|
||||
::
|
||||
|
||||
PUT URI:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/certificates/www.example.com.pem
|
||||
https://octavia-haproxy-img-00328.local/v0.5/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/certificates/www.example.com.pem
|
||||
(If PUT data does not contain an RSA key)
|
||||
|
||||
JSON Response:
|
||||
@ -852,7 +852,7 @@ explicitly restarted
|
||||
::
|
||||
|
||||
PUT URI:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/certificates/www.example.com.pem
|
||||
https://octavia-haproxy-img-00328.local/v0.5/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/certificates/www.example.com.pem
|
||||
(If the first certificate and the RSA key do not have the same modulus.)
|
||||
|
||||
JSON Response:
|
||||
@ -865,7 +865,7 @@ explicitly restarted
|
||||
::
|
||||
|
||||
PUT URI:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/certificates/www.example.com.pem
|
||||
https://octavia-haproxy-img-00328.local/v0.5/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/certificates/www.example.com.pem
|
||||
|
||||
JSON Response:
|
||||
{
|
||||
@ -988,7 +988,7 @@ Delete SSL certificate PEM file
|
||||
::
|
||||
|
||||
DELETE URL:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/certificates/www.example.com.pem
|
||||
https://octavia-haproxy-img-00328.local/v0.5/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/certificates/www.example.com.pem
|
||||
|
||||
JSON Response:
|
||||
{
|
||||
@ -1000,7 +1000,7 @@ Delete SSL certificate PEM file
|
||||
::
|
||||
|
||||
DELETE URL:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/certificates/www.example.com.pem
|
||||
https://octavia-haproxy-img-00328.local/v0.5/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/certificates/www.example.com.pem
|
||||
|
||||
JSON Response:
|
||||
{
|
||||
@ -1071,7 +1071,7 @@ out of the haproxy daemon status interface for tracking health and stats).
|
||||
::
|
||||
|
||||
PUT URL:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/listeners/d459b1c8-54b0-4030-9bec-4f449e73b1ef/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/haproxy
|
||||
https://octavia-haproxy-img-00328.local/v0.5/listeners/d459b1c8-54b0-4030-9bec-4f449e73b1ef/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/haproxy
|
||||
(Upload PUT data should be a raw haproxy.conf file.)
|
||||
|
||||
JSON Response:
|
||||
@ -1134,7 +1134,7 @@ Get listener haproxy configuration
|
||||
::
|
||||
|
||||
GET URL:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/listeners/7e9f91eb-b3e6-4e3b-a1a7-d6f7fdc1de7c/haproxy
|
||||
https://octavia-haproxy-img-00328.local/v0.5/listeners/7e9f91eb-b3e6-4e3b-a1a7-d6f7fdc1de7c/haproxy
|
||||
|
||||
Response is the raw haproxy.cfg:
|
||||
|
||||
@ -1210,7 +1210,7 @@ Plug VIP
|
||||
::
|
||||
|
||||
POST URL:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/plug/vip/203.0.113.2
|
||||
https://octavia-haproxy-img-00328.local/v0.5/plug/vip/203.0.113.2
|
||||
|
||||
JSON POST parameters:
|
||||
{
|
||||
@ -1292,7 +1292,7 @@ Plug Network
|
||||
::
|
||||
|
||||
POST URL:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/plug/network/
|
||||
https://octavia-haproxy-img-00328.local/v0.5/plug/network/
|
||||
|
||||
JSON POST parameters:
|
||||
{
|
||||
@ -1362,7 +1362,7 @@ not be available for some time.
|
||||
::
|
||||
|
||||
PUT URI:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/certificate
|
||||
https://octavia-haproxy-img-00328.local/v0.5/certificate
|
||||
(Put data should contain the certificate information, concatenated as
|
||||
described above)
|
||||
|
||||
@ -1376,7 +1376,7 @@ not be available for some time.
|
||||
::
|
||||
|
||||
PUT URI:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/certificates
|
||||
https://octavia-haproxy-img-00328.local/v0.5/certificates
|
||||
(If PUT data does not contain a certificate)
|
||||
|
||||
JSON Response:
|
||||
@ -1389,7 +1389,7 @@ not be available for some time.
|
||||
::
|
||||
|
||||
PUT URI:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/certificate
|
||||
https://octavia-haproxy-img-00328.local/v0.5/certificate
|
||||
(If PUT data does not contain an RSA key)
|
||||
|
||||
JSON Response:
|
||||
@ -1402,7 +1402,7 @@ not be available for some time.
|
||||
::
|
||||
|
||||
PUT URI:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/certificate
|
||||
https://octavia-haproxy-img-00328.local/v0.5/certificate
|
||||
(If the first certificate and the RSA key do not have the same modulus.)
|
||||
|
||||
JSON Response:
|
||||
@ -1441,7 +1441,7 @@ OK
|
||||
::
|
||||
|
||||
PUT URI:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/vrrp/upload
|
||||
https://octavia-haproxy-img-00328.local/v0.5/vrrp/upload
|
||||
|
||||
JSON Response:
|
||||
{
|
||||
@ -1489,7 +1489,7 @@ Start, Stop, or Reload keepalived
|
||||
::
|
||||
|
||||
PUT URL:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/vrrp/start
|
||||
https://octavia-haproxy-img-00328.local/v0.5/vrrp/start
|
||||
|
||||
JSON Response:
|
||||
{
|
||||
@ -1502,7 +1502,7 @@ Start, Stop, or Reload keepalived
|
||||
::
|
||||
|
||||
PUT URL:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/vrrp/BAD_TEST_DATA
|
||||
https://octavia-haproxy-img-00328.local/v0.5/vrrp/BAD_TEST_DATA
|
||||
|
||||
JSON Response:
|
||||
{
|
||||
@ -1515,7 +1515,7 @@ Start, Stop, or Reload keepalived
|
||||
::
|
||||
|
||||
PUT URL:
|
||||
https://octavia-haproxy-img-00328.local/v0.1/vrrp/stop
|
||||
https://octavia-haproxy-img-00328.local/v0.5/vrrp/stop
|
||||
|
||||
JSON Response:
|
||||
{
|
||||
@ -1523,4 +1523,58 @@ Start, Stop, or Reload keepalived
|
||||
'details': 'keeepalived process with PID 3352 not found',
|
||||
}
|
||||
|
||||
Update the amphora agent configuration
|
||||
--------------------------------------
|
||||
|
||||
* **URL:** /*:version*/config
|
||||
* **Method:** PUT
|
||||
|
||||
* **Data params:** A amphora-agent configuration file
|
||||
* **Success Response:**
|
||||
|
||||
* Code: 202
|
||||
|
||||
* Content: OK
|
||||
|
||||
* **Error Response:**
|
||||
|
||||
* Code: 500
|
||||
|
||||
* message: Unable to update amphora-agent configuration.
|
||||
* details: *(The exception details)*
|
||||
|
||||
* **Response:**
|
||||
|
||||
| OK
|
||||
|
||||
* **Implied actions:**
|
||||
|
||||
* The running amphora-agent configuration file is mutated.
|
||||
|
||||
**Notes:** Only options that are marked mutable in the oslo configuration
|
||||
will be updated.
|
||||
|
||||
**Examples:**
|
||||
|
||||
* Success code 202:
|
||||
|
||||
::
|
||||
|
||||
PUT URL:
|
||||
https://octavia-haproxy-img-00328.local/v0.5/config
|
||||
(Upload PUT data should be a raw amphora-agent.conf file.)
|
||||
|
||||
JSON Response:
|
||||
{
|
||||
'message': 'OK'
|
||||
}
|
||||
|
||||
* Error code 500:
|
||||
|
||||
::
|
||||
|
||||
JSON Response:
|
||||
{
|
||||
'message': 'Unable to update amphora-agent configuration.',
|
||||
'details': *(The exception output)*,
|
||||
}
|
||||
|
@ -12,8 +12,12 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
import stat
|
||||
|
||||
import flask
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import six
|
||||
import webob
|
||||
from werkzeug import exceptions
|
||||
@ -28,7 +32,10 @@ from octavia.amphorae.backends.agent.api_server import plug
|
||||
from octavia.amphorae.backends.agent.api_server import udp_listener_base
|
||||
from octavia.amphorae.backends.agent.api_server import util
|
||||
|
||||
BUFFER = 1024
|
||||
CONF = cfg.CONF
|
||||
PATH_PREFIX = '/' + api_server.VERSION
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# make the error pages all json
|
||||
@ -81,6 +88,9 @@ class Server(object):
|
||||
self.app.add_url_rule(rule=PATH_PREFIX + '/listeners/<listener_id>',
|
||||
view_func=self.delete_listener,
|
||||
methods=['DELETE'])
|
||||
self.app.add_url_rule(rule=PATH_PREFIX + '/config',
|
||||
view_func=self.upload_config,
|
||||
methods=['PUT'])
|
||||
self.app.add_url_rule(rule=PATH_PREFIX + '/details',
|
||||
view_func=self.get_details,
|
||||
methods=['GET'])
|
||||
@ -217,3 +227,27 @@ class Server(object):
|
||||
|
||||
def get_interface(self, ip_addr):
|
||||
return self._amphora_info.get_interface(ip_addr)
|
||||
|
||||
def upload_config(self):
|
||||
try:
|
||||
stream = flask.request.stream
|
||||
file_path = cfg.find_config_files(project=CONF.project,
|
||||
prog=CONF.prog)[0]
|
||||
flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
|
||||
# mode 00600
|
||||
mode = stat.S_IRUSR | stat.S_IWUSR
|
||||
with os.fdopen(os.open(file_path, flags, mode), 'wb') as cfg_file:
|
||||
b = stream.read(BUFFER)
|
||||
while b:
|
||||
cfg_file.write(b)
|
||||
b = stream.read(BUFFER)
|
||||
|
||||
CONF.mutate_config_files()
|
||||
except Exception as e:
|
||||
LOG.error("Unable to update amphora-agent configuration: "
|
||||
"{}".format(str(e)))
|
||||
return webob.Response(json=dict(
|
||||
message="Unable to update amphora-agent configuration.",
|
||||
details=str(e)), status=500)
|
||||
|
||||
return webob.Response(json={'message': 'OK'}, status=202)
|
||||
|
@ -33,18 +33,9 @@ def round_robin_addr(addrinfo_list):
|
||||
|
||||
class UDPStatusSender(object):
|
||||
def __init__(self):
|
||||
self.dests = []
|
||||
for ipport in CONF.health_manager.controller_ip_port_list:
|
||||
try:
|
||||
ip, port = ipport.rsplit(':', 1)
|
||||
except ValueError:
|
||||
LOG.error("Invalid ip and port '%s' in health_manager "
|
||||
"controller_ip_port_list", ipport)
|
||||
break
|
||||
self.update(ip, port)
|
||||
self._update_dests()
|
||||
self.v4sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
self.v6sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
|
||||
self.key = str(CONF.health_manager.heartbeat_key)
|
||||
|
||||
def update(self, dest, port):
|
||||
addrlist = socket.getaddrinfo(dest, port, 0, socket.SOCK_DGRAM)
|
||||
@ -55,7 +46,9 @@ class UDPStatusSender(object):
|
||||
break
|
||||
|
||||
def _send_msg(self, dest, msg):
|
||||
envelope_str = status_message.wrap_envelope(msg, self.key)
|
||||
# Note: heartbeat_key is mutable and must be looked up for each call
|
||||
envelope_str = status_message.wrap_envelope(
|
||||
msg, str(CONF.health_manager.heartbeat_key))
|
||||
# dest = (family, socktype, proto, canonname, sockaddr)
|
||||
# e.g. 0 = sock family, 4 = sockaddr - what we actually need
|
||||
try:
|
||||
@ -71,7 +64,25 @@ class UDPStatusSender(object):
|
||||
# if the message isn't received
|
||||
pass
|
||||
|
||||
# The controller_ip_port_list configuration has mutated, reload it.
|
||||
def _update_dests(self):
|
||||
self.dests = []
|
||||
for ipport in CONF.health_manager.controller_ip_port_list:
|
||||
try:
|
||||
ip, port = ipport.rsplit(':', 1)
|
||||
except ValueError:
|
||||
LOG.error("Invalid ip and port '%s' in health_manager "
|
||||
"controller_ip_port_list", ipport)
|
||||
break
|
||||
self.update(ip, port)
|
||||
self.current_controller_ip_port_list = (
|
||||
CONF.health_manager.controller_ip_port_list)
|
||||
|
||||
def dosend(self, obj):
|
||||
# Check for controller_ip_port_list mutation
|
||||
if not (self.current_controller_ip_port_list ==
|
||||
CONF.health_manager.controller_ip_port_list):
|
||||
self._update_dests()
|
||||
dest = round_robin_addr(self.dests)
|
||||
if dest is None:
|
||||
LOG.error('No controller address found. Unable to send heartbeat.')
|
||||
|
@ -116,3 +116,8 @@ class HealthMonitorProvisioningError(ProvisioningErrors):
|
||||
class NodeProvisioningError(ProvisioningErrors):
|
||||
|
||||
message = _('couldn\'t provision Node')
|
||||
|
||||
|
||||
class AmpDriverNotImplementedError(AmphoraDriverError):
|
||||
|
||||
message = _('Amphora does not implement this feature.')
|
||||
|
@ -219,6 +219,16 @@ class AmphoraLoadBalancerDriver(object):
|
||||
"""
|
||||
pass
|
||||
|
||||
def update_agent_config(self, amphora, agent_config):
|
||||
"""Upload and update the amphora agent configuration.
|
||||
|
||||
:param amphora: amphora object, needs id and network ip(s)
|
||||
:type amphora: object
|
||||
:param agent_config: The new amphora agent configuration file.
|
||||
:type agent_config: string
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class HealthMixin(object):
|
||||
|
@ -289,6 +289,31 @@ class HaproxyAmphoraLoadBalancerDriver(
|
||||
self.client.upload_cert_pem(
|
||||
amp, listener_id, name, pem)
|
||||
|
||||
def update_amphora_agent_config(self, amphora, agent_config,
|
||||
timeout_dict=None):
|
||||
"""Update the amphora agent configuration file.
|
||||
|
||||
:param amphora: The amphora to update.
|
||||
:type amphora: object
|
||||
:param agent_config: The new amphora agent configuration.
|
||||
:type agent_config: string
|
||||
: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
|
||||
:returns: None
|
||||
|
||||
Note: This will mutate the amphora agent config and adopt the
|
||||
new values.
|
||||
"""
|
||||
try:
|
||||
self.client.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))
|
||||
raise driver_except.AmpDriverNotImplementedError()
|
||||
|
||||
|
||||
# Check a custom hostname
|
||||
class CustomHostNameCheckingAdapter(requests.adapters.HTTPAdapter):
|
||||
@ -515,3 +540,7 @@ class AmphoraAPIClient(object):
|
||||
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)
|
||||
|
@ -104,11 +104,18 @@ class NoopManager(object):
|
||||
load_balancer.id, amphorae_network_config, 'post_vip_plug')
|
||||
|
||||
def upload_cert_amp(self, amphora, pem_file):
|
||||
LOG.debug("Amphora %s no-op, upload cert amphora %s,with pem fle %s",
|
||||
LOG.debug("Amphora %s no-op, upload cert amphora %s,with pem file %s",
|
||||
self.__class__.__name__, amphora.id, pem_file)
|
||||
self.amphoraconfig[amphora.id, pem_file] = (amphora.id, pem_file,
|
||||
'update_amp_cert_file')
|
||||
|
||||
def update_agent_config(self, amphora, agent_config):
|
||||
LOG.debug("Amphora %s no-op, update agent config amphora "
|
||||
"%s, with agent config %s",
|
||||
self.__class__.__name__, amphora.id, agent_config)
|
||||
self.amphoraconfig[amphora.id, agent_config] = (
|
||||
amphora.id, agent_config, 'update_agent_config')
|
||||
|
||||
|
||||
class NoopAmphoraLoadBalancerDriver(
|
||||
driver_base.AmphoraLoadBalancerDriver,
|
||||
@ -164,6 +171,9 @@ class NoopAmphoraLoadBalancerDriver(
|
||||
|
||||
self.driver.upload_cert_amp(amphora, pem_file)
|
||||
|
||||
def update_agent_config(self, amphora, agent_config):
|
||||
self.driver.update_agent_config(amphora, agent_config)
|
||||
|
||||
def update_vrrp_conf(self, loadbalancer):
|
||||
pass
|
||||
|
||||
|
@ -174,6 +174,7 @@ healthmanager_opts = [
|
||||
default=None,
|
||||
help=_('Number of processes for amphora stats update.')),
|
||||
cfg.StrOpt('heartbeat_key',
|
||||
mutable=True,
|
||||
help=_('key used to validate amphora sending '
|
||||
'the message'), secret=True),
|
||||
cfg.IntOpt('heartbeat_timeout',
|
||||
@ -191,9 +192,11 @@ healthmanager_opts = [
|
||||
help=_('List of controller ip and port pairs for the '
|
||||
'heartbeat receivers. Example 127.0.0.1:5555, '
|
||||
'192.168.0.1:5555'),
|
||||
mutable=True,
|
||||
default=[]),
|
||||
cfg.IntOpt('heartbeat_interval',
|
||||
default=10,
|
||||
mutable=True,
|
||||
help=_('Sleep time between sending heartbeats.')),
|
||||
|
||||
# Used for updating health and stats
|
||||
@ -377,6 +380,7 @@ controller_worker_opts = [
|
||||
cfg.StrOpt('loadbalancer_topology',
|
||||
default=constants.TOPOLOGY_SINGLE,
|
||||
choices=constants.SUPPORTED_LB_TOPOLOGIES,
|
||||
mutable=True,
|
||||
help=_('Load balancer topology configuration. '
|
||||
'SINGLE - One amphora per load balancer. '
|
||||
'ACTIVE_STANDBY - Two amphora per load balancer.')),
|
||||
|
@ -334,6 +334,7 @@ AMP_COMPUTE_CONNECTIVITY_WAIT = 'octavia-amp-compute-connectivity-wait'
|
||||
AMP_LISTENER_UPDATE = 'octavia-amp-listeners-update'
|
||||
|
||||
GENERATE_SERVER_PEM_TASK = 'GenerateServerPEMTask'
|
||||
AMPHORA_CONFIG_UPDATE_TASK = 'AmphoraConfigUpdateTask'
|
||||
|
||||
# Batch Member Update constants
|
||||
UNORDERED_MEMBER_UPDATES_FLOW = 'octavia-unordered-member-updates-flow'
|
||||
|
@ -346,3 +346,11 @@ def ip_not_reserved(ip_address):
|
||||
if ip_address in CONF.networking.reserved_ips:
|
||||
raise exceptions.InvalidOption(value=ip_address,
|
||||
option='member address')
|
||||
|
||||
|
||||
def is_flavor_spares_compatible(flavor):
|
||||
if flavor:
|
||||
# If a compute flavor is specified, the flavor is not spares compatible
|
||||
if flavor.get(constants.COMPUTE_FLAVOR, None):
|
||||
return False
|
||||
return True
|
||||
|
@ -108,18 +108,22 @@ class ControllerWorker(base_taskflow.BaseTaskFlowEngine):
|
||||
|
||||
:returns: amphora_id
|
||||
"""
|
||||
create_amp_tf = self._taskflow_load(
|
||||
self._amphora_flows.get_create_amphora_flow(),
|
||||
store={constants.BUILD_TYPE_PRIORITY:
|
||||
constants.LB_CREATE_SPARES_POOL_PRIORITY}
|
||||
)
|
||||
with tf_logging.DynamicLoggingListener(
|
||||
create_amp_tf, log=LOG,
|
||||
hide_inputs_outputs_of=self._exclude_result_logging_tasks):
|
||||
try:
|
||||
create_amp_tf = self._taskflow_load(
|
||||
self._amphora_flows.get_create_amphora_flow(),
|
||||
store={constants.BUILD_TYPE_PRIORITY:
|
||||
constants.LB_CREATE_SPARES_POOL_PRIORITY,
|
||||
constants.FLAVOR: None}
|
||||
)
|
||||
with tf_logging.DynamicLoggingListener(
|
||||
create_amp_tf, log=LOG,
|
||||
hide_inputs_outputs_of=self._exclude_result_logging_tasks):
|
||||
|
||||
create_amp_tf.run()
|
||||
create_amp_tf.run()
|
||||
|
||||
return create_amp_tf.storage.fetch('amphora')
|
||||
return create_amp_tf.storage.fetch('amphora')
|
||||
except Exception as e:
|
||||
LOG.error('Failed to create an amphora due to: {}'.format(str(e)))
|
||||
|
||||
def delete_amphora(self, amphora_id):
|
||||
"""Deletes an existing Amphora.
|
||||
|
@ -95,6 +95,10 @@ class AmphoraFlows(object):
|
||||
requires=constants.AMPHORA_ID,
|
||||
provides=constants.AMPHORA))
|
||||
|
||||
post_map_amp_to_lb.add(amphora_driver_tasks.AmphoraConfigUpdate(
|
||||
name=sf_name + '-' + constants.AMPHORA_CONFIG_UPDATE_TASK,
|
||||
requires=(constants.AMPHORA, constants.FLAVOR)))
|
||||
|
||||
if role == constants.ROLE_MASTER:
|
||||
post_map_amp_to_lb.add(database_tasks.MarkAmphoraMasterInDB(
|
||||
name=sf_name + '-' + constants.MARK_AMP_MASTER_INDB,
|
||||
@ -252,7 +256,7 @@ class AmphoraFlows(object):
|
||||
# Setup the task that maps an amphora to a load balancer
|
||||
allocate_and_associate_amp = database_tasks.MapLoadbalancerToAmphora(
|
||||
name=sf_name + '-' + constants.MAP_LOADBALANCER_TO_AMPHORA,
|
||||
requires=constants.LOADBALANCER_ID,
|
||||
requires=(constants.LOADBALANCER_ID, constants.FLAVOR),
|
||||
provides=constants.AMPHORA_ID)
|
||||
|
||||
# Define a subflow for if we successfully map an amphora
|
||||
|
@ -20,6 +20,7 @@ from stevedore import driver as stevedore_driver
|
||||
from taskflow import task
|
||||
from taskflow.types import failure
|
||||
|
||||
from octavia.amphorae.backends.agent import agent_jinja_cfg
|
||||
from octavia.amphorae.driver_exceptions import exceptions as driver_except
|
||||
from octavia.common import constants
|
||||
from octavia.controller.worker import task_utils as task_utilities
|
||||
@ -357,7 +358,7 @@ class AmphoraVRRPStart(BaseAmphoraTask):
|
||||
|
||||
|
||||
class AmphoraComputeConnectivityWait(BaseAmphoraTask):
|
||||
""""Task to wait for the compute instance to be up."""
|
||||
"""Task to wait for the compute instance to be up."""
|
||||
|
||||
def execute(self, amphora):
|
||||
"""Execute get_info routine for an amphora until it responds."""
|
||||
@ -373,3 +374,28 @@ class AmphoraComputeConnectivityWait(BaseAmphoraTask):
|
||||
self.amphora_repo.update(db_apis.get_session(), amphora.id,
|
||||
status=constants.ERROR)
|
||||
raise
|
||||
|
||||
|
||||
class AmphoraConfigUpdate(BaseAmphoraTask):
|
||||
"""Task to push a new amphora agent configuration to the amphroa."""
|
||||
|
||||
def execute(self, amphora, flavor):
|
||||
# Extract any flavor based settings
|
||||
if flavor:
|
||||
topology = flavor.get(constants.LOADBALANCER_TOPOLOGY,
|
||||
CONF.controller_worker.loadbalancer_topology)
|
||||
else:
|
||||
topology = CONF.controller_worker.loadbalancer_topology
|
||||
|
||||
# Build the amphora agent config
|
||||
agent_cfg_tmpl = agent_jinja_cfg.AgentJinjaTemplater()
|
||||
agent_config = agent_cfg_tmpl.build_agent_config(amphora.id, topology)
|
||||
|
||||
# Push the new configuration to the amphroa
|
||||
try:
|
||||
self.amphora_driver.update_amphora_agent_config(amphora,
|
||||
agent_config)
|
||||
except driver_except.AmpDriverNotImplementedError:
|
||||
LOG.error('Amphora {} does not support agent configuration '
|
||||
'update. Please update the amphora image for this '
|
||||
'amphora. Skipping.'.format(amphora.id))
|
||||
|
@ -27,6 +27,7 @@ from taskflow.types import failure
|
||||
from octavia.common import constants
|
||||
from octavia.common import data_models
|
||||
import octavia.common.tls_utils.cert_parser as cert_parser
|
||||
from octavia.common import validate
|
||||
from octavia.controller.worker import task_utils as task_utilities
|
||||
from octavia.db import api as db_apis
|
||||
from octavia.db import repositories as repo
|
||||
@ -479,7 +480,7 @@ class AssociateFailoverAmphoraWithLBID(BaseDatabaseTask):
|
||||
class MapLoadbalancerToAmphora(BaseDatabaseTask):
|
||||
"""Maps and assigns a load balancer to an amphora in the database."""
|
||||
|
||||
def execute(self, loadbalancer_id, server_group_id=None):
|
||||
def execute(self, loadbalancer_id, server_group_id=None, flavor=None):
|
||||
"""Allocates an Amphora for the load balancer in the database.
|
||||
|
||||
:param loadbalancer_id: The load balancer id to map to an amphora
|
||||
@ -495,6 +496,13 @@ class MapLoadbalancerToAmphora(BaseDatabaseTask):
|
||||
"pool allocation.")
|
||||
return None
|
||||
|
||||
# Validate the flavor is spares compatible
|
||||
if not validate.is_flavor_spares_compatible(flavor):
|
||||
LOG.debug("Load balancer has a flavor that is not compatible with "
|
||||
"using spares pool amphora. Skipping spares pool "
|
||||
"allocation.")
|
||||
return None
|
||||
|
||||
amp = self.amphora_repo.allocate_and_associate(
|
||||
db_apis.get_session(),
|
||||
loadbalancer_id)
|
||||
|
@ -19,6 +19,7 @@ import socket
|
||||
import stat
|
||||
import subprocess
|
||||
|
||||
import fixtures
|
||||
import mock
|
||||
import netifaces
|
||||
from oslo_config import fixture as oslo_fixture
|
||||
@ -36,6 +37,7 @@ from octavia.tests.common import utils as test_utils
|
||||
import octavia.tests.unit.base as base
|
||||
|
||||
|
||||
AMP_AGENT_CONF_PATH = '/etc/octavia/amphora-agent.conf'
|
||||
RANDOM_ERROR = 'random error'
|
||||
OK = dict(message='OK')
|
||||
|
||||
@ -49,6 +51,11 @@ class TestServerTestCase(base.TestCase):
|
||||
self.conf.config(group="haproxy_amphora", base_path='/var/lib/octavia')
|
||||
self.conf.config(group="controller_worker",
|
||||
loadbalancer_topology=consts.TOPOLOGY_SINGLE)
|
||||
self.conf.load_raw_values(project='fake_project')
|
||||
self.conf.load_raw_values(prog='fake_prog')
|
||||
self.useFixture(fixtures.MockPatch(
|
||||
'oslo_config.cfg.find_config_files',
|
||||
return_value=[AMP_AGENT_CONF_PATH]))
|
||||
with mock.patch('distro.id',
|
||||
return_value='ubuntu'):
|
||||
self.ubuntu_test_server = server.Server()
|
||||
@ -2650,3 +2657,38 @@ class TestServerTestCase(base.TestCase):
|
||||
self.assertEqual(200, rv.status_code)
|
||||
self.assertEqual(expected_dict,
|
||||
json.loads(rv.data.decode('utf-8')))
|
||||
|
||||
def test_ubuntu_upload_config(self):
|
||||
self._test_upload_config(consts.UBUNTU)
|
||||
|
||||
def test_centos_upload_config(self):
|
||||
self._test_upload_config(consts.CENTOS)
|
||||
|
||||
@mock.patch('oslo_config.cfg.CONF.mutate_config_files')
|
||||
def _test_upload_config(self, distro, mock_mutate):
|
||||
server.BUFFER = 5 # test the while loop
|
||||
m = self.useFixture(
|
||||
test_utils.OpenFixture(AMP_AGENT_CONF_PATH)).mock_open
|
||||
with mock.patch('os.open'), mock.patch.object(os, 'fdopen', m):
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.put('/' + api_server.VERSION +
|
||||
'/config', data='TestTest')
|
||||
elif distro == consts.CENTOS:
|
||||
rv = self.centos_app.put('/' + api_server.VERSION +
|
||||
'/config', data='TestTest')
|
||||
self.assertEqual(202, rv.status_code)
|
||||
self.assertEqual(OK, json.loads(rv.data.decode('utf-8')))
|
||||
handle = m()
|
||||
handle.write.assert_any_call(six.b('TestT'))
|
||||
handle.write.assert_any_call(six.b('est'))
|
||||
mock_mutate.assert_called_once_with()
|
||||
|
||||
# Test the exception handling
|
||||
mock_mutate.side_effect = Exception('boom')
|
||||
if distro == consts.UBUNTU:
|
||||
rv = self.ubuntu_app.put('/' + api_server.VERSION +
|
||||
'/config', data='TestTest')
|
||||
elif distro == consts.CENTOS:
|
||||
rv = self.centos_app.put('/' + api_server.VERSION +
|
||||
'/config', data='TestTest')
|
||||
self.assertEqual(500, rv.status_code)
|
||||
|
@ -127,3 +127,37 @@ class TestHealthSender(base.TestCase):
|
||||
|
||||
# Should not raise an exception
|
||||
sender.dosend(SAMPLE_MSG)
|
||||
|
||||
# Test an controller_ip_port_list update
|
||||
sendto_mock.reset_mock()
|
||||
mock_getaddrinfo.reset_mock()
|
||||
self.conf.config(group="health_manager",
|
||||
controller_ip_port_list=['192.0.2.20:80'])
|
||||
mock_getaddrinfo.return_value = [(socket.AF_INET,
|
||||
socket.SOCK_DGRAM,
|
||||
socket.IPPROTO_UDP,
|
||||
'',
|
||||
('192.0.2.20', 80))]
|
||||
sender = health_sender.UDPStatusSender()
|
||||
sender.dosend(SAMPLE_MSG)
|
||||
sendto_mock.assert_called_once_with(SAMPLE_MSG_BIN,
|
||||
('192.0.2.20', 80))
|
||||
mock_getaddrinfo.assert_called_once_with('192.0.2.20', '80',
|
||||
0, socket.SOCK_DGRAM)
|
||||
sendto_mock.reset_mock()
|
||||
mock_getaddrinfo.reset_mock()
|
||||
|
||||
self.conf.config(group="health_manager",
|
||||
controller_ip_port_list=['192.0.2.21:81'])
|
||||
mock_getaddrinfo.return_value = [(socket.AF_INET,
|
||||
socket.SOCK_DGRAM,
|
||||
socket.IPPROTO_UDP,
|
||||
'',
|
||||
('192.0.2.21', 81))]
|
||||
sender.dosend(SAMPLE_MSG)
|
||||
mock_getaddrinfo.assert_called_once_with('192.0.2.21', '81',
|
||||
0, socket.SOCK_DGRAM)
|
||||
sendto_mock.assert_called_once_with(SAMPLE_MSG_BIN,
|
||||
('192.0.2.21', 81))
|
||||
sendto_mock.reset_mock()
|
||||
mock_getaddrinfo.reset_mock()
|
||||
|
@ -375,6 +375,11 @@ class TestHaproxyAmphoraLoadBalancerDriverTest(base.TestCase):
|
||||
self.driver.client.get_info.assert_called_once_with(self.amp)
|
||||
self.assertEqual(ref_versions, result)
|
||||
|
||||
def test_update_amphora_agent_config(self):
|
||||
self.driver.update_amphora_agent_config(self.amp, six.b('test'))
|
||||
self.driver.client.update_agent_config.assert_called_once_with(
|
||||
self.amp, six.b('test'), timeout_dict=None)
|
||||
|
||||
|
||||
class TestAmphoraAPIClientTest(base.TestCase):
|
||||
|
||||
@ -1067,3 +1072,16 @@ class TestAmphoraAPIClientTest(base.TestCase):
|
||||
self.assertRaises(exc.InternalServerError,
|
||||
self.driver.get_interface,
|
||||
self.amp, ip_addr)
|
||||
|
||||
@requests_mock.mock()
|
||||
def test_update_agent_config(self, m):
|
||||
m.put("{base}/config".format(base=self.base_url))
|
||||
resp_body = self.driver.update_agent_config(self.amp, "some_file")
|
||||
self.assertEqual(200, resp_body.status_code)
|
||||
|
||||
@requests_mock.mock()
|
||||
def test_update_agent_config_error(self, m):
|
||||
m.put("{base}/config".format(base=self.base_url), status_code=500)
|
||||
self.assertRaises(exc.InternalServerError,
|
||||
self.driver.update_agent_config, self.amp,
|
||||
"some_file")
|
||||
|
@ -63,6 +63,7 @@ class TestNoopAmphoraLoadBalancerDriver(base.TestCase):
|
||||
vip_subnet=network_models.Subnet(id=self.FAKE_UUID_1))
|
||||
}
|
||||
self.pem_file = 'test_pem_file'
|
||||
self.agent_config = 'test agent config'
|
||||
self.timeout_dict = {constants.REQ_CONN_TIMEOUT: 1,
|
||||
constants.REQ_READ_TIMEOUT: 2,
|
||||
constants.CONN_MAX_RETRIES: 3,
|
||||
@ -147,3 +148,10 @@ class TestNoopAmphoraLoadBalancerDriver(base.TestCase):
|
||||
(self.amphora.id, self.pem_file, 'update_amp_cert_file'),
|
||||
self.driver.driver.amphoraconfig[(
|
||||
self.amphora.id, self.pem_file)])
|
||||
|
||||
def test_update_agent_config(self):
|
||||
self.driver.update_agent_config(self.amphora, self.agent_config)
|
||||
self.assertEqual(
|
||||
(self.amphora.id, self.agent_config, 'update_agent_config'),
|
||||
self.driver.driver.amphoraconfig[(
|
||||
self.amphora.id, self.agent_config)])
|
||||
|
@ -442,3 +442,13 @@ class TestValidations(base.TestCase):
|
||||
self.assertRaises(exceptions.InvalidOption,
|
||||
validate.ip_not_reserved,
|
||||
'2001:0DB8::5')
|
||||
|
||||
def test_is_flavor_spares_compatible(self):
|
||||
not_compat_flavor = {constants.COMPUTE_FLAVOR: 'chocolate'}
|
||||
compat_flavor = {constants.LOADBALANCER_TOPOLOGY:
|
||||
constants.TOPOLOGY_SINGLE}
|
||||
|
||||
self.assertTrue(validate.is_flavor_spares_compatible(None))
|
||||
self.assertTrue(validate.is_flavor_spares_compatible(compat_flavor))
|
||||
self.assertFalse(
|
||||
validate.is_flavor_spares_compatible(not_compat_flavor))
|
||||
|
@ -366,41 +366,45 @@ class TestAmphoraFlows(base.TestCase):
|
||||
|
||||
self.assertIsInstance(amp_flow, flow.Flow)
|
||||
|
||||
self.assertIn(constants.FLAVOR, amp_flow.requires)
|
||||
self.assertIn(constants.AMPHORA_ID, amp_flow.requires)
|
||||
self.assertIn(constants.AMPHORA, amp_flow.provides)
|
||||
|
||||
self.assertEqual(1, len(amp_flow.provides))
|
||||
self.assertEqual(1, len(amp_flow.requires))
|
||||
self.assertEqual(2, len(amp_flow.requires))
|
||||
|
||||
amp_flow = self.AmpFlow._get_post_map_lb_subflow(
|
||||
'SOMEPREFIX', constants.ROLE_BACKUP)
|
||||
|
||||
self.assertIsInstance(amp_flow, flow.Flow)
|
||||
|
||||
self.assertIn(constants.FLAVOR, amp_flow.requires)
|
||||
self.assertIn(constants.AMPHORA_ID, amp_flow.requires)
|
||||
self.assertIn(constants.AMPHORA, amp_flow.provides)
|
||||
|
||||
self.assertEqual(1, len(amp_flow.provides))
|
||||
self.assertEqual(1, len(amp_flow.requires))
|
||||
self.assertEqual(2, len(amp_flow.requires))
|
||||
|
||||
amp_flow = self.AmpFlow._get_post_map_lb_subflow(
|
||||
'SOMEPREFIX', constants.ROLE_STANDALONE)
|
||||
|
||||
self.assertIsInstance(amp_flow, flow.Flow)
|
||||
|
||||
self.assertIn(constants.FLAVOR, amp_flow.requires)
|
||||
self.assertIn(constants.AMPHORA_ID, amp_flow.requires)
|
||||
self.assertIn(constants.AMPHORA, amp_flow.provides)
|
||||
|
||||
self.assertEqual(1, len(amp_flow.provides))
|
||||
self.assertEqual(1, len(amp_flow.requires))
|
||||
self.assertEqual(2, len(amp_flow.requires))
|
||||
|
||||
amp_flow = self.AmpFlow._get_post_map_lb_subflow(
|
||||
'SOMEPREFIX', 'BOGUS_ROLE')
|
||||
|
||||
self.assertIsInstance(amp_flow, flow.Flow)
|
||||
|
||||
self.assertIn(constants.FLAVOR, amp_flow.requires)
|
||||
self.assertIn(constants.AMPHORA_ID, amp_flow.requires)
|
||||
self.assertIn(constants.AMPHORA, amp_flow.provides)
|
||||
|
||||
self.assertEqual(1, len(amp_flow.provides))
|
||||
self.assertEqual(1, len(amp_flow.requires))
|
||||
self.assertEqual(2, len(amp_flow.requires))
|
||||
|
@ -33,6 +33,7 @@ LISTENER_ID = uuidutils.generate_uuid()
|
||||
LB_ID = uuidutils.generate_uuid()
|
||||
CONN_MAX_RETRIES = 10
|
||||
CONN_RETRY_INTERVAL = 6
|
||||
FAKE_CONFIG_FILE = 'fake config file'
|
||||
|
||||
_amphora_mock = mock.MagicMock()
|
||||
_amphora_mock.id = AMP_ID
|
||||
@ -71,6 +72,8 @@ class TestAmphoraDriverTasks(base.TestCase):
|
||||
active_connection_max_retries=CONN_MAX_RETRIES)
|
||||
conf.config(group="haproxy_amphora",
|
||||
active_connection_rety_interval=CONN_RETRY_INTERVAL)
|
||||
conf.config(group="controller_worker",
|
||||
loadbalancer_topology=constants.TOPOLOGY_SINGLE)
|
||||
super(TestAmphoraDriverTasks, self).setUp()
|
||||
|
||||
def test_amp_listener_update(self,
|
||||
@ -615,3 +618,50 @@ class TestAmphoraDriverTasks(base.TestCase):
|
||||
amp_compute_conn_wait_obj.execute, _amphora_mock)
|
||||
mock_amphora_repo_update.assert_called_once_with(
|
||||
_session_mock, AMP_ID, status=constants.ERROR)
|
||||
|
||||
@mock.patch('octavia.amphorae.backends.agent.agent_jinja_cfg.'
|
||||
'AgentJinjaTemplater.build_agent_config')
|
||||
def test_amphora_config_update(self,
|
||||
mock_build_config,
|
||||
mock_driver,
|
||||
mock_generate_uuid,
|
||||
mock_log,
|
||||
mock_get_session,
|
||||
mock_listener_repo_get,
|
||||
mock_listener_repo_update,
|
||||
mock_amphora_repo_update):
|
||||
mock_build_config.return_value = FAKE_CONFIG_FILE
|
||||
amp_config_update_obj = amphora_driver_tasks.AmphoraConfigUpdate()
|
||||
mock_driver.update_amphora_agent_config.side_effect = [
|
||||
None, None, driver_except.AmpDriverNotImplementedError,
|
||||
driver_except.TimeOutException]
|
||||
# With Flavor
|
||||
flavor = {constants.LOADBALANCER_TOPOLOGY:
|
||||
constants.TOPOLOGY_ACTIVE_STANDBY}
|
||||
amp_config_update_obj.execute(_amphora_mock, flavor)
|
||||
mock_build_config.assert_called_once_with(
|
||||
_amphora_mock.id, constants.TOPOLOGY_ACTIVE_STANDBY)
|
||||
mock_driver.update_amphora_agent_config.assert_called_once_with(
|
||||
_amphora_mock, FAKE_CONFIG_FILE)
|
||||
# With no Flavor
|
||||
mock_driver.reset_mock()
|
||||
mock_build_config.reset_mock()
|
||||
amp_config_update_obj.execute(_amphora_mock, None)
|
||||
mock_build_config.assert_called_once_with(
|
||||
_amphora_mock.id, constants.TOPOLOGY_SINGLE)
|
||||
mock_driver.update_amphora_agent_config.assert_called_once_with(
|
||||
_amphora_mock, FAKE_CONFIG_FILE)
|
||||
# With amphora that does not support config update
|
||||
mock_driver.reset_mock()
|
||||
mock_build_config.reset_mock()
|
||||
amp_config_update_obj.execute(_amphora_mock, flavor)
|
||||
mock_build_config.assert_called_once_with(
|
||||
_amphora_mock.id, constants.TOPOLOGY_ACTIVE_STANDBY)
|
||||
mock_driver.update_amphora_agent_config.assert_called_once_with(
|
||||
_amphora_mock, FAKE_CONFIG_FILE)
|
||||
# With an unknown exception
|
||||
mock_driver.reset_mock()
|
||||
mock_build_config.reset_mock()
|
||||
self.assertRaises(driver_except.TimeOutException,
|
||||
amp_config_update_obj.execute,
|
||||
_amphora_mock, flavor)
|
||||
|
@ -141,7 +141,8 @@ class TestControllerWorker(base.TestCase):
|
||||
assert_called_once_with(
|
||||
'TEST',
|
||||
store={constants.BUILD_TYPE_PRIORITY:
|
||||
constants.LB_CREATE_SPARES_POOL_PRIORITY}))
|
||||
constants.LB_CREATE_SPARES_POOL_PRIORITY,
|
||||
constants.FLAVOR: None}))
|
||||
|
||||
_flow_mock.run.assert_called_once_with()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user