Followup patch for UDP support

1. Removes the misc_dynamic setting from the UDP-CONNECT health monitor
   as our script does not use it.
2. Adds a release note for the UDP features.
3. Updates the API reference for UDP support.
4. Adds a comment to the keepalived config with the LB ID.
5. Updates the status message type to be the correct UDP protocol.
6. Fix error during deleting a listener if there are multiple amphoraes.
7. Refactors systemd service script handling.

Story: 2003306
Task: 24258
Change-Id: I09240023d066ac5a71836d01045cda6ce5678712
changes/90/587690/19
Michael Johnson 4 years ago
parent 4c6846a568
commit cc97397d1c
  1. 28
      api-ref/source/parameters.yaml
  2. 2
      api-ref/source/v2/healthmonitor.inc
  3. 2
      api-ref/source/v2/l7policy.inc
  4. 4
      api-ref/source/v2/pool.inc
  5. 3
      elements/amphora-agent/pkg-map
  6. 41
      octavia/amphorae/backends/agent/api_server/keepalivedlvs.py
  7. 36
      octavia/amphorae/backends/agent/api_server/listener.py
  8. 21
      octavia/amphorae/backends/agent/api_server/server.py
  9. 3
      octavia/amphorae/backends/agent/api_server/templates/keepalived.systemd.j2
  10. 66
      octavia/amphorae/backends/agent/api_server/util.py
  11. 50
      octavia/amphorae/drivers/haproxy/rest_api_driver.py
  12. 4
      octavia/api/root_controller.py
  13. 9
      octavia/api/v2/controllers/pool.py
  14. 8
      octavia/common/constants.py
  15. 1
      octavia/common/jinja/lvs/jinja_cfg.py
  16. 1
      octavia/common/jinja/lvs/templates/base.j2
  17. 2
      octavia/common/jinja/lvs/templates/keepalivedlvs.cfg.j2
  18. 24
      octavia/common/jinja/lvs/templates/macros.j2
  19. 466
      octavia/tests/functional/amphorae/backend/agent/api_server/test_keepalivedlvs.py
  20. 27
      octavia/tests/functional/amphorae/backend/agent/api_server/test_server.py
  21. 6
      octavia/tests/functional/api/test_root_controller.py
  22. 356
      octavia/tests/unit/amphorae/backends/agent/api_server/test_keepalivedlvs.py
  23. 196
      octavia/tests/unit/amphorae/backends/agent/api_server/test_util.py
  24. 55
      octavia/tests/unit/amphorae/drivers/haproxy/test_rest_api_driver.py
  25. 32
      octavia/tests/unit/common/jinja/lvs/test_lvs_jinja_cfg.py
  26. 20
      releasenotes/notes/Add-UDP-protocol-support-9c011a23525092a1.yaml

@ -411,7 +411,7 @@ healthmonitor-timeout-optional:
healthmonitor-type:
description: |
The type of health monitor. One of ``HTTP``, ``HTTPS``, ``PING``, ``TCP``,
or ``TLS-HELLO``.
``TLS-HELLO``, or ``UDP-CONNECT``.
in: body
required: true
type: string
@ -838,15 +838,15 @@ project_id-optional-deprecated:
type: string
protocol:
description: |
The protocol for the resource. One of ``HTTP``, ``HTTPS``, ``TCP``, or
``TERMINATED_HTTPS``.
The protocol for the resource. One of ``HTTP``, ``HTTPS``, ``TCP``,
``TERMINATED_HTTPS``, or ``UDP``.
in: body
required: true
type: integer
protocol-pools:
description: |
The protocol for the resource. One of ``HTTP``, ``HTTPS``, ``PROXY``, or
``TCP``.
The protocol for the resource. One of ``HTTP``, ``HTTPS``, ``PROXY``,
``TCP``, or ``UDP``.
in: body
required: true
type: string
@ -995,10 +995,26 @@ session_persistence_cookie:
in: body
required: false
type: string
session_persistence_granularity:
description: |
The netmask used to determine UDP session persistence. Currently only
valid for UDP pools with session persistence of SOURCE_IP. Default netmask
is 255.255.255.255, meaning per client full IP.
in: body
required: false
type: string
session_persistence_timeout:
description: |
The timeout, in seconds, after which a UDP flow may be rescheduled to a
different member. Currently only applies to UDP pools with session
persistence of SOURCE_IP. Default is 360.
in: body
required: false
type: integer
session_persistence_type:
description: |
Session persistence type for the pool. One of ``APP_COOKIE``,
``HTTP_COOKIE``, ``SOURCE_IP``.
``HTTP_COOKIE``, or ``SOURCE_IP``.
in: body
required: true
type: string

@ -115,7 +115,7 @@ At a minimum, you must specify these health monitor attributes:
times out.
- ``type`` The type of health monitor. One of ``HTTP``, ``HTTPS``, ``PING``,
``TCP``, or ``TLS-HELLO``.
``TCP``, ``TLS-HELLO``, or ``UDP-CONNECT``.
Some attributes receive default values if you omit them from the request:

@ -110,6 +110,8 @@ L7 policies with ``action`` of ``REDIRECT_TO_URL`` will return a HTTP
L7 policies with ``action`` of ``REJECT`` will return a ``Forbidden (403)``
response code to the requester.
.. note:: Pools of type ``UDP`` cannot be used in L7 policies at this time.
.. rest_status_code:: success ../http-status.yaml
- 201

