434 lines
21 KiB
Python
434 lines
21 KiB
Python
# Copyright 2017 Rackspace US, Inc.
|
|
#
|
|
# 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 random
|
|
|
|
import mock
|
|
from oslo_utils import uuidutils
|
|
|
|
from octavia.amphorae.backends.agent import api_server
|
|
from octavia.amphorae.backends.agent.api_server import amphora_info
|
|
from octavia.amphorae.backends.agent.api_server import util
|
|
from octavia.common.jinja.haproxy.combined_listeners import jinja_cfg
|
|
from octavia.tests.common import utils as test_utils
|
|
import octavia.tests.unit.base as base
|
|
from octavia.tests.unit.common.sample_configs import sample_configs_combined
|
|
|
|
|
|
class TestAmphoraInfo(base.TestCase):
|
|
|
|
API_VERSION = random.randrange(0, 10000)
|
|
BASE_AMP_PATH = '/var/lib/octavia'
|
|
BASE_CRT_PATH = BASE_AMP_PATH + '/certs'
|
|
HAPROXY_VERSION = random.randrange(0, 10000)
|
|
KEEPALIVED_VERSION = random.randrange(0, 10000)
|
|
IPVSADM_VERSION = random.randrange(0, 10000)
|
|
FAKE_LISTENER_ID_1 = uuidutils.generate_uuid()
|
|
FAKE_LISTENER_ID_2 = uuidutils.generate_uuid()
|
|
FAKE_LISTENER_ID_3 = uuidutils.generate_uuid()
|
|
FAKE_LISTENER_ID_4 = uuidutils.generate_uuid()
|
|
LB_ID_1 = uuidutils.generate_uuid()
|
|
|
|
def setUp(self):
|
|
super(TestAmphoraInfo, self).setUp()
|
|
self.osutils_mock = mock.MagicMock()
|
|
self.amp_info = amphora_info.AmphoraInfo(self.osutils_mock)
|
|
self.udp_driver = mock.MagicMock()
|
|
|
|
# setup a fake haproxy config file
|
|
templater = jinja_cfg.JinjaTemplater(
|
|
base_amp_path=self.BASE_AMP_PATH,
|
|
base_crt_dir=self.BASE_CRT_PATH)
|
|
tls_tupel = sample_configs_combined.sample_tls_container_tuple(
|
|
id='tls_container_id',
|
|
certificate='imaCert1', private_key='imaPrivateKey1',
|
|
primary_cn='FakeCN')
|
|
self.rendered_haproxy_cfg = templater.render_loadbalancer_obj(
|
|
sample_configs_combined.sample_amphora_tuple(),
|
|
[sample_configs_combined.sample_listener_tuple(
|
|
proto='TERMINATED_HTTPS', tls=True, sni=True)],
|
|
tls_tupel)
|
|
path = util.config_path(self.LB_ID_1)
|
|
self.useFixture(test_utils.OpenFixture(path,
|
|
self.rendered_haproxy_cfg))
|
|
|
|
def _return_version(self, package_name):
|
|
if package_name == 'ipvsadm':
|
|
return self.IPVSADM_VERSION
|
|
elif package_name == 'keepalived':
|
|
return self.KEEPALIVED_VERSION
|
|
else:
|
|
return self.HAPROXY_VERSION
|
|
|
|
@mock.patch.object(amphora_info, "webob")
|
|
@mock.patch('octavia.amphorae.backends.agent.api_server.'
|
|
'amphora_info.AmphoraInfo._get_version_of_installed_package',
|
|
return_value=HAPROXY_VERSION)
|
|
@mock.patch('socket.gethostname', return_value='FAKE_HOST')
|
|
def test_compile_amphora_info(self, mock_gethostname, mock_pkg_version,
|
|
mock_webob):
|
|
original_version = api_server.VERSION
|
|
api_server.VERSION = self.API_VERSION
|
|
expected_dict = {'api_version': self.API_VERSION,
|
|
'hostname': 'FAKE_HOST',
|
|
'haproxy_version': self.HAPROXY_VERSION}
|
|
self.amp_info.compile_amphora_info()
|
|
mock_webob.Response.assert_called_once_with(json=expected_dict)
|
|
api_server.VERSION = original_version
|
|
|
|
@mock.patch.object(amphora_info, "webob")
|
|
@mock.patch('octavia.amphorae.backends.agent.api_server.'
|
|
'amphora_info.AmphoraInfo._get_version_of_installed_package')
|
|
@mock.patch('socket.gethostname', return_value='FAKE_HOST')
|
|
def test_compile_amphora_info_for_udp(self, mock_gethostname,
|
|
mock_pkg_version, mock_webob):
|
|
|
|
mock_pkg_version.side_effect = self._return_version
|
|
self.udp_driver.get_subscribed_amp_compile_info.side_effect = [
|
|
['keepalived', 'ipvsadm']]
|
|
original_version = api_server.VERSION
|
|
api_server.VERSION = self.API_VERSION
|
|
expected_dict = {'api_version': self.API_VERSION,
|
|
'hostname': 'FAKE_HOST',
|
|
'haproxy_version': self.HAPROXY_VERSION,
|
|
'keepalived_version': self.KEEPALIVED_VERSION,
|
|
'ipvsadm_version': self.IPVSADM_VERSION
|
|
}
|
|
self.amp_info.compile_amphora_info(extend_udp_driver=self.udp_driver)
|
|
mock_webob.Response.assert_called_once_with(json=expected_dict)
|
|
api_server.VERSION = original_version
|
|
|
|
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
|
|
'get_listeners', return_value=[FAKE_LISTENER_ID_1,
|
|
FAKE_LISTENER_ID_2])
|
|
@mock.patch('octavia.amphorae.backends.agent.api_server.'
|
|
'amphora_info.AmphoraInfo._get_meminfo')
|
|
@mock.patch('octavia.amphorae.backends.agent.api_server.'
|
|
'amphora_info.AmphoraInfo._cpu')
|
|
@mock.patch('os.statvfs')
|
|
@mock.patch('octavia.amphorae.backends.agent.api_server.'
|
|
'amphora_info.AmphoraInfo._get_networks')
|
|
@mock.patch('octavia.amphorae.backends.agent.api_server.'
|
|
'amphora_info.AmphoraInfo._load')
|
|
@mock.patch('octavia.amphorae.backends.agent.api_server.'
|
|
'amphora_info.AmphoraInfo._get_version_of_installed_package')
|
|
@mock.patch('octavia.amphorae.backends.agent.api_server.'
|
|
'amphora_info.AmphoraInfo._count_haproxy_processes')
|
|
@mock.patch('socket.gethostname', return_value='FAKE_HOST')
|
|
def test_compile_amphora_details(self, mhostname, m_count, m_pkg_version,
|
|
m_load, m_get_nets, m_os, m_cpu,
|
|
mget_mem, mget_listener):
|
|
mget_mem.return_value = {'SwapCached': 0, 'Buffers': 344792,
|
|
'MemTotal': 21692784, 'Cached': 4271856,
|
|
'Slab': 534384, 'MemFree': 12685624,
|
|
'Shmem': 9520}
|
|
m_cpu.return_value = {'user': '252551', 'softirq': '8336',
|
|
'system': '52554', 'total': 7503411}
|
|
m_pkg_version.side_effect = self._return_version
|
|
mdisk_info = mock.MagicMock()
|
|
m_os.return_value = mdisk_info
|
|
mdisk_info.f_blocks = 34676992
|
|
mdisk_info.f_bfree = 28398016
|
|
mdisk_info.f_frsize = 4096
|
|
mdisk_info.f_bavail = 26630646
|
|
m_get_nets.return_value = {'eth1': {'network_rx': 996,
|
|
'network_tx': 418},
|
|
'eth2': {'network_rx': 848,
|
|
'network_tx': 578}}
|
|
m_load.return_value = ['0.09', '0.11', '0.10']
|
|
m_count.return_value = 5
|
|
original_version = api_server.VERSION
|
|
api_server.VERSION = self.API_VERSION
|
|
expected_dict = {u'active': True,
|
|
u'api_version': self.API_VERSION,
|
|
u'cpu': {u'soft_irq': u'8336',
|
|
u'system': u'52554',
|
|
u'total': 7503411,
|
|
u'user': u'252551'},
|
|
u'disk': {u'available': 109079126016,
|
|
u'used': 25718685696},
|
|
u'haproxy_count': 5,
|
|
u'haproxy_version': self.HAPROXY_VERSION,
|
|
u'hostname': u'FAKE_HOST',
|
|
u'listeners': sorted([self.FAKE_LISTENER_ID_1,
|
|
self.FAKE_LISTENER_ID_2]),
|
|
u'load': [u'0.09', u'0.11', u'0.10'],
|
|
u'memory': {u'buffers': 344792,
|
|
u'cached': 4271856,
|
|
u'free': 12685624,
|
|
u'shared': 9520,
|
|
u'slab': 534384,
|
|
u'swap_used': 0,
|
|
u'total': 21692784},
|
|
u'networks': {u'eth1': {u'network_rx': 996,
|
|
u'network_tx': 418},
|
|
u'eth2': {u'network_rx': 848,
|
|
u'network_tx': 578}},
|
|
u'packages': {},
|
|
u'topology': u'SINGLE',
|
|
u'topology_status': u'OK'}
|
|
actual = self.amp_info.compile_amphora_details()
|
|
self.assertEqual(expected_dict, actual.json)
|
|
api_server.VERSION = original_version
|
|
|
|
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
|
|
'get_udp_listeners',
|
|
return_value=[FAKE_LISTENER_ID_3, FAKE_LISTENER_ID_4])
|
|
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
|
|
'get_loadbalancers')
|
|
@mock.patch('octavia.amphorae.backends.agent.api_server.'
|
|
'amphora_info.AmphoraInfo._get_meminfo')
|
|
@mock.patch('octavia.amphorae.backends.agent.api_server.'
|
|
'amphora_info.AmphoraInfo._cpu')
|
|
@mock.patch('os.statvfs')
|
|
@mock.patch('octavia.amphorae.backends.agent.api_server.'
|
|
'amphora_info.AmphoraInfo._get_networks')
|
|
@mock.patch('octavia.amphorae.backends.agent.api_server.'
|
|
'amphora_info.AmphoraInfo._load')
|
|
@mock.patch('octavia.amphorae.backends.agent.api_server.'
|
|
'amphora_info.AmphoraInfo._get_version_of_installed_package')
|
|
@mock.patch('octavia.amphorae.backends.agent.api_server.'
|
|
'amphora_info.AmphoraInfo._count_haproxy_processes')
|
|
@mock.patch('socket.gethostname', return_value='FAKE_HOST')
|
|
def test_compile_amphora_details_for_udp(self, mhostname, m_count,
|
|
m_pkg_version, m_load, m_get_nets,
|
|
m_os, m_cpu, mget_mem,
|
|
mock_get_lb, mget_udp_listener):
|
|
mget_mem.return_value = {'SwapCached': 0, 'Buffers': 344792,
|
|
'MemTotal': 21692784, 'Cached': 4271856,
|
|
'Slab': 534384, 'MemFree': 12685624,
|
|
'Shmem': 9520}
|
|
m_cpu.return_value = {'user': '252551', 'softirq': '8336',
|
|
'system': '52554', 'total': 7503411}
|
|
m_pkg_version.side_effect = self._return_version
|
|
mdisk_info = mock.MagicMock()
|
|
m_os.return_value = mdisk_info
|
|
mdisk_info.f_blocks = 34676992
|
|
mdisk_info.f_bfree = 28398016
|
|
mdisk_info.f_frsize = 4096
|
|
mdisk_info.f_bavail = 26630646
|
|
m_get_nets.return_value = {'eth1': {'network_rx': 996,
|
|
'network_tx': 418},
|
|
'eth2': {'network_rx': 848,
|
|
'network_tx': 578}}
|
|
m_load.return_value = ['0.09', '0.11', '0.10']
|
|
m_count.return_value = 5
|
|
self.udp_driver.get_subscribed_amp_compile_info.return_value = [
|
|
'keepalived', 'ipvsadm']
|
|
self.udp_driver.is_listener_running.side_effect = [True, False]
|
|
mock_get_lb.return_value = [self.LB_ID_1]
|
|
original_version = api_server.VERSION
|
|
api_server.VERSION = self.API_VERSION
|
|
expected_dict = {u'active': True,
|
|
u'api_version': self.API_VERSION,
|
|
u'cpu': {u'soft_irq': u'8336',
|
|
u'system': u'52554',
|
|
u'total': 7503411,
|
|
u'user': u'252551'},
|
|
u'disk': {u'available': 109079126016,
|
|
u'used': 25718685696},
|
|
u'haproxy_count': 5,
|
|
u'haproxy_version': self.HAPROXY_VERSION,
|
|
u'keepalived_version': self.KEEPALIVED_VERSION,
|
|
u'ipvsadm_version': self.IPVSADM_VERSION,
|
|
u'udp_listener_process_count': 1,
|
|
u'hostname': u'FAKE_HOST',
|
|
u'listeners': sorted(list(set(
|
|
[self.FAKE_LISTENER_ID_3,
|
|
self.FAKE_LISTENER_ID_4,
|
|
'sample_listener_id_1']))),
|
|
u'load': [u'0.09', u'0.11', u'0.10'],
|
|
u'memory': {u'buffers': 344792,
|
|
u'cached': 4271856,
|
|
u'free': 12685624,
|
|
u'shared': 9520,
|
|
u'slab': 534384,
|
|
u'swap_used': 0,
|
|
u'total': 21692784},
|
|
u'networks': {u'eth1': {u'network_rx': 996,
|
|
u'network_tx': 418},
|
|
u'eth2': {u'network_rx': 848,
|
|
u'network_tx': 578}},
|
|
u'packages': {},
|
|
u'topology': u'SINGLE',
|
|
u'topology_status': u'OK'}
|
|
actual = self.amp_info.compile_amphora_details(self.udp_driver)
|
|
self.assertEqual(expected_dict, actual.json)
|
|
api_server.VERSION = original_version
|
|
|
|
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
|
|
'is_lb_running')
|
|
def test__count_haproxy_process(self, mock_is_running):
|
|
|
|
# Test no listeners passed in
|
|
result = self.amp_info._count_haproxy_processes([])
|
|
self.assertEqual(0, result)
|
|
|
|
# Test with a listener specified
|
|
mock_is_running.side_effect = [True, False]
|
|
result = self.amp_info._count_haproxy_processes(
|
|
[uuidutils.generate_uuid(), uuidutils.generate_uuid()])
|
|
self.assertEqual(1, result)
|
|
|
|
def test__count_udp_listener_processes(self):
|
|
self.udp_driver.is_listener_running.side_effect = [True, False, True]
|
|
expected = 2
|
|
actual = self.amp_info._count_udp_listener_processes(
|
|
self.udp_driver, [self.FAKE_LISTENER_ID_1,
|
|
self.FAKE_LISTENER_ID_2,
|
|
self.FAKE_LISTENER_ID_3])
|
|
self.assertEqual(expected, actual)
|
|
|
|
@mock.patch('octavia.amphorae.backends.agent.api_server.'
|
|
'amphora_info.AmphoraInfo._get_version_of_installed_package')
|
|
def test__get_extend_body_from_udp_driver(self, m_get_version):
|
|
self.udp_driver.get_subscribed_amp_compile_info.return_value = [
|
|
'keepalived', 'ipvsadm']
|
|
m_get_version.side_effect = self._return_version
|
|
expected = {
|
|
"keepalived_version": self.KEEPALIVED_VERSION,
|
|
"ipvsadm_version": self.IPVSADM_VERSION
|
|
}
|
|
actual = self.amp_info._get_extend_body_from_udp_driver(
|
|
self.udp_driver)
|
|
self.assertEqual(expected, actual)
|
|
|
|
def test__get_meminfo(self):
|
|
# Known data test
|
|
meminfo = ('MemTotal: 21692784 kB\n'
|
|
'MemFree: 12685624 kB\n'
|
|
'MemAvailable: 17384072 kB\n'
|
|
'Buffers: 344792 kB\n'
|
|
'Cached: 4271856 kB\n'
|
|
'SwapCached: 0 kB\n'
|
|
'Active: 5808816 kB\n'
|
|
'Inactive: 2445236 kB\n'
|
|
'Active(anon): 3646184 kB\n'
|
|
'Inactive(anon): 8672 kB\n'
|
|
'Active(file): 2162632 kB\n'
|
|
'Inactive(file): 2436564 kB\n'
|
|
'Unevictable: 52664 kB\n'
|
|
'Mlocked: 52664 kB\n'
|
|
'SwapTotal: 20476924 kB\n'
|
|
'SwapFree: 20476924 kB\n'
|
|
'Dirty: 92 kB\n'
|
|
'Writeback: 0 kB\n'
|
|
'AnonPages: 3690088 kB\n'
|
|
'Mapped: 108520 kB\n'
|
|
'Shmem: 9520 kB\n'
|
|
'Slab: 534384 kB\n'
|
|
'SReclaimable: 458160 kB\n'
|
|
'SUnreclaim: 76224 kB\n'
|
|
'KernelStack: 11776 kB\n'
|
|
'PageTables: 33088 kB\n'
|
|
'NFS_Unstable: 0 kB\n'
|
|
'Bounce: 0 kB\n'
|
|
'WritebackTmp: 0 kB\n'
|
|
'CommitLimit: 31323316 kB\n'
|
|
'Committed_AS: 6930732 kB\n'
|
|
'VmallocTotal: 34359738367 kB\n'
|
|
'VmallocUsed: 0 kB\n'
|
|
'VmallocChunk: 0 kB\n'
|
|
'HardwareCorrupted: 0 kB\n'
|
|
'AnonHugePages: 1400832 kB\n'
|
|
'CmaTotal: 0 kB\n'
|
|
'CmaFree: 0 kB\n'
|
|
'HugePages_Total: 0\n'
|
|
'HugePages_Free: 0\n'
|
|
'HugePages_Rsvd: 0\n'
|
|
'HugePages_Surp: 0\n'
|
|
'Hugepagesize: 2048 kB\n'
|
|
'DirectMap4k: 130880 kB\n'
|
|
'DirectMap2M: 8376320 kB\n'
|
|
'DirectMap1G: 14680064 kB\n')
|
|
|
|
self.useFixture(test_utils.OpenFixture('/proc/meminfo',
|
|
contents=meminfo))
|
|
|
|
expected_result = {'SwapCached': 0, 'DirectMap2M': 8376320,
|
|
'CmaTotal': 0, 'Inactive': 2445236,
|
|
'KernelStack': 11776, 'SwapTotal': 20476924,
|
|
'VmallocUsed': 0, 'Buffers': 344792,
|
|
'MemTotal': 21692784, 'Mlocked': 52664,
|
|
'Cached': 4271856, 'AnonPages': 3690088,
|
|
'Unevictable': 52664, 'SUnreclaim': 76224,
|
|
'MemFree': 12685624, 'Writeback': 0,
|
|
'NFS_Unstable': 0, 'VmallocTotal': 34359738367,
|
|
'MemAvailable': 17384072, 'CmaFree': 0,
|
|
'SwapFree': 20476924, 'AnonHugePages': 1400832,
|
|
'DirectMap1G': 14680064, 'Hugepagesize': 2048,
|
|
'Dirty': 92, 'Bounce': 0, 'PageTables': 33088,
|
|
'SReclaimable': 458160, 'Active': 5808816,
|
|
'Mapped': 108520, 'Slab': 534384,
|
|
'Active(anon)': 3646184, 'VmallocChunk': 0,
|
|
'Inactive(file)': 2436564, 'WritebackTmp': 0,
|
|
'Shmem': 9520, 'Inactive(anon)': 8672,
|
|
'HardwareCorrupted': 0, 'Active(file)': 2162632,
|
|
'DirectMap4k': 130880, 'Committed_AS': 6930732,
|
|
'CommitLimit': 31323316}
|
|
|
|
result = self.amp_info._get_meminfo()
|
|
self.assertEqual(expected_result, result)
|
|
|
|
def test__cpu(self):
|
|
|
|
sample_stat = 'cpu 252551 802 52554 7181757 7411 0 8336 0 0 0'
|
|
|
|
expected_result = {'user': '252551', 'iowait': '7411', 'nice': '802',
|
|
'softirq': '8336', 'idle': '7181757',
|
|
'system': '52554', 'total': 7503411, 'irq': '0'}
|
|
|
|
self.useFixture(test_utils.OpenFixture('/proc/stat',
|
|
contents=sample_stat))
|
|
|
|
result = self.amp_info._cpu()
|
|
|
|
self.assertEqual(expected_result, result)
|
|
|
|
def test__load(self):
|
|
|
|
sample_loadavg = '0.09 0.11 0.10 2/630 15346'
|
|
|
|
expected_result = ['0.09', '0.11', '0.10']
|
|
|
|
self.useFixture(test_utils.OpenFixture('/proc/loadavg',
|
|
contents=sample_loadavg))
|
|
|
|
result = self.amp_info._load()
|
|
|
|
self.assertEqual(expected_result, result)
|
|
|
|
@mock.patch('pyroute2.NetNS')
|
|
def test__get_networks(self, mock_netns):
|
|
|
|
# The output of get_links is huge, just pulling out the parts we
|
|
# care about for this test.
|
|
sample_get_links_minimal = [
|
|
{'attrs': [('IFLA_IFNAME', 'lo')]},
|
|
{'attrs': [('IFLA_IFNAME', 'eth1'),
|
|
('IFLA_STATS64', {'tx_bytes': 418, 'rx_bytes': 996})]},
|
|
{'attrs': [('IFLA_IFNAME', 'eth2'),
|
|
('IFLA_STATS64', {'tx_bytes': 578, 'rx_bytes': 848})]},
|
|
{'attrs': [('IFLA_IFNAME', 'eth3')]}]
|
|
|
|
netns_handle = mock_netns.return_value.__enter__.return_value
|
|
netns_handle.get_links.return_value = sample_get_links_minimal
|
|
|
|
expected_result = {'eth1': {'network_rx': 996, 'network_tx': 418},
|
|
'eth2': {'network_rx': 848, 'network_tx': 578}}
|
|
|
|
result = self.amp_info._get_networks()
|
|
|
|
self.assertEqual(expected_result, result)
|