Generalize subcloud network reconfiguration
This commit refactors the subcloud network reconfiguration, allowing for a more flexible and generalized approach, adding the option to fallback to the management network as well. Test Plan: PASS: Run dcmanager subcloud update with network paramaters (dcmanager subcloud update --sysadmin-password <password> --management-subnet <network-subnet> --management-gateway-ip <network-gateway-ip> --management-start-ip <network-start-ip> --management-end-ip <network-end-ip> --bootstrap-address <bootstrap-address> <subcloud_name>) - The update_playbook will be called and update the subcloud (subcloud route to systemcontroller and admin endpoints) - A new route to the subcloud is created on the system controller. - Subcloud service endpoint URLs are updated in keystone (openstack endpoint list|grep <subcloud-name>) on the system controller. PASS: verify successful deployment of a new subcloud PASS: verify successful reconfiguration of a subcloud from mgmt to admin network Depends-On: https://review.opendev.org/c/starlingx/ansible-playbooks/+/878504 Story: 2010319 Task: 47706 Signed-off-by: Hugo Brito <hugo.brito@windriver.com> Change-Id: I1df57a206e21fa2444bd645c456c4d5d1b539066
This commit is contained in:
parent
ad1f05ac5f
commit
3d7cb75e22
|
@ -319,13 +319,13 @@ The attributes of a subcloud which are modifiable:
|
||||||
|
|
||||||
- group_id
|
- group_id
|
||||||
|
|
||||||
- admin-subnet
|
- management-subnet
|
||||||
|
|
||||||
- admin-gateway-ip
|
- management-gateway-ip
|
||||||
|
|
||||||
- admin-node-0-address
|
- management-start-ip
|
||||||
|
|
||||||
- admin-node-1-address
|
- management-end-ip
|
||||||
|
|
||||||
**Normal response codes**
|
**Normal response codes**
|
||||||
|
|
||||||
|
@ -346,10 +346,12 @@ serviceUnavailable (503)
|
||||||
- location: subcloud_location
|
- location: subcloud_location
|
||||||
- management-state: subcloud_management_state
|
- management-state: subcloud_management_state
|
||||||
- group_id: subcloud_group_id
|
- group_id: subcloud_group_id
|
||||||
- admin-subnet: subcloud_admin_subnet
|
- management-subnet: subcloud_management_subnet
|
||||||
- admin-gateway-ip: subcloud_admin_gateway_ip
|
- management-gateway-ip: subcloud_management_gateway_ip
|
||||||
- admin-node-0-address: subcloud_admin_node_0_address
|
- management-start-ip: subcloud_management_start_ip
|
||||||
- admin-node-1-address: subcloud_admin_node_1_address
|
- management-end-ip: subcloud_management_end_ip
|
||||||
|
- bootstrap-address: bootstrap_address
|
||||||
|
- sysadmin-password: sysadmin_password
|
||||||
|
|
||||||
Request Example
|
Request Example
|
||||||
----------------
|
----------------
|
||||||
|
|
|
@ -398,30 +398,6 @@ strategy_steps:
|
||||||
in: body
|
in: body
|
||||||
required: false
|
required: false
|
||||||
type: array
|
type: array
|
||||||
subcloud_admin_gateway_ip:
|
|
||||||
description: |
|
|
||||||
The admin gateway ip of a subcloud.
|
|
||||||
in: body
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
subcloud_admin_node_0_address:
|
|
||||||
description: |
|
|
||||||
The admin node-0 address of a subcloud.
|
|
||||||
in: body
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
subcloud_admin_node_1_address:
|
|
||||||
description: |
|
|
||||||
The admin node-1 address of a subcloud.
|
|
||||||
in: body
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
subcloud_admin_subnet:
|
|
||||||
description: |
|
|
||||||
The admin subnet of a subcloud.
|
|
||||||
in: body
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
subcloud_apply_type:
|
subcloud_apply_type:
|
||||||
description: |
|
description: |
|
||||||
The apply type for the update. `serial` or `parallel`.
|
The apply type for the update. `serial` or `parallel`.
|
||||||
|
@ -555,12 +531,36 @@ subcloud_location:
|
||||||
in: body
|
in: body
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
|
subcloud_management_end_ip:
|
||||||
|
description: |
|
||||||
|
The management end ip of a subcloud.
|
||||||
|
in: body
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
subcloud_management_gateway_ip:
|
||||||
|
description: |
|
||||||
|
The management gateway ip of a subcloud.
|
||||||
|
in: body
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
subcloud_management_start_ip:
|
||||||
|
description: |
|
||||||
|
The management start ip of a subcloud.
|
||||||
|
in: body
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
subcloud_management_state:
|
subcloud_management_state:
|
||||||
description: |
|
description: |
|
||||||
Management state of the subcloud.
|
Management state of the subcloud.
|
||||||
in: body
|
in: body
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
|
subcloud_management_subnet:
|
||||||
|
description: |
|
||||||
|
The management subnet of a subcloud.
|
||||||
|
in: body
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
subcloud_name:
|
subcloud_name:
|
||||||
description: |
|
description: |
|
||||||
The name of a subcloud.
|
The name of a subcloud.
|
||||||
|
|
|
@ -96,6 +96,11 @@ INSTALL_VALUES_ADDRESSES = [
|
||||||
'network_address'
|
'network_address'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
SUBCLOUD_MANDATORY_NETWORK_PARAMS = [
|
||||||
|
'management_subnet', 'management_gateway_ip',
|
||||||
|
'management_start_ip', 'management_end_ip'
|
||||||
|
]
|
||||||
|
|
||||||
ANSIBLE_BOOTSTRAP_VALIDATE_CONFIG_VARS = \
|
ANSIBLE_BOOTSTRAP_VALIDATE_CONFIG_VARS = \
|
||||||
consts.ANSIBLE_CURRENT_VERSION_BASE_PATH + \
|
consts.ANSIBLE_CURRENT_VERSION_BASE_PATH + \
|
||||||
'/roles/bootstrap/validate-config/vars/main.yml'
|
'/roles/bootstrap/validate-config/vars/main.yml'
|
||||||
|
@ -452,7 +457,7 @@ class SubcloudsController(object):
|
||||||
|
|
||||||
# Ensure systemcontroller gateway is in the management subnet
|
# Ensure systemcontroller gateway is in the management subnet
|
||||||
# for the systemcontroller region.
|
# for the systemcontroller region.
|
||||||
management_address_pool = self._get_management_address_pool(context)
|
management_address_pool = self._get_network_address_pool()
|
||||||
systemcontroller_subnet_str = "%s/%d" % (
|
systemcontroller_subnet_str = "%s/%d" % (
|
||||||
management_address_pool.network,
|
management_address_pool.network,
|
||||||
management_address_pool.prefix)
|
management_address_pool.prefix)
|
||||||
|
@ -592,6 +597,39 @@ class SubcloudsController(object):
|
||||||
{'start': subcloud_admin_address_start,
|
{'start': subcloud_admin_address_start,
|
||||||
'end': subcloud_admin_address_end})
|
'end': subcloud_admin_address_end})
|
||||||
|
|
||||||
|
# TODO(nicodemos): Check if subcloud is online and network already exist in the
|
||||||
|
# subcloud when the lock/unlock is not required for network reconfiguration
|
||||||
|
def _validate_network_reconfiguration(self, payload, subcloud):
|
||||||
|
if payload.get('management-state'):
|
||||||
|
pecan.abort(422, _("Management state and network reconfiguration must "
|
||||||
|
"be updated separately"))
|
||||||
|
if subcloud.management_state != dccommon_consts.MANAGEMENT_UNMANAGED:
|
||||||
|
pecan.abort(422, _("A subcloud must be unmanaged to perform network "
|
||||||
|
"reconfiguration"))
|
||||||
|
if not payload.get('bootstrap_address'):
|
||||||
|
pecan.abort(422, _("The bootstrap_address parameter is required for "
|
||||||
|
"network reconfiguration"))
|
||||||
|
# Check if all parameters exist
|
||||||
|
if not all(payload.get(value) is not None for value in (
|
||||||
|
SUBCLOUD_MANDATORY_NETWORK_PARAMS)):
|
||||||
|
mandatory_params = ', '.join('--{}'.format(param.replace(
|
||||||
|
'_', '-')) for param in SUBCLOUD_MANDATORY_NETWORK_PARAMS)
|
||||||
|
abort_msg = (
|
||||||
|
"The following parameters are necessary for "
|
||||||
|
"subcloud network reconfiguration: {}".format(mandatory_params)
|
||||||
|
)
|
||||||
|
pecan.abort(422, _(abort_msg))
|
||||||
|
|
||||||
|
# Check if any network values are already in use
|
||||||
|
for param in SUBCLOUD_MANDATORY_NETWORK_PARAMS:
|
||||||
|
if payload.get(param) == getattr(subcloud, param):
|
||||||
|
pecan.abort(422, _("%s already in use by the subcloud.") % param)
|
||||||
|
|
||||||
|
# Check if password is valid
|
||||||
|
valid, msg = utils.is_password_valid(payload)
|
||||||
|
if not valid:
|
||||||
|
pecan.abort(400, _(msg))
|
||||||
|
|
||||||
def _format_ip_address(self, payload):
|
def _format_ip_address(self, payload):
|
||||||
"""Format IP addresses in 'bootstrap_values' and 'install_values'.
|
"""Format IP addresses in 'bootstrap_values' and 'install_values'.
|
||||||
|
|
||||||
|
@ -857,13 +895,17 @@ class SubcloudsController(object):
|
||||||
if patches[patch_id]['patchstate'] in [patching_v1.PATCH_STATE_PARTIAL_APPLY, patching_v1.PATCH_STATE_PARTIAL_REMOVE]:
|
if patches[patch_id]['patchstate'] in [patching_v1.PATCH_STATE_PARTIAL_APPLY, patching_v1.PATCH_STATE_PARTIAL_REMOVE]:
|
||||||
pecan.abort(422, _('Subcloud add is not allowed while system controller patching is still in progress.'))
|
pecan.abort(422, _('Subcloud add is not allowed while system controller patching is still in progress.'))
|
||||||
|
|
||||||
def _get_management_address_pool(self, context):
|
def _get_network_address_pool(
|
||||||
"""Get the system controller's management address pool"""
|
self, network='management',
|
||||||
ks_client = self.get_ks_client()
|
region_name=dccommon_consts.DEFAULT_REGION_NAME):
|
||||||
|
"""Get the region network address pool"""
|
||||||
|
ks_client = self.get_ks_client(region_name)
|
||||||
endpoint = ks_client.endpoint_cache.get_endpoint('sysinv')
|
endpoint = ks_client.endpoint_cache.get_endpoint('sysinv')
|
||||||
sysinv_client = SysinvClient(dccommon_consts.DEFAULT_REGION_NAME,
|
sysinv_client = SysinvClient(region_name,
|
||||||
ks_client.session,
|
ks_client.session,
|
||||||
endpoint=endpoint)
|
endpoint=endpoint)
|
||||||
|
if network == 'admin':
|
||||||
|
return sysinv_client.get_admin_address_pool()
|
||||||
return sysinv_client.get_management_address_pool()
|
return sysinv_client.get_management_address_pool()
|
||||||
|
|
||||||
# TODO(gsilvatr): refactor to use implementation from common/utils and test
|
# TODO(gsilvatr): refactor to use implementation from common/utils and test
|
||||||
|
@ -1260,7 +1302,6 @@ class SubcloudsController(object):
|
||||||
subcloud_ref)
|
subcloud_ref)
|
||||||
except exceptions.SubcloudNameNotFound:
|
except exceptions.SubcloudNameNotFound:
|
||||||
pecan.abort(404, _('Subcloud not found'))
|
pecan.abort(404, _('Subcloud not found'))
|
||||||
|
|
||||||
subcloud_id = subcloud.id
|
subcloud_id = subcloud.id
|
||||||
|
|
||||||
if verb is None:
|
if verb is None:
|
||||||
|
@ -1269,39 +1310,26 @@ class SubcloudsController(object):
|
||||||
if not payload:
|
if not payload:
|
||||||
pecan.abort(400, _('Body required'))
|
pecan.abort(400, _('Body required'))
|
||||||
|
|
||||||
|
# Check if exist any network reconfiguration parameters
|
||||||
|
reconfigure_network = any(payload.get(value) is not None for value in (
|
||||||
|
SUBCLOUD_MANDATORY_NETWORK_PARAMS))
|
||||||
|
|
||||||
|
if reconfigure_network:
|
||||||
|
system_controller_mgmt_pool = self._get_network_address_pool()
|
||||||
|
# Required parameters
|
||||||
|
payload['name'] = subcloud.name
|
||||||
|
payload['system_controller_network'] = (
|
||||||
|
system_controller_mgmt_pool.network)
|
||||||
|
payload['system_controller_network_prefix'] = (
|
||||||
|
system_controller_mgmt_pool.prefix
|
||||||
|
)
|
||||||
|
# Validation
|
||||||
|
self._validate_network_reconfiguration(payload, subcloud)
|
||||||
|
|
||||||
management_state = payload.get('management-state')
|
management_state = payload.get('management-state')
|
||||||
group_id = payload.get('group_id')
|
group_id = payload.get('group_id')
|
||||||
description = payload.get('description')
|
description = payload.get('description')
|
||||||
location = payload.get('location')
|
location = payload.get('location')
|
||||||
admin_subnet_str = payload.get('admin_subnet')
|
|
||||||
admin_start_ip_str = payload.get('admin_start_address')
|
|
||||||
admin_end_ip_str = payload.get('admin_end_address')
|
|
||||||
admin_gateway_ip_str = payload.get('admin_gateway_ip')
|
|
||||||
|
|
||||||
# Syntax checking
|
|
||||||
|
|
||||||
if (admin_subnet_str and admin_gateway_ip_str and
|
|
||||||
admin_start_ip_str and admin_end_ip_str):
|
|
||||||
# Required parameters
|
|
||||||
payload['name'] = subcloud.name
|
|
||||||
payload['systemcontroller_gateway_ip'] = (
|
|
||||||
subcloud.systemcontroller_gateway_ip)
|
|
||||||
|
|
||||||
# Parse/validate the admin subnet
|
|
||||||
subcloud_subnets = []
|
|
||||||
subclouds = db_api.subcloud_get_all(context)
|
|
||||||
for subcloud in subclouds:
|
|
||||||
subcloud_subnets.append(IPNetwork(subcloud.management_subnet))
|
|
||||||
|
|
||||||
self._validate_admin_network_config(admin_subnet_str,
|
|
||||||
admin_start_ip_str,
|
|
||||||
admin_end_ip_str,
|
|
||||||
admin_gateway_ip_str,
|
|
||||||
subcloud_subnets)
|
|
||||||
# Password only required when update admin network
|
|
||||||
valid, msg = utils.is_password_valid(payload)
|
|
||||||
if not valid:
|
|
||||||
pecan.abort(400, _(msg))
|
|
||||||
|
|
||||||
# Syntax checking
|
# Syntax checking
|
||||||
if management_state and \
|
if management_state and \
|
||||||
|
@ -1335,27 +1363,16 @@ class SubcloudsController(object):
|
||||||
if self._validate_install_values(payload, subcloud):
|
if self._validate_install_values(payload, subcloud):
|
||||||
payload['data_install'] = json.dumps(payload[INSTALL_VALUES])
|
payload['data_install'] = json.dumps(payload[INSTALL_VALUES])
|
||||||
try:
|
try:
|
||||||
# Inform dcmanager that subcloud has been updated.
|
if reconfigure_network:
|
||||||
# It will do all the real work...
|
|
||||||
if payload.get('admin_subnet'):
|
|
||||||
if payload.get('management-state'):
|
|
||||||
pecan.abort(422, _('Management state and network configuration must be updated separately'))
|
|
||||||
if subcloud.management_state != dccommon_consts.MANAGEMENT_UNMANAGED:
|
|
||||||
pecan.abort(422, _("Subcloud must be unmanaged to update admin network"))
|
|
||||||
|
|
||||||
subcloud = db_api.subcloud_update(
|
|
||||||
context, subcloud_id,
|
|
||||||
deploy_status=consts.DEPLOY_STATE_RECONFIGURING_NETWORK)
|
|
||||||
self.dcmanager_rpc_client.update_subcloud_with_network_reconfig(
|
self.dcmanager_rpc_client.update_subcloud_with_network_reconfig(
|
||||||
context, subcloud_id, payload)
|
context, subcloud_id, payload)
|
||||||
return db_api.subcloud_db_model_to_dict(subcloud)
|
return db_api.subcloud_db_model_to_dict(subcloud)
|
||||||
else:
|
subcloud = self.dcmanager_rpc_client.update_subcloud(
|
||||||
subcloud = self.dcmanager_rpc_client.update_subcloud(
|
context, subcloud_id, management_state=management_state,
|
||||||
context, subcloud_id, management_state=management_state,
|
description=description, location=location,
|
||||||
description=description, location=location,
|
group_id=group_id, data_install=payload.get('data_install'),
|
||||||
group_id=group_id, data_install=payload.get('data_install'),
|
force=force_flag)
|
||||||
force=force_flag)
|
return subcloud
|
||||||
return subcloud
|
|
||||||
except RemoteError as e:
|
except RemoteError as e:
|
||||||
pecan.abort(422, e.value)
|
pecan.abort(422, e.value)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
@ -26,6 +26,7 @@ import shutil
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from cgtsclient.exc import HTTPConflict
|
||||||
from eventlet import greenpool
|
from eventlet import greenpool
|
||||||
from fm_api import constants as fm_const
|
from fm_api import constants as fm_const
|
||||||
from fm_api import fm_api
|
from fm_api import fm_api
|
||||||
|
@ -1741,6 +1742,11 @@ class SubcloudManager(manager.Manager):
|
||||||
return db_api.subcloud_db_model_to_dict(subcloud)
|
return db_api.subcloud_db_model_to_dict(subcloud)
|
||||||
|
|
||||||
def update_subcloud_with_network_reconfig(self, context, subcloud_id, payload):
|
def update_subcloud_with_network_reconfig(self, context, subcloud_id, payload):
|
||||||
|
subcloud = db_api.subcloud_get(context, subcloud_id)
|
||||||
|
subcloud = db_api.subcloud_update(
|
||||||
|
context, subcloud.id,
|
||||||
|
deploy_status=consts.DEPLOY_STATE_RECONFIGURING_NETWORK
|
||||||
|
)
|
||||||
subcloud_name = payload['name']
|
subcloud_name = payload['name']
|
||||||
try:
|
try:
|
||||||
self._create_intermediate_ca_cert(payload)
|
self._create_intermediate_ca_cert(payload)
|
||||||
|
@ -1754,22 +1760,25 @@ class SubcloudManager(manager.Manager):
|
||||||
update_command = self.compose_update_command(
|
update_command = self.compose_update_command(
|
||||||
subcloud_name, subcloud_inventory_file)
|
subcloud_name, subcloud_inventory_file)
|
||||||
except Exception:
|
except Exception:
|
||||||
LOG.exception("Failed to prepare subcloud %s for update."
|
LOG.exception(
|
||||||
% subcloud_name)
|
"Failed to prepare subcloud %s for update." % subcloud_name)
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
apply_thread = threading.Thread(
|
apply_thread = threading.Thread(
|
||||||
target=self._run_admin_network_update_playbook,
|
target=self._run_network_reconfig_playbook,
|
||||||
args=(subcloud_name, update_command, overrides_file, payload, context, subcloud_id))
|
args=(subcloud_name, update_command, overrides_file,
|
||||||
|
payload, context, subcloud))
|
||||||
apply_thread.start()
|
apply_thread.start()
|
||||||
except Exception:
|
except Exception:
|
||||||
LOG.exception("Failed to update subcloud %s" % subcloud_name)
|
LOG.exception("Failed to update subcloud %s" % subcloud_name)
|
||||||
|
|
||||||
def _run_admin_network_update_playbook(
|
def _run_network_reconfig_playbook(
|
||||||
self, subcloud_name, update_command, overrides_file, payload, context, subcloud_id):
|
self, subcloud_name, update_command, overrides_file,
|
||||||
|
payload, context, subcloud
|
||||||
|
):
|
||||||
log_file = (os.path.join(consts.DC_ANSIBLE_LOG_DIR, subcloud_name) +
|
log_file = (os.path.join(consts.DC_ANSIBLE_LOG_DIR, subcloud_name) +
|
||||||
'_playbook_output.log')
|
'_playbook_output.log')
|
||||||
subcloud = db_api.subcloud_get(context, subcloud_id)
|
subcloud_id = subcloud.id
|
||||||
try:
|
try:
|
||||||
run_playbook(log_file, update_command)
|
run_playbook(log_file, update_command)
|
||||||
utils.delete_subcloud_inventory(overrides_file)
|
utils.delete_subcloud_inventory(overrides_file)
|
||||||
|
@ -1787,9 +1796,14 @@ class SubcloudManager(manager.Manager):
|
||||||
m_ks_client = OpenStackDriver(
|
m_ks_client = OpenStackDriver(
|
||||||
region_name=dccommon_consts.DEFAULT_REGION_NAME,
|
region_name=dccommon_consts.DEFAULT_REGION_NAME,
|
||||||
region_clients=None).keystone_client
|
region_clients=None).keystone_client
|
||||||
self._create_subcloud_admin_route(payload, m_ks_client)
|
self._create_subcloud_route(payload, m_ks_client, subcloud)
|
||||||
|
except HTTPConflict:
|
||||||
|
# The route already exists
|
||||||
|
LOG.warning(
|
||||||
|
"Failed to create route to subcloud %s" % subcloud_name)
|
||||||
except Exception:
|
except Exception:
|
||||||
LOG.exception("Failed to create route to admin")
|
LOG.exception(
|
||||||
|
"Failed to create route to subcloud %s." % subcloud_name)
|
||||||
db_api.subcloud_update(
|
db_api.subcloud_update(
|
||||||
context, subcloud_id,
|
context, subcloud_id,
|
||||||
deploy_status=consts.DEPLOY_STATE_RECONFIGURING_NETWORK_FAILED,
|
deploy_status=consts.DEPLOY_STATE_RECONFIGURING_NETWORK_FAILED,
|
||||||
|
@ -1797,7 +1811,8 @@ class SubcloudManager(manager.Manager):
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
self._update_services_endpoint(context, payload, m_ks_client)
|
self._update_services_endpoint(
|
||||||
|
context, payload, subcloud_name, m_ks_client)
|
||||||
except Exception:
|
except Exception:
|
||||||
LOG.exception("Failed to update subcloud %s endpoints" % subcloud_name)
|
LOG.exception("Failed to update subcloud %s endpoints" % subcloud_name)
|
||||||
db_api.subcloud_update(
|
db_api.subcloud_update(
|
||||||
|
@ -1810,45 +1825,46 @@ class SubcloudManager(manager.Manager):
|
||||||
self._delete_subcloud_routes(m_ks_client, subcloud)
|
self._delete_subcloud_routes(m_ks_client, subcloud)
|
||||||
|
|
||||||
db_api.subcloud_update(
|
db_api.subcloud_update(
|
||||||
context, subcloud_id,
|
context, subcloud_id, deploy_status=consts.DEPLOY_STATE_DONE
|
||||||
deploy_status=consts.DEPLOY_STATE_DONE
|
|
||||||
)
|
)
|
||||||
|
|
||||||
subcloud = db_api.subcloud_update(
|
subcloud = db_api.subcloud_update(
|
||||||
context,
|
context,
|
||||||
subcloud_id,
|
subcloud_id,
|
||||||
description=payload.get('description', subcloud.description),
|
description=payload.get('description', subcloud.description),
|
||||||
management_subnet=payload.get('admin_subnet'),
|
management_subnet=payload.get('management_subnet'),
|
||||||
management_gateway_ip=payload.get('admin_gateway_ip'),
|
management_gateway_ip=payload.get('management_gateway_ip'),
|
||||||
management_start_ip=payload.get('admin_start_address'),
|
management_start_ip=payload.get('management_start_ip'),
|
||||||
management_end_ip=payload.get('admin_end_address'),
|
management_end_ip=payload.get('management_end_ip'),
|
||||||
location=payload.get('location', subcloud.location),
|
location=payload.get('location', subcloud.location),
|
||||||
group_id=payload.get('group_id', subcloud.group_id),
|
group_id=payload.get('group_id', subcloud.group_id),
|
||||||
data_install=payload.get('data_install', subcloud.data_install)
|
data_install=payload.get('data_install', subcloud.data_install)
|
||||||
)
|
)
|
||||||
|
|
||||||
def _create_subcloud_admin_route(self, payload, keystone_client):
|
# Regenerate the addn_hosts_dc file
|
||||||
subcloud_subnet = netaddr.IPNetwork(utils.get_management_subnet(payload))
|
self._create_addn_hosts_dc(context)
|
||||||
|
|
||||||
|
def _create_subcloud_route(self, payload, keystone_client, subcloud):
|
||||||
|
subcloud_subnet = netaddr.IPNetwork(payload.get('management_subnet'))
|
||||||
endpoint = keystone_client.endpoint_cache.get_endpoint('sysinv')
|
endpoint = keystone_client.endpoint_cache.get_endpoint('sysinv')
|
||||||
sysinv_client = SysinvClient(dccommon_consts.DEFAULT_REGION_NAME,
|
sysinv_client = SysinvClient(dccommon_consts.DEFAULT_REGION_NAME,
|
||||||
keystone_client.session,
|
keystone_client.session,
|
||||||
endpoint=endpoint)
|
endpoint=endpoint)
|
||||||
systemcontroller_gateway_ip = payload.get('systemcontroller_gateway_ip')
|
|
||||||
# TODO(nicodemos) delete old route
|
|
||||||
cached_regionone_data = self._get_cached_regionone_data(
|
cached_regionone_data = self._get_cached_regionone_data(
|
||||||
keystone_client, sysinv_client)
|
keystone_client, sysinv_client)
|
||||||
for mgmt_if_uuid in cached_regionone_data['mgmt_interface_uuids']:
|
for mgmt_if_uuid in cached_regionone_data['mgmt_interface_uuids']:
|
||||||
sysinv_client.create_route(mgmt_if_uuid,
|
sysinv_client.create_route(mgmt_if_uuid,
|
||||||
str(subcloud_subnet.ip),
|
str(subcloud_subnet.ip),
|
||||||
subcloud_subnet.prefixlen,
|
subcloud_subnet.prefixlen,
|
||||||
systemcontroller_gateway_ip,
|
subcloud.systemcontroller_gateway_ip,
|
||||||
1)
|
1)
|
||||||
|
|
||||||
def _update_services_endpoint(self, context, payload, m_ks_client):
|
def _update_services_endpoint(
|
||||||
endpoint_ip = str(ipaddress.ip_network(payload.get('admin_subnet'))[2])
|
self, context, payload, subcloud_name, m_ks_client):
|
||||||
subcloud_name = payload.get('name')
|
endpoint_ip = str(ipaddress.ip_network(
|
||||||
|
payload.get('management_subnet'))[2])
|
||||||
if netaddr.IPAddress(endpoint_ip).version == 6:
|
if netaddr.IPAddress(endpoint_ip).version == 6:
|
||||||
endpoint_ip = '[' + endpoint_ip + ']'
|
endpoint_ip = f"[{endpoint_ip}]"
|
||||||
|
|
||||||
services_endpoints = {
|
services_endpoints = {
|
||||||
"keystone": "https://{}:5001/v3".format(endpoint_ip),
|
"keystone": "https://{}:5001/v3".format(endpoint_ip),
|
||||||
|
@ -1911,14 +1927,17 @@ class SubcloudManager(manager.Manager):
|
||||||
payload['sysadmin_password'])
|
payload['sysadmin_password'])
|
||||||
payload['override_values']['ansible_become_pass'] = (
|
payload['override_values']['ansible_become_pass'] = (
|
||||||
payload['sysadmin_password'])
|
payload['sysadmin_password'])
|
||||||
payload['override_values']['admin_gateway_address'] = (
|
|
||||||
payload['admin_gateway_ip'])
|
payload['override_values']['sc_gateway_address'] = (
|
||||||
payload['override_values']['admin_floating_address'] = (
|
payload['management_gateway_ip'])
|
||||||
payload['admin_start_address']
|
payload['override_values']['sc_floating_address'] = (
|
||||||
)
|
payload['management_start_ip'])
|
||||||
payload['override_values']['admin_subnet'] = (
|
payload['override_values']['system_controller_network'] = (
|
||||||
payload['admin_subnet']
|
payload['system_controller_network'])
|
||||||
)
|
payload['override_values']['system_controller_network_prefix'] = (
|
||||||
|
payload['system_controller_network_prefix'])
|
||||||
|
payload['override_values']['sc_subnet'] = payload['management_subnet']
|
||||||
|
|
||||||
payload['override_values']['dc_root_ca_cert'] = payload['dc_root_ca_cert']
|
payload['override_values']['dc_root_ca_cert'] = payload['dc_root_ca_cert']
|
||||||
payload['override_values']['sc_ca_cert'] = payload['sc_ca_cert']
|
payload['override_values']['sc_ca_cert'] = payload['sc_ca_cert']
|
||||||
payload['override_values']['sc_ca_key'] = payload['sc_ca_key']
|
payload['override_values']['sc_ca_key'] = payload['sc_ca_key']
|
||||||
|
|
|
@ -278,9 +278,9 @@ class TestSubcloudPost(testroot.DCManagerApiTest,
|
||||||
'192.168.204.100')
|
'192.168.204.100')
|
||||||
|
|
||||||
p = mock.patch.object(subclouds.SubcloudsController,
|
p = mock.patch.object(subclouds.SubcloudsController,
|
||||||
'_get_management_address_pool')
|
'_get_network_address_pool')
|
||||||
self.mock_get_management_address_pool = p.start()
|
self.mock_get_network_address_pool = p.start()
|
||||||
self.mock_get_management_address_pool.return_value = \
|
self.mock_get_network_address_pool.return_value = \
|
||||||
self.management_address_pool
|
self.management_address_pool
|
||||||
self.addCleanup(p.stop)
|
self.addCleanup(p.stop)
|
||||||
|
|
||||||
|
@ -1234,21 +1234,30 @@ class TestSubcloudAPIOther(testroot.DCManagerApiTest):
|
||||||
self.assertEqual(response.status_int, 200)
|
self.assertEqual(response.status_int, 200)
|
||||||
|
|
||||||
@mock.patch.object(rpc_client, 'ManagerClient')
|
@mock.patch.object(rpc_client, 'ManagerClient')
|
||||||
@mock.patch.object(subclouds.SubcloudsController, '_validate_admin_network_config')
|
@mock.patch.object(subclouds.SubcloudsController, '_get_network_address_pool')
|
||||||
|
@mock.patch.object(subclouds.SubcloudsController,
|
||||||
|
'_validate_network_reconfiguration')
|
||||||
@mock.patch.object(subclouds.SubcloudsController, '_get_patch_data')
|
@mock.patch.object(subclouds.SubcloudsController, '_get_patch_data')
|
||||||
def test_patch_subcloud_admin_values(self, mock_get_patch_data,
|
def test_patch_subcloud_network_values(
|
||||||
mock_validate_admin_network_config,
|
self, mock_get_patch_data, mock_validate_network_reconfiguration,
|
||||||
mock_rpc_client):
|
mock_mgmt_address_pool, mock_rpc_client):
|
||||||
subcloud = fake_subcloud.create_fake_subcloud(self.ctx)
|
subcloud = fake_subcloud.create_fake_subcloud(self.ctx)
|
||||||
db_api.subcloud_update(self.ctx, subcloud.id,
|
db_api.subcloud_update(
|
||||||
availability_status=dccommon_consts.AVAILABILITY_ONLINE)
|
self.ctx, subcloud.id,
|
||||||
|
availability_status=dccommon_consts.AVAILABILITY_ONLINE)
|
||||||
fake_password = (
|
fake_password = (
|
||||||
base64.b64encode('testpass'.encode("utf-8"))).decode('ascii')
|
base64.b64encode('testpass'.encode("utf-8"))).decode('ascii')
|
||||||
payload = {'sysadmin_password': fake_password,
|
payload = {'sysadmin_password': fake_password,
|
||||||
'admin_subnet': "192.168.102.0/24",
|
'bootstrap_address': "192.168.102.2",
|
||||||
'admin_start_address': "192.168.102.5",
|
'management_subnet': "192.168.102.0/24",
|
||||||
'admin_end_address': "192.168.102.49",
|
'management_start_ip': "192.168.102.5",
|
||||||
'admin_gateway_ip': "192.168.102.1"}
|
'management_end_ip': "192.168.102.49",
|
||||||
|
'management_gateway_ip': "192.168.102.1"}
|
||||||
|
|
||||||
|
fake_management_address_pool = FakeAddressPool('192.168.204.0', 24,
|
||||||
|
'192.168.204.2',
|
||||||
|
'192.168.204.100')
|
||||||
|
mock_mgmt_address_pool.return_value = fake_management_address_pool
|
||||||
|
|
||||||
mock_rpc_client().update_subcloud_with_network_reconfig.return_value = True
|
mock_rpc_client().update_subcloud_with_network_reconfig.return_value = True
|
||||||
mock_get_patch_data.return_value = payload
|
mock_get_patch_data.return_value = payload
|
||||||
|
@ -1256,7 +1265,7 @@ class TestSubcloudAPIOther(testroot.DCManagerApiTest):
|
||||||
headers=FAKE_HEADERS,
|
headers=FAKE_HEADERS,
|
||||||
params=payload)
|
params=payload)
|
||||||
self.assertEqual(response.status_int, 200)
|
self.assertEqual(response.status_int, 200)
|
||||||
mock_validate_admin_network_config.assert_called_once()
|
mock_validate_network_reconfiguration.assert_called_once()
|
||||||
mock_rpc_client().update_subcloud_with_network_reconfig.assert_called_once_with(
|
mock_rpc_client().update_subcloud_with_network_reconfig.assert_called_once_with(
|
||||||
mock.ANY,
|
mock.ANY,
|
||||||
subcloud.id,
|
subcloud.id,
|
||||||
|
|
|
@ -661,17 +661,19 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
||||||
self.assertEqual("subcloud new location",
|
self.assertEqual("subcloud new location",
|
||||||
updated_subcloud.location)
|
updated_subcloud.location)
|
||||||
|
|
||||||
|
@mock.patch.object(subcloud_manager.SubcloudManager,
|
||||||
|
'_create_addn_hosts_dc')
|
||||||
@mock.patch.object(subcloud_manager.SubcloudManager,
|
@mock.patch.object(subcloud_manager.SubcloudManager,
|
||||||
'_delete_subcloud_routes')
|
'_delete_subcloud_routes')
|
||||||
@mock.patch.object(subcloud_manager.SubcloudManager,
|
@mock.patch.object(subcloud_manager.SubcloudManager,
|
||||||
'_update_services_endpoint')
|
'_update_services_endpoint')
|
||||||
@mock.patch.object(subcloud_manager.SubcloudManager,
|
@mock.patch.object(subcloud_manager.SubcloudManager,
|
||||||
'_create_subcloud_admin_route')
|
'_create_subcloud_route')
|
||||||
@mock.patch.object(subcloud_manager, 'OpenStackDriver')
|
@mock.patch.object(subcloud_manager, 'OpenStackDriver')
|
||||||
@mock.patch.object(subcloud_manager, 'run_playbook')
|
@mock.patch.object(subcloud_manager, 'run_playbook')
|
||||||
def test_update_subcloud_with_admin_values(
|
def test_update_subcloud_network_reconfiguration(
|
||||||
self, mock_run_playbook, mock_keystone_client, mock_create_route,
|
self, mock_run_playbook, mock_keystone_client, mock_create_route,
|
||||||
mock_update_endpoints, mock_delete_route):
|
mock_update_endpoints, mock_delete_route, mock_addn_hosts_dc):
|
||||||
subcloud = self.create_subcloud_static(
|
subcloud = self.create_subcloud_static(
|
||||||
self.ctx,
|
self.ctx,
|
||||||
name='subcloud1',
|
name='subcloud1',
|
||||||
|
@ -683,10 +685,10 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
||||||
payload = {'name': subcloud.name,
|
payload = {'name': subcloud.name,
|
||||||
'description': "subcloud description",
|
'description': "subcloud description",
|
||||||
'location': "subcloud location",
|
'location': "subcloud location",
|
||||||
'admin_subnet': "192.168.102.0/24",
|
'management_subnet': "192.168.102.0/24",
|
||||||
'admin_start_address': "192.168.102.5",
|
'management_start_ip': "192.168.102.5",
|
||||||
'admin_end_address': "192.168.102.49",
|
'management_end_ip': "192.168.102.49",
|
||||||
'admin_gateway_ip': "192.168.102.1"}
|
'management_gateway_ip': "192.168.102.1"}
|
||||||
|
|
||||||
fake_dcmanager_notification = FakeDCManagerNotifications()
|
fake_dcmanager_notification = FakeDCManagerNotifications()
|
||||||
|
|
||||||
|
@ -695,14 +697,15 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
||||||
mock_dcmanager_api.return_value = fake_dcmanager_notification
|
mock_dcmanager_api.return_value = fake_dcmanager_notification
|
||||||
|
|
||||||
sm = subcloud_manager.SubcloudManager()
|
sm = subcloud_manager.SubcloudManager()
|
||||||
sm._run_admin_network_update_playbook(
|
sm._run_network_reconfig_playbook(
|
||||||
subcloud.name, mock.ANY, None, payload, self.ctx, subcloud.id)
|
subcloud.name, mock.ANY, None, payload, self.ctx, subcloud)
|
||||||
|
|
||||||
mock_run_playbook.assert_called_once()
|
mock_run_playbook.assert_called_once()
|
||||||
mock_keystone_client.assert_called_once()
|
mock_keystone_client.assert_called_once()
|
||||||
mock_create_route.assert_called_once()
|
mock_create_route.assert_called_once()
|
||||||
mock_update_endpoints.assert_called_once()
|
mock_update_endpoints.assert_called_once()
|
||||||
mock_delete_route.assert_called_once()
|
mock_delete_route.assert_called_once()
|
||||||
|
mock_addn_hosts_dc.assert_called_once()
|
||||||
|
|
||||||
# Verify subcloud was updated with correct values
|
# Verify subcloud was updated with correct values
|
||||||
updated_subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud.name)
|
updated_subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud.name)
|
||||||
|
@ -710,13 +713,13 @@ class TestSubcloudManager(base.DCManagerTestCase):
|
||||||
updated_subcloud.description)
|
updated_subcloud.description)
|
||||||
self.assertEqual(payload['location'],
|
self.assertEqual(payload['location'],
|
||||||
updated_subcloud.location)
|
updated_subcloud.location)
|
||||||
self.assertEqual(payload['admin_subnet'],
|
self.assertEqual(payload['management_subnet'],
|
||||||
updated_subcloud.management_subnet)
|
updated_subcloud.management_subnet)
|
||||||
self.assertEqual(payload['admin_gateway_ip'],
|
self.assertEqual(payload['management_gateway_ip'],
|
||||||
updated_subcloud.management_gateway_ip)
|
updated_subcloud.management_gateway_ip)
|
||||||
self.assertEqual(payload['admin_start_address'],
|
self.assertEqual(payload['management_start_ip'],
|
||||||
updated_subcloud.management_start_ip)
|
updated_subcloud.management_start_ip)
|
||||||
self.assertEqual(payload['admin_end_address'],
|
self.assertEqual(payload['management_end_ip'],
|
||||||
updated_subcloud.management_end_ip)
|
updated_subcloud.management_end_ip)
|
||||||
|
|
||||||
def test_update_subcloud_with_install_values(self):
|
def test_update_subcloud_with_install_values(self):
|
||||||
|
|
Loading…
Reference in New Issue