@ -95,7 +95,7 @@ is ready for further configuration.
At a minimum, you must specify these pool attributes:
- ``protocol`` The protocol for which this pool and its members
listen. A valid value is ``HTTP``, ``HTTPS``, ``PROXY``, or ``TCP``.
listen. A valid value is ``HTTP``, ``HTTPS``, ``PROXY``, ``TCP``, or ``UDP``.
- ``lb_algorithm`` The load-balancer algorithm, such as
``ROUND_ROBIN``, ``LEAST_CONNECTIONS``, and ``SOURCE_IP``, that
@ -202,6 +202,8 @@ Pool Session Persistence Object
- type: session_persistence_type
- cookie_name: session_persistence_cookie
- persistence_timeout: session_persistence_timeout
- persistence_granularity: session_persistence_granularity
Pool Session Persistence Object Example
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

@ -1,7 +1,8 @@
{
"family": {
"redhat": {
"amphora-agent": "openstack-octavia-amphora-agent"
"amphora-agent": "openstack-octavia-amphora-agent",
"netcat-openbsd": "nmap-ncat"
}
},
"default": {

@ -96,17 +96,19 @@ class KeepalivedLvs(udp_listener_base.UdpListenerApiServerBase):
if init_system == consts.INIT_SYSTEMD:
template = SYSTEMD_TEMPLATE
init_enable_cmd = ("systemctl enable "
"octavia-keepalivedlvs-%s"
% str(listener_id))
# Render and install the network namespace systemd service
util.install_netns_systemd_service()
util.run_systemctl_command(
consts.ENABLE, consts.AMP_NETNS_SVC_PREFIX)
elif init_system == consts.INIT_UPSTART:
template = UPSTART_TEMPLATE
elif init_system == consts.INIT_SYSVINIT:
template = SYSVINIT_TEMPLATE
init_enable_cmd = "insserv {file}".format(file=file_path)
else:
raise util.UnknownInitError()
# Render and install the keepalivedlvs init script
if init_system == consts.INIT_SYSTEMD:
# mode 00644
mode = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH
@ -124,12 +126,17 @@ class KeepalivedLvs(udp_listener_base.UdpListenerApiServerBase):
check_pid=check_pid,
keepalived_cmd=consts.KEEPALIVED_CMD,
keepalived_cfg=util.keepalived_lvs_cfg_path(listener_id),
amphora_nsname=consts.AMPHORA_NAMESPACE
amphora_nsname=consts.AMPHORA_NAMESPACE,
amphora_netns=consts.AMP_NETNS_SVC_PREFIX
)
text_file.write(text)
# Make sure the new service is enabled on boot
if init_system != consts.INIT_UPSTART:
# Make sure the keepalivedlvs service is enabled on boot
if init_system == consts.INIT_SYSTEMD:
util.run_systemctl_command(
consts.ENABLE, "octavia-keepalivedlvs-%s" % str(listener_id))
elif init_system == consts.INIT_SYSVINIT:
init_enable_cmd = "insserv {file}".format(file=file_path)
try:
subprocess.check_output(init_enable_cmd.split(),
stderr=subprocess.STDOUT)
@ -232,9 +239,7 @@ class KeepalivedLvs(udp_listener_base.UdpListenerApiServerBase):
def get_all_udp_listeners_status(self):
"""Gets the status of all UDP listeners
This method will not consult the stats socket
so a listener might show as ACTIVE but still be
in ERROR
Gets the status of all UDP listeners on the amphora.
"""
listeners = list()
@ -243,7 +248,7 @@ class KeepalivedLvs(udp_listener_base.UdpListenerApiServerBase):
listeners.append({
'status': status,
'uuid': udp_listener,
'type': 'lvs',
'type': 'UDP',
})
return listeners
@ -265,14 +270,14 @@ class KeepalivedLvs(udp_listener_base.UdpListenerApiServerBase):
stats = dict(
status=status,
uuid=listener_id,
type=''
type='UDP'
)
return webob.Response(json=stats)
stats = dict(
status=status,
uuid=listener_id,
type='lvs'
type='UDP'
)
try:
@ -280,7 +285,8 @@ class KeepalivedLvs(udp_listener_base.UdpListenerApiServerBase):
listener_id)
except subprocess.CalledProcessError as e:
return webob.Response(json=dict(
message="Error get kernel lvs status for udp listener",
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)
@ -321,15 +327,14 @@ class KeepalivedLvs(udp_listener_base.UdpListenerApiServerBase):
init_path = util.keepalived_lvs_init_path(init_system, listener_id)
if init_system == consts.INIT_SYSTEMD:
init_disable_cmd = (
"systemctl disable octavia-keepalivedlvs-"
"{list}".format(list=listener_id))
util.run_systemctl_command(
consts.DISABLE, "octavia-keepalivedlvs-%s" % str(listener_id))
elif init_system == consts.INIT_SYSVINIT:
init_disable_cmd = "insserv -r {file}".format(file=init_path)
elif init_system != consts.INIT_UPSTART:
raise util.UnknownInitError()
if init_system != consts.INIT_UPSTART:
if init_system == consts.INIT_SYSVINIT:
try:
subprocess.check_output(init_disable_cmd.split(),
stderr=subprocess.STDOUT)

@ -43,7 +43,7 @@ CONF = cfg.CONF
UPSTART_CONF = 'upstart.conf.j2'
SYSVINIT_CONF = 'sysvinit.conf.j2'
SYSTEMD_CONF = 'systemd.conf.j2'
AMPHORA_NETNS = 'amphora-netns'
consts.AMP_NETNS_SVC_PREFIX = 'amphora-netns'
JINJA_ENV = jinja2.Environment(
autoescape=True,
@ -161,8 +161,10 @@ class Listener(object):
if init_system == consts.INIT_SYSTEMD:
template = SYSTEMD_TEMPLATE
init_enable_cmd = "systemctl enable haproxy-{list}".format(
list=listener_id)
# Render and install the network namespace systemd service
util.install_netns_systemd_service()
util.run_systemctl_command(
consts.ENABLE, consts.AMP_NETNS_SVC_PREFIX + '.service')
elif init_system == consts.INIT_UPSTART:
template = UPSTART_TEMPLATE
elif init_system == consts.INIT_SYSVINIT:
@ -187,20 +189,6 @@ class Listener(object):
mode = (stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP |
stat.S_IROTH | stat.S_IXOTH)
if init_system == consts.INIT_SYSTEMD:
# TODO(bcafarel): implement this for other init systems
# netns handling depends on a separate unit file
netns_path = os.path.join(consts.SYSTEMD_DIR,
AMPHORA_NETNS + '.service')
if not os.path.exists(netns_path):
with os.fdopen(os.open(netns_path, flags, mode),
'w') as text_file:
text = JINJA_ENV.get_template(
AMPHORA_NETNS + '.systemd.j2').render(
amphora_nsname=consts.AMPHORA_NAMESPACE,
HasIFUPAll=self._osutils.has_ifup_all())
text_file.write(text)
hap_major, hap_minor = haproxy_compatibility.get_haproxy_versions()
if not os.path.exists(init_path):
with os.fdopen(os.open(init_path, flags, mode), 'w') as text_file:
@ -214,7 +202,7 @@ class Listener(object):
respawn_count=util.CONF.haproxy_amphora.respawn_count,
respawn_interval=(util.CONF.haproxy_amphora.
respawn_interval),
amphora_netns=AMPHORA_NETNS,
amphora_netns=consts.AMP_NETNS_SVC_PREFIX,
amphora_nsname=consts.AMPHORA_NAMESPACE,
HasIFUPAll=self._osutils.has_ifup_all(),
haproxy_major_version=hap_major,
@ -223,7 +211,10 @@ class Listener(object):
text_file.write(text)
# Make sure the new service is enabled on boot
if init_system != consts.INIT_UPSTART:
if init_system == consts.INIT_SYSTEMD:
util.run_systemctl_command(
consts.ENABLE, "haproxy-{list}".format(list=listener_id))
elif init_system == consts.INIT_SYSVINIT:
try:
subprocess.check_output(init_enable_cmd.split(),
stderr=subprocess.STDOUT)
@ -336,14 +327,15 @@ class Listener(object):
init_path = util.init_path(listener_id, init_system)
if init_system == consts.INIT_SYSTEMD:
init_disable_cmd = "systemctl disable haproxy-{list}".format(
list=listener_id)
util.run_systemctl_command(
consts.DISABLE, "haproxy-{list}".format(
list=listener_id))
elif init_system == consts.INIT_SYSVINIT:
init_disable_cmd = "insserv -r {file}".format(file=init_path)
elif init_system != consts.INIT_UPSTART:
raise util.UnknownInitError()
if init_system != consts.INIT_UPSTART:
if init_system == consts.INIT_SYSVINIT:
try:
subprocess.check_output(init_disable_cmd.split(),
stderr=subprocess.STDOUT)

@ -26,6 +26,7 @@ from octavia.amphorae.backends.agent.api_server import listener
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
from octavia.amphorae.backends.agent.api_server import util
PATH_PREFIX = '/' + api_server.VERSION
@ -43,17 +44,6 @@ def register_app_error_handler(app):
app.register_error_handler(code, make_json_error)
def check_and_return_request_listener_protocol(request):
try:
protocol_dict = request.get_json()
assert type(protocol_dict) is dict
assert 'protocol' in protocol_dict
except Exception:
raise exceptions.BadRequest(
description='Invalid protocol information for Listener')
return protocol_dict['protocol']
class Server(object):
def __init__(self):
self.app = flask.Flask(__name__)
@ -146,16 +136,14 @@ class Server(object):
return self._udp_listener.get_udp_listener_config(listener_id)
def start_stop_listener(self, listener_id, action):
protocol = check_and_return_request_listener_protocol(
flask.request)
protocol = util.get_listener_protocol(listener_id)
if protocol == 'UDP':
return self._udp_listener.manage_udp_listener(
listener_id, action)
return self._listener.start_stop_listener(listener_id, action)
def delete_listener(self, listener_id):
protocol = check_and_return_request_listener_protocol(
flask.request)
protocol = util.get_listener_protocol(listener_id)
if protocol == 'UDP':
return self._udp_listener.delete_udp_listener(listener_id)
return self._listener.delete_listener(listener_id)
@ -174,8 +162,7 @@ class Server(object):
other_listeners=udp_listeners)
def get_listener_status(self, listener_id):
protocol = check_and_return_request_listener_protocol(
flask.request)
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)

