distcloud/distributedcloud/dcmanager/tests/unit/audit/test_kube_audit_manager.py

318 lines
14 KiB
Python

# Copyright (c) 2017-2024 Wind River Systems, Inc.
# All Rights Reserved.
#
# 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 uuid
import mock
from dccommon import consts as dccommon_consts
from dcmanager.audit import firmware_audit
from dcmanager.audit import kubernetes_audit
from dcmanager.audit import patch_audit
from dcmanager.audit import subcloud_audit_manager
from dcmanager.tests import base
from dcmanager.tests import utils
PREVIOUS_KUBE_VERSION = 'v1.2.3'
UPGRADED_KUBE_VERSION = 'v1.2.3-a'
class FakeDCManagerStateAPI(object):
def __init__(self):
self.update_subcloud_availability = mock.MagicMock()
self.update_subcloud_endpoint_status = mock.MagicMock()
class FakeKubeVersion(object):
def __init__(self,
obj_id=1,
version=UPGRADED_KUBE_VERSION,
target=True,
state='active'):
self.id = obj_id
self.uuid = str(uuid.uuid4())
self.version = version
self.target = target
self.state = state
self.upgrade_from = []
self.applied_patches = []
self.available_patches = []
def to_dict(self):
return dict(self.__dict__)
class FakeKubeUpgrade(object):
def __init__(self):
pass
class FakeAuditWorkerAPI(object):
def __init__(self):
self.audit_subclouds = mock.MagicMock()
class FakeSysinvClient(object):
def __init__(self):
self.region = None
self.session = None
self.get_kube_versions = mock.MagicMock()
self.get_kube_upgrades = mock.MagicMock()
class TestKubernetesAudit(base.DCManagerTestCase):
def setUp(self):
super(TestKubernetesAudit, self).setUp()
self.ctxt = utils.dummy_context()
# Mock the DCManager subcloud state API
self.fake_dcmanager_state_api = FakeDCManagerStateAPI()
p = mock.patch('dcmanager.rpc.client.SubcloudStateClient')
self.mock_dcmanager_state_api = p.start()
self.mock_dcmanager_state_api.return_value = \
self.fake_dcmanager_state_api
self.addCleanup(p.stop)
# Mock the Audit Worker API
self.fake_audit_worker_api = FakeAuditWorkerAPI()
p = mock.patch('dcmanager.audit.rpcapi.ManagerAuditWorkerClient')
self.mock_audit_worker_api = p.start()
self.mock_audit_worker_api.return_value = self.fake_audit_worker_api
self.addCleanup(p.stop)
# Note: mock where an item is used, not where it comes from
p = mock.patch.object(patch_audit, 'OpenStackDriver')
self.mock_patch_audit_driver = p.start()
self.mock_patch_audit_driver.return_value = mock.MagicMock()
self.addCleanup(p.stop)
p = mock.patch.object(patch_audit, 'SysinvClient')
self.mock_patch_audit_sys = p.start()
self.mock_patch_audit_sys.return_value = mock.MagicMock()
self.addCleanup(p.stop)
p = mock.patch.object(patch_audit, 'PatchingClient')
self.mock_patch_audit_pc = p.start()
self.mock_patch_audit_pc.return_value = mock.MagicMock()
self.addCleanup(p.stop)
p = mock.patch.object(patch_audit, 'SoftwareClient')
self.mock_patch_audit_sc = p.start()
self.addCleanup(p.stop)
p = mock.patch.object(firmware_audit, 'OpenStackDriver')
self.mock_firmware_audit_driver = p.start()
self.mock_firmware_audit_driver.return_value = mock.MagicMock()
self.addCleanup(p.stop)
p = mock.patch.object(firmware_audit, 'SysinvClient')
self.mock_firmware_audit_sys = p.start()
self.mock_firmware_audit_sys .return_value = mock.MagicMock()
self.addCleanup(p.stop)
p = mock.patch.object(kubernetes_audit, 'OpenStackDriver')
self.kube_openstack_driver = mock.MagicMock()
self.mock_kube_audit_driver = p.start()
self.mock_kube_audit_driver.return_value = self.kube_openstack_driver
self.addCleanup(p.stop)
p = mock.patch.object(kubernetes_audit, 'SysinvClient')
self.kube_sysinv_client = FakeSysinvClient()
self.mock_kube_audit_sys = p.start()
self.mock_kube_audit_sys.return_value = self.kube_sysinv_client
self.addCleanup(p.stop)
# Set the kube upgrade objects as being empty for all regions
self.kube_sysinv_client.get_kube_upgrades.return_value = []
def _rpc_convert(self, object_list):
# Convert to dict like what would happen calling via RPC
dict_results = []
for result in object_list:
dict_results.append(result.to_dict())
return dict_results
def get_kube_audit_data(self, am):
patch_audit_data, firmware_audit_data, kubernetes_audit_data, kube_rootca, \
software_audit_data = am._get_audit_data(True, True, True, True)
# Convert to dict like what would happen calling via RPC
kubernetes_audit_data = self._rpc_convert(kubernetes_audit_data)
return kubernetes_audit_data
def test_init(self):
audit = kubernetes_audit.KubernetesAudit(self.ctxt,
self.fake_dcmanager_state_api)
self.assertIsNotNone(audit)
self.assertEqual(self.ctxt, audit.context)
self.assertEqual(self.fake_dcmanager_state_api,
audit.state_rpc_client)
@mock.patch.object(subcloud_audit_manager, 'context')
def test_no_kubernetes_audit_data_to_sync(self, mock_context):
mock_context.get_admin_context.return_value = self.ctxt
audit = kubernetes_audit.KubernetesAudit(self.ctxt,
self.fake_dcmanager_state_api)
am = subcloud_audit_manager.SubcloudAuditManager()
am.kubernetes_audit = audit
kubernetes_audit_data = self.get_kube_audit_data(am)
subclouds = {base.SUBCLOUD_1['name']: base.SUBCLOUD_1['region_name'],
base.SUBCLOUD_2['name']: base.SUBCLOUD_2['region_name']}
for name, region in subclouds.items():
audit.subcloud_kubernetes_audit(name, region, kubernetes_audit_data)
expected_calls = [
mock.call(mock.ANY,
subcloud_name=name,
subcloud_region=region,
endpoint_type=dccommon_consts.ENDPOINT_TYPE_KUBERNETES,
sync_status=dccommon_consts.SYNC_STATUS_IN_SYNC)]
self.fake_dcmanager_state_api.update_subcloud_endpoint_status. \
assert_has_calls(expected_calls)
@mock.patch.object(subcloud_audit_manager, 'context')
def test_kubernetes_audit_data_out_of_sync_older(self, mock_context):
mock_context.get_admin_context.return_value = self.ctxt
audit = kubernetes_audit.KubernetesAudit(self.ctxt,
self.fake_dcmanager_state_api)
am = subcloud_audit_manager.SubcloudAuditManager()
am.kubernetes_audit = audit
# Set the region one data as being the upgraded version
self.kube_sysinv_client.get_kube_versions.return_value = [
FakeKubeVersion(version=UPGRADED_KUBE_VERSION),
]
kubernetes_audit_data = self.get_kube_audit_data(am)
subclouds = {base.SUBCLOUD_1['name']: base.SUBCLOUD_1['region_name'],
base.SUBCLOUD_2['name']: base.SUBCLOUD_2['region_name']}
for name, region in subclouds.items():
# return different kube versions in the subclouds
self.kube_sysinv_client.get_kube_versions.return_value = [
FakeKubeVersion(version=PREVIOUS_KUBE_VERSION),
]
audit.subcloud_kubernetes_audit(name, region, kubernetes_audit_data)
expected_calls = [
mock.call(mock.ANY,
subcloud_name=name,
subcloud_region=region,
endpoint_type=dccommon_consts.ENDPOINT_TYPE_KUBERNETES,
sync_status=dccommon_consts.SYNC_STATUS_OUT_OF_SYNC)]
self.fake_dcmanager_state_api.update_subcloud_endpoint_status. \
assert_has_calls(expected_calls)
@mock.patch.object(subcloud_audit_manager, 'context')
def test_kubernetes_audit_data_out_of_sync_newer(self, mock_context):
mock_context.get_admin_context.return_value = self.ctxt
audit = kubernetes_audit.KubernetesAudit(self.ctxt,
self.fake_dcmanager_state_api)
am = subcloud_audit_manager.SubcloudAuditManager()
am.kubernetes_audit = audit
# Set the region one data as being the previous version
self.kube_sysinv_client.get_kube_versions.return_value = [
FakeKubeVersion(version=PREVIOUS_KUBE_VERSION),
]
kubernetes_audit_data = self.get_kube_audit_data(am)
subclouds = {base.SUBCLOUD_1['name']: base.SUBCLOUD_1['region_name'],
base.SUBCLOUD_2['name']: base.SUBCLOUD_2['region_name']}
for name, region in subclouds.items():
# return different kube versions in the subclouds
self.kube_sysinv_client.get_kube_versions.return_value = [
FakeKubeVersion(version=UPGRADED_KUBE_VERSION),
]
audit.subcloud_kubernetes_audit(name, region, kubernetes_audit_data)
expected_calls = [
mock.call(mock.ANY,
subcloud_name=name,
subcloud_region=region,
endpoint_type=dccommon_consts.ENDPOINT_TYPE_KUBERNETES,
sync_status=dccommon_consts.SYNC_STATUS_OUT_OF_SYNC)]
self.fake_dcmanager_state_api.update_subcloud_endpoint_status. \
assert_has_calls(expected_calls)
@mock.patch.object(subcloud_audit_manager, 'context')
def test_kubernetes_audit_data_in_sync(self,
mock_context):
mock_context.get_admin_context.return_value = self.ctxt
audit = kubernetes_audit.KubernetesAudit(self.ctxt,
self.fake_dcmanager_state_api)
am = subcloud_audit_manager.SubcloudAuditManager()
am.kubernetes_audit = audit
# Set the region one data as being the upgraded version
self.kube_sysinv_client.get_kube_versions.return_value = [
FakeKubeVersion(version=UPGRADED_KUBE_VERSION),
]
kubernetes_audit_data = self.get_kube_audit_data(am)
subclouds = {base.SUBCLOUD_1['name']: base.SUBCLOUD_1['region_name'],
base.SUBCLOUD_2['name']: base.SUBCLOUD_2['region_name']}
for name, region in subclouds.items():
# return same kube versions in the subclouds
self.kube_sysinv_client.get_kube_versions.return_value = [
FakeKubeVersion(version=UPGRADED_KUBE_VERSION),
]
audit.subcloud_kubernetes_audit(name, region, kubernetes_audit_data)
expected_calls = [
mock.call(mock.ANY,
subcloud_name=name,
subcloud_region=region,
endpoint_type=dccommon_consts.ENDPOINT_TYPE_KUBERNETES,
sync_status=dccommon_consts.SYNC_STATUS_IN_SYNC)]
self.fake_dcmanager_state_api.update_subcloud_endpoint_status. \
assert_has_calls(expected_calls)
@mock.patch.object(subcloud_audit_manager, 'context')
def test_kubernetes_audit_data_in_sync_but_existing_upgrade(self,
mock_context):
# If a subcloud has an existing upgrade, it is out of sync
# even if the kube versions match
mock_context.get_admin_context.return_value = self.ctxt
audit = kubernetes_audit.KubernetesAudit(self.ctxt,
self.fake_dcmanager_state_api)
am = subcloud_audit_manager.SubcloudAuditManager()
am.kubernetes_audit = audit
# mock that there is a kube upgrade (only queried in subclouds)
self.kube_sysinv_client.get_kube_upgrades.return_value = [
FakeKubeUpgrade()
]
# Set the region one data as being the upgraded version
self.kube_sysinv_client.get_kube_versions.return_value = [
FakeKubeVersion(version=UPGRADED_KUBE_VERSION),
]
kubernetes_audit_data = self.get_kube_audit_data(am)
subclouds = {base.SUBCLOUD_1['name']: base.SUBCLOUD_1['region_name'],
base.SUBCLOUD_2['name']: base.SUBCLOUD_2['region_name']}
for name, region in subclouds.items():
# return same kube versions in the subclouds
self.kube_sysinv_client.get_kube_versions.return_value = [
FakeKubeVersion(version=UPGRADED_KUBE_VERSION),
]
audit.subcloud_kubernetes_audit(name, region, kubernetes_audit_data)
expected_calls = [
mock.call(mock.ANY,
subcloud_name=name,
subcloud_region=region,
endpoint_type=dccommon_consts.ENDPOINT_TYPE_KUBERNETES,
sync_status=dccommon_consts.SYNC_STATUS_OUT_OF_SYNC)]
self.fake_dcmanager_state_api.update_subcloud_endpoint_status. \
assert_has_calls(expected_calls)