Merge "Redfish: Adds 'reset_ilo_credential' for redfish systems"

This commit is contained in:
Jenkins 2017-07-19 03:34:36 +00:00 committed by Gerrit Code Review
commit c2523811e4
14 changed files with 419 additions and 0 deletions

View File

@ -76,6 +76,7 @@ SUPPORTED_REDFISH_METHODS = [
'set_one_time_boot',
'update_persistent_boot',
'set_pending_boot_mode',
'reset_ilo_credential',
]
LOG = log.get_logger(__name__)

View File

@ -16,6 +16,7 @@ __author__ = 'HPE'
import sushy
from proliantutils.redfish.resources.account_service import account_service
from proliantutils.redfish.resources.manager import manager
from proliantutils.redfish.resources.system import system
from proliantutils.redfish.resources import update_service
@ -63,3 +64,12 @@ class HPESushy(sushy.Sushy):
return (update_service.
HPEUpdateService(self._conn, update_service_url,
redfish_version=self.redfish_version))
def get_account_service(self):
"""Return a HPEAccountService object"""
account_service_url = utils.get_subresource_path_by(self,
'AccountService')
return (account_service.
HPEAccountService(self._conn, account_service_url,
redfish_version=self.redfish_version))

View File

@ -117,6 +117,7 @@ class RedfishOperations(operations.IloOperations):
# for error reporting purpose
self.host = redfish_controller_ip
self._root_prefix = root_prefix
self._username = username
try:
self._sushy = main.HPESushy(
@ -570,3 +571,25 @@ class RedfishOperations(operations.IloOperations):
{'device': device, 'error': str(e)})
LOG.debug(msg)
raise exception.IloError(msg)
def reset_ilo_credential(self, password):
"""Resets the iLO password.
:param password: The password to be set.
:raises: IloError, if account not found or on an error from iLO.
"""
try:
acc_service = self._sushy.get_account_service()
member = acc_service.accounts.get_member_details(self._username)
if member is None:
msg = (self._("No account found with username: %s")
% self._username)
LOG.debug(msg)
raise exception.IloError(msg)
member.update_credentials(password)
except sushy.exceptions.SushyError as e:
msg = (self._('The Redfish controller failed to update '
'credentials for %(username)s. Error %(error)s') %
{'username': self._username, 'error': str(e)})
LOG.debug(msg)
raise exception.IloError(msg)

View File

@ -0,0 +1,50 @@
# Copyright 2017 Hewlett Packard Enterprise Development LP
#
# 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.
__author__ = 'HPE'
from sushy.resources import base
class HPEAccount(base.ResourceBase):
username = base.Field('UserName')
def update_credentials(self, password):
"""Update credentials of a redfish system
:param password: password to be updated
"""
data = {
'Password': password,
}
self._conn.patch(self.path, data=data)
class HPEAccountCollection(base.ResourceCollectionBase):
@property
def _resource_type(self):
return HPEAccount
def get_member_details(self, username):
"""Returns the HPEAccount object
:param username: username of account
:returns: HPEAccount object if criterion matches, None otherwise
"""
members = self.get_members()
for member in members:
if member.username == username:
return member

View File

@ -0,0 +1,44 @@
# Copyright 2017 Hewlett Packard Enterprise Development LP
#
# 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 sushy.resources import base
from proliantutils.redfish.resources.account_service import account
from proliantutils.redfish import utils
class HPEAccountService(base.ResourceBase):
"""Class that extends the functionality of AccountService resource class
This class extends the functionality of Account resource class
from sushy
"""
_accounts = None
@property
def accounts(self):
"""Property to provide instance of HPEAccountCollection
"""
if self._accounts is None:
self._accounts = account.HPEAccountCollection(
self._conn, utils.get_subresource_path_by(self, 'Accounts'),
redfish_version=self.redfish_version)
return self._accounts
def refresh(self):
super(HPEAccountService, self).refresh()
self._accounts = None

View File

@ -0,0 +1,31 @@
{
"@odata.context": "/redfish/v1/$metadata#AccountService/Accounts/Members/$entity",
"@odata.etag": "W/\"BA0D308D\"",
"@odata.id": "/redfish/v1/AccountService/Accounts/1/",
"@odata.type": "#ManagerAccount.v1_0_0.ManagerAccount",
"Description": "iLO User Account",
"Id": "1",
"Name": "User Account",
"Oem":
{
"Hpe":
{
"@odata.type": "#HpeiLOAccount.v2_0_0.HpeiLOAccount",
"LoginName": "foo",
"Privileges":
{
"HostBIOSConfigPriv": true,
"HostNICConfigPriv": true,
"HostStorageConfigPriv": true,
"LoginPriv": true,
"RemoteConsolePriv": true,
"SystemRecoveryConfigPriv": true,
"UserConfigPriv": true,
"VirtualMediaPriv": true,
"VirtualPowerAndResetPriv": true,
"iLOConfigPriv": true
}
}
},
"UserName": "foo"
}