@ -1,7 +1,8 @@
[Unit]
Description=Keepalive Daemon (LVS and VRRP)
After=network-online.target
After=network-online.target {{ amphora_netns }}.service
Wants=network-online.target
Requires={{ amphora_netns }}.service
[Service]
# Force context as we start keepalived under "ip netns exec"

@ -15,13 +15,18 @@
import os
import re
import stat
import subprocess
import jinja2
from oslo_config import cfg
from oslo_log import log as logging
from octavia.amphorae.backends.agent.api_server import osutils
from octavia.common import constants as consts
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
class UnknownInitError(Exception):
@ -88,10 +93,6 @@ def keepalived_lvs_cfg_path(listener_id):
str(listener_id))
def keepalived_lvs_iptables_dir():
return os.path.join(CONF.haproxy_amphora.base_path, 'lvs/iptables/')
def haproxy_dir(listener_id):
return os.path.join(CONF.haproxy_amphora.base_path, listener_id)
@ -110,7 +111,7 @@ def get_haproxy_pid(listener_id):
def get_keepalivedlvs_pid(listener_id):
pid_file, _, _ = keepalived_lvs_pids_path(listener_id)
pid_file = keepalived_lvs_pids_path(listener_id)[0]
with open(pid_file, 'r') as f:
return f.readline().rstrip()
@ -195,7 +196,7 @@ def get_udp_listeners():
def is_udp_listener_running(listener_id):
pid_file, _, _ = keepalived_lvs_pids_path(listener_id)
pid_file = keepalived_lvs_pids_path(listener_id)[0]
return os.path.exists(pid_file) and os.path.exists(
os.path.join('/proc', get_keepalivedlvs_pid(listener_id)))
@ -215,3 +216,56 @@ def get_os_init_system():
return consts.INIT_UPSTART
return consts.INIT_SYSVINIT
return consts.INIT_UNKOWN
def install_netns_systemd_service():
os_utils = osutils.BaseOS.get_os_util()
flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
# mode 00644
mode = (stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH)
# TODO(bcafarel): implement this for other init systems
# netns handling depends on a separate unit file
netns_path = os.path.join(consts.SYSTEMD_DIR,
consts.AMP_NETNS_SVC_PREFIX + '.service')
jinja_env = jinja2.Environment(
autoescape=True, loader=jinja2.FileSystemLoader(os.path.dirname(
os.path.realpath(__file__)
) + consts.AGENT_API_TEMPLATES))
if not os.path.exists(netns_path):
with os.fdopen(os.open(netns_path, flags, mode), 'w') as text_file:
text = jinja_env.get_template(
consts.AMP_NETNS_SVC_PREFIX + '.systemd.j2').render(
amphora_nsname=consts.AMPHORA_NAMESPACE,
HasIFUPAll=os_utils.has_ifup_all())
text_file.write(text)
def run_systemctl_command(command, service):
cmd = "systemctl {cmd} {srvc}".format(cmd=command, srvc=service)
try:
subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
LOG.error("Failed to %(cmd)s %(srvc)s service: "
"%(err)s %(out)s", {'cmd': command, 'srvc': service,
'err': e, 'out': e.output})
def get_listener_protocol(listener_id):
"""Returns the L4 protocol for a listener.
If the listener is a TCP based listener (haproxy) return TCP.
If the listener is a UDP based listener (lvs) return UDP.
If the listener is not identifiable, return None.
:param listener_id: The ID of the listener to identify.
:returns: TCP, UDP, or None
"""
if os.path.exists(config_path(listener_id)):
return consts.PROTOCOL_TCP
elif os.path.exists(keepalived_lvs_cfg_path(listener_id)):
return consts.PROTOCOL_UDP
return None

