Add per-host board management protocol setting in sysinv

Use bm_type field to store board management protocol setting,
available bm protocols:
  redfish
  ipmi
  dynamic
  none (bm is not provisioned)

The old service parameter bmc_access_method is removed.

Partial-Bug: 1852328
Change-Id: I5097e53f6fc1bfbe23d2a1b765b5bc0e25423c22
Signed-off-by: Bin Qian <bin.qian@windriver.com>
This commit is contained in:
Bin Qian 2019-11-28 09:02:16 -05:00
parent d5b39786ba
commit 24f8c503ac
7 changed files with 94 additions and 86 deletions

View File

@ -1,2 +1,2 @@
SRC_DIR="sysinv"
TIS_PATCH_VER=339
TIS_PATCH_VER=340

View File

@ -1392,14 +1392,24 @@ class HostController(rest.RestController):
delta.add(key)
ihost_orig[key] = defaults[key]
bm_list = ['bm_type', 'bm_ip',
'bm_username', 'bm_password']
bm_list = ['bm_ip', 'bm_username', 'bm_password']
for bmi in bm_list:
if bmi in ihost_dict:
delta.add(bmi)
changed_paths.append({'path': '/' + str(bmi),
'value': ihost_dict[bmi],
'op': 'replace'})
if ihost_dict.get('bm_ip') and ihost_dict.get('bm_username'):
implict_bm_type = constants.HOST_BM_TYPE_DEFAULT
else:
implict_bm_type = constants.HOST_BM_TYPE_DEPROVISIONED
if ihost_dict.get('bm_type') is None:
ihost_dict['bm_type'] = implict_bm_type
delta.add('bm_type')
changed_paths.append({'path': '/bm_type',
'value': ihost_dict['bm_type'],
'op': 'replace'})
self._bm_semantic_check_and_update(ihost_orig, ihost_dict,
delta, changed_paths,
@ -3804,15 +3814,24 @@ class HostController(rest.RestController):
password_exists = password_exists and patch_bm_password is not None
bm_type_orig = ohost.get('bm_type') or ""
bm_type_patch = phost.get('bm_type') or ""
if bm_type_patch.lower() == 'none':
bm_type_patch = ''
if (not bm_type_patch) and (bm_type_orig != bm_type_patch):
LOG.info("bm_type None from %s to %s." %
(ohost['bm_type'], phost['bm_type']))
bm_type_patch = phost.get('bm_type')
# Semantic Check: Validate BM type against supported list
if bm_type_patch not in constants.HOST_BM_VALID_TYPE_LIST:
raise wsme.exc.ClientSideError(
_("%s: Rejected: '%s' is not a supported board management "
"type. Must be one of %s" %
(phost['hostname'],
phost['bm_type'],
constants.HOST_BM_VALID_TYPE_LIST)))
bm_type_changed_to_none = True
bm_type_orig = ohost.get('bm_type')
if bm_type_orig != bm_type_patch:
if bm_type_patch == constants.HOST_BM_TYPE_DEPROVISIONED:
LOG.info("BM is to be deprovisioned")
bm_type_changed_to_none = True
else:
LOG.info("BM type %s is changed to %s" %
(bm_type_orig, bm_type_patch))
if 'bm_ip' in delta:
obm_ip = ohost['bm_ip'] or ""
@ -3827,50 +3846,19 @@ class HostController(rest.RestController):
"controller IP Address is not user-modifiable." %
(constants.REGION_PRIMARY, phost['hostname'])))
if (phost['bm_ip'] or phost['bm_type'] or phost['bm_username']):
if (not phost['bm_type'] or
(phost['bm_type'] and phost['bm_type'].lower() ==
constants.BM_TYPE_NONE)) and not bm_type_changed_to_none:
raise wsme.exc.ClientSideError(
_("%s: Rejected: Board Management controller Type "
"is not provisioned. Provisionable values: 'bmc'."
% phost['hostname']))
if phost['bm_ip'] or phost['bm_type'] or phost['bm_username']:
if bm_type_patch == constants.HOST_BM_TYPE_DEPROVISIONED:
if not bm_type_changed_to_none:
raise wsme.exc.ClientSideError(
_("%s: Rejected: Board Management controller Type "
"is not provisioned. Provisionable values: '%s'." %
(phost['hostname'],
constants.HOST_BM_VALID_PROVISIONED_TYPE_LIST)))
elif not phost['bm_username']:
raise wsme.exc.ClientSideError(
_("%s: Rejected: Board Management controller username "
"is not configured." % phost['hostname']))
# Semantic Check: Validate BM type against supported list
# ilo, quanta is kept for backwards compatability only
valid_bm_type_list = [None, 'None', constants.BM_TYPE_NONE,
constants.BM_TYPE_GENERIC,
'ilo', 'ilo3', 'ilo4', 'quanta']
if not phost['bm_type']:
phost['bm_type'] = None
if not (phost['bm_type'] in valid_bm_type_list):
raise wsme.exc.ClientSideError(
_("%s: Rejected: '%s' is not a supported board management "
"type. Must be one of %s" %
(phost['hostname'],
phost['bm_type'],
valid_bm_type_list)))
bm_type_str = phost['bm_type']
if (phost['bm_type'] and
bm_type_str.lower() != constants.BM_TYPE_NONE):
LOG.info("Updating bm_type from %s to %s" %
(phost['bm_type'], constants.BM_TYPE_GENERIC))
phost['bm_type'] = constants.BM_TYPE_GENERIC
if hostupdate:
hostupdate.ihost_val_update(
{'bm_type': constants.BM_TYPE_GENERIC})
else:
phost['bm_type'] = None
if hostupdate:
hostupdate.ihost_val_update({'bm_type': None})
if (phost['bm_type'] and phost['bm_ip'] and
(ohost['bm_ip'] != phost['bm_ip'])):
if not cutils.is_valid_ip(phost['bm_ip']):

View File

@ -154,8 +154,6 @@ FORCE_LOCKING = "Force Locking"
OPERATIONAL_ENABLED = 'enabled'
OPERATIONAL_DISABLED = 'disabled'
BM_TYPE_GENERIC = 'bmc'
BM_TYPE_NONE = 'none'
PROVISIONED = 'provisioned'
PROVISIONING = 'provisioning'
UNPROVISIONED = 'unprovisioned'
@ -956,7 +954,6 @@ SERVICE_PARAM_PLAT_MTCE_HBS_FAILURE_THRESHOLD = 'heartbeat_failure_threshold'
SERVICE_PARAM_PLAT_MTCE_HBS_DEGRADE_THRESHOLD = 'heartbeat_degrade_threshold'
SERVICE_PARAM_PLAT_MTCE_MNFA_THRESHOLD = 'mnfa_threshold'
SERVICE_PARAM_PLAT_MTCE_MNFA_TIMEOUT = 'mnfa_timeout'
SERVICE_PARAM_PLAT_MTCE_BMC_ACCESS_METHOD = 'bmc_access_method'
SERVICE_PARAM_PLAT_MTCE_WORKER_BOOT_TIMEOUT_DEFAULT = 720
SERVICE_PARAM_PLAT_MTCE_CONTROLLER_BOOT_TIMEOUT_DEFAULT = 1200
@ -966,7 +963,6 @@ SERVICE_PARAM_PLAT_MTCE_HBS_FAILURE_THRESHOLD_DEFAULT = 10
SERVICE_PARAM_PLAT_MTCE_HBS_DEGRADE_THRESHOLD_DEFAULT = 6
SERVICE_PARAM_PLAT_MTCE_MNFA_THRESHOLD_DEFAULT = 2
SERVICE_PARAM_PLAT_MTCE_MNFA_TIMEOUT_DEFAULT = 0
SERVICE_PARAM_PLAT_MTCE_BMC_ACCESS_METHOD_DEFAULT = 'learn'
# default time to live seconds
PM_TTL_DEFAULT = 86400
@ -1538,3 +1534,18 @@ MELLANOX_DRIVERS = [DRIVER_MLX_CX3,
# Traffic control
TRAFFIC_CONTROL_SCRIPT = '/usr/local/bin/tc_setup.sh'
# Host Board Management Constants
HOST_BM_TYPE_DEPROVISIONED = "none"
HOST_BM_TYPE_IPMI = "ipmi"
HOST_BM_TYPE_REDFISH = "redfish"
HOST_BM_TYPE_DYNAMIC = "dynamic"
HOST_BM_TYPE_DEFAULT = HOST_BM_TYPE_DYNAMIC
HOST_BM_VALID_TYPE_LIST = [HOST_BM_TYPE_DEPROVISIONED,
HOST_BM_TYPE_DYNAMIC,
HOST_BM_TYPE_IPMI,
HOST_BM_TYPE_REDFISH]
HOST_BM_VALID_PROVISIONED_TYPE_LIST = [HOST_BM_TYPE_DYNAMIC,
HOST_BM_TYPE_IPMI,
HOST_BM_TYPE_REDFISH]

View File

@ -228,25 +228,6 @@ def _validate_mnfa_timeout(name, value):
SERVICE_PARAM_PLAT_MTCE_MNFA_TIMEOUT_MAX)
def _validate_bmc_access_method(name, value):
error = False
try:
if str(value) != SERVICE_PARAM_PLAT_MTCE_BMC_ACCESS_METHOD_LEARN and \
str(value) != SERVICE_PARAM_PLAT_MTCE_BMC_ACCESS_METHOD_IPMI and \
str(value) != SERVICE_PARAM_PLAT_MTCE_BMC_ACCESS_METHOD_REDFISH:
error = True
except ValueError:
error = True
if error is True:
raise wsme.exc.ClientSideError(_(
"Method must be one of '%s', '%s' or '%s'" %
(SERVICE_PARAM_PLAT_MTCE_BMC_ACCESS_METHOD_LEARN,
SERVICE_PARAM_PLAT_MTCE_BMC_ACCESS_METHOD_IPMI,
SERVICE_PARAM_PLAT_MTCE_BMC_ACCESS_METHOD_REDFISH)))
def _validate_ipv4(name, value):
"""Check if router_id value is valid"""
if not netaddr.valid_ipv4(value):
@ -369,7 +350,6 @@ PLATFORM_MTCE_PARAMETER_MANDATORY = [
constants.SERVICE_PARAM_PLAT_MTCE_HBS_DEGRADE_THRESHOLD,
constants.SERVICE_PARAM_PLAT_MTCE_MNFA_THRESHOLD,
constants.SERVICE_PARAM_PLAT_MTCE_MNFA_TIMEOUT,
constants.SERVICE_PARAM_PLAT_MTCE_BMC_ACCESS_METHOD,
]
PLATFORM_SYSINV_PARAMETER_PROTECTED = ['firewall_rules_id']
@ -392,9 +372,6 @@ SERVICE_PARAM_PLAT_MTCE_MNFA_THRESHOLD_MIN = 2
SERVICE_PARAM_PLAT_MTCE_MNFA_THRESHOLD_MAX = 100
SERVICE_PARAM_PLAT_MTCE_MNFA_TIMEOUT_MIN = 100
SERVICE_PARAM_PLAT_MTCE_MNFA_TIMEOUT_MAX = 86400
SERVICE_PARAM_PLAT_MTCE_BMC_ACCESS_METHOD_LEARN = 'learn'
SERVICE_PARAM_PLAT_MTCE_BMC_ACCESS_METHOD_REDFISH = 'redfish'
SERVICE_PARAM_PLAT_MTCE_BMC_ACCESS_METHOD_IPMI = 'ipmi'
PLATFORM_MTCE_PARAMETER_VALIDATOR = {
@ -414,8 +391,6 @@ PLATFORM_MTCE_PARAMETER_VALIDATOR = {
_validate_mnfa_threshold,
constants.SERVICE_PARAM_PLAT_MTCE_MNFA_TIMEOUT:
_validate_mnfa_timeout,
constants.SERVICE_PARAM_PLAT_MTCE_BMC_ACCESS_METHOD:
_validate_bmc_access_method,
}
PLATFORM_MTCE_PARAMETER_RESOURCE = {
@ -427,7 +402,6 @@ PLATFORM_MTCE_PARAMETER_RESOURCE = {
constants.SERVICE_PARAM_PLAT_MTCE_HBS_DEGRADE_THRESHOLD: 'platform::mtce::params::heartbeat_degrade_threshold',
constants.SERVICE_PARAM_PLAT_MTCE_MNFA_THRESHOLD: 'platform::mtce::params::mnfa_threshold',
constants.SERVICE_PARAM_PLAT_MTCE_MNFA_TIMEOUT: 'platform::mtce::params::mnfa_timeout',
constants.SERVICE_PARAM_PLAT_MTCE_BMC_ACCESS_METHOD: 'platform::mtce::params::bmc_access_method',
}
RADOSGW_CONFIG_PARAMETER_MANDATORY = [

View File

@ -485,11 +485,6 @@ class ConductorManager(service.PeriodicService):
'name': constants.SERVICE_PARAM_PLAT_MTCE_MNFA_TIMEOUT,
'value': constants.SERVICE_PARAM_PLAT_MTCE_MNFA_TIMEOUT_DEFAULT,
},
{'service': constants.SERVICE_TYPE_PLATFORM,
'section': constants.SERVICE_PARAM_SECTION_PLATFORM_MAINTENANCE,
'name': constants.SERVICE_PARAM_PLAT_MTCE_BMC_ACCESS_METHOD,
'value': constants.SERVICE_PARAM_PLAT_MTCE_BMC_ACCESS_METHOD_DEFAULT,
},
{'service': constants.SERVICE_TYPE_RADOSGW,
'section': constants.SERVICE_PARAM_SECTION_RADOSGW_CONFIG,
'name': constants.SERVICE_PARAM_NAME_RADOSGW_SERVICE_ENABLED,

View File

@ -36,6 +36,7 @@ class FakeConductorAPI(object):
self.evaluate_app_reapply = mock.MagicMock()
self.update_clock_synchronization_config = mock.MagicMock()
self.store_default_config = mock.MagicMock()
self.create_barbican_secret = mock.MagicMock()
def create_ihost(self, context, values):
# Create the host in the DB as the code under test expects this
@ -574,6 +575,45 @@ class TestPatch(TestHost):
self.assertEqual(constants.AVAILABILITY_ONLINE, ihost.availability)
self.assertEqual(constants.PROVISIONED, ihost.invprovision)
def test_update_host_bm_valid(self):
# Create controller-0, provisioned
c0_host = self._create_controller_0(
invprovision=constants.PROVISIONED,
administrative=constants.ADMIN_UNLOCKED,
operational=constants.OPERATIONAL_DISABLED,
availability=constants.AVAILABILITY_OFFLINE)
self._create_test_host_platform_interface(c0_host)
bm_ip = '128.224.141.222'
bm_username = 'root'
for bm_type in constants.HOST_BM_VALID_PROVISIONED_TYPE_LIST:
bm_password = 'password' + bm_type
response = self._patch_host(c0_host['hostname'],
[{'path': '/bm_type',
'value': bm_type,
'op': 'replace'},
{'path': '/bm_ip',
'value': bm_ip,
'op': 'replace'},
{'path': '/bm_username',
'value': bm_username,
'op': 'replace'},
{'path': '/bm_password',
'value': bm_password,
'op': 'replace'}],
'')
self.assertEqual(response.content_type, 'application/json')
self.assertEqual(response.status_code, http_client.OK)
ihost = self._get_test_host_by_hostname(c0_host['hostname'])
self.assertEqual(bm_type, ihost.bm_type)
self.assertEqual(bm_ip, ihost.bm_ip)
self.assertEqual(bm_username, ihost.bm_username)
self.fake_conductor_api.create_barbican_secret.assert_called_with(
mock.ANY, ihost.uuid, bm_password)
def test_unlock_action_controller(self):
# Create controller-0
c0_host = self._create_controller_0(

View File

@ -134,7 +134,7 @@ def get_test_ihost(**kw):
'serialid': kw.get('serialid', 'sysinv123456'),
'bm_ip': kw.get('bm_ip', "128.224.150.193"),
'bm_mac': kw.get('bm_mac', "a4:5d:36:fc:a5:6c"),
'bm_type': kw.get('bm_type', constants.BM_TYPE_GENERIC),
'bm_type': kw.get('bm_type', constants.HOST_BM_TYPE_DEPROVISIONED),
'bm_username': kw.get('bm_username', "ihostbmusername"),
'action': kw.get('action', "none"),
'task': kw.get('task', None),