Show Subcloud Deploy Configuration Status

This commit extends the
"dcmanager subcloud show <subcloud> --detail" command to display
the deploy configuration status of the subcloud from the
systemcontroller.

The presence of a 260.002 alarm related to a 'host' resource on an
online subcloud indicates an outdated deploy configuration.
In such cases, the field will display "Deployment: configurations
out-of-date". Otherwise, the field  will show:
"Deployment: configurations up-to-date". The field will not be shown
for offline subclouds.

Test Plan:
PASS: Successful bring up of a fresh DC system with these changes.
PASS: Run 'dcmanager subcloud show' without the '--detail' argument.
Verify that the new field is not being shown.
PASS: Run 'dcmanager subcloud show --detail' for an online subcloud
containing a 260.002 alarm related to a host resource. Verify that
the output contains 'Deployment: configurations out-of-date'.
PASS: Run 'dcmanager subcloud show --detail' for an online subcloud
containing a 260.002 alarm related to a resource other than host.
Verify that the output contains 'Deployment: configurations
up-to-date'.
PASS: Run 'dcmanager subcloud show --detail' for an online subcloud
without a 260.002 alarm. Verify that the output contains 'Deployment:
configurations up-to-date'.
PASS: Run 'dcmanager subcloud show --detail' for an offline subcloud.
Verify that the new field is not being shown.

Story: 2010719
Task: 48025

Change-Id: I223919ded1d6d5dfcc81c2060c5e6491ed8a73d9
Signed-off-by: Enzo Candotti <enzo.candotti@windriver.com>
This commit is contained in:
Enzo Candotti 2023-05-16 16:20:11 -03:00
parent f174505b66
commit e88471cd38
4 changed files with 88 additions and 8 deletions

View File