View File

@ -0,0 +1,15 @@
{
"@odata.context": "/redfish/v1/$metadata#Accounts",
"@odata.etag": "W/\"72D11D4D\"",
"@odata.id": "/redfish/v1/AccountService/Accounts/",
"@odata.type": "#ManagerAccountCollection.ManagerAccountCollection",
"Description": "iLO User Accounts",
"Members":
[
{
"@odata.id": "/redfish/v1/AccountService/Accounts/1/"
}
],
"Members@odata.count": 1,
"Name": "Accounts"
}

View File

@ -0,0 +1,24 @@
{
"@odata.context": "/redfish/v1/$metadata#AccountService",
"@odata.etag": "W/\"677A002B\"",
"@odata.id": "/redfish/v1/AccountService/",
"@odata.type": "#AccountService.v1_0_2.AccountService",
"Accounts":
{
"@odata.id": "/redfish/v1/AccountService/Accounts/"
},
"Description": "iLO User Accounts",
"Id": "AccountService",
"Name": "Account Service",
"Oem":
{
"Hpe":
{
"@odata.type": "#HpeiLOAccountService.v2_0_0.HpeiLOAccountService",
"AuthFailureDelayTimeSeconds": 10,
"AuthFailureLoggingThreshold": 3,
"AuthFailuresBeforeDelay": 1,
"MinPasswordLength": 8
}
}
}

View File

@ -0,0 +1,72 @@
# Copyright 2017 Hewlett Packard Enterprise Development LP
#
# 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.
__author__ = 'HPE'
import json
import mock
import testtools
from proliantutils.redfish.resources.account_service import account
class HPEAccountCollectionTestCase(testtools.TestCase):
def setUp(self):
super(HPEAccountCollectionTestCase, self).setUp()
self.conn = mock.MagicMock()
with open('proliantutils/tests/redfish/'
'json_samples/account_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = (
json.loads(f.read()))
self.account_coll_obj = account.HPEAccountCollection(
self.conn, '/redfish/v1/AccountService/Accounts',
redfish_version='1.0.2')
def test_get_member_details(self):
with open('proliantutils/tests/redfish/'
'json_samples/account.json', 'r') as f:
self.conn.get.return_value.json.return_value = (
json.loads(f.read()))
obj = self.account_coll_obj.get_member_details('foo')
self.assertIsInstance(obj, account.HPEAccount)
self.assertIsNone(self.account_coll_obj.get_member_details('bar'))
class HPEAccountTestCase(testtools.TestCase):
def setUp(self):
super(HPEAccountTestCase, self).setUp()
self.conn = mock.MagicMock()
with open('proliantutils/tests/redfish/'
'json_samples/account.json', 'r') as f:
account_json = json.loads(f.read())
self.conn.get.return_value.json.return_value = account_json
self.acc_inst = account.HPEAccount(
self.conn, '/redfish/v1/AccountService/Accounts/1',
redfish_version='1.0.2')
def test_attributes(self):
self.assertEqual('foo', self.acc_inst.username)
def test_update_credentials(self):
target_uri = '/redfish/v1/AccountService/Accounts/1'
self.acc_inst.update_credentials('fake-password')
self.acc_inst._conn.patch.assert_called_once_with(
target_uri, data={'Password': 'fake-password'})

View File

