Merge "MGMT address_pool reconfiguration for AIO-SX"

This commit is contained in:
Zuul 2023-11-21 17:03:03 +00:00 committed by Gerrit Code Review
commit 47516972f4
12 changed files with 414 additions and 38 deletions

View File

@ -1,6 +1,6 @@
#!/bin/bash
#
# Copyright (c) 2013-2022 Wind River Systems, Inc.
# Copyright (c) 2013-2023 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
@ -22,6 +22,7 @@
. /etc/platform/platform.conf
PLATFORM_DIR=/opt/platform
ETC_PLATFORM_DIR=/etc/platform
VAULT_DIR=$PLATFORM_DIR/.keyring/${SW_VERSION}/python_keyring
CONFIG_DIR=$CONFIG_PATH
VOLATILE_CONFIG_PASS="/var/run/.config_pass"
@ -98,9 +99,31 @@ EOF
get_ip()
{
local host=$1
local ipaddr=""
# the host IP will be in the DNSMASQ files in /etc/platform/
if [ "$system_mode" = "simplex" ] && [ -e $COMPLETED ]; then
local host_local="${host}.internal"
local dnsmasq_file=dnsmasq.addn_hosts
# Replace the dnsmasq files with new Management Network range
if [ -e $ETC_PLATFORM_DIR/.mgmt_network_reconfiguration_unlock ] && \
[ -e $ETC_PLATFORM_DIR/.mgmt_network_reconfiguration_ongoing ]; then
dnsmasq_file=dnsmasq.addn_hosts.temp
fi
ipaddr=$(cat $ETC_PLATFORM_DIR/${dnsmasq_file} | awk -v host=$host_local '$2 == host {print $1}')
if [ -n "$ipaddr" ]
then
echo $ipaddr
return
fi
fi
# Check /etc/hosts for the hostname
local ipaddr=$(cat /etc/hosts | awk -v host=$host '$2 == host {print $1}')
ipaddr=$(cat /etc/hosts | awk -v host=$host '$2 == host {print $1}')
if [ -n "$ipaddr" ]
then
echo $ipaddr
@ -249,7 +272,7 @@ start()
fatal_error "Initial manifest application failed; Host must be re-installed."
fi
echo "Configuring controller node..."
echo "Configuring controller node... ( IP: ${IPADDR} )"
# Remove the flag if it exists
rm -f ${ACTIVE_CONTROLLER_NOT_FOUND_FLAG}
@ -514,6 +537,26 @@ start()
fi
fi
# Replace the dnsmasq files with new Management Network range
if [ -e $ETC_PLATFORM_DIR/.mgmt_network_reconfiguration_unlock ] && \
[ -e $ETC_PLATFORM_DIR/.mgmt_network_reconfiguration_ongoing ]; then
echo "Management networking reconfiguration ongoing, replacing dnsmasq config files."
if [ -e $CONFIG_DIR/dnsmasq.addn_hosts.temp ] && \
[ -e $CONFIG_DIR/dnsmasq.hosts.temp ]; then
mv -f $CONFIG_DIR/dnsmasq.hosts.temp $CONFIG_DIR/dnsmasq.hosts
mv -f $CONFIG_DIR/dnsmasq.addn_hosts.temp $CONFIG_DIR/dnsmasq.addn_hosts
# update the cached files too
mv -f $ETC_PLATFORM_DIR/dnsmasq.hosts.temp $ETC_PLATFORM_DIR/dnsmasq.hosts
mv -f $ETC_PLATFORM_DIR/dnsmasq.addn_hosts.temp $ETC_PLATFORM_DIR/dnsmasq.addn_hosts
else
fatal_error "Management networking reconfiguration ongoing and dnsmasq files do not exist."
fi
# delete flags
rm -f $ETC_PLATFORM_DIR/.mgmt_network_reconfiguration_ongoing
rm -f $ETC_PLATFORM_DIR/.mgmt_network_reconfiguration_unlock
fi
hostname > /etc/hostname
if [ $? -ne 0 ]
then

View File

@ -2062,6 +2062,11 @@ class AgentManager(service.PeriodicService):
# Set ready flag for maintenance to proceed with the unlock of
# the initial controller.
utils.touch(constants.UNLOCK_READY_FLAG)
elif (os.path.isfile(tsc.MGMT_NETWORK_RECONFIGURATION_ONGOING) and
applied_classes == ['openstack::keystone::endpoint::reconfig']):
# Set ready flag for maintenance to proceed with the unlock
# after mgmt ip reconfiguration
utils.touch(constants.UNLOCK_READY_FLAG)
except Exception:
LOG.exception("failed to apply runtime manifest")
raise

View File

@ -63,6 +63,11 @@ SUBCLOUD_WRITABLE_ADDRPOOLS = ['system-controller-subnet',
# so we can't depend on the address pool having a static name.
SUBCLOUD_WRITABLE_NETWORK_TYPES = ['admin']
# Address pool for the management network in an AIO-SX installation
# is allowed to be deleted/modified post install
MANAGEMENT_ADDRESS_POOL = 'management'
AIOSX_WRITABLE_ADDRPOOLS = [MANAGEMENT_ADDRESS_POOL]
class AddressPoolPatchType(types.JsonPatchType):
"""A complex type that represents a single json-patch operation."""
@ -346,15 +351,55 @@ class AddressPoolController(rest.RestController):
addr = netaddr.IPAddress(address)
utils.is_valid_address_within_subnet(addr, subnet)
def _is_aiosx_writable_pool(self, addrpool, check_host_locked):
if (utils.get_system_mode() == constants.SYSTEM_MODE_SIMPLEX and
addrpool.name in AIOSX_WRITABLE_ADDRPOOLS):
# The mgmt address pool is just writable when the controller is locked
if(check_host_locked):
chosts = pecan.request.dbapi.ihost_get_by_personality(
constants.CONTROLLER)
for host in chosts:
if utils.is_aio_simplex_host_unlocked(host):
msg = _("Cannot complete the action because Host {} "
"is in administrative state = unlocked"
.format(host['hostname']))
raise wsme.exc.ClientSideError(msg)
return True
return False
def _validate_aiosx_mgmt_update(self, addrpool, new_name=None):
# There are ansible rules using the explicit name: 'management' in the addrpool
# since the AIO-SX allows mgmt network reconfiguration it is necessary to enforce
# the use of addrpool named 'management'.
if (utils.get_system_mode() == constants.SYSTEM_MODE_SIMPLEX and
addrpool.name in AIOSX_WRITABLE_ADDRPOOLS):
networks = pecan.request.dbapi.networks_get_by_pool(addrpool.id)
if networks and cutils.is_initial_config_complete() and \
any(network.type == constants.NETWORK_TYPE_MGMT
for network in networks):
if (new_name != MANAGEMENT_ADDRESS_POOL):
msg = _("Cannot complete the action because the "
"address pool for mgmt network must be named as '{}'."
.format(MANAGEMENT_ADDRESS_POOL))
raise ValueError(msg)
def _check_pool_readonly(self, addrpool):
# The admin and system controller address pools which exist on the
# subcloud are expected for re-home a subcloud to new system controllers.
if addrpool.name not in SUBCLOUD_WRITABLE_ADDRPOOLS:
if (addrpool.name not in SUBCLOUD_WRITABLE_ADDRPOOLS and
not self._is_aiosx_writable_pool(addrpool, True)):
networks = pecan.request.dbapi.networks_get_by_pool(addrpool.id)
# An addresspool except the admin and system controller's pools
# are considered read-only after the initial configuration is
# complete. During bootstrap it should be modifiable even though
# it is allocated to a network.
# The management address pool can be changed just for AIO-SX
if networks and cutils.is_initial_config_complete():
if any(network.type in SUBCLOUD_WRITABLE_NETWORK_TYPES
for network in networks):
@ -461,6 +506,7 @@ class AddressPoolController(rest.RestController):
def _validate_updates(self, addrpool, updates):
if 'name' in updates:
AddressPool._validate_name(updates['name'])
self._validate_aiosx_mgmt_update(addrpool, updates['name'])
if 'order' in updates:
AddressPool._validate_allocation_order(updates['order'])
if 'ranges' in updates:
@ -608,13 +654,25 @@ class AddressPoolController(rest.RestController):
addresses = pecan.request.dbapi.addresses_get_by_pool(
addrpool.id)
if addresses:
# check if an address of this pool was assigned to an interface
# e.g: address assigned to a data interface
addr_assigned_to_interface = False
for addr in addresses:
if(addr.interface_id):
addr_assigned_to_interface = True
break
# All of the initial configured addresspools are not deleteable,
# except the admin and system controller address pools on the
# subcloud. These can be deleted/re-added during re-homing
# except:
# - The admin and system controller address pools on the subcloud.
# - The management address pool for AIO-SX
# The admin and system controller can be deleted/re-added during re-homing
# a subcloud to new system controllers
if cutils.is_initial_config_complete() and \
(networks or addr_assigned_to_interface) and \
(addrpool.name not in SUBCLOUD_WRITABLE_ADDRPOOLS) and \
not any(network.type == constants.NETWORK_TYPE_ADMIN
not self._is_aiosx_writable_pool(addrpool, True) and \
not any(network.type == constants.NETWORK_TYPE_ADMIN
for network in networks):
raise exception.AddressPoolInUseByAddresses()
else:

View File

@ -2187,10 +2187,15 @@ class HostController(rest.RestController):
ihost_obj['uuid'], {'capabilities': ihost_obj['capabilities']})
# Notify maintenance about updated mgmt_ip
address_name = cutils.format_address_name(ihost_obj.hostname,
constants.NETWORK_TYPE_MGMT)
address = pecan.request.dbapi.address_get_by_name(address_name)
ihost_obj['mgmt_ip'] = address.address
# During mgmt network reconfiguration, do not change the mgmt IP
# in maintencance as it will be updated after the unlock.
if os.path.isfile(tsc.MGMT_NETWORK_RECONFIGURATION_ONGOING):
ihost_obj['mgmt_ip'] = cutils.gethostbyname(constants.CONTROLLER_0_FQDN)
else:
address_name = cutils.format_address_name(ihost_obj.hostname,
constants.NETWORK_TYPE_MGMT)
address = pecan.request.dbapi.address_get_by_name(address_name)
ihost_obj['mgmt_ip'] = address.address
hostupdate.notify_mtce = True

View File

@ -160,6 +160,14 @@ class InterfaceNetworkController(rest.RestController):
result = pecan.request.dbapi.interface_network_create(interface_network_dict)
# Management Network reconfiguration after initial config complete
# is just supported by AIO-SX, set the flag
if (network_type == constants.NETWORK_TYPE_MGMT and
utils.get_system_mode() == constants.SYSTEM_MODE_SIMPLEX and
cutils.is_initial_config_complete() and
host.hostname == constants.CONTROLLER_0_HOSTNAME):
pecan.request.rpcapi.set_mgmt_network_reconfig_flag(pecan.request.context)
# Update address mode based on network type
if network_type in [constants.NETWORK_TYPE_MGMT,
constants.NETWORK_TYPE_OAM,
@ -180,6 +188,7 @@ class InterfaceNetworkController(rest.RestController):
# Assign an address to the interface
_update_host_address(host, interface_obj, network_type)
if network_type == constants.NETWORK_TYPE_MGMT:
ethernet_port_mac = None
if not interface_obj.uses:

View File

@ -161,7 +161,7 @@ class NetworkController(rest.RestController):
pecan.request.context, network_uuid)
return Network.convert_with_links(rpc_network)
def _check_network_type(self, networktype):
def _check_network_type(self, networktype, pool_uuid=None):
networks = pecan.request.dbapi.networks_get_by_type(networktype)
if networks:
raise exception.NetworkAlreadyExists(type=networktype)
@ -172,6 +172,16 @@ class NetworkController(rest.RestController):
"role of {}."
.format(networktype, constants.DISTRIBUTED_CLOUD_ROLE_SUBCLOUD))
raise wsme.exc.ClientSideError(msg)
if (networktype == constants.NETWORK_TYPE_MGMT):
# There are ansible rules using the explicit name: 'management' in the addrpool
# since the AIO-SX allows mgmt network reconfiguration it is necessary to enforce
# the use of addrpool named 'management'.
if pool_uuid:
pool = pecan.request.dbapi.address_pool_get(pool_uuid)
if pool['name'] != "management":
msg = _("Network of type {} must use the addrpool named '{}'."
.format(networktype, address_pool.MANAGEMENT_ADDRESS_POOL))
raise wsme.exc.ClientSideError(msg)
def _check_network_pool(self, pool):
# ensure address pool exists and is not already inuse
@ -203,10 +213,25 @@ class NetworkController(rest.RestController):
self._populate_network_addresses(pool, network, addresses)
def _create_mgmt_network_address(self, pool):
addresses = collections.OrderedDict()
addresses[constants.CONTROLLER_HOSTNAME] = None
addresses[constants.CONTROLLER_0_HOSTNAME] = None
addresses[constants.CONTROLLER_1_HOSTNAME] = None
addresses = {}
if pool.floating_address:
addresses.update(
{constants.CONTROLLER_HOSTNAME: pool.floating_address})
else:
addresses.update({constants.CONTROLLER_HOSTNAME: None})
if pool.controller0_address:
addresses.update(
{constants.CONTROLLER_0_HOSTNAME: pool.controller0_address})
else:
addresses.update({constants.CONTROLLER_0_HOSTNAME: None})
if pool.controller1_address:
addresses.update(
{constants.CONTROLLER_1_HOSTNAME: pool.controller1_address})
else:
addresses.update({constants.CONTROLLER_1_HOSTNAME: None})
if pool.gateway_address is not None:
if utils.get_distributed_cloud_role() == \
@ -362,10 +387,11 @@ class NetworkController(rest.RestController):
network = network.as_dict()
network['uuid'] = str(uuid.uuid4())
# Perform semantic validation
self._check_network_type(network['type'])
pool_uuid = network.pop('pool_uuid', None)
# Perform semantic validation
self._check_network_type(network['type'], pool_uuid)
if pool_uuid:
pool = pecan.request.dbapi.address_pool_get(pool_uuid)
network.update({'address_pool_id': pool.id})
@ -431,16 +457,31 @@ class NetworkController(rest.RestController):
def delete(self, network_uuid):
"""Delete a network."""
network = pecan.request.dbapi.network_get(network_uuid)
if cutils.is_initial_config_complete() and \
network['type'] in [constants.NETWORK_TYPE_MGMT,
constants.NETWORK_TYPE_OAM,
if cutils.is_initial_config_complete():
if (network['type'] in [constants.NETWORK_TYPE_OAM,
constants.NETWORK_TYPE_CLUSTER_HOST,
constants.NETWORK_TYPE_PXEBOOT,
constants.NETWORK_TYPE_CLUSTER_POD,
constants.NETWORK_TYPE_CLUSTER_SERVICE,
constants.NETWORK_TYPE_STORAGE]:
msg = _("Cannot delete type {} network {} after initial "
"configuration completion"
.format(network['type'], network_uuid))
raise wsme.exc.ClientSideError(msg)
constants.NETWORK_TYPE_STORAGE] or
(network['type'] in [constants.NETWORK_TYPE_MGMT] and
utils.get_system_mode() != constants.SYSTEM_MODE_SIMPLEX)):
msg = _("Cannot delete type {} network {} after initial "
"configuration completion"
.format(network['type'], network_uuid))
raise wsme.exc.ClientSideError(msg)
elif (network['type'] in [constants.NETWORK_TYPE_MGMT] and
utils.get_system_mode() == constants.SYSTEM_MODE_SIMPLEX):
# For AIO-SX the mgmt network can be be reconfigured if host is locked
chosts = pecan.request.dbapi.ihost_get_by_personality(
constants.CONTROLLER)
for host in chosts:
if utils.is_aio_simplex_host_unlocked(host):
msg = _("Cannot delete type {} network {} because Host {} "
"is in administrative state = unlocked"
.format(network['type'], network_uuid, host['hostname']))
raise wsme.exc.ClientSideError(msg)
pecan.request.dbapi.network_destroy(network_uuid)

View File

@ -1205,15 +1205,6 @@ class ConductorManager(service.PeriodicService):
mac_address)
f_out.write(line)
# Update host files atomically and reload dnsmasq
if (not os.path.isfile(dnsmasq_hosts_file) or
not filecmp.cmp(temp_dnsmasq_hosts_file, dnsmasq_hosts_file)):
os.rename(temp_dnsmasq_hosts_file, dnsmasq_hosts_file)
if (not os.path.isfile(dnsmasq_addn_hosts_file) or
not filecmp.cmp(temp_dnsmasq_addn_hosts_file,
dnsmasq_addn_hosts_file)):
os.rename(temp_dnsmasq_addn_hosts_file, dnsmasq_addn_hosts_file)
# If there is no distributed cloud addn_hosts file, create an empty one
# so dnsmasq will not complain.
dnsmasq_addn_hosts_dc_file = os.path.join(tsc.CONFIG_PATH, 'dnsmasq.addn_hosts_dc')
@ -1224,6 +1215,38 @@ class ConductorManager(service.PeriodicService):
f_out_addn_dc.write(' ')
os.rename(temp_dnsmasq_addn_hosts_dc_file, dnsmasq_addn_hosts_dc_file)
# The controller IP will be in the dnsmasq.addn_hosts
# since the /opt/platform is not mounted during the startup it is necessary to copy
# DNSMASQ files to /etc/platform/
if cutils.is_aio_simplex_system(self.dbapi):
ETC_PLAT = tsc.PLATFORM_CONF_PATH + '/'
if os.path.isfile(dnsmasq_hosts_file):
shutil.copy2(dnsmasq_hosts_file, ETC_PLAT)
if os.path.isfile(dnsmasq_addn_hosts_file):
shutil.copy2(dnsmasq_addn_hosts_file, ETC_PLAT)
if os.path.isfile(temp_dnsmasq_hosts_file):
shutil.copy2(temp_dnsmasq_hosts_file, ETC_PLAT)
if os.path.isfile(temp_dnsmasq_addn_hosts_file):
shutil.copy2(temp_dnsmasq_addn_hosts_file, ETC_PLAT)
# Ignore the dnsmasq restart when an management network reconfiguration is in process.
# This is necessary, otherwise the DNSMASQ will answer DNS requests with the new MGMT IP
# but the new mgmt IP range was not configured in the system yet.
# The new Management Network IP range will be applied after the host-unlock
if os.path.isfile(tsc.MGMT_NETWORK_RECONFIGURATION_ONGOING):
LOG.info("Ignoring DNSMASQ changes in runtime due to Management Network reconfiguration.")
return
# Update host files atomically and reload dnsmasq
if (not os.path.isfile(dnsmasq_hosts_file) or
not filecmp.cmp(temp_dnsmasq_hosts_file, dnsmasq_hosts_file)):
os.rename(temp_dnsmasq_hosts_file, dnsmasq_hosts_file)
if (not os.path.isfile(dnsmasq_addn_hosts_file) or
not filecmp.cmp(temp_dnsmasq_addn_hosts_file,
dnsmasq_addn_hosts_file)):
os.rename(temp_dnsmasq_addn_hosts_file, dnsmasq_addn_hosts_file)
os.system("pkill -HUP dnsmasq")
def _generate_dnsmasq_conf_file(self):
@ -2078,6 +2101,33 @@ class ConductorManager(service.PeriodicService):
if utils.config_is_reboot_required(host.config_target):
config_uuid = self._config_set_reboot_required(config_uuid)
self._puppet.update_host_config(host, config_uuid)
elif os.path.isfile(tsc.MGMT_NETWORK_RECONFIGURATION_ONGOING):
# Remove unlock ready flag to prevent maintenance rebooting the
# node until the runtime manifest is finished.
try:
if os.path.isfile(constants.UNLOCK_READY_FLAG):
os.remove(constants.UNLOCK_READY_FLAG)
except OSError:
LOG.exception("Failed to remove unlock ready flag: %s" %
constants.UNLOCK_READY_FLAG)
personalities = [constants.CONTROLLER]
# Update sysinv and keystone endpoints before the reboot
config_uuid = self._config_update_hosts(context, personalities,
host_uuids=[host.uuid])
config_dict = {
"personalities": personalities,
"host_uuids": [host.uuid],
"classes": ['openstack::keystone::endpoint::reconfig']
}
self._config_apply_runtime_manifest(
context, config_uuid, config_dict, force=True)
# Regenerate config target uuid, node is going for reboot!
config_uuid = self._config_update_hosts(context, personalities)
if utils.config_is_reboot_required(host.config_target):
config_uuid = self._config_set_reboot_required(config_uuid)
self._puppet.update_host_config(host, config_uuid)
def _ceph_mon_create(self, host):
if not StorageBackendConfig.has_backend(
@ -12566,6 +12616,15 @@ class ConductorManager(service.PeriodicService):
return iinterfaces
def set_mgmt_network_reconfig_flag(self, context):
"""set the management network reconfiguration
flag to ignore the DNSMASQ changes in runtime.
"""
if not os.path.isfile(tsc.MGMT_NETWORK_RECONFIGURATION_ONGOING):
LOG.info("Management Network reconfiguration detected.")
open(tsc.MGMT_NETWORK_RECONFIGURATION_ONGOING, 'w').close()
def mgmt_ip_set_by_ihost(self,
context,
ihost_uuid,

View File

@ -837,6 +837,13 @@ class ConductorAPI(sysinv.openstack.common.rpc.proxy.RpcProxy):
return self.call(context, self.make_msg(
'remove_admin_firewall_config'))
def set_mgmt_network_reconfig_flag(self, context):
"""Synchronously, have the conductor update the mgmt network reconfig flag.
:param context: request context.
"""
return self.call(context, self.make_msg(
'set_mgmt_network_reconfig_flag'))
def update_host_filesystem_config(self, context,
host=None,
filesystem_list=None):

View File

@ -13,9 +13,11 @@ import io
import os
import tempfile
import yaml
import tsconfig.tsconfig as tsc
from stevedore import extension
from tsconfig import tsconfig
from sysinv.common import constants
from oslo_log import log as logging
from sysinv.puppet import common
@ -194,6 +196,16 @@ class PuppetOperator(object):
self._write_host_config(host, config)
# Hiera file updated. Check if Management Network reconfiguration is ongoing
if (os.path.isfile(tsc.MGMT_NETWORK_RECONFIGURATION_ONGOING) and
(host.action == constants.FORCE_UNLOCK_ACTION or
host.action == constants.UNLOCK_ACTION)):
if not os.path.isfile(tsc.MGMT_NETWORK_RECONFIGURATION_UNLOCK):
LOG.info("Management Network reconfiguration will be applied during "
"the startup. Hiera files updated and host-unlock detected")
open(tsc.MGMT_NETWORK_RECONFIGURATION_UNLOCK, 'w').close()
def read_host_config(self, host, version=None):
""""""
path = self.get_hieradata_path(version)

View File

@ -522,6 +522,133 @@ class TestDelete(NetworkTestCase):
)
class TestDeleteAIOSimplex(NetworkTestCase):
""" Tests AIO Simplex deletion.
Typically delete APIs return NO CONTENT.
python2 and python3 libraries may return different
content_type (None, or empty json) when NO_CONTENT returned.
"""
system_type = constants.TIS_AIO_BUILD
system_mode = constants.SYSTEM_MODE_SIMPLEX
def setUp(self):
super(TestDeleteAIOSimplex, self).setUp()
def _setup_context(self, host_locked=False):
if host_locked:
admin = constants.ADMIN_LOCKED
else:
admin = constants.ADMIN_UNLOCKED
self.host = self._create_test_host(constants.CONTROLLER, constants.WORKER,
administrative=admin,
operational=constants.OPERATIONAL_ENABLED,
availability=constants.AVAILABILITY_AVAILABLE,
invprovision=constants.PROVISIONED,
vim_progress_status=constants.VIM_SERVICES_ENABLED)
self._create_test_host_cpus(self.host, platform=2, vswitch=2, application=11)
def _test_delete_allowed(self, network_type):
# Delete the API object
self.delete_object = self._create_db_object(network_type=network_type)
uuid = self.delete_object.uuid
response = self.delete(self.get_single_url(uuid),
headers=self.API_HEADERS)
# Verify the expected API response for the delete
self.assertEqual(response.status_code, http_client.NO_CONTENT)
def _test_delete_after_initial_config_not_allowed(self, network_type):
# Delete the API object
self.delete_object = self._create_db_object(network_type=network_type)
with mock.patch('sysinv.common.utils.is_initial_config_complete',
lambda: True):
uuid = self.delete_object.uuid
response = self.delete(self.get_single_url(uuid),
headers=self.API_HEADERS,
expect_errors=True)
# Verify the expected API response for the delete
self.assertEqual(response.status_code, http_client.BAD_REQUEST)
expected_error = ("Cannot delete type %s network %s after"
" initial configuration completion" %
(network_type, uuid))
self.assertIn(expected_error, response.json['error_message'])
def _test_delete_mgmt_after_initial_config_not_allowed(self, network_type):
# Delete the API object
self.delete_object = self._create_db_object(network_type=network_type)
with mock.patch('sysinv.common.utils.is_initial_config_complete',
lambda: True):
uuid = self.delete_object.uuid
response = self.delete(self.get_single_url(uuid),
headers=self.API_HEADERS,
expect_errors=True)
# Verify the expected API response for the delete
self.assertEqual(response.status_code, http_client.BAD_REQUEST)
expected_error = ("Cannot delete type %s network %s because Host "
"controller-0 is in administrative state = unlocked" %
(network_type, uuid))
self.assertIn(expected_error, response.json['error_message'])
def _test_delete_after_initial_config_allowed(self, network_type):
# Delete the API object
self.delete_object = self._create_db_object(network_type=network_type)
with mock.patch('sysinv.common.utils.is_initial_config_complete',
lambda: True):
uuid = self.delete_object.uuid
response = self.delete(self.get_single_url(uuid),
headers=self.API_HEADERS)
# Verify the expected API response for the delete
self.assertEqual(response.status_code, http_client.NO_CONTENT)
def test_delete_management(self):
self._test_delete_allowed(constants.NETWORK_TYPE_MGMT)
def test_delete_management_after_initial_config_not_allowed_host_unlocked(self):
self._setup_context(host_locked=False)
self._test_delete_mgmt_after_initial_config_not_allowed(
constants.NETWORK_TYPE_MGMT
)
def test_delete_management_after_initial_config_allowed_host_locked(self):
self._setup_context(host_locked=True)
self._test_delete_after_initial_config_allowed(
constants.NETWORK_TYPE_MGMT
)
# just to make sure that the other networks can't be deleted
def test_delete_oam(self):
self._setup_context(host_locked=False)
self._test_delete_allowed(constants.NETWORK_TYPE_OAM)
def test_delete_oam_after_initial_config(self):
self._setup_context(host_locked=False)
self._test_delete_after_initial_config_not_allowed(
constants.NETWORK_TYPE_OAM
)
def test_delete_data(self):
self._setup_context(host_locked=False)
self._test_delete_allowed(constants.NETWORK_TYPE_DATA)
def test_delete_data_after_initial_config(self):
self._setup_context(host_locked=False)
self._test_delete_after_initial_config_allowed(
constants.NETWORK_TYPE_DATA
)
class TestList(NetworkTestCase):
""" Network list operations
"""

View File

@ -558,7 +558,7 @@ class StorageHostTestCase(BaseHostTestCase):
class AIOHostTestCase(BaseHostTestCase):
system_mode = constants.TIS_AIO_BUILD
system_type = constants.TIS_AIO_BUILD
def setUp(self):
super(AIOHostTestCase, self).setUp()
@ -568,7 +568,7 @@ class AIOHostTestCase(BaseHostTestCase):
class ProvisionedAIOHostTestCase(BaseHostTestCase):
system_mode = constants.TIS_AIO_BUILD
system_type = constants.TIS_AIO_BUILD
def setUp(self):
super(ProvisionedAIOHostTestCase, self).setUp()

View File

@ -208,6 +208,16 @@ INITIAL_K8S_CONFIG_COMPLETE = os.path.join(
VOLATILE_CONTROLLER_CONFIG_COMPLETE = os.path.join(
VOLATILE_PATH, ".controller_config_complete")
# Set when mgmt network reconfiguration is executed after
# INITIAL_CONTROLLER_CONFIG_COMPLETE
MGMT_NETWORK_RECONFIGURATION_ONGOING = os.path.join(
PLATFORM_CONF_PATH, ".mgmt_network_reconfiguration_ongoing")
# Set when host-unlock was executed and hieradata was updated
# with new MGMT IP RANGE.
MGMT_NETWORK_RECONFIGURATION_UNLOCK = os.path.join(
PLATFORM_CONF_PATH, ".mgmt_network_reconfiguration_unlock")
# Worker configuration flags
# Set after initial application of node manifest