@ -95,7 +95,6 @@ class HaproxyAmphoraLoadBalancerDriver(
self.client.upload_udp_config(amp, listener.id, config,
timeout_dict=timeout_dict)
self.client.reload_listener(amp, listener.id,
listener.protocol,
timeout_dict=timeout_dict)
else:
certs = self._process_tls_certificates(listener)
@ -121,8 +120,7 @@ class HaproxyAmphoraLoadBalancerDriver(
# Generate Keepalived LVS configuration from listener object
config = self.udp_jinja.build_config(listener=listener)
self.client.upload_udp_config(amp, listener.id, config)
self.client.reload_listener(amp, listener.id,
listener.protocol)
self.client.reload_listener(amp, listener.id)
def update(self, listener, vip):
if listener.protocol == 'UDP':
@ -145,8 +143,7 @@ class HaproxyAmphoraLoadBalancerDriver(
tls_cert=certs['tls_cert'],
user_group=CONF.haproxy_amphora.user_group)
self.client.upload_config(amp, listener.id, config)
self.client.reload_listener(amp, listener.id,
listener.protocol)
self.client.reload_listener(amp, listener.id)
def upload_cert_amp(self, amp, pem):
LOG.debug("Amphora %s updating cert in REST driver "
@ -154,37 +151,13 @@ class HaproxyAmphoraLoadBalancerDriver(
self.__class__.__name__, amp.id)
self.client.update_cert_for_rotation(amp, pem)
def _check_if_need_add_listener_protocol(self, func):
# as _apply func will be called by create/update/delete and some cert
# related function. But there is only a port of them can accept
# listener protocol parameter, including:
# start/stop functions call _apply by functools.
# delete function call _apply directly.
# escape cert operation based on function name.
# So add this check for verify if the target function need a protocol
# parameter.
called_by_functools = (not hasattr(func, '__name__') and
hasattr(func, 'func') and
func.func.__name__.find('cert') < 0)
called_directly = (hasattr(func, '__name__') and
func.__name__.find('cert') < 0)
return called_directly or called_by_functools
def _apply(self, func, listener=None, amphora=None, *args):
if amphora is None:
for amp in listener.load_balancer.amphorae:
if amp.status != consts.DELETED:
if self._check_if_need_add_listener_protocol(func):
_list = list(args)
_list.append(listener.protocol)
args = _list
func(amp, listener.id, *args)
else:
if amphora.status != consts.DELETED:
if self._check_if_need_add_listener_protocol(func):
_list = list(args)
_list.append(listener.protocol)
args = _list
func(amphora, listener.id, *args)
def stop(self, listener, vip):
@ -428,21 +401,17 @@ class AmphoraAPIClient(object):
data=config)
return exc.check_exception(r)
def get_listener_status(self, amp, listener_id, protocol=None):
protocol_dict = {'protocol': protocol}
def get_listener_status(self, amp, listener_id):
r = self.get(
amp,
'listeners/{listener_id}'.format(listener_id=listener_id),
json=protocol_dict)
'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, protocol, timeout_dict=None):
protocol_dict = {'protocol': protocol}
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,
json=protocol_dict)
listener_id=listener_id, action=action), timeout_dict=timeout_dict)
return exc.check_exception(r)
def upload_cert_pem(self, amp, listener_id, pem_filename, pem_file):
@ -465,12 +434,9 @@ class AmphoraAPIClient(object):
return r.json().get("md5sum")
return None
def delete_listener(self, amp, listener_id, protocol):
protocol_dict = {'protocol': protocol}
def delete_listener(self, amp, listener_id):
r = self.delete(
amp, 'listeners/{listener_id}'.format(listener_id=listener_id),
json=protocol_dict)
amp, 'listeners/{listener_id}'.format(listener_id=listener_id))
return exc.check_exception(r, (404,))
def get_info(self, amp):

