octavia/octavia/tests/unit/cmd/test_health_checker.py
Gregory Thiemonge ce2e0d7425 Add test coverage for SCTP health checker script
Also removed a block of shell code in install-ubuntu.rst,
because the block triggered an error in the doc job and it was unused.

Change-Id: I41033e8cd9710a91b9502db11577b1f1cb85fa46
2021-02-19 08:34:46 +01:00

329 lines
11 KiB
Python

# Copyright 2020 Red Hat, 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 socket
import struct
from unittest import mock
from oslo_config import cfg
from oslo_config import fixture as oslo_fixture
from octavia.cmd import health_checker
from octavia.tests.common import utils as test_utils
from octavia.tests.unit import base
CONF = cfg.CONF
class TestHealthCheckerCMD(base.TestCase):
def setUp(self):
super(TestHealthCheckerCMD, self).setUp()
self.CONF = self.useFixture(oslo_fixture.Config(cfg.CONF))
def test_crc32c(self):
data = b'STRING1234'
result = health_checker.crc32c(data)
self.assertEqual(result, 0x30e0e107)
@mock.patch('random.randint', return_value=42424242)
def test__sctp_build_init_packet(self, mock_randint):
expected_packet = bytearray(
b'\x04\xd2\x16.\x00\x00\x00\x00\x1d9\x96\r\x01\x00\x00\x14:\xde'
b'h\xb1\x00\x01\xa0\x00\x00\n\xff\xff\x02\x87W\xb2')
src_port = 1234
dest_port = 5678
tag = 987654321
pkt = health_checker._sctp_build_init_packet(
src_port, dest_port, tag)
self.assertEqual(pkt, expected_packet)
decoded_src_port = struct.unpack_from('!H', pkt, 0)[0]
decoded_dest_port = struct.unpack_from('!H', pkt, 2)[0]
self.assertEqual(src_port, decoded_src_port)
self.assertEqual(dest_port, decoded_dest_port)
decoded_tag = struct.unpack_from('!L', pkt, 16)[0]
self.assertEqual(tag, decoded_tag)
decoded_checksum = struct.unpack_from('!L', pkt, 8)[0]
# Reset and re-compute checksum
pkt[8] = pkt[9] = pkt[10] = pkt[11] = 0
checksum = health_checker.crc32c(pkt)
self.assertEqual(checksum, decoded_checksum)
def test__sctp_build_abort_packet(self):
expected_packet = bytearray(
b'\x04\xd2\x16.\x02\x93wM3\x83\xbbN\x06\x01\x00\x04')
src_port = 1234
dest_port = 5678
verification_tag = 43218765
pkt = health_checker._sctp_build_abort_packet(
src_port, dest_port, verification_tag)
self.assertEqual(pkt, expected_packet)
decoded_src_port = struct.unpack_from('!H', pkt, 0)[0]
decoded_dest_port = struct.unpack_from('!H', pkt, 2)[0]
self.assertEqual(src_port, decoded_src_port)
self.assertEqual(dest_port, decoded_dest_port)
decoded_tag = struct.unpack_from('!L', pkt, 4)[0]
self.assertEqual(verification_tag, decoded_tag)
decoded_checksum = struct.unpack_from('!L', pkt, 8)[0]
# Reset and re-compute checksum
pkt[8] = pkt[9] = pkt[10] = pkt[11] = 0
checksum = health_checker.crc32c(pkt)
self.assertEqual(checksum, decoded_checksum)
def test__sctp_decode_packet(self):
# IPv4 INIT ACK packet
data = (b'\x45\x00\x00\x00\x00\x01\x01\x01'
b'\x00\x00\xff\x06\x7f\x00\x00\x00'
b'\x7f\x00\x00\x02\x16.\x04\xd2'
b'\x02\x93\x77\x4d\x00\x00\x00\x32'
b'\x02\x00\x00\x16')
family = socket.AF_INET
expected_tag = 43218765
ret = health_checker._sctp_decode_packet(data, family, expected_tag)
self.assertEqual(ret, 2) # INIT ACK
# IPv6 ABORT packet
data = (b'\x16.\x04\xd2\x02\x93\x77\x4d\x00\x00\x00\x32'
b'\x06\x00\x00\x16')
family = socket.AF_INET6
expected_tag = 43218765
ret = health_checker._sctp_decode_packet(data, family, expected_tag)
self.assertEqual(ret, 6) # ABORT
def test__sctp_decode_packet_too_short(self):
# IPv4 packet with different verification tag
data = (b'\x45\x00\x00\x00\x00\x01')
family = socket.AF_INET
expected_tag = 43218765
ret = health_checker._sctp_decode_packet(data, family, expected_tag)
self.assertFalse(ret)
def test__sctp_decode_packet_unexpected(self):
# IPv4 packet with different verification tag
data = (b'\x45\x00\x00\x00\x00\x01\x01\x01'
b'\x00\x00\xff\x06\x7f\x00\x00\x00'
b'\x7f\x00\x00\x02\x16.\x04\xd2'
b'\x02\x91\x17\x4d\x00\x00\x00\x32'
b'\x02\x00\x00\x16')
family = socket.AF_INET
expected_tag = 43218765
ret = health_checker._sctp_decode_packet(data, family, expected_tag)
self.assertFalse(ret)
@mock.patch("time.time")
@mock.patch("socket.socket")
@mock.patch("octavia.cmd.health_checker._sctp_decode_packet")
@mock.patch("octavia.cmd.health_checker._sctp_build_abort_packet")
def test_sctp_health_check(self, mock_build_abort_packet,
mock_decode_packet, mock_socket,
mock_time):
mock_time.side_effect = [1, 2, 3, 4]
socket_mock = mock.Mock()
socket_mock.recvfrom = mock.Mock()
socket_mock.recvfrom.side_effect = [
socket.timeout(),
(None, None)
]
mock_socket.return_value = socket_mock
mock_decode_packet.return_value = 2 # INIT ACK
abrt_mock = mock.Mock()
mock_build_abort_packet.return_value = abrt_mock
mock_open = self.useFixture(
test_utils.OpenFixture('/proc/net/protocols',
'bar\n')).mock_open
with mock.patch('builtins.open', mock_open):
ret = health_checker.sctp_health_check(
"192.168.0.27", 1234, timeout=3)
self.assertEqual(0, ret) # Success
mock_decode_packet.assert_called()
socket_mock.send.assert_called_with(abrt_mock)
@mock.patch("time.time")
@mock.patch("socket.socket")
@mock.patch("octavia.cmd.health_checker._sctp_decode_packet")
@mock.patch("octavia.cmd.health_checker._sctp_build_abort_packet")
def test_sctp_health_check_with_sctp_support(self,
mock_build_abort_packet,
mock_decode_packet,
mock_socket,
mock_time):
mock_time.side_effect = [1, 2, 3, 4]
socket_mock = mock.Mock()
socket_mock.recvfrom = mock.Mock()
socket_mock.recvfrom.side_effect = [
socket.timeout(),
(None, None)
]
mock_socket.return_value = socket_mock
mock_decode_packet.return_value = 2 # INIT ACK
abrt_mock = mock.Mock()
mock_build_abort_packet.return_value = abrt_mock
mock_open = self.useFixture(
test_utils.OpenFixture('/proc/net/protocols',
'SCTP\n')).mock_open
with mock.patch('builtins.open', mock_open):
ret = health_checker.sctp_health_check(
"192.168.0.27", 1234, timeout=3)
self.assertEqual(0, ret) # Success
mock_decode_packet.assert_called()
for call in socket_mock.send.mock_calls:
self.assertNotEqual(mock.call(abrt_mock), call)
@mock.patch("time.time")
@mock.patch("socket.socket")
@mock.patch("octavia.cmd.health_checker._sctp_decode_packet")
@mock.patch("octavia.cmd.health_checker._sctp_build_abort_packet")
def test_sctp_health_check_fail(self, mock_build_abort_packet,
mock_decode_packet, mock_socket,
mock_time):
mock_time.side_effect = [1, 2, 3, 4]
socket_mock = mock.Mock()
socket_mock.recvfrom = mock.Mock()
socket_mock.recvfrom.side_effect = [
socket.timeout(),
(None, None)
]
mock_socket.return_value = socket_mock
mock_decode_packet.return_value = 6 # ABRT
abrt_mock = mock.Mock()
mock_build_abort_packet.return_value = abrt_mock
mock_open = self.useFixture(
test_utils.OpenFixture('/proc/net/protocols',
'bar\n')).mock_open
with mock.patch('builtins.open', mock_open):
ret = health_checker.sctp_health_check(
"192.168.0.27", 1234, timeout=3)
self.assertEqual(1, ret) # Error
mock_decode_packet.assert_called()
for call in socket_mock.send.mock_calls:
self.assertNotEqual(mock.call(abrt_mock), call)
@mock.patch("time.time")
@mock.patch("socket.socket")
@mock.patch("octavia.cmd.health_checker._sctp_decode_packet")
@mock.patch("octavia.cmd.health_checker._sctp_build_abort_packet")
def test_sctp_health_check_error(self, mock_build_abort_packet,
mock_decode_packet, mock_socket,
mock_time):
mock_time.side_effect = [1, 2, 3, 4]
socket_mock = mock.Mock()
socket_mock.recvfrom = mock.Mock()
socket_mock.recvfrom.side_effect = [
socket.timeout(),
(None, None)
]
mock_socket.return_value = socket_mock
mock_decode_packet.return_value = 1234 # Unknown
abrt_mock = mock.Mock()
mock_build_abort_packet.return_value = abrt_mock
mock_open = self.useFixture(
test_utils.OpenFixture('/proc/net/protocols',
'bar\n')).mock_open
with mock.patch('builtins.open', mock_open):
ret = health_checker.sctp_health_check(
"192.168.0.27", 1234, timeout=3)
self.assertEqual(3, ret) # Unknown error
mock_decode_packet.assert_called()
socket_mock.send.assert_called_with(abrt_mock)
@mock.patch("time.time")
@mock.patch("socket.socket")
@mock.patch("octavia.cmd.health_checker._sctp_decode_packet")
@mock.patch("octavia.cmd.health_checker._sctp_build_abort_packet")
def test_sctp_health_check_timeout(self, mock_build_abort_packet,
mock_decode_packet, mock_socket,
mock_time):
mock_time.side_effect = [1, 2, 3, 4]
socket_mock = mock.Mock()
socket_mock.recvfrom = mock.Mock()
socket_mock.recvfrom.side_effect = [
socket.timeout(),
socket.timeout(),
socket.timeout(),
socket.timeout(),
]
mock_socket.return_value = socket_mock
abrt_mock = mock.Mock()
mock_build_abort_packet.return_value = abrt_mock
mock_open = self.useFixture(
test_utils.OpenFixture('/proc/net/protocols',
'bar\n')).mock_open
with mock.patch('builtins.open', mock_open):
ret = health_checker.sctp_health_check(
"192.168.0.27", 1234, timeout=3)
self.assertEqual(2, ret) # Timeout
mock_decode_packet.assert_not_called()
for call in socket_mock.send.mock_calls:
self.assertNotEqual(mock.call(abrt_mock), call)