config/sysinv/sysinv/sysinv/sysinv/tests/api/test_oamnetwork.py

431 lines
16 KiB
Python

#
# Copyright (c) 2020-2024 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
"""
Tests for the API / oamnetwork / methods.
"""
import mock
from six.moves import http_client
from oslo_utils import uuidutils
from sysinv.common import constants
from sysinv.tests.api import base
from sysinv.tests.db import base as dbbase
from sysinv.tests.db import utils as dbutils
class OAMNetworkTestCase(base.FunctionalTest, dbbase.BaseHostTestCase):
# API_HEADERS are a generic header passed to most API calls
API_HEADERS = {'User-Agent': 'sysinv-test'}
# API_PREFIX is the prefix for the URL
API_PREFIX = '/iextoam'
# RESULT_KEY is the python table key for the list of results
RESULT_KEY = 'iextoam'
# expected_api_fields are attributes that should be populated by
# an API query
expected_api_fields = ['uuid',
'oam_subnet',
'oam_gateway_ip',
'oam_floating_ip',
'oam_c0_ip',
'oam_c1_ip',
'region_config',
'oam_start_ip',
'oam_end_ip',
'isystem_uuid',
'created_at',
'updated_at']
# hidden_api_fields are attributes that should not be populated by
# an API query
hidden_api_fields = []
def setUp(self):
super(OAMNetworkTestCase, self).setUp()
def get_single_url(self, uuid):
return '%s/%s' % (self.API_PREFIX, uuid)
def assert_fields(self, api_object):
# check the uuid is a uuid
assert(uuidutils.is_uuid_like(api_object['uuid']))
# Verify that expected attributes are returned
for field in self.expected_api_fields:
self.assertIn(field, api_object)
# Verify that hidden attributes are not returned
for field in self.hidden_api_fields:
self.assertNotIn(field, api_object)
class TestPost(OAMNetworkTestCase):
def setUp(self):
super(TestPost, self).setUp()
def test_post_not_allowed(self):
response = self.post_json(self.API_PREFIX,
{},
headers=self.API_HEADERS,
expect_errors=True)
# Verify the expected API response
self.assertEqual(response.content_type, 'application/json')
self.assertEqual(response.status_code, http_client.FORBIDDEN)
self.assertIn("Operation not permitted.",
response.json['error_message'])
class TestDeleteMixin(OAMNetworkTestCase):
""" Tests deletion.
Typically delete APIs return NO CONTENT.
python2 and python3 libraries may return different
content_type (None, or empty json) when NO_CONTENT returned.
"""
def setUp(self):
super(TestDeleteMixin, self).setUp()
def test_delete_not_allowed(self):
# Delete the API object
response = self.delete(self.get_single_url(self.oam.uuid),
headers=self.API_HEADERS,
expect_errors=True)
# Verify the expected API response
self.assertEqual(response.content_type, 'application/json')
self.assertEqual(response.status_code, http_client.FORBIDDEN)
self.assertIn("Operation not permitted.",
response.json['error_message'])
class TestListMixin(OAMNetworkTestCase):
def setUp(self):
super(TestListMixin, self).setUp()
def test_get(self):
response = self.get_json(self.get_single_url(self.oam.uuid),
headers=self.API_HEADERS)
# Verify the expected API response
self.assertEqual(response['oam_start_ip'],
str(self.oam_subnet[2]))
def test_list(self):
response = self.get_json(self.get_single_url(""),
headers=self.API_HEADERS)
# Verify the expected API response
self.assertEqual(response['iextoams'][0]['oam_start_ip'],
str(self.oam_subnet[2]))
class TestPatchMixin(OAMNetworkTestCase):
def setUp(self):
super(TestPatchMixin, self).setUp()
def _test_patch_success(self, patch_obj):
# Patch the API object
m = mock.Mock()
update_oam_config = \
"sysinv.conductor.rpcapi.ConductorAPI.update_oam_config"
with mock.patch(update_oam_config, m.update_oam_config):
response = self.patch_dict_json(self.get_single_url(self.oam.uuid),
headers=self.API_HEADERS, **patch_obj)
# Verify the expected API response
self.assertEqual(response.content_type, 'application/json')
self.assertEqual(response.status_code, http_client.OK)
m.update_oam_config.assert_called_once()
def _test_patch_fail(self, patch_obj, status_code, error_message):
# Patch the API object
m = mock.Mock()
update_oam_config = \
"sysinv.conductor.rpcapi.ConductorAPI.update_oam_config"
with mock.patch(update_oam_config, m.update_oam_config):
response = self.patch_dict_json(self.get_single_url(self.oam.uuid),
headers=self.API_HEADERS,
expect_errors=True,
**patch_obj)
# Verify the expected API response
self.assertEqual(response.content_type, 'application/json')
self.assertEqual(response.status_code, status_code)
self.assertIn(error_message, response.json['error_message'])
m.update_oam_config.assert_not_called()
def test_patch_same_address(self):
oam_floating_ip = self.oam_subnet[2]
oam_c0_ip = self.oam_subnet[3]
oam_c1_ip = self.oam_subnet[4]
patch_obj = {
'oam_floating_ip': str(oam_floating_ip),
'oam_c0_ip': str(oam_c0_ip),
'oam_c1_ip': str(oam_c1_ip),
}
self._test_patch_success(patch_obj)
def test_patch_new_address(self):
oam_floating_ip = self.oam_subnet[2] + 100
oam_c0_ip = self.oam_subnet[3] + 100
oam_c1_ip = self.oam_subnet[4] + 100
patch_obj = {
'oam_floating_ip': str(oam_floating_ip),
'oam_c0_ip': str(oam_c0_ip),
'oam_c1_ip': str(oam_c1_ip),
}
self._test_patch_success(patch_obj)
def test_patch_new_address_in_range(self):
oam_start_ip = self.oam_subnet[1]
oam_end_ip = self.oam_subnet[128]
oam_floating_ip = self.oam_subnet[2] + 100
oam_c0_ip = self.oam_subnet[3] + 100
oam_c1_ip = self.oam_subnet[4] + 100
patch_obj = {
'oam_start_ip': str(oam_start_ip),
'oam_end_ip': str(oam_end_ip),
'oam_floating_ip': str(oam_floating_ip),
'oam_c0_ip': str(oam_c0_ip),
'oam_c1_ip': str(oam_c1_ip),
}
self._test_patch_success(patch_obj)
def test_patch_incomplete(self):
fields = {'floating_address_id': None, 'controller0_address_id': None,
'controller1_address_id': None, 'gateway_address_id': None}
network = self._find_network_by_type(constants.NETWORK_TYPE_OAM)
addrpools = self._find_network_address_pools(network.id)
for addrpool in addrpools:
addresses = self.dbapi.addresses_get_by_pool(addrpool.id)
for address in addresses:
self.dbapi.address_update(address.uuid, {'address_pool_id': None})
self.dbapi.address_pool_update(addrpool.uuid, fields)
oam_floating_ip = self.oam_subnet[2] + 100
patch_obj = {
'oam_floating_ip': str(oam_floating_ip),
}
self._test_patch_fail(patch_obj, http_client.BAD_REQUEST,
"Invalid address None")
def test_patch_change_family(self):
oam_floating_ip = self.change_family_oam_subnet[2]
oam_c0_ip = self.change_family_oam_subnet[3]
oam_c1_ip = self.change_family_oam_subnet[4]
patch_obj = {
'oam_floating_ip': str(oam_floating_ip),
'oam_c0_ip': str(oam_c0_ip),
'oam_c1_ip': str(oam_c1_ip),
}
self._test_patch_fail(patch_obj, http_client.BAD_REQUEST,
"Invalid IP version")
def test_patch_duplicate_address(self):
oam_floating_ip = self.oam_subnet[2]
oam_c0_ip = self.oam_subnet[3]
oam_c1_ip = self.oam_subnet[3]
patch_obj = {
'oam_floating_ip': str(oam_floating_ip),
'oam_c0_ip': str(oam_c0_ip),
'oam_c1_ip': str(oam_c1_ip),
}
self._test_patch_fail(patch_obj, http_client.BAD_REQUEST,
"must be unique")
def test_patch_oam_floating_ip_out_of_subnet(self):
oam_floating_ip = self.oam_subnet[2] - 100
oam_c0_ip = self.oam_subnet[3]
oam_c1_ip = self.oam_subnet[4]
patch_obj = {
'oam_floating_ip': str(oam_floating_ip),
'oam_c0_ip': str(oam_c0_ip),
'oam_c1_ip': str(oam_c1_ip),
}
error_message = "IP Address %s is not in subnet" % str(oam_floating_ip)
self._test_patch_fail(patch_obj, http_client.BAD_REQUEST,
error_message)
def test_patch_oam_c0_ip_out_of_subnet(self):
oam_floating_ip = self.oam_subnet[2]
oam_c0_ip = self.oam_subnet[3] - 100
oam_c1_ip = self.oam_subnet[4]
patch_obj = {
'oam_floating_ip': str(oam_floating_ip),
'oam_c0_ip': str(oam_c0_ip),
'oam_c1_ip': str(oam_c1_ip),
}
error_message = "IP Address %s is not in subnet" % str(oam_c0_ip)
self._test_patch_fail(patch_obj, http_client.BAD_REQUEST,
error_message)
def test_patch_oam_c1_ip_out_of_subnet(self):
oam_floating_ip = self.oam_subnet[2]
oam_c0_ip = self.oam_subnet[3]
oam_c1_ip = self.oam_subnet[4] - 100
patch_obj = {
'oam_floating_ip': str(oam_floating_ip),
'oam_c0_ip': str(oam_c0_ip),
'oam_c1_ip': str(oam_c1_ip),
}
error_message = "IP Address %s is not in subnet" % str(oam_c1_ip)
self._test_patch_fail(patch_obj, http_client.BAD_REQUEST,
error_message)
def test_patch_oam_floating_ip_out_of_range(self):
oam_start_ip = self.oam_subnet[1]
oam_end_ip = self.oam_subnet[32]
oam_floating_ip = self.oam_subnet[2] + 100
oam_c0_ip = self.oam_subnet[3]
oam_c1_ip = self.oam_subnet[4]
patch_obj = {
'oam_start_ip': str(oam_start_ip),
'oam_end_ip': str(oam_end_ip),
'oam_floating_ip': str(oam_floating_ip),
'oam_c0_ip': str(oam_c0_ip),
'oam_c1_ip': str(oam_c1_ip),
}
error_message = ("Invalid oam_floating_ip=%s. Please configure a valid"
" IP address in range") % str(oam_floating_ip)
self._test_patch_fail(patch_obj, http_client.BAD_REQUEST,
error_message)
def test_patch_oam_c0_ip_out_of_range(self):
oam_start_ip = self.oam_subnet[1]
oam_end_ip = self.oam_subnet[32]
oam_floating_ip = self.oam_subnet[2]
oam_c0_ip = self.oam_subnet[3] + 100
oam_c1_ip = self.oam_subnet[4]
patch_obj = {
'oam_start_ip': str(oam_start_ip),
'oam_end_ip': str(oam_end_ip),
'oam_floating_ip': str(oam_floating_ip),
'oam_c0_ip': str(oam_c0_ip),
'oam_c1_ip': str(oam_c1_ip),
}
error_message = ("Invalid oam_c0_ip=%s. Please configure a valid"
" IP address in range") % str(oam_c0_ip)
self._test_patch_fail(patch_obj, http_client.BAD_REQUEST,
error_message)
def test_patch_oam_c1_ip_out_of_range(self):
oam_start_ip = self.oam_subnet[1]
oam_end_ip = self.oam_subnet[32]
oam_floating_ip = self.oam_subnet[2]
oam_c0_ip = self.oam_subnet[3]
oam_c1_ip = self.oam_subnet[4] + 100
patch_obj = {
'oam_start_ip': str(oam_start_ip),
'oam_end_ip': str(oam_end_ip),
'oam_floating_ip': str(oam_floating_ip),
'oam_c0_ip': str(oam_c0_ip),
'oam_c1_ip': str(oam_c1_ip),
}
error_message = ("Invalid oam_c1_ip=%s. Please configure a valid"
" IP address in range") % str(oam_c1_ip)
self._test_patch_fail(patch_obj, http_client.BAD_REQUEST,
error_message)
def test_patch_oam_during_platform_upgrade(self):
oam_floating_ip = self.oam_subnet[2]
oam_c0_ip = self.oam_subnet[3]
oam_c1_ip = self.oam_subnet[4]
patch_obj = {
'oam_floating_ip': str(oam_floating_ip),
'oam_c0_ip': str(oam_c0_ip),
'oam_c1_ip': str(oam_c1_ip),
}
dbutils.create_test_upgrade(
state=constants.UPGRADE_STARTING
)
error_message = "Action rejected while a " \
"platform upgrade is in progress"
self._test_patch_fail(patch_obj, http_client.BAD_REQUEST,
error_message)
def test_patch_oam_during_kubernetes_upgrade(self):
oam_floating_ip = self.oam_subnet[2]
oam_c0_ip = self.oam_subnet[3]
oam_c1_ip = self.oam_subnet[4]
patch_obj = {
'oam_floating_ip': str(oam_floating_ip),
'oam_c0_ip': str(oam_c0_ip),
'oam_c1_ip': str(oam_c1_ip),
}
dbutils.create_test_kube_upgrade()
error_message = "Action rejected while a " \
"kubernetes upgrade is in progress"
self._test_patch_fail(patch_obj, http_client.BAD_REQUEST,
error_message)
def test_patch_oam_simplex_to_duplex(self):
system_dict = self.system.as_dict()
system_dict['capabilities'].update({'simplex_to_duplex_migration': True})
self.dbapi.isystem_update(self.system.uuid, system_dict)
oam_floating_ip = self.oam_subnet[2] + 100
oam_c0_ip = self.oam_subnet[3] + 100
oam_c1_ip = self.oam_subnet[4] + 100
patch_obj = {
'oam_floating_ip': str(oam_floating_ip),
'oam_c0_ip': str(oam_c0_ip),
'oam_c1_ip': str(oam_c1_ip),
}
addresses = {a['name']: a for a in
self.dbapi.addresses_get_all()}
self.assertIn('%s-%s' % (constants.CONTROLLER_0_HOSTNAME,
constants.NETWORK_TYPE_OAM),
addresses.keys())
self.assertIn('%s-%s' % (constants.CONTROLLER_1_HOSTNAME,
constants.NETWORK_TYPE_OAM),
addresses.keys())
self._test_patch_success(patch_obj)
class IPv4TestDelete(TestDeleteMixin,
OAMNetworkTestCase):
pass
class IPv6TestDelete(TestDeleteMixin,
dbbase.BaseIPv6Mixin,
OAMNetworkTestCase):
pass
class IPv4TestList(TestListMixin,
OAMNetworkTestCase):
pass
class IPv6TestList(TestListMixin,
dbbase.BaseIPv6Mixin,
OAMNetworkTestCase):
pass
class IPv4TestPatch(TestListMixin,
OAMNetworkTestCase):
pass
class IPv6TestPatch(TestPatchMixin,
dbbase.BaseIPv6Mixin,
OAMNetworkTestCase):
pass