@ -73,6 +73,8 @@ class RootController(rest.RestController):
if CONF.api_settings.api_v2_enabled:
self._add_a_version(versions, 'v2.0', 'v2', 'SUPPORTED',
'2016-12-11T00:00:00Z', host_url)
self._add_a_version(versions, 'v2.1', 'v2', 'CURRENT',
self._add_a_version(versions, 'v2.1', 'v2', 'SUPPORTED',
'2018-04-20T00:00:00Z', host_url)
self._add_a_version(versions, 'v2.2', 'v2', 'CURRENT',
'2018-07-31T00:00:00Z', host_url)
return {'versions': versions}

@ -130,20 +130,17 @@ class PoolsController(base.BaseController):
def _is_only_specified_in_request(self, request, **kwargs):
request_attrs = []
check_attrs = kwargs['check_exist_attrs']
excaped_attrs = ['from_data_model',
escaped_attrs = ['from_data_model',
'translate_dict_keys_to_data_model', 'to_dict']
for attr in dir(request):
if attr.startswith('_') or attr in excaped_attrs:
if attr.startswith('_') or attr in escaped_attrs:
continue
else:
request_attrs.append(attr)
for req_attr in request_attrs:
if (getattr(
request, req_attr) and req_attr not in check_attrs) or (
not getattr(
request, req_attr) and req_attr in check_attrs):
if (getattr(request, req_attr) and req_attr not in check_attrs):
return False
return True

@ -32,7 +32,6 @@ HEALTH_MONITOR_HTTP = 'HTTP'
HEALTH_MONITOR_HTTPS = 'HTTPS'
HEALTH_MONITOR_TLS_HELLO = 'TLS-HELLO'
HEALTH_MONITOR_UDP_CONNECT = 'UDP-CONNECT'
UDP_CONNECT_SCRIPT_MIN_INTERVAL = 3
SUPPORTED_HEALTH_MONITOR_TYPES = (HEALTH_MONITOR_HTTP, HEALTH_MONITOR_HTTPS,
HEALTH_MONITOR_PING, HEALTH_MONITOR_TCP,
HEALTH_MONITOR_TLS_HELLO,
@ -547,3 +546,10 @@ OCTAVIA = 'octavia'
# FLAVORS
# TODO(johnsom) When flavors are implemented, this should be removed.
SUPPORTED_FLAVORS = ()
# systemctl commands
DISABLE = 'disable'
ENABLE = 'enable'
# systemd amphora netns service prefix
AMP_NETNS_SVC_PREFIX = 'amphora-netns'

@ -100,6 +100,7 @@ class LvsJinjaTemplater(object):
"""
t_listener = self._transform_listener(listener)
ret_value = {
'id': loadbalancer.id,
'vip_address': loadbalancer.vip.ip_address,
'listener': t_listener,
'enabled': loadbalancer.enabled

@ -13,6 +13,7 @@
# under the License.
#
#}
# Configuration for Loadbalancer {{ loadbalancer.id }}
# Configuration for Listener {{ udp_listener_id }}
{% block global_definitions %}{% endblock global_definitions %}

@ -17,7 +17,7 @@
{% from 'macros.j2' import virtualserver_macro %}
{% set udp_listener_id = loadbalancer.listener.id %}
{% block global_definitions %}
net_namespace amphora-haproxy
net_namespace {{ constants.AMPHORA_NAMESPACE }}
{% endblock global_definitions %}
{% block proxies %}
{% if loadbalancer.enabled and loadbalancer.listener.enabled %}

@ -26,7 +26,6 @@ misc_path "{{ health_monitor.check_script_path }} {{ member.address }} {{ member
MISC_CHECK {
{{ misc_path_macro(member, health_monitor) }}
misc_timeout {{ pool.health_monitor.delay }}
misc_dynamic
}
{%- endmacro -%}
@ -52,10 +51,6 @@ MISC_CHECK {
{% if listener.connection_limit %}
uthreshold {{ listener.connection_limit }}
{% endif %}
{% if pool.session_persistence and pool.session_persistence.type == constants.SESSION_PERSISTENCE_SOURCE_IP %}
persistence_timeout {{ pool.session_persistence.persistence_timeout }}
persistence_granularity {{ pool.session_persistence.persistence_granularity }}
{% endif %}
{{- health_monitor_rs_macro(constants, pool, member) }}
}
{% endmacro %}
@ -84,11 +79,22 @@ MISC_CHECK {
{% if need_render|length > 0 %}
virtual_server {{ lb_vip_address }} {{ listener.protocol_port }} {
{{ lb_algo_macro(default_pool) }}
{% if not default_pool.session_persistence %}
ops
{% endif %}
lb_kind NAT
protocol {{ listener.protocol_mode }}
protocol {{ listener.protocol_mode.upper() }}
{% if default_pool.session_persistence and default_pool.session_persistence.type == constants.SESSION_PERSISTENCE_SOURCE_IP %}
{# set our defined defaults as I saw this not be consistent #}
{# in testing #}
{% if default_pool.session_persistence.persistence_timeout %}
persistence_timeout {{ default_pool.session_persistence.persistence_timeout }}
{% else %}
persistence_timeout 360
{% endif %}
{% if default_pool.session_persistence.persistence_granularity %}
persistence_granularity {{ default_pool.session_persistence.persistence_granularity }}
{% else %}
persistence_granularity 255.255.255.255
{% endif %}
{% endif %}
{{ health_monitor_vs_macro(default_pool) }}
{% if default_pool.protocol.lower() == "udp" %}

@ -0,0 +1,466 @@
# Copyright 2015 Hewlett Packard Enterprise Development Company LP
#
# 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 stat
import subprocess
import flask
import mock
from werkzeug import exceptions
from oslo_utils import uuidutils
from octavia.amphorae.backends.agent.api_server import keepalivedlvs
from octavia.amphorae.backends.agent.api_server import server
from octavia.amphorae.backends.agent.api_server import util
from octavia.common import constants as consts
from octavia.tests.common import utils as test_utils
from octavia.tests.unit import base
class KeepalivedLvsTestCase(base.TestCase):
FAKE_ID = uuidutils.generate_uuid()
LISTENER_ID = 'listener-1111-1111-1111-listenerid00'
POOL_ID = 'poolpool-1111-1111-1111-poolid000000'
MEMBER_ID1 = 'memberid-1111-1111-1111-memberid1111'
MEMBER_ID2 = 'memberid-2222-2222-2222-memberid2222'
HEALTHMONITOR_ID = 'hmidhmid-1111-1111-1111-healthmonito'
NORMAL_CFG_CONTENT = (
"# Configuration for Listener %(listener_id)s\n\n"
"net_namespace haproxy-amphora\n\n"
"virtual_server 10.0.0.2 80 {\n"
" lb_algo rr\n"
" lb_kind NAT\n"
" protocol udp\n"
" delay_loop 30\n"
" delay_before_retry 31\n"
" retry 3\n\n\n"
" # Configuration for Pool %(pool_id)s\n"
" # Configuration for HealthMonitor %(hm_id)s\n"
" # Configuration for Member %(member1_id)s\n"
" real_server 10.0.0.99 82 {\n"
" weight 13\n"
" inhibit_on_failure\n"
" uthreshold 98\n"
" persistence_timeout 33\n"
" persistence_granularity 255.255.0.0\n"
" delay_before_retry 31\n"
" retry 3\n"
" MISC_CHECK {\n"
" misc_path \"/var/lib/octavia/lvs/check/"
"udp_check.sh 10.0.0.99 82\"\n"
" misc_timeout 30\n"
" misc_dynamic\n"
" }\n"
" }\n\n"
" # Configuration for Member %(member2_id)s\n"
" real_server 10.0.0.98 82 {\n"
" weight 13\n"
" inhibit_on_failure\n"
" uthreshold 98\n"
" persistence_timeout 33\n"
" persistence_granularity 255.255.0.0\n"
" delay_before_retry 31\n"
" retry 3\n"
" MISC_CHECK {\n"
" misc_path \"/var/lib/octavia/lvs/check/"
"udp_check.sh 10.0.0.98 82\"\n"
" misc_timeout 30\n"
" misc_dynamic\n"
" }\n"
" }\n\n"
"}\n\n") % {'listener_id': LISTENER_ID, 'pool_id': POOL_ID,
'hm_id': HEALTHMONITOR_ID, 'member1_id': MEMBER_ID1,
'member2_id': MEMBER_ID2}
PROC_CONTENT = (
"IP Virtual Server version 1.2.1 (size=4096)\n"
"Prot LocalAddress:Port Scheduler Flags\n"
" -> RemoteAddress:Port Forward Weight ActiveConn InActConn\n"
"UDP 0A000002:0050 sh\n"
" -> 0A000063:0052 Masq 13 1 0\n"
" -> 0A000062:0052 Masq 13 1 0\n"
)
NORMAL_PID_CONTENT = "1988"
TEST_URL = server.PATH_PREFIX + '/listeners/%s/%s/udp_listener'
def setUp(self):
super(KeepalivedLvsTestCase, self).setUp()
self.app = flask.Flask(__name__)
self.client = self.app.test_client()
self._ctx = self.app.test_request_context()
self._ctx.push()
self.test_keepalivedlvs = keepalivedlvs.KeepalivedLvs()
self.app.add_url_rule(
rule=self.TEST_URL % ('<amphora_id>', '<listener_id>'),
view_func=(lambda amphora_id, listener_id:
self.test_keepalivedlvs.upload_udp_listener_config(
listener_id)),
methods=['PUT'])
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'run_systemctl_command')
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'install_netns_systemd_service')
@mock.patch('pyroute2.NetNS')
@mock.patch('shutil.copy2')
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'get_os_init_system', return_value=consts.INIT_SYSTEMD)
@mock.patch('os.chmod')
@mock.patch('os.path.exists')
@mock.patch('os.makedirs')
@mock.patch('os.remove')
@mock.patch('subprocess.check_output')
def test_upload_udp_listener_config_no_vrrp_check_dir(
self, m_check_output, m_os_rm, m_os_mkdir, m_exists, m_os_chmod,
m_os_sysinit, m_copy2, mock_netns, mock_install_netns,
mock_systemctl):
m_exists.side_effect = [False, False, True, True, False, False]
cfg_path = util.keepalived_lvs_cfg_path(self.FAKE_ID)
m = self.useFixture(test_utils.OpenFixture(cfg_path)).mock_open
with mock.patch('os.open') as m_open, mock.patch.object(os,
'fdopen',
m) as m_fdopen:
m_open.side_effect = ['TEST-WRITE-CFG',
'TEST-WRITE-SYSINIT']
res = self.client.put(self.TEST_URL % ('123', self.FAKE_ID),
data=self.NORMAL_CFG_CONTENT)
mock_install_netns.assert_called_once()
systemctl_calls = [
mock.call(consts.ENABLE,
consts.AMP_NETNS_SVC_PREFIX),
mock.call(consts.ENABLE,
'octavia-keepalivedlvs-%s' % str(self.FAKE_ID)),
]
mock_systemctl.assert_has_calls(systemctl_calls)
os_mkdir_calls = [
mock.call(util.keepalived_lvs_dir()),
mock.call(util.keepalived_backend_check_script_dir())
]
m_os_mkdir.assert_has_calls(os_mkdir_calls)
m_os_chmod.assert_called_with(
util.keepalived_backend_check_script_path(), stat.S_IEXEC)
flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
mode = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH
systemd_cfg_path = util.keepalived_lvs_init_path(
consts.INIT_SYSTEMD, self.FAKE_ID)
m_open_calls = [
mock.call(cfg_path, flags, mode),
mock.call(systemd_cfg_path, flags, mode)
]
m_open.assert_has_calls(m_open_calls)
m_fdopen.assert_any_call('TEST-WRITE-CFG', 'wb')
m_fdopen.assert_any_call('TEST-WRITE-SYSINIT', 'w')
self.assertEqual(200, res.status_code)
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'run_systemctl_command')
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'install_netns_systemd_service')
@mock.patch('pyroute2.NetNS')
@mock.patch('shutil.copy2')
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'get_os_init_system', return_value=consts.INIT_SYSTEMD)
@mock.patch('os.chmod')
@mock.patch('os.path.exists')
@mock.patch('os.makedirs')
@mock.patch('os.remove')
@mock.patch('subprocess.check_output')
def test_upload_udp_listener_config_with_vrrp_check_dir(
self, m_check_output, m_os_rm, m_os_mkdir, m_exists, m_os_chmod,
m_os_sysinit, m_copy2, mock_netns, mock_install_netns,
mock_systemctl):
m_exists.side_effect = [False, False, True, True, True, False, False]
cfg_path = util.keepalived_lvs_cfg_path(self.FAKE_ID)
m = self.useFixture(test_utils.OpenFixture(cfg_path)).mock_open
with mock.patch('os.open') as m_open, mock.patch.object(os,
'fdopen',
m) as m_fdopen:
m_open.side_effect = ['TEST-WRITE-CFG',
'TEST-WRITE-SYSINIT',
'TEST-WRITE-UDP-VRRP-CHECK']
res = self.client.put(self.TEST_URL % ('123', self.FAKE_ID),
data=self.NORMAL_CFG_CONTENT)
os_mkdir_calls = [
mock.call(util.keepalived_lvs_dir()),
mock.call(util.keepalived_backend_check_script_dir())
]
m_os_mkdir.assert_has_calls(os_mkdir_calls)
mock_install_netns.assert_called_once()
systemctl_calls = [
mock.call(consts.ENABLE,
consts.AMP_NETNS_SVC_PREFIX),
mock.call(consts.ENABLE,
'octavia-keepalivedlvs-%s' % str(self.FAKE_ID)),
]
mock_systemctl.assert_has_calls(systemctl_calls)
m_os_chmod.assert_called_with(
util.keepalived_backend_check_script_path(), stat.S_IEXEC)
flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
mode = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH
systemd_cfg_path = util.keepalived_lvs_init_path(
consts.INIT_SYSTEMD, self.FAKE_ID)
script_path = os.path.join(
util.keepalived_check_scripts_dir(),
keepalivedlvs.KEEPALIVED_CHECK_SCRIPT_NAME)
m_open_calls = [
mock.call(cfg_path, flags, mode),
mock.call(systemd_cfg_path, flags, mode),
mock.call(script_path, flags, stat.S_IEXEC)
]
m_open.assert_has_calls(m_open_calls)
m_fdopen.assert_any_call('TEST-WRITE-CFG', 'wb')
m_fdopen.assert_any_call('TEST-WRITE-SYSINIT', 'w')
m_fdopen.assert_any_call('TEST-WRITE-UDP-VRRP-CHECK', 'w')
self.assertEqual(200, res.status_code)
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'run_systemctl_command')
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'install_netns_systemd_service')
@mock.patch('shutil.copy2')
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'get_os_init_system', return_value=consts.INIT_SYSTEMD)
@mock.patch('os.chmod')
@mock.patch('os.path.exists')
@mock.patch('os.makedirs')
@mock.patch('os.remove')
@mock.patch('subprocess.check_output')
def test_upload_udp_listener_config_start_service_failure(
self, m_check_output, m_os_rm, m_os_mkdir, m_exists, m_os_chmod,
m_os_sysinit, m_copy2, mock_install_netns, mock_systemctl):
m_exists.side_effect = [False, False, True, True, True, False]
m_check_output.side_effect = subprocess.CalledProcessError(1, 'blah!')
cfg_path = util.keepalived_lvs_cfg_path(self.FAKE_ID)
m = self.useFixture(test_utils.OpenFixture(cfg_path)).mock_open
with mock.patch('os.open') as m_open, mock.patch.object(os,
'fdopen',
m) as m_fdopen:
m_open.side_effect = ['TEST-WRITE-CFG',
'TEST-WRITE-SYSINIT']
res = self.client.put(self.TEST_URL % ('123', self.FAKE_ID),
data=self.NORMAL_CFG_CONTENT)
os_mkdir_calls = [
mock.call(util.keepalived_lvs_dir()),
mock.call(util.keepalived_backend_check_script_dir())
]
m_os_mkdir.assert_has_calls(os_mkdir_calls)
mock_install_netns.assert_called_once()
systemctl_calls = [
mock.call(consts.ENABLE,
consts.AMP_NETNS_SVC_PREFIX),
mock.call(consts.ENABLE,
'octavia-keepalivedlvs-%s' % str(self.FAKE_ID)),
]
mock_systemctl.assert_has_calls(systemctl_calls)
m_os_chmod.assert_called_with(
util.keepalived_backend_check_script_path(), stat.S_IEXEC)
flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
mode = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH
systemd_cfg_path = util.keepalived_lvs_init_path(
consts.INIT_SYSTEMD, self.FAKE_ID)
m_open_calls = [
mock.call(cfg_path, flags, mode),
mock.call(systemd_cfg_path, flags, mode)
]
m_open.assert_has_calls(m_open_calls)
m_fdopen.assert_any_call('TEST-WRITE-CFG', 'wb')
m_fdopen.assert_any_call('TEST-WRITE-SYSINIT', 'w')
self.assertEqual(500, res.status_code)
@mock.patch('subprocess.check_output')
@mock.patch('octavia.amphorae.backends.agent.api_server.'
'keepalivedlvs.KeepalivedLvs.'
'_check_udp_listener_exists')
def test_manage_udp_listener(self, mock_udp_exist, mock_check_output):
res = self.test_keepalivedlvs.manage_udp_listener(self.FAKE_ID,
'start')
cmd = ("/usr/sbin/service octavia-keepalivedlvs-{listener_id}"
" {action}".format(listener_id=self.FAKE_ID, action='start'))
mock_check_output.assert_called_once_with(cmd.split(),
stderr=subprocess.STDOUT)
self.assertEqual(202, res.status_code)
res = self.test_keepalivedlvs.manage_udp_listener(self.FAKE_ID,
'restart')
self.assertEqual(400, res.status_code)
mock_check_output.side_effect = subprocess.CalledProcessError(1,
'blah!')
res = self.test_keepalivedlvs.manage_udp_listener(self.FAKE_ID,
'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.'
'get_os_init_system', return_value=consts.INIT_SYSTEMD)
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'get_keepalivedlvs_pid')
@mock.patch('subprocess.check_output')
@mock.patch('os.remove')
@mock.patch('os.path.exists')
def test_delete_udp_listener(self, m_exist, m_remove, m_check_output,
mget_pid, m_init_sys, mget_udp_listeners):
m_exist.return_value = True
res = self.test_keepalivedlvs.delete_udp_listener(self.FAKE_ID)
cmd1 = ("/usr/sbin/service "
"octavia-keepalivedlvs-{0} stop".format(self.FAKE_ID))
cmd2 = ("systemctl disable "
"octavia-keepalivedlvs-{list}".format(list=self.FAKE_ID))
calls = [
mock.call(cmd1.split(), stderr=subprocess.STDOUT),
mock.call(cmd2.split(), stderr=subprocess.STDOUT)
]
m_check_output.assert_has_calls(calls)
self.assertEqual(200, res.status_code)
@mock.patch.object(keepalivedlvs, "webob")
@mock.patch('os.path.exists')
def test_delete_udp_listener_not_exist(self, m_exist, m_webob):
m_exist.return_value = False
self.test_keepalivedlvs.delete_udp_listener(self.FAKE_ID)
calls = [
mock.call(
json=dict(message='UDP Listener Not Found',
details="No UDP listener with UUID: "
"{0}".format(self.FAKE_ID)), status=404),
mock.call(json={'message': 'OK'})
]
m_webob.Response.assert_has_calls(calls)
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'get_keepalivedlvs_pid')
@mock.patch('subprocess.check_output')
@mock.patch('os.path.exists')
def test_delete_udp_listener_stop_service_fail(self, m_exist,
m_check_output, mget_pid):
m_exist.return_value = True
m_check_output.side_effect = subprocess.CalledProcessError(1,
'Woops!')
res = self.test_keepalivedlvs.delete_udp_listener(self.FAKE_ID)
self.assertEqual(500, res.status_code)
self.assertEqual({'message': 'Error stopping keepalivedlvs',
'details': None}, res.json)
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'get_os_init_system', return_value=consts.INIT_SYSVINIT)
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'get_keepalivedlvs_pid')
@mock.patch('subprocess.check_output')
@mock.patch('os.remove')
@mock.patch('os.path.exists')
def test_delete_udp_listener_disable_service_fail(self, m_exist, m_remove,
m_check_output, mget_pid,
m_init_sys):
m_exist.return_value = True
m_check_output.side_effect = [True,
subprocess.CalledProcessError(
1, 'Woops!')]
res = self.test_keepalivedlvs.delete_udp_listener(self.FAKE_ID)
self.assertEqual(500, res.status_code)
self.assertEqual({
'message': 'Error disabling '
'octavia-keepalivedlvs-%s service' % self.FAKE_ID,
'details': None}, res.json)
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'get_os_init_system')
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'get_keepalivedlvs_pid')
@mock.patch('subprocess.check_output')
@mock.patch('os.remove')
@mock.patch('os.path.exists')
def test_delete_udp_listener_unsupported_sysinit(self, m_exist, m_remove,
m_check_output, mget_pid,
m_init_sys):
m_exist.return_value = True
self.assertRaises(
util.UnknownInitError, self.test_keepalivedlvs.delete_udp_listener,
self.FAKE_ID)

@ -57,9 +57,6 @@ class TestServerTestCase(base.TestCase):
self.conf = self.useFixture(oslo_fixture.Config(config.cfg.CONF))
self.conf.config(group="haproxy_amphora", base_path='/var/lib/octavia')
mock.patch('octavia.amphorae.backends.agent.api_server.server.'
'check_and_return_request_listener_protocol',
return_value='TCP').start()
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'get_os_init_system', return_value=consts.INIT_SYSTEMD)
@ -400,27 +397,39 @@ class TestServerTestCase(base.TestCase):
hostname='test-host'),
json.loads(rv.data.decode('utf-8')))
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'get_listener_protocol', 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):
def test_delete_ubuntu_listener_systemd(self, mock_init_system,
mock_get_proto):