@ -21,6 +21,7 @@
"data_install": null,
"data_upgrade": null,
"oam_floating_ip": "192.168.101.2",
"config_sync_status": "Deployment: configurations up-to-date"
"endpoint_sync_status": [
{
"sync_status": "in-sync",

View File

@ -1,4 +1,4 @@
# Copyright (c) 2020-2022 Wind River Systems, Inc.
# Copyright (c) 2020-2023 Wind River Systems, Inc.
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
@ -124,6 +124,11 @@ SYNC_STATUS_UNKNOWN = "unknown"
SYNC_STATUS_IN_SYNC = "in-sync"
SYNC_STATUS_OUT_OF_SYNC = "out-of-sync"
# Subcloud deploy configuration status
DEPLOY_CONFIG_UP_TO_DATE = 'Deployment: configurations up-to-date'
DEPLOY_CONFIG_OUT_OF_DATE = 'Deployment: configurations out-of-date'
MONITORED_ALARM_ENTITIES = ['host.starlingx.windriver.com', ]
# OS type
OS_RELEASE_FILE = '/etc/os-release'
OS_CENTOS = 'centos'

View File

@ -39,6 +39,7 @@ from pecan import expose
from pecan import request
from dccommon import consts as dccommon_consts
from dccommon.drivers.openstack.fm import FmClient
from dccommon.drivers.openstack import patching_v1
from dccommon.drivers.openstack.patching_v1 import PatchingClient
from dccommon.drivers.openstack.sdk_platform import OpenStackDriver
@ -61,6 +62,7 @@ from dcmanager.common import utils
from dcmanager.db import api as db_api
from dcmanager.rpc import client as rpc_client
from fm_api.constants import FM_ALARM_ID_UNSYNCHRONIZED_RESOURCE
from six.moves import range
CONF = cfg.CONF
@ -1006,12 +1008,11 @@ class SubcloudsController(object):
return sysinv_client.get_management_address_pool()
# TODO(gsilvatr): refactor to use implementation from common/utils and test
def _get_oam_addresses(self, context, subcloud_name):
def _get_oam_addresses(self, context, subcloud_name, sc_ks_client):
"""Get the subclouds oam addresses"""
# First need to retrieve the Subcloud's Keystone session
try:
sc_ks_client = self.get_ks_client(subcloud_name)
endpoint = sc_ks_client.endpoint_cache.get_endpoint('sysinv')
sysinv_client = SysinvClient(subcloud_name,
sc_ks_client.session,
@ -1027,6 +1028,31 @@ class SubcloudsController(object):
LOG.error(message)
return None
def _get_deploy_config_sync_status(self, context, subcloud_name, keystone_client):
"""Get the deploy configuration insync status of the subcloud """
detected_alarms = None
try:
fm_client = FmClient(subcloud_name, keystone_client.session)
detected_alarms = fm_client.get_alarms_by_id(
FM_ALARM_ID_UNSYNCHRONIZED_RESOURCE)
except Exception as ex:
LOG.error(str(ex))
return None
out_of_date = False
if detected_alarms:
# Check if any alarm.entity_instance_id contains any of the values
# in MONITORED_ALARM_ENTITIES.
# We want to scope 260.002 alarms to the host entity only.
out_of_date = any(
any(entity_id in alarm.entity_instance_id
for entity_id in dccommon_consts.MONITORED_ALARM_ENTITIES)
for alarm in detected_alarms
)
sync_status = dccommon_consts.DEPLOY_CONFIG_OUT_OF_DATE if out_of_date \
else dccommon_consts.DEPLOY_CONFIG_UP_TO_DATE
return sync_status
def _add_subcloud_to_database(self, context, payload):
try:
db_api.subcloud_get_by_name(context, payload['name'])
@ -1190,16 +1216,26 @@ class SubcloudsController(object):
if detail is not None:
oam_floating_ip = "unavailable"
config_sync_status = "unknown"
if subcloud.availability_status == dccommon_consts.AVAILABILITY_ONLINE:
# Get the keystone client that will be used
# for _get_deploy_config_sync_status and _get_oam_addresses
sc_ks_client = self.get_ks_client(subcloud.name)
oam_addresses = self._get_oam_addresses(context,
subcloud.name)
subcloud.name, sc_ks_client)
if oam_addresses is not None:
oam_floating_ip = oam_addresses.oam_floating_ip
floating_ip_dict = {"oam_floating_ip":
oam_floating_ip}
subcloud_dict.update(floating_ip_dict)
deploy_config_state = self._get_deploy_config_sync_status(
context, subcloud.name, sc_ks_client)
if deploy_config_state is not None:
config_sync_status = deploy_config_state
extra_details = {"oam_floating_ip": oam_floating_ip,
"config_sync_status": config_sync_status}
subcloud_dict.update(extra_details)
return subcloud_dict
@utils.synchronized(LOCK_NAME)

View File

@ -1051,12 +1051,18 @@ class TestSubcloudAPIOther(testroot.DCManagerApiTest):
self.assertEqual(response.json.get('oam_floating_ip', None), None)
self.assertEqual(response.json['name'], subcloud.name)
@mock.patch.object(subclouds.SubcloudsController,
'_get_deploy_config_sync_status')
@mock.patch.object(subclouds.SubcloudsController,
'_get_oam_addresses')
@mock.patch.object(subclouds.SubcloudsController,
'get_ks_client')
@mock.patch.object(rpc_client, 'ManagerClient')
def test_get_online_subcloud_with_additional_detail(self,
mock_rpc_client,
mock_get_oam_addresses):
mock_get_ks_client,
mock_get_oam_addresses,
mock_get_deploy_config_sync_status):
subcloud = fake_subcloud.create_fake_subcloud(self.ctx)
updated_subcloud = db_api.subcloud_update(
self.ctx, subcloud.id, availability_status=dccommon_consts.AVAILABILITY_ONLINE)
@ -1069,11 +1075,14 @@ class TestSubcloudAPIOther(testroot.DCManagerApiTest):
'10.10.10.3',
'10.10.10.1',
'10.10.10.2')
mock_get_ks_client.return_value = 'ks_client'
mock_get_oam_addresses.return_value = oam_addresses
mock_get_deploy_config_sync_status.return_value = dccommon_consts.DEPLOY_CONFIG_UP_TO_DATE
response = self.app.get(get_url, headers=FAKE_HEADERS)
self.assertEqual(response.content_type, 'application/json')
self.assertEqual(response.status_code, http_client.OK)
self.assertEqual('10.10.10.2', response.json['oam_floating_ip'])
self.assertEqual('Deployment: configurations up-to-date', response.json['config_sync_status'])
@mock.patch.object(rpc_client, 'ManagerClient')
def test_get_offline_subcloud_with_additional_detail(self,
@ -1084,18 +1093,47 @@ class TestSubcloudAPIOther(testroot.DCManagerApiTest):
self.assertEqual(response.content_type, 'application/json')
self.assertEqual(response.status_code, http_client.OK)
self.assertEqual('unavailable', response.json['oam_floating_ip'])
self.assertEqual('unknown', response.json['config_sync_status'])
@mock.patch.object(subclouds.SubcloudsController,
'_get_deploy_config_sync_status')
@mock.patch.object(subclouds.SubcloudsController,
'_get_oam_addresses')
@mock.patch.object(subclouds.SubcloudsController,
'get_ks_client')
@mock.patch.object(rpc_client, 'ManagerClient')
def test_get_subcloud_deploy_config_status_unknown(self,
mock_rpc_client,
mock_get_ks_client,
mock_get_oam_addresses,
mock_get_deploy_config_sync_status):
subcloud = fake_subcloud.create_fake_subcloud(self.ctx)
updated_subcloud = db_api.subcloud_update(
self.ctx, subcloud.id, availability_status=dccommon_consts.AVAILABILITY_ONLINE)
get_url = FAKE_URL + '/' + str(updated_subcloud.id) + '/detail'
mock_get_ks_client.return_value = 'ks_client'
mock_get_oam_addresses.return_value = None
mock_get_deploy_config_sync_status.return_value = None
response = self.app.get(get_url, headers=FAKE_HEADERS)
self.assertEqual(response.content_type, 'application/json')
self.assertEqual(response.status_code, http_client.OK)
self.assertEqual('unknown', response.json['config_sync_status'])
@mock.patch.object(subclouds.SubcloudsController,
'_get_oam_addresses')
@mock.patch.object(subclouds.SubcloudsController,
'get_ks_client')
@mock.patch.object(rpc_client, 'ManagerClient')
def test_get_subcloud_oam_ip_unavailable(self,
mock_rpc_client,
mock_get_ks_client,
mock_get_oam_addresses):
subcloud = fake_subcloud.create_fake_subcloud(self.ctx)
updated_subcloud = db_api.subcloud_update(
self.ctx, subcloud.id, availability_status=dccommon_consts.AVAILABILITY_ONLINE)
get_url = FAKE_URL + '/' + str(updated_subcloud.id) + '/detail'
mock_get_ks_client.return_value = 'ks_client'
mock_get_oam_addresses.return_value = None
response = self.app.get(get_url, headers=FAKE_HEADERS)
self.assertEqual(response.content_type, 'application/json')