octavia/octavia/tests/unit/amphorae/backends/agent/api_server/test_loadbalancer.py

258 lines
11 KiB
Python

# Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# 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 subprocess
from unittest import mock
from oslo_config import cfg
from oslo_config import fixture as oslo_fixture
from oslo_utils import uuidutils
from octavia.amphorae.backends.agent.api_server import loadbalancer
from octavia.amphorae.backends.agent.api_server import util as agent_util
from octavia.common import constants as consts
from octavia.tests.common import utils as test_utils
import octavia.tests.unit.base as base
CONF = cfg.CONF
LISTENER_ID1 = uuidutils.generate_uuid()
LB_ID1 = uuidutils.generate_uuid()
class ListenerTestCase(base.TestCase):
def setUp(self):
super().setUp()
self.mock_platform = mock.patch("distro.id").start()
self.mock_platform.return_value = "ubuntu"
self.test_loadbalancer = loadbalancer.Loadbalancer()
@mock.patch('os.path.exists')
@mock.patch('octavia.amphorae.backends.agent.api_server' +
'.util.get_haproxy_pid')
def test_check_haproxy_status(self, mock_pid, mock_exists):
mock_pid.return_value = '1245'
mock_exists.side_effect = [True, True]
self.assertEqual(
consts.ACTIVE,
self.test_loadbalancer._check_haproxy_status(LISTENER_ID1))
mock_exists.side_effect = [True, False]
self.assertEqual(
consts.OFFLINE,
self.test_loadbalancer._check_haproxy_status(LISTENER_ID1))
mock_exists.side_effect = [False]
self.assertEqual(
consts.OFFLINE,
self.test_loadbalancer._check_haproxy_status(LISTENER_ID1))
@mock.patch('octavia.amphorae.backends.agent.api_server.loadbalancer.'
'Loadbalancer._check_haproxy_status')
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'vrrp_check_script_update')
@mock.patch('os.path.exists')
@mock.patch('octavia.amphorae.backends.agent.api_server.loadbalancer.'
'Loadbalancer._check_lb_exists')
@mock.patch('subprocess.check_output')
@mock.patch('octavia.amphorae.backends.utils.haproxy_query.HAProxyQuery')
def test_start_stop_lb(self, mock_haproxy_query, mock_check_output,
mock_lb_exists, mock_path_exists, mock_vrrp_update,
mock_check_status):
listener_id = uuidutils.generate_uuid()
conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
mock_path_exists.side_effect = [False, True, True, False, False]
mock_check_status.side_effect = ['bogus', consts.OFFLINE]
# Happy path - No VRRP
ref_command_split = ['/usr/sbin/service']
ref_command_split.append('haproxy-{}'.format(listener_id))
ref_command_split.append(consts.AMP_ACTION_START)
result = self.test_loadbalancer.start_stop_lb(
listener_id, consts.AMP_ACTION_START)
mock_check_output.assert_called_once_with(ref_command_split,
stderr=subprocess.STDOUT)
mock_lb_exists.assert_called_once_with(listener_id)
mock_vrrp_update.assert_not_called()
self.assertEqual(202, result.status_code)
self.assertEqual('OK', result.json['message'])
ref_details = ('Configuration file is valid\n'
'haproxy daemon for {0} started'.format(listener_id))
self.assertEqual(ref_details, result.json['details'])
# Happy path - VRRP - RELOAD
conf.config(group="controller_worker",
loadbalancer_topology=consts.TOPOLOGY_ACTIVE_STANDBY)
mock_lb_exists.reset_mock()
mock_vrrp_update.reset_mock()
mock_check_output.reset_mock()
ref_command_split = ['/usr/sbin/service']
ref_command_split.append('haproxy-{}'.format(listener_id))
ref_command_split.append(consts.AMP_ACTION_RELOAD)
result = self.test_loadbalancer.start_stop_lb(
listener_id, consts.AMP_ACTION_RELOAD)
mock_check_output.assert_called_once_with(ref_command_split,
stderr=subprocess.STDOUT)
mock_lb_exists.assert_called_once_with(listener_id)
mock_vrrp_update.assert_called_once_with(listener_id,
consts.AMP_ACTION_RELOAD)
self.assertEqual(202, result.status_code)
self.assertEqual('OK', result.json['message'])
ref_details = ('Listener {0} {1}ed'.format(listener_id,
consts.AMP_ACTION_RELOAD))
self.assertEqual(ref_details, result.json['details'])
# Happy path - VRRP - RELOAD - OFFLINE
mock_lb_exists.reset_mock()
mock_vrrp_update.reset_mock()
mock_check_output.reset_mock()
ref_command_split = ['/usr/sbin/service']
ref_command_split.append('haproxy-{}'.format(listener_id))
ref_command_split.append(consts.AMP_ACTION_START)
result = self.test_loadbalancer.start_stop_lb(
listener_id, consts.AMP_ACTION_RELOAD)
mock_check_output.assert_called_once_with(ref_command_split,
stderr=subprocess.STDOUT)
mock_lb_exists.assert_called_once_with(listener_id)
mock_vrrp_update.assert_called_once_with(listener_id,
consts.AMP_ACTION_RELOAD)
self.assertEqual(202, result.status_code)
self.assertEqual('OK', result.json['message'])
ref_details = ('Configuration file is valid\n'
'haproxy daemon for {0} started'.format(listener_id))
self.assertEqual(ref_details, result.json['details'])
# Unhappy path - Not already running
conf.config(group="controller_worker",
loadbalancer_topology=consts.TOPOLOGY_SINGLE)
mock_lb_exists.reset_mock()
mock_vrrp_update.reset_mock()
mock_check_output.reset_mock()
ref_command_split = ['/usr/sbin/service']
ref_command_split.append('haproxy-{}'.format(listener_id))
ref_command_split.append(consts.AMP_ACTION_START)
mock_check_output.side_effect = subprocess.CalledProcessError(
output=b'bogus', returncode=-2, cmd='sit')
result = self.test_loadbalancer.start_stop_lb(
listener_id, consts.AMP_ACTION_START)
mock_check_output.assert_called_once_with(ref_command_split,
stderr=subprocess.STDOUT)
mock_lb_exists.assert_called_once_with(listener_id)
mock_vrrp_update.assert_not_called()
self.assertEqual(500, result.status_code)
self.assertEqual('Error {}ing haproxy'.format(consts.AMP_ACTION_START),
result.json['message'])
self.assertEqual('bogus', result.json['details'])
# Unhappy path - Already running
mock_lb_exists.reset_mock()
mock_vrrp_update.reset_mock()
mock_check_output.reset_mock()
ref_command_split = ['/usr/sbin/service']
ref_command_split.append('haproxy-{}'.format(listener_id))
ref_command_split.append(consts.AMP_ACTION_START)
mock_check_output.side_effect = subprocess.CalledProcessError(
output=b'Job is already running', returncode=-2, cmd='sit')
result = self.test_loadbalancer.start_stop_lb(
listener_id, consts.AMP_ACTION_START)
mock_check_output.assert_called_once_with(ref_command_split,
stderr=subprocess.STDOUT)
mock_lb_exists.assert_called_once_with(listener_id)
mock_vrrp_update.assert_not_called()
self.assertEqual(202, result.status_code)
self.assertEqual('OK', result.json['message'])
ref_details = ('Configuration file is valid\n'
'haproxy daemon for {0} started'.format(listener_id))
self.assertEqual(ref_details, result.json['details'])
# Invalid action
mock_check_output.reset_mock()
mock_lb_exists.reset_mock()
mock_path_exists.reset_mock()
mock_vrrp_update.reset_mock()
result = self.test_loadbalancer.start_stop_lb(listener_id, 'bogus')
self.assertEqual(400, result.status_code)
self.assertEqual('Invalid Request', result.json['message'])
self.assertEqual('Unknown action: bogus', result.json['details'])
mock_lb_exists.assert_not_called()
mock_path_exists.assert_not_called()
mock_vrrp_update.assert_not_called()
mock_check_output.assert_not_called()
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'config_path')
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'get_haproxy_pid')
@mock.patch('os.path.exists')
def test_get_listeners_on_lb(self, mock_exists, mock_get_haproxy_pid,
mock_config_path):
fake_cfg_path = '/some/fake/cfg/file.cfg'
mock_config_path.return_value = fake_cfg_path
mock_get_haproxy_pid.return_value = 'fake_pid'
# Finds two listeners
mock_exists.side_effect = [True, True]
fake_cfg_data = 'frontend list1\nbackend foo\nfrontend list2'
self.useFixture(
test_utils.OpenFixture(fake_cfg_path, fake_cfg_data)).mock_open
result = self.test_loadbalancer._get_listeners_on_lb(LB_ID1)
self.assertEqual(['list1', 'list2'], result)
mock_exists.assert_has_calls([mock.call(agent_util.pid_path(LB_ID1)),
mock.call('/proc/fake_pid')])
# No PID file, no listeners
mock_exists.reset_mock()
mock_exists.side_effect = [False]
result = self.test_loadbalancer._get_listeners_on_lb(LB_ID1)
self.assertEqual([], result)
mock_exists.assert_called_once_with(agent_util.pid_path(LB_ID1))
# PID file, no running process, no listeners
mock_exists.reset_mock()
mock_exists.side_effect = [True, False]
result = self.test_loadbalancer._get_listeners_on_lb(LB_ID1)
self.assertEqual([], result)
mock_exists.assert_has_calls([mock.call(agent_util.pid_path(LB_ID1)),
mock.call('/proc/fake_pid')])
# PID file, running process, no listeners
mock_exists.reset_mock()
mock_exists.side_effect = [True, True]
fake_cfg_data = 'backend only'
self.useFixture(
test_utils.OpenFixture(fake_cfg_path, fake_cfg_data)).mock_open
result = self.test_loadbalancer._get_listeners_on_lb(LB_ID1)
self.assertEqual([], result)
mock_exists.assert_has_calls([mock.call(agent_util.pid_path(LB_ID1)),
mock.call('/proc/fake_pid')])