MGMT address_pool reconfiguration for AIO-SX
This change allows the reconfiguration of the management address_pool for an AIO-SX installation. The reconfiguration can be done even when the system was already configured and unlocked, but it needs to lock the controller in order to reconfigure the management network. Since there are ansible rules using the name: "management" as the address_pool, it is necessary to enforce the use of the address_pool named "management" in order to create the mgmt network. During a management network reconfiguration the DNSMASQ changes must not be applied in runtime, to all changes take effect the host lock/unlock is mandatory. Test plan PASS: AIO-SX IPv4 delete and create another management address-pool and create the management network with it PASS: AIO-SX IPv6 delete and create another management address-pool and create the management network with it PASS: AIO-SX IPv4 fresh install PASS: AIO-SX IPv6 fresh install PASS: AIO-DX IPv4 fresh install PASS: AIO-DX IPv6 fresh install PASS: STANDARD IPv4 fresh install PASS: DC with AIO-SX IPv4 fresh install Story: 2010722 Task: 48469 Change-Id: I2de156162dc83d2c16437d2d8068054de19a3b20 Signed-off-by: Fabiano Correa Mercer <fabiano.correamercer@windriver.com> Signed-off-by: Teresa Ho <teresa.ho@windriver.com>
This commit is contained in:
parent
ab82680c1d
commit
8503921d55
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
@ -2034,6 +2057,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(
|
||||
@ -12516,6 +12566,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,
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
"""
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user