Files
zaza-openstack-tests/unit_tests/utilities/test_zaza_utilities_generic.py
T
Bas de Bruijne 9fb488efef Update series upgrade automation (#952)
* update series upgrade test

* bug fixes

* bug fixes
2023-01-23 12:39:10 +00:00

555 lines
20 KiB
Python

# Copyright 2018 Canonical Ltd.
#
# 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 asyncio
import mock
import unit_tests.utils as ut_utils
from zaza.openstack.utilities import generic as generic_utils
import zaza.openstack.utilities.exceptions as zaza_exceptions
FAKE_STATUS = {
'can-upgrade-to': '',
'charm': 'local:trusty/app-136',
'subordinate-to': [],
'units': {'app/0': {'leader': True,
'machine': '0',
'subordinates': {
'app-hacluster/0': {
'charm': 'local:trusty/hacluster-0',
'leader': True}}},
'app/1': {'machine': '1',
'subordinates': {
'app-hacluster/1': {
'charm': 'local:trusty/hacluster-0'}}},
'app/2': {'machine': '2',
'subordinates': {
'app-hacluster/2': {
'charm': 'local:trusty/hacluster-0'}}}}}
class TestGenericUtils(ut_utils.BaseTestCase):
def setUp(self):
super(TestGenericUtils, self).setUp()
# Patch all subprocess calls
self.patch(
'zaza.openstack.utilities.generic.subprocess',
new_callable=mock.MagicMock(),
name='subprocess'
)
# Juju Status Object and data
self.juju_status = mock.MagicMock()
self.juju_status.applications.__getitem__.return_value = FAKE_STATUS
self.patch_object(generic_utils, "model")
self.model.get_status.return_value = self.juju_status
def test_dict_to_yaml(self):
_dict_data = {"key": "value"}
_str_data = "key: value\n"
self.assertEqual(generic_utils.dict_to_yaml(_dict_data),
_str_data)
def test_get_network_config(self):
self.patch_object(generic_utils.os.path, "exists")
self.patch_object(generic_utils, "get_yaml_config")
self.patch_object(generic_utils, "get_undercloud_env_vars")
net_topology = "topo"
_data = {net_topology: {"network": "DATA"}}
self.get_yaml_config.return_value = _data
# YAML file does not exist
self.exists.return_value = False
with self.assertRaises(Exception):
generic_utils.get_network_config(net_topology)
# No environmental variables
self.exists.return_value = True
self.assertEqual(
generic_utils.get_network_config(net_topology,
ignore_env_vars=True),
_data[net_topology])
self.get_yaml_config.assert_called_once_with("network.yaml")
self.get_undercloud_env_vars.assert_not_called()
# Update with environmental variables
_more_data = {"network": "NEW",
"other": "DATA"}
self.get_undercloud_env_vars.return_value = _more_data
_data[net_topology].update(_more_data)
self.assertEqual(
generic_utils.get_network_config(net_topology),
_data[net_topology])
self.get_undercloud_env_vars.assert_called_once_with()
def test_get_pkg_version(self):
self.patch_object(generic_utils.model, "get_units")
self.patch_object(generic_utils.juju_utils, "remote_run")
_pkg = "os-thingy"
_version = "2:27.0.0-0ubuntu1~cloud0"
_dpkg_output = ("ii {} {} all OpenStack thingy\n"
.format(_pkg, _version))
self.remote_run.return_value = _dpkg_output
_unit1 = mock.MagicMock()
_unit1.entity_id = "os-thingy/7"
_unit2 = mock.MagicMock()
_unit2.entity_id = "os-thingy/12"
_units = [_unit1, _unit2]
self.get_units.return_value = _units
# Matching
self.assertEqual(generic_utils.get_pkg_version(_pkg, _pkg),
_version)
# Mismatched
_different_dpkg_output = ("ii {} {} all OpenStack thingy\n"
.format(_pkg, "DIFFERENT"))
self.remote_run.side_effect = [_dpkg_output, _different_dpkg_output]
with self.assertRaises(Exception):
generic_utils.get_pkg_version(_pkg, _pkg)
def test_get_undercloud_env_vars(self):
self.patch_object(generic_utils.os.environ, "get")
def _get_env(key):
return _env.get(key)
self.get.side_effect = _get_env
# Prefered OSCI TEST_ env vars
_env = {"NET_ID": "netid",
"NAME_SERVER": "10.0.0.10",
"GATEWAY": "10.0.0.1",
"CIDR_EXT": "10.0.0.0/24",
"FIP_RANGE": "10.0.200.0:10.0.200.254",
"TEST_NET_ID": "test_netid",
"TEST_NAME_SERVER": "10.9.0.10",
"TEST_GATEWAY": "10.9.0.1",
"TEST_CIDR_EXT": "10.9.0.0/24",
"TEST_FIP_RANGE": "10.9.200.0:10.0.200.254"}
_expected_result = {}
_expected_result["net_id"] = _env["TEST_NET_ID"]
_expected_result["external_dns"] = _env["TEST_NAME_SERVER"]
_expected_result["default_gateway"] = _env["TEST_GATEWAY"]
_expected_result["external_net_cidr"] = _env["TEST_CIDR_EXT"]
_expected_result["start_floating_ip"] = _env[
"TEST_FIP_RANGE"].split(":")[0]
_expected_result["end_floating_ip"] = _env[
"TEST_FIP_RANGE"].split(":")[1]
self.assertEqual(generic_utils.get_undercloud_env_vars(),
_expected_result)
# Overriding local configure.network named variables
_override = {"start_floating_ip": "10.100.50.0",
"end_floating_ip": "10.100.50.254",
"default_gateway": "10.100.0.1",
"external_net_cidr": "10.100.0.0/16"}
_env.update(_override)
_expected_result.update(_override)
self.assertEqual(generic_utils.get_undercloud_env_vars(),
_expected_result)
def test_get_yaml_config(self):
self.patch("builtins.open",
new_callable=mock.mock_open(),
name="_open")
_yaml = "data: somedata"
_yaml_dict = {"data": "somedata"}
_filename = "filename"
_fileobj = mock.MagicMock()
_fileobj.read.return_value = _yaml
self._open.return_value = _fileobj
self.assertEqual(generic_utils.get_yaml_config(_filename),
_yaml_dict)
self._open.assert_called_once_with(_filename, "r")
def test_reboot(self):
_unit = "app/2"
generic_utils.reboot(_unit)
self.subprocess.check_call.assert_called_once_with(
['juju', 'ssh', _unit,
'sudo', 'reboot', '&&', 'exit'])
def test_run_via_ssh(self):
_unit = "app/2"
_cmd = "hostname"
generic_utils.run_via_ssh(_unit, _cmd)
self.subprocess.check_call.assert_called_once_with(
['juju', 'ssh', _unit,
'sudo ' + _cmd])
def test_set_origin(self):
"application, origin='openstack-origin', pocket='distro'):"
self.patch_object(generic_utils.model, "async_set_application_config",
new_callable=mock.MagicMock(),
name="set_application_config")
future = asyncio.Future()
future.set_result(None)
self.set_application_config.return_value = future
_application = "application"
_origin = "source"
_pocket = "cloud:fake-cloud"
generic_utils.set_origin(_application, origin=_origin, pocket=_pocket)
self.set_application_config.assert_called_once_with(
_application, {_origin: _pocket})
def test_set_origin_auto(self):
self.patch_object(generic_utils.model, "async_set_application_config",
new_callable=mock.MagicMock(),
name="set_application_config")
set_future = asyncio.Future()
set_future.set_result(None)
self.set_application_config.return_value = set_future
self.patch_object(generic_utils.model, "async_get_application_config",
new_callable=mock.MagicMock(),
name="get_application_config")
get_future = asyncio.Future()
get_future.set_result({"source": {"value": "distro"}})
self.get_application_config.return_value = get_future
_application = "application"
_origin = "auto"
_pocket = "cloud:fake-cloud"
generic_utils.set_origin(_application, origin=_origin, pocket=_pocket)
self.set_application_config.assert_called_once_with(
_application, {"source": _pocket})
def test_set_dpkg_non_interactive_on_unit(self):
self.patch_object(generic_utils, "model")
_unit_name = "app/1"
generic_utils.set_dpkg_non_interactive_on_unit(_unit_name)
self.model.run_on_unit.assert_called_with(
"app/1",
'grep \'DPkg::options { "--force-confdef"; };\' '
'/etc/apt/apt.conf.d/50unattended-upgrades || '
'echo \'DPkg::options { "--force-confdef"; };\' >> '
'/etc/apt/apt.conf.d/50unattended-upgrades')
def test_get_process_id_list(self):
self.patch(
"zaza.openstack.utilities.generic.model.run_on_unit",
new_callable=mock.MagicMock(),
name="_run"
)
# Return code is OK and STDOUT contains output
returns_ok = {
"Code": 0,
"Stdout": "1 2",
"Stderr": ""
}
self._run.return_value = returns_ok
p_id_list = generic_utils.get_process_id_list(
"ceph-osd/0",
"ceph-osd",
False
)
expected = ["1", "2"]
cmd = 'pidof -x "ceph-osd" || exit 0 && exit 1'
self.assertEqual(p_id_list, expected)
self._run.assert_called_once_with(unit_name="ceph-osd/0",
command=cmd)
# Return code is not OK
returns_nok = {
"Code": 1,
"Stdout": "",
"Stderr": "Something went wrong"
}
self._run.return_value = returns_nok
with self.assertRaises(zaza_exceptions.ProcessIdsFailed):
generic_utils.get_process_id_list("ceph-osd/0", "ceph")
cmd = 'pidof -x "ceph"'
self._run.assert_called_once_with(unit_name="ceph-osd/0",
command=cmd)
def test_get_unit_process_ids(self):
self.patch(
"zaza.openstack.utilities.generic.get_process_id_list",
new_callable=mock.MagicMock(),
name="_get_pids"
)
pids = ["1", "2"]
self._get_pids.return_value = pids
unit_processes = {
"ceph-osd/0": {
"ceph-osd": 2
},
"unit/0": {
"pr1": 2,
"pr2": 2
}
}
expected = {
"ceph-osd/0": {
"ceph-osd": ["1", "2"]
},
"unit/0": {
"pr1": ["1", "2"],
"pr2": ["1", "2"]
}
}
result = generic_utils.get_unit_process_ids(unit_processes)
self.assertEqual(result, expected)
def test_validate_unit_process_ids(self):
expected = {
"ceph-osd/0": {
"ceph-osd": 2
},
"unit/0": {
"pr1": 2,
"pr2": [1, 2]
}
}
# Unit count mismatch
actual = {}
with self.assertRaises(zaza_exceptions.UnitCountMismatch):
generic_utils.validate_unit_process_ids(expected, actual)
# Unit not found in actual dict
actual = {
"ceph-osd/0": {
"ceph-osd": ["1", "2"]
},
# unit/0 not in the dict
"unit/1": {
"pr1": ["1", "2"],
"pr2": ["1", "2"]
}
}
with self.assertRaises(zaza_exceptions.UnitNotFound):
generic_utils.validate_unit_process_ids(expected, actual)
# Process names count doesn't match
actual = {
"ceph-osd/0": {
"ceph-osd": ["1", "2"]
},
"unit/0": {
# Only one process name instead of 2 expected
"pr1": ["1", "2"]
}
}
with self.assertRaises(zaza_exceptions.ProcessNameCountMismatch):
generic_utils.validate_unit_process_ids(expected, actual)
# Process name doesn't match
actual = {
"ceph-osd/0": {
"ceph-osd": ["1", "2"]
},
"unit/0": {
# Bad process name
"bad_name": ["1", "2"],
"pr2": ["1", "2"]
}
}
with self.assertRaises(zaza_exceptions.ProcessNameMismatch):
generic_utils.validate_unit_process_ids(expected, actual)
# PID count doesn't match
actual = {
"ceph-osd/0": {
"ceph-osd": ["1", "2"]
},
"unit/0": {
# Only one PID instead of 2 expected
"pr1": ["2"],
"pr2": ["1", "2"]
}
}
with self.assertRaises(zaza_exceptions.PIDCountMismatch):
generic_utils.validate_unit_process_ids(expected, actual)
actual = {
"ceph-osd/0": {
"ceph-osd": ["1", "2"]
},
"unit/0": {
"pr1": ["1", "2"],
# 3 PID instead of [1, 2] expected
"pr2": ["1", "2", "3"]
}
}
with self.assertRaises(zaza_exceptions.PIDCountMismatch):
generic_utils.validate_unit_process_ids(expected, actual)
# It should work now...
actual = {
"ceph-osd/0": {
"ceph-osd": ["1", "2"]
},
"unit/0": {
"pr1": ["1", "2"],
"pr2": ["1", "2"]
}
}
ret = generic_utils.validate_unit_process_ids(expected, actual)
self.assertTrue(ret)
def test_get_ubuntu_release(self):
# Normal case
expected = 0
actual = generic_utils.get_ubuntu_release('oneiric')
self.assertEqual(expected, actual)
# Ubuntu release doesn't exist
bad_name = 'bad_name'
with self.assertRaises(zaza_exceptions.UbuntuReleaseNotFound):
generic_utils.get_ubuntu_release(bad_name)
def test_is_port_open(self):
self.patch(
'zaza.openstack.utilities.generic.telnetlib.Telnet',
new_callable=mock.MagicMock(),
name='telnet'
)
_port = "80"
_addr = "10.5.254.20"
self.assertTrue(generic_utils.is_port_open(_port, _addr))
self.telnet.assert_called_with(_addr, _port)
self.telnet.side_effect = generic_utils.socket.error
self.assertFalse(generic_utils.is_port_open(_port, _addr))
def test_get_unit_hostnames(self):
self.patch(
"zaza.openstack.utilities.generic.model.run_on_unit",
new_callable=mock.MagicMock(),
name="_run"
)
_unit1 = mock.MagicMock()
_unit1.entity_id = "testunit/1"
_unit2 = mock.MagicMock()
_unit2.entity_id = "testunit/2"
_hostname1 = "host1.domain"
_hostname2 = "host2.domain"
expected = {
_unit1.entity_id: _hostname1,
_unit2.entity_id: _hostname2,
}
_units = [_unit1, _unit2]
self._run.side_effect = [{"Stdout": _hostname1},
{"Stdout": _hostname2}]
actual = generic_utils.get_unit_hostnames(_units)
self.assertEqual(actual, expected)
expected_run_calls = [
mock.call('testunit/1', 'hostname'),
mock.call('testunit/2', 'hostname')]
self._run.assert_has_calls(expected_run_calls)
self._run.reset_mock()
self._run.side_effect = [{"Stdout": _hostname1},
{"Stdout": _hostname2}]
expected_run_calls = [
mock.call('testunit/1', 'hostname -f'),
mock.call('testunit/2', 'hostname -f')]
actual = generic_utils.get_unit_hostnames(_units, fqdn=True)
self._run.assert_has_calls(expected_run_calls)
def test_port_knock_units(self):
self.patch(
"zaza.openstack.utilities.generic.is_port_open",
new_callable=mock.MagicMock(),
name="_is_port_open"
)
_units = [
mock.MagicMock(),
mock.MagicMock(),
]
self._is_port_open.side_effect = [True, True]
self.assertIsNone(generic_utils.port_knock_units(_units))
self.assertEqual(self._is_port_open.call_count, len(_units))
self._is_port_open.side_effect = [True, False]
self.assertIsNotNone(generic_utils.port_knock_units(_units))
# check when func is expecting failure, i.e. should succeed
self._is_port_open.reset_mock()
self._is_port_open.side_effect = [False, False]
self.assertIsNone(generic_utils.port_knock_units(_units,
expect_success=False))
self.assertEqual(self._is_port_open.call_count, len(_units))
def test_check_commands_on_units(self):
self.patch(
"zaza.openstack.utilities.generic.model.run_on_unit",
new_callable=mock.MagicMock(),
name="_run"
)
num_units = 2
_units = [mock.MagicMock() for i in range(num_units)]
num_cmds = 3
cmds = ["/usr/bin/fakecmd"] * num_cmds
# Test success, all calls return 0
# zero is a string to replicate run_on_unit return data type
_cmd_results = [{"Code": "0"}] * len(_units) * len(cmds)
self._run.side_effect = _cmd_results
result = generic_utils.check_commands_on_units(cmds, _units)
self.assertIsNone(result)
self.assertEqual(self._run.call_count, len(_units) * len(cmds))
# Test failure, some call returns 1
_cmd_results[2] = {"Code": "1"}
self._run.side_effect = _cmd_results
result = generic_utils.check_commands_on_units(cmds, _units)
self.assertIsNotNone(result)
def test_systemctl(self):
self.patch_object(generic_utils.model, "get_unit_from_name")
self.patch_object(generic_utils.model, "run_on_unit")
_unit = mock.MagicMock()
_unit.entity_id = "unit/2"
_command = "stop"
_service = "servicename"
_systemctl = "/bin/systemctl {} {}".format(_command, _service)
self.run_on_unit.return_value = {"Code": 0}
self.get_unit_from_name.return_value = _unit
# With Unit object
generic_utils.systemctl(_unit, _service, command=_command)
self.run_on_unit.assert_called_with(_unit.entity_id, _systemctl)
# With string name unit
generic_utils.systemctl(_unit.entity_id, _service, command=_command)
self.run_on_unit.assert_called_with(_unit.entity_id, _systemctl)
# Failed return code
self.run_on_unit.return_value = {"Code": 1}
with self.assertRaises(AssertionError):
generic_utils.systemctl(
_unit.entity_id, _service, command=_command)