@ -0,0 +1,67 @@
# Copyright 2017 Hewlett Packard Enterprise Development LP
#
# 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.
__author__ = 'HPE'
import json
import mock
import testtools
from proliantutils.redfish.resources.account_service import account
from proliantutils.redfish.resources.account_service import account_service
class HPEAccountServiceTestCase(testtools.TestCase):
def setUp(self):
super(HPEAccountServiceTestCase, self).setUp()
self.conn = mock.MagicMock()
with open('proliantutils/tests/redfish/'
'json_samples/account_service.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.acc_inst = account_service.HPEAccountService(
self.conn, '/redfish/v1/AccountService',
redfish_version='1.0.2')
def test_accounts(self):
self.assertIsNone(self.acc_inst._accounts)
self.conn.get.return_value.json.reset_mock()
with open('proliantutils/tests/redfish/'
'json_samples/account_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
accounts = self.acc_inst.accounts
self.assertIsInstance(accounts, account.HPEAccountCollection)
def test_accounts_on_refresh(self):
with open('proliantutils/tests/redfish/'
'json_samples/account_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
accounts = self.acc_inst.accounts
self.assertIsInstance(accounts, account.HPEAccountCollection)
with open('proliantutils/tests/redfish/'
'json_samples/account_service.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.acc_inst.refresh()
self.assertIsNone(self.acc_inst._accounts)
with open('proliantutils/tests/redfish/'
'json_samples/account_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.assertIsInstance(accounts, account.HPEAccountCollection)

View File

@ -21,6 +21,7 @@ import testtools
from proliantutils import exception
from proliantutils.redfish import main
from proliantutils.redfish.resources.account_service import account_service
from proliantutils.redfish.resources.manager import manager
from proliantutils.redfish.resources.system import system
from proliantutils.redfish.resources import update_service
@ -88,3 +89,12 @@ class HPESushyTestCase(testtools.TestCase):
mock_update_service.assert_called_once_with(
self.hpe_sushy._conn, "/redfish/v1/UpdateService/",
self.hpe_sushy.redfish_version)
@mock.patch.object(account_service, 'HPEAccountService', autospec=True)
def test_get_account_service(self, mock_account_service):
acc_inst = self.hpe_sushy.get_account_service()
self.assertIsInstance(acc_inst,
account_service.HPEAccountService.__class__)
mock_account_service.assert_called_once_with(
self.hpe_sushy._conn, "/redfish/v1/AccountService/",
self.hpe_sushy.redfish_version)

View File

@ -22,6 +22,8 @@ import testtools
from proliantutils import exception
from proliantutils.redfish import main
from proliantutils.redfish import redfish
from proliantutils.redfish.resources.account_service import account
from proliantutils.redfish.resources.account_service import account_service
from proliantutils.redfish.resources.manager import manager
from proliantutils.redfish.resources.manager import virtual_media
from proliantutils.redfish.resources.system import constants as sys_cons
@ -573,3 +575,73 @@ class RedfishOperationsTestCase(testtools.TestCase):
'The Redfish controller failed to set one time boot.',
self.rf_client.set_one_time_boot,
'CDROM')
def _setup_reset_ilo_credential(self):
self.conn = mock.Mock()
with open('proliantutils/tests/redfish/'
'json_samples/account_service.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
account_mock = account_service.HPEAccountService(
self.conn, '/redfish/v1/AccountService',
redfish_version='1.0.2')
with open('proliantutils/tests/redfish/'
'json_samples/account_collection.json', 'r') as f:
account_collection_json = json.loads(f.read())
with open('proliantutils/tests/redfish/'
'json_samples/account.json', 'r') as f:
account_json = json.loads(f.read())
return account_mock, account_collection_json, account_json
@mock.patch.object(main.HPESushy, 'get_account_service')
def test_reset_ilo_credential(self, account_mock):
account_mock.return_value, account_collection_json, account_json = (
self._setup_reset_ilo_credential())
self.conn.get.return_value.json.side_effect = [
account_collection_json, account_json]
self.rf_client.reset_ilo_credential('fake-password')
(self.sushy.get_account_service.return_value.
accounts.get_member_details.return_value.
update_credentials.assert_called_once_with('fake-password'))
@mock.patch.object(main.HPESushy, 'get_account_service')
def test_reset_ilo_credential_fail(self, account_mock):
account_mock.return_value, account_collection_json, account_json = (
self._setup_reset_ilo_credential())
self.conn.get.return_value.json.side_effect = [
account_collection_json, account_json]
(self.sushy.get_account_service.return_value.accounts.
get_member_details.return_value.
update_credentials.side_effect) = sushy.exceptions.SushyError
self.assertRaisesRegex(
exception.IloError,
'The Redfish controller failed to update credentials',
self.rf_client.reset_ilo_credential, 'fake-password')
@mock.patch.object(account.HPEAccount, 'update_credentials')
def test_reset_ilo_credential_get_account_service_fail(self, update_mock):
account_service_not_found_error = sushy.exceptions.SushyError
account_service_not_found_error.message = (
'HPEAccountService not found!!')
self.sushy.get_account_service.side_effect = (
account_service_not_found_error)
self.assertRaisesRegex(
exception.IloError,
'The Redfish controller failed to update credentials for foo. '
'Error HPEAccountService not found!!',
self.rf_client.reset_ilo_credential, 'fake-password')
self.assertFalse(update_mock.called)
@mock.patch.object(main.HPESushy, 'get_account_service')
def test_reset_ilo_credential_no_member(self, account_mock):
(self.sushy.get_account_service.return_value.accounts.
get_member_details.return_value) = None
self.assertRaisesRegex(
exception.IloError,
'No account found with username: foo',
self.rf_client.reset_ilo_credential, 'fake-password')