Implement backup/restore bios config functionality using eLCM
This implements backup/restore functions for bios config using eLCM api. Related-Bug: #1639688 Change-Id: I8d4bd0d7508873e892b010597f87ca76b045ad93
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
# Copyright 2015 FUJITSU LIMITED
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from . import elcm # noqa
|
||||
from . import scci # noqa
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
eLCM functionality.
|
||||
"""
|
||||
|
||||
import time
|
||||
|
||||
from oslo_serialization import jsonutils
|
||||
import requests
|
||||
|
||||
@@ -46,6 +48,7 @@ Timeout values
|
||||
"""
|
||||
PROFILE_CREATE_TIMEOUT = 300 # 300 secs
|
||||
PROFILE_SET_TIMEOUT = 300 # 300 secs
|
||||
BIOS_CONFIG_SESSION_TIMEOUT = 30 * 60 # 30 mins
|
||||
|
||||
|
||||
class ELCMInvalidResponse(scci.SCCIError):
|
||||
@@ -66,6 +69,12 @@ class ELCMSessionNotFound(scci.SCCIError):
|
||||
super(ELCMSessionNotFound, self).__init__(message)
|
||||
|
||||
|
||||
class ELCMSessionTimeout(scci.SCCIError):
|
||||
"""ELCMSessionTimeout"""
|
||||
def __init__(self, message):
|
||||
super(ELCMSessionTimeout, self).__init__(message)
|
||||
|
||||
|
||||
def _parse_elcm_response_body_as_json(response):
|
||||
"""parse eLCM response body as json data
|
||||
|
||||
@@ -495,3 +504,175 @@ def elcm_session_delete(irmc_info, session_id, terminate=False):
|
||||
'"%(session)s" with error code %(error)s' %
|
||||
{'session': session_id,
|
||||
'error': resp.status_code}))
|
||||
|
||||
|
||||
def _process_session_bios_config(irmc_info, operation, session_id,
|
||||
session_timeout=BIOS_CONFIG_SESSION_TIMEOUT):
|
||||
"""process session for Bios config backup/restore operation
|
||||
|
||||
:param irmc_info: node info
|
||||
:param operation: one of 'BACKUP' and 'RESTORE'
|
||||
:param session_id: session id
|
||||
:param session_timeout: session timeout
|
||||
:return: a dict with following values:
|
||||
{
|
||||
'bios_config': <data in case of BACKUP operation>,
|
||||
'warning': <warning message if there is>
|
||||
}
|
||||
"""
|
||||
session_expiration = time.time() + session_timeout
|
||||
|
||||
while time.time() < session_expiration:
|
||||
# Get session status to check
|
||||
session = elcm_session_get_status(irmc_info=irmc_info,
|
||||
session_id=session_id)
|
||||
|
||||
status = session['Session']['Status']
|
||||
if status == 'running' or status == 'activated':
|
||||
# Sleep a bit
|
||||
time.sleep(5)
|
||||
elif status == 'terminated regularly':
|
||||
result = {}
|
||||
|
||||
if operation == 'BACKUP':
|
||||
# Bios profile is created, get the data now
|
||||
result['bios_config'] = elcm_profile_get(
|
||||
irmc_info=irmc_info,
|
||||
profile_name=PROFILE_BIOS_CONFIG)
|
||||
elif operation == 'RESTORE':
|
||||
# Bios config applied successfully
|
||||
pass
|
||||
|
||||
# Cleanup operation by deleting related session and profile.
|
||||
# In case of error, report it as warning instead of error.
|
||||
try:
|
||||
elcm_session_delete(irmc_info=irmc_info,
|
||||
session_id=session_id,
|
||||
terminate=True)
|
||||
elcm_profile_delete(irmc_info=irmc_info,
|
||||
profile_name=PROFILE_BIOS_CONFIG)
|
||||
except scci.SCCIError as e:
|
||||
result['warning'] = e
|
||||
|
||||
return result
|
||||
else:
|
||||
# Error occurred, get session log to see what happened
|
||||
session_log = elcm_session_get_log(irmc_info=irmc_info,
|
||||
session_id=session_id)
|
||||
|
||||
raise scci.SCCIClientError(
|
||||
('Failed to %(operation)s bios config. '
|
||||
'Session log is "%(session_log)s".' %
|
||||
{'operation': operation,
|
||||
'session_log': jsonutils.dumps(session_log)}))
|
||||
|
||||
else:
|
||||
raise ELCMSessionTimeout(
|
||||
('Failed to %(operation)s bios config. '
|
||||
'Session %(session_id)s log is timeout.' %
|
||||
{'operation': operation,
|
||||
'session_id': session_id}))
|
||||
|
||||
|
||||
def backup_bios_config(irmc_info):
|
||||
"""backup current bios configuration
|
||||
|
||||
This function sends a BACKUP request to the server. Then when the bios
|
||||
config data are ready for retrieving, it will returns the data to the
|
||||
caller. Note that this operation may take time.
|
||||
|
||||
:param irmc_info: node info
|
||||
:return: a dict with following values:
|
||||
{
|
||||
'bios_config': <bios config data>,
|
||||
'warning': <warning message if there is>
|
||||
}
|
||||
"""
|
||||
# 1. Make sure there is no BiosConfig profile in the store
|
||||
try:
|
||||
# Get the profile first, if not found, then an exception
|
||||
# will be raised.
|
||||
elcm_profile_get(irmc_info=irmc_info,
|
||||
profile_name=PROFILE_BIOS_CONFIG)
|
||||
# Profile found, delete it
|
||||
elcm_profile_delete(irmc_info=irmc_info,
|
||||
profile_name=PROFILE_BIOS_CONFIG)
|
||||
except ELCMProfileNotFound:
|
||||
# Ignore this error as it's not an error in this case
|
||||
pass
|
||||
|
||||
# 2. Send request to create a new profile for BiosConfig
|
||||
session = elcm_profile_create(irmc_info=irmc_info,
|
||||
param_path=PARAM_PATH_BIOS_CONFIG)
|
||||
|
||||
# 3. Profile creation is in progress, we monitor the session
|
||||
session_timeout = irmc_info.get('irmc_bios_session_timeout',
|
||||
BIOS_CONFIG_SESSION_TIMEOUT)
|
||||
return _process_session_bios_config(
|
||||
irmc_info=irmc_info,
|
||||
operation='BACKUP',
|
||||
session_id=session['Session']['Id'],
|
||||
session_timeout=session_timeout)
|
||||
|
||||
|
||||
def restore_bios_config(irmc_info, bios_config):
|
||||
"""restore bios configuration
|
||||
|
||||
This function sends a RESTORE request to the server. Then when the bios
|
||||
is ready for restoring, it will apply the provided settings and return.
|
||||
Note that this operation may take time.
|
||||
|
||||
:param irmc_info: node info
|
||||
:param bios_config: bios config
|
||||
"""
|
||||
def _process_bios_config():
|
||||
try:
|
||||
if isinstance(bios_config, dict):
|
||||
input_data = bios_config
|
||||
else:
|
||||
input_data = jsonutils.loads(bios_config)
|
||||
|
||||
# The input data must contain flag "@Processing":"execute" in the
|
||||
# equivalent section.
|
||||
bios_cfg = input_data['Server']['SystemConfig']['BiosConfig']
|
||||
bios_cfg['@Processing'] = 'execute'
|
||||
|
||||
# NOTE: It seems without 2 sub profiles IrmcConfig and
|
||||
# OSInstallation present in the input_data, the process will fail.
|
||||
# The info for this error can be found in the session log.
|
||||
# Work-around: add 2 sub profiles with empty content.
|
||||
input_data['Server']['SystemConfig']['IrmcConfig'] = {}
|
||||
input_data['Server']['SystemConfig']['OSInstallation'] = {}
|
||||
|
||||
return input_data
|
||||
except (TypeError, ValueError, KeyError):
|
||||
raise scci.SCCIInvalidInputError(
|
||||
('Invalid input bios config "%s".' % bios_config))
|
||||
|
||||
# 1. Parse the bios config and create the input data
|
||||
input_data = _process_bios_config()
|
||||
|
||||
# 2. Make sure there is no BiosConfig profile in the store
|
||||
try:
|
||||
# Get the profile first, if not found, then an exception
|
||||
# will be raised.
|
||||
elcm_profile_get(irmc_info=irmc_info,
|
||||
profile_name=PROFILE_BIOS_CONFIG)
|
||||
# Profile found, delete it
|
||||
elcm_profile_delete(irmc_info=irmc_info,
|
||||
profile_name=PROFILE_BIOS_CONFIG)
|
||||
except ELCMProfileNotFound:
|
||||
# Ignore this error as it's not an error in this case
|
||||
pass
|
||||
|
||||
# 3. Send a request to apply the param values
|
||||
session = elcm_profile_set(irmc_info=irmc_info,
|
||||
input_data=input_data)
|
||||
|
||||
# 4. Param values applying is in progress, we monitor the session
|
||||
session_timeout = irmc_info.get('irmc_bios_session_timeout',
|
||||
BIOS_CONFIG_SESSION_TIMEOUT)
|
||||
_process_session_bios_config(irmc_info=irmc_info,
|
||||
operation='RESTORE',
|
||||
session_id=session['Session']['Id'],
|
||||
session_timeout=session_timeout)
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
Test class for iRMC eLCM functionality.
|
||||
"""
|
||||
|
||||
import mock
|
||||
from oslo_utils import encodeutils
|
||||
import requests
|
||||
from requests_mock.contrib import fixture as rm_fixture
|
||||
@@ -684,3 +685,247 @@ class ELCMTestCase(testtools.TestCase):
|
||||
session_id=session_id)
|
||||
|
||||
self.assertIsNone(result)
|
||||
|
||||
@mock.patch.object(elcm, 'elcm_profile_delete')
|
||||
@mock.patch.object(elcm, 'elcm_profile_get')
|
||||
@mock.patch.object(elcm, 'elcm_session_delete')
|
||||
@mock.patch.object(elcm, 'elcm_session_get_status')
|
||||
@mock.patch.object(elcm.time, 'sleep')
|
||||
def test__process_session_bios_config_get_ok(self, mock_sleep,
|
||||
mock_session_get,
|
||||
mock_session_delete,
|
||||
mock_profile_get,
|
||||
mock_profile_delete):
|
||||
session_id = 123
|
||||
expected_bios_cfg = {
|
||||
'Server': {
|
||||
'SystemConfig': {
|
||||
'BiosConfig': {
|
||||
'key1': 'val1'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mock_session_get.side_effect = [
|
||||
{'Session': {'Id': session_id,
|
||||
'Status': 'activated'}},
|
||||
{'Session': {'Id': session_id,
|
||||
'Status': 'running'}},
|
||||
{'Session': {'Id': session_id,
|
||||
'Status': 'terminated regularly'}}]
|
||||
mock_profile_get.return_value = expected_bios_cfg
|
||||
|
||||
result = elcm._process_session_bios_config(irmc_info=self.irmc_info,
|
||||
operation='BACKUP',
|
||||
session_id=session_id)
|
||||
self.assertEqual(expected_bios_cfg, result['bios_config'])
|
||||
|
||||
mock_session_get.assert_has_calls([
|
||||
mock.call(irmc_info=self.irmc_info, session_id=session_id),
|
||||
mock.call(irmc_info=self.irmc_info, session_id=session_id),
|
||||
mock.call(irmc_info=self.irmc_info, session_id=session_id)])
|
||||
mock_profile_get.assert_called_once_with(
|
||||
irmc_info=self.irmc_info,
|
||||
profile_name=elcm.PROFILE_BIOS_CONFIG)
|
||||
|
||||
self.assertEqual(2, mock_sleep.call_count)
|
||||
self.assertEqual(1, mock_session_delete.call_count)
|
||||
self.assertEqual(1, mock_profile_delete.call_count)
|
||||
|
||||
@mock.patch.object(elcm, 'elcm_profile_delete')
|
||||
@mock.patch.object(elcm, 'elcm_profile_get')
|
||||
@mock.patch.object(elcm, 'elcm_session_delete')
|
||||
@mock.patch.object(elcm, 'elcm_session_get_status')
|
||||
@mock.patch.object(elcm.time, 'sleep')
|
||||
def test__process_session_bios_config_set_ok(self, mock_sleep,
|
||||
mock_session_get,
|
||||
mock_session_delete,
|
||||
mock_profile_get,
|
||||
mock_profile_delete):
|
||||
session_id = 123
|
||||
mock_session_get.side_effect = [
|
||||
{'Session': {'Id': session_id,
|
||||
'Status': 'activated'}},
|
||||
{'Session': {'Id': session_id,
|
||||
'Status': 'running'}},
|
||||
{'Session': {'Id': session_id,
|
||||
'Status': 'terminated regularly'}}]
|
||||
|
||||
elcm._process_session_bios_config(irmc_info=self.irmc_info,
|
||||
operation='RESTORE',
|
||||
session_id=session_id)
|
||||
|
||||
mock_session_get.assert_has_calls([
|
||||
mock.call(irmc_info=self.irmc_info, session_id=session_id),
|
||||
mock.call(irmc_info=self.irmc_info, session_id=session_id),
|
||||
mock.call(irmc_info=self.irmc_info, session_id=session_id)])
|
||||
mock_profile_get.assert_not_called()
|
||||
self.assertEqual(2, mock_sleep.call_count)
|
||||
self.assertEqual(1, mock_session_delete.call_count)
|
||||
self.assertEqual(1, mock_profile_delete.call_count)
|
||||
|
||||
@mock.patch.object(elcm, 'elcm_profile_delete')
|
||||
@mock.patch.object(elcm, 'elcm_profile_get')
|
||||
@mock.patch.object(elcm, 'elcm_session_delete')
|
||||
@mock.patch.object(elcm, 'elcm_session_get_status')
|
||||
@mock.patch.object(elcm.time, 'sleep')
|
||||
def test__process_session_bios_config_timeout(self, mock_sleep,
|
||||
mock_session_get,
|
||||
mock_session_delete,
|
||||
mock_profile_get,
|
||||
mock_profile_delete):
|
||||
session_id = 123
|
||||
mock_session_get.return_value = {'Session': {'Id': session_id,
|
||||
'Status': 'running'}}
|
||||
|
||||
self.assertRaises(elcm.ELCMSessionTimeout,
|
||||
elcm._process_session_bios_config,
|
||||
irmc_info=self.irmc_info,
|
||||
operation='BACKUP',
|
||||
session_id=session_id,
|
||||
session_timeout=0.5)
|
||||
|
||||
self.assertEqual(True, mock_sleep.called)
|
||||
self.assertEqual(True, mock_session_get.called)
|
||||
mock_profile_get.assert_not_called()
|
||||
mock_session_delete.assert_not_called()
|
||||
mock_profile_delete.assert_not_called()
|
||||
|
||||
@mock.patch.object(elcm, 'elcm_profile_delete')
|
||||
@mock.patch.object(elcm, 'elcm_session_delete')
|
||||
@mock.patch.object(elcm, 'elcm_session_get_log')
|
||||
@mock.patch.object(elcm, 'elcm_session_get_status')
|
||||
def test__process_session_bios_config_error(self,
|
||||
mock_session_get,
|
||||
mock_session_get_log,
|
||||
mock_session_delete,
|
||||
mock_profile_delete):
|
||||
session_id = 123
|
||||
mock_session_get.return_value = {'Session': {'Id': session_id,
|
||||
'Status': 'error'}}
|
||||
|
||||
self.assertRaises(scci.SCCIClientError,
|
||||
elcm._process_session_bios_config,
|
||||
irmc_info=self.irmc_info,
|
||||
operation='RESTORE',
|
||||
session_id=session_id,
|
||||
session_timeout=0.5)
|
||||
|
||||
self.assertEqual(True, mock_session_get.called)
|
||||
self.assertEqual(True, mock_session_get_log.called)
|
||||
mock_session_delete.assert_not_called()
|
||||
mock_profile_delete.assert_not_called()
|
||||
|
||||
@mock.patch.object(elcm, 'elcm_profile_delete')
|
||||
@mock.patch.object(elcm, 'elcm_profile_create')
|
||||
@mock.patch.object(elcm, 'elcm_profile_get')
|
||||
@mock.patch.object(elcm, 'elcm_session_delete')
|
||||
@mock.patch.object(elcm, 'elcm_session_get_status')
|
||||
@mock.patch.object(elcm.time, 'sleep')
|
||||
def test_backup_bios_config_ok(self, mock_sleep, mock_session_get,
|
||||
mock_session_delete, mock_profile_get,
|
||||
mock_profile_create, mock_profile_delete):
|
||||
session_id = 123
|
||||
expected_bios_cfg = {
|
||||
'Server': {
|
||||
'SystemConfig': {
|
||||
'BiosConfig': {
|
||||
'key1': 'val1'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mock_session_get.side_effect = [
|
||||
{'Session': {'Id': session_id,
|
||||
'Status': 'activated'}},
|
||||
{'Session': {'Id': session_id,
|
||||
'Status': 'running'}},
|
||||
{'Session': {'Id': session_id,
|
||||
'Status': 'terminated regularly'}}]
|
||||
mock_profile_get.return_value = expected_bios_cfg
|
||||
mock_profile_create.return_value = {'Session': {'Id': session_id,
|
||||
'Status': 'activated'}}
|
||||
|
||||
result = elcm.backup_bios_config(irmc_info=self.irmc_info)
|
||||
self.assertEqual(expected_bios_cfg, result['bios_config'])
|
||||
|
||||
self.assertEqual(2, mock_sleep.call_count)
|
||||
self.assertEqual(True, mock_session_get.called)
|
||||
self.assertEqual(1, mock_session_delete.call_count)
|
||||
self.assertEqual(2, mock_profile_get.call_count)
|
||||
self.assertEqual(1, mock_profile_create.call_count)
|
||||
self.assertEqual(2, mock_profile_delete.call_count)
|
||||
|
||||
@mock.patch.object(elcm, 'elcm_profile_delete')
|
||||
@mock.patch.object(elcm, 'elcm_profile_get')
|
||||
@mock.patch.object(elcm, 'elcm_profile_set')
|
||||
@mock.patch.object(elcm, 'elcm_session_delete')
|
||||
@mock.patch.object(elcm, 'elcm_session_get_status')
|
||||
@mock.patch.object(elcm.time, 'sleep')
|
||||
def _test_restore_bios_config_ok(self, mock_sleep, mock_session_get,
|
||||
mock_session_delete, mock_profile_set,
|
||||
mock_profile_get, mock_profile_delete,
|
||||
bios_cfg):
|
||||
session_id = 123
|
||||
mock_session_get.side_effect = [
|
||||
{'Session': {'Id': session_id,
|
||||
'Status': 'activated'}},
|
||||
{'Session': {'Id': session_id,
|
||||
'Status': 'running'}},
|
||||
{'Session': {'Id': session_id,
|
||||
'Status': 'terminated regularly'}}]
|
||||
mock_profile_set.return_value = {'Session': {'Id': session_id,
|
||||
'Status': 'activated'}}
|
||||
|
||||
elcm.restore_bios_config(irmc_info=self.irmc_info,
|
||||
bios_config=bios_cfg)
|
||||
|
||||
self.assertEqual(2, mock_sleep.call_count)
|
||||
self.assertEqual(True, mock_session_get.called)
|
||||
self.assertEqual(1, mock_session_delete.call_count)
|
||||
self.assertEqual(1, mock_profile_get.call_count)
|
||||
self.assertEqual(1, mock_profile_set.call_count)
|
||||
self.assertEqual(2, mock_profile_delete.call_count)
|
||||
|
||||
def test_restore_bios_config_ok_with_dict(self):
|
||||
bios_cfg = {
|
||||
'Server': {
|
||||
'SystemConfig': {
|
||||
'BiosConfig': {
|
||||
'key1': 'val1'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self._test_restore_bios_config_ok(bios_cfg=bios_cfg)
|
||||
|
||||
def test_restore_bios_config_ok_with_str(self):
|
||||
bios_cfg = ('{"Server":'
|
||||
' {"SystemConfig":'
|
||||
' {"BiosConfig":'
|
||||
' {'
|
||||
' "key1": "val1"'
|
||||
' }'
|
||||
' }'
|
||||
' }'
|
||||
'}')
|
||||
self._test_restore_bios_config_ok(bios_cfg=bios_cfg)
|
||||
|
||||
def _test_restore_bios_config_invalid_input(self, bios_cfg):
|
||||
self.assertRaises(scci.SCCIInvalidInputError,
|
||||
elcm.restore_bios_config,
|
||||
irmc_info=self.irmc_info,
|
||||
bios_config=bios_cfg)
|
||||
|
||||
def test_restore_bios_config_invalid_input_dict(self):
|
||||
bios_cfg = {
|
||||
'Server': {
|
||||
'SystemConfig': {
|
||||
}
|
||||
}
|
||||
}
|
||||
self._test_restore_bios_config_invalid_input(bios_cfg=bios_cfg)
|
||||
|
||||
def test_restore_bios_config_invalid_input_str(self):
|
||||
bios_cfg = '{"key": "val"}'
|
||||
self._test_restore_bios_config_invalid_input(bios_cfg=bios_cfg)
|
||||
|
||||
Reference in New Issue
Block a user