This commit addresses some of the bug fixes which includes - 1. Changing boot_mode to lower, as accepted by set_pending_boot_mode 2. Increasing the timeout and number of retries to check for status of iLO 3. Fallback to RIBCL for reset_ilo for 'gen9' servers. Change-Id: Iacd122ffecaecbda671bcf21fad660f5c9592a61
726 lines
28 KiB
Python
726 lines
28 KiB
Python
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
|
#
|
|
# 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__ = 'HP'
|
|
|
|
import base64
|
|
import gzip
|
|
import hashlib
|
|
import httplib
|
|
import json
|
|
import StringIO
|
|
import urlparse
|
|
|
|
from proliantutils import exception
|
|
from proliantutils.ilo import common
|
|
from proliantutils.ilo import operations
|
|
|
|
""" Currently this class supports only secure boot and firmware settings
|
|
related API's .
|
|
|
|
TODO : Add rest of the API's that exists in RIBCL. """
|
|
|
|
|
|
class RISOperations(operations.IloOperations):
|
|
|
|
def __init__(self, host, login, password, bios_password=None):
|
|
self.host = host
|
|
self.login = login
|
|
self.password = password
|
|
self.bios_password = bios_password
|
|
# Message registry support
|
|
self.message_registries = {}
|
|
|
|
def _rest_op(self, operation, suburi, request_headers, request_body):
|
|
"""Generic REST Operation handler."""
|
|
|
|
url = urlparse.urlparse('https://' + self.host + suburi)
|
|
# Used for logging on redirection error.
|
|
start_url = url.geturl()
|
|
|
|
if request_headers is None:
|
|
request_headers = dict()
|
|
|
|
# Use self.login/self.password and Basic Auth
|
|
if self.login is not None and self.password is not None:
|
|
hr = "BASIC " + base64.b64encode(self.login + ":" + self.password)
|
|
request_headers['Authorization'] = hr
|
|
|
|
redir_count = 5
|
|
while redir_count:
|
|
conn = None
|
|
if url.scheme == 'https':
|
|
conn = httplib.HTTPSConnection(host=url.netloc, strict=True)
|
|
elif url.scheme == 'http':
|
|
conn = httplib.HTTPConnection(host=url.netloc, strict=True)
|
|
|
|
try:
|
|
conn.request(operation, url.path, headers=request_headers,
|
|
body=json.dumps(request_body))
|
|
resp = conn.getresponse()
|
|
body = resp.read()
|
|
except Exception as e:
|
|
raise exception.IloConnectionError(e)
|
|
|
|
# NOTE:Do not assume every HTTP operation will return a JSON body.
|
|
# For example, ExtendedError structures are only required for
|
|
# HTTP 400 errors and are optional elsewhere as they are mostly
|
|
# redundant for many of the other HTTP status code. In particular,
|
|
# 200 OK responses should not have to return any body.
|
|
|
|
# NOTE: this makes sure the headers names are all lower cases
|
|
# because HTTP says they are case insensitive
|
|
headers = dict((x.lower(), y) for x, y in resp.getheaders())
|
|
|
|
# Follow HTTP redirect
|
|
if resp.status == 301 and 'location' in headers:
|
|
url = urlparse.urlparse(headers['location'])
|
|
redir_count -= 1
|
|
else:
|
|
break
|
|
else:
|
|
# Redirected for 5th time. Throw error
|
|
msg = ("URL Redirected 5 times continuously. "
|
|
"URL incorrect: %s" % start_url)
|
|
raise exception.IloConnectionError(msg)
|
|
|
|
response = dict()
|
|
try:
|
|
if body:
|
|
response = json.loads(body.decode('utf-8'))
|
|
except ValueError:
|
|
# if it doesn't decode as json
|
|
# NOTE: resources may return gzipped content
|
|
# try to decode as gzip (we should check the headers for
|
|
# Content-Encoding=gzip)
|
|
try:
|
|
gzipper = gzip.GzipFile(fileobj=StringIO.StringIO(body))
|
|
uncompressed_string = gzipper.read().decode('UTF-8')
|
|
response = json.loads(uncompressed_string)
|
|
except Exception as e:
|
|
raise exception.IloError(e)
|
|
|
|
return resp.status, headers, response
|
|
|
|
def _rest_get(self, suburi, request_headers=None):
|
|
"""REST GET operation.
|
|
|
|
HTTP response codes could be 500, 404 etc.
|
|
"""
|
|
return self._rest_op('GET', suburi, request_headers, None)
|
|
|
|
def _rest_patch(self, suburi, request_headers, request_body):
|
|
"""REST PATCH operation.
|
|
|
|
HTTP response codes could be 500, 404, 202 etc.
|
|
"""
|
|
if not isinstance(request_headers, dict):
|
|
request_headers = dict()
|
|
request_headers['Content-Type'] = 'application/json'
|
|
return self._rest_op('PATCH', suburi, request_headers, request_body)
|
|
|
|
def _rest_put(self, suburi, request_headers, request_body):
|
|
"""REST PUT operation.
|
|
|
|
HTTP response codes could be 500, 404, 202 etc.
|
|
"""
|
|
if not isinstance(request_headers, dict):
|
|
request_headers = dict()
|
|
request_headers['Content-Type'] = 'application/json'
|
|
return self._rest_op('PUT', suburi, request_headers, request_body)
|
|
|
|
def _rest_post(self, suburi, request_headers, request_body):
|
|
"""REST POST operation.
|
|
|
|
The response body after the operation could be the new resource, or
|
|
ExtendedError, or it could be empty.
|
|
"""
|
|
if not isinstance(request_headers, dict):
|
|
request_headers = dict()
|
|
request_headers['Content-Type'] = 'application/json'
|
|
return self._rest_op('POST', suburi, request_headers, request_body)
|
|
|
|
def _rest_delete(self, suburi, request_headers):
|
|
"""REST DELETE operation.
|
|
|
|
HTTP response codes could be 500, 404 etc.
|
|
"""
|
|
return self._rest_op('DELETE', suburi, request_headers, None)
|
|
|
|
def _get_collection(self, collection_uri, request_headers=None):
|
|
"""Generator function that returns collection members."""
|
|
|
|
# get the collection
|
|
status, headers, thecollection = self._rest_get(collection_uri)
|
|
|
|
if status != 200:
|
|
msg = self._get_extended_error(thecollection)
|
|
raise exception.IloError(msg)
|
|
|
|
while status < 300:
|
|
# verify expected type
|
|
# Don't limit to version 0 here as we will rev to 1.0 at some
|
|
# point hopefully with minimal changes
|
|
ctype = self._get_type(thecollection)
|
|
if (ctype not in ['Collection.0', 'Collection.1']):
|
|
raise exception.IloError("collection not found")
|
|
|
|
# if this collection has inline items, return those
|
|
# NOTE: Collections are very flexible in how the represent
|
|
# members. They can be inline in the collection as members
|
|
# of the 'Items' array, or they may be href links in the
|
|
# links/Members array. The could actually be both. Typically,
|
|
# iLO implements the inline (Items) for only when the collection
|
|
# is read only. We have to render it with the href links when an
|
|
# array contains PATCHable items because its complex to PATCH
|
|
# inline collection members.
|
|
|
|
if 'Items' in thecollection:
|
|
# iterate items
|
|
for item in thecollection['Items']:
|
|
# if the item has a self uri pointer,
|
|
# supply that for convenience.
|
|
memberuri = None
|
|
if 'links' in item and 'self' in item['links']:
|
|
memberuri = item['links']['self']['href']
|
|
yield 200, None, item, memberuri
|
|
|
|
# else walk the member links
|
|
elif ('links' in thecollection and
|
|
'Member' in thecollection['links']):
|
|
# iterate members
|
|
for memberuri in thecollection['links']['Member']:
|
|
# for each member return the resource indicated by the
|
|
# member link
|
|
status, headers, member = self._rest_get(memberuri['href'])
|
|
yield status, headers, member, memberuri['href']
|
|
|
|
# page forward if there are more pages in the collection
|
|
if ('links' in thecollection and
|
|
'NextPage' in thecollection['links']):
|
|
next_link_uri = (collection_uri + '?page=' + str(
|
|
thecollection['links']['NextPage']['page']))
|
|
status, headers, thecollection = self._rest_get(next_link_uri)
|
|
|
|
# else we are finished iterating the collection
|
|
else:
|
|
break
|
|
|
|
def _get_type(self, obj):
|
|
"""Return the type of an object."""
|
|
typever = obj['Type']
|
|
typesplit = typever.split('.')
|
|
return typesplit[0] + '.' + typesplit[1]
|
|
|
|
def _operation_allowed(self, headers_dict, operation):
|
|
"""Checks if specified operation is allowed on the resource."""
|
|
|
|
if 'allow' in headers_dict:
|
|
if operation in headers_dict['allow']:
|
|
return True
|
|
return False
|
|
|
|
def _render_extended_error_message_list(self, extended_error):
|
|
"""Parse the ExtendedError object and retruns the message.
|
|
|
|
Build a list of decoded messages from the extended_error using the
|
|
message registries. An ExtendedError JSON object is a response from
|
|
the with its own schema. This function knows how to parse the
|
|
ExtendedError object and, using any loaded message registries,
|
|
render an array of plain language strings that represent
|
|
the response.
|
|
"""
|
|
messages = []
|
|
if isinstance(extended_error, dict):
|
|
if ('Type' in extended_error and
|
|
extended_error['Type'].startswith('ExtendedError.')):
|
|
for msg in extended_error['Messages']:
|
|
message_id = msg['MessageID']
|
|
x = message_id.split('.')
|
|
registry = x[0]
|
|
msgkey = x[len(x) - 1]
|
|
|
|
# if the correct message registry is loaded,
|
|
# do string resolution
|
|
if (registry in self.message_registries and msgkey in
|
|
self.message_registries[registry]['Messages']):
|
|
rmsgs = self.message_registries[registry]['Messages']
|
|
msg_dict = rmsgs[msgkey]
|
|
msg_str = message_id + ': ' + msg_dict['Message']
|
|
|
|
for argn in range(0, msg_dict['NumberOfArgs']):
|
|
subst = '%' + str(argn+1)
|
|
m = str(msg['MessageArgs'][argn])
|
|
msg_str = msg_str.replace(subst, m)
|
|
|
|
if ('Resolution' in msg_dict and
|
|
msg_dict['Resolution'] != 'None'):
|
|
msg_str += ' ' + msg_dict['Resolution']
|
|
|
|
messages.append(msg_str)
|
|
else:
|
|
# no message registry, simply return the msg object
|
|
# in string form
|
|
messages.append(str(message_id))
|
|
|
|
return messages
|
|
|
|
def _get_extended_error(self, extended_error):
|
|
"""Gets the list of decoded messages from the extended_error."""
|
|
return self._render_extended_error_message_list(extended_error)
|
|
|
|
def _get_host_details(self):
|
|
"""Get the system details."""
|
|
# Assuming only one system present as part of collection,
|
|
# as we are dealing with iLO's here.
|
|
status, headers, system = self._rest_get('/rest/v1/Systems/1')
|
|
if status < 300:
|
|
stype = self._get_type(system)
|
|
if stype not in ['ComputerSystem.0', 'ComputerSystem.1']:
|
|
msg = "%s is not a valid system type " % stype
|
|
raise exception.IloError(msg)
|
|
else:
|
|
msg = self._get_extended_error(system)
|
|
raise exception.IloError(msg)
|
|
|
|
return system
|
|
|
|
def _check_bios_resource(self, properties=[]):
|
|
"""Check if the bios resource exists."""
|
|
|
|
system = self._get_host_details()
|
|
if ('links' in system['Oem']['Hp'] and
|
|
'BIOS' in system['Oem']['Hp']['links']):
|
|
# Get the BIOS URI and Settings
|
|
bios_uri = system['Oem']['Hp']['links']['BIOS']['href']
|
|
status, headers, bios_settings = self._rest_get(bios_uri)
|
|
|
|
if status >= 300:
|
|
msg = self._get_extended_error(bios_settings)
|
|
raise exception.IloError(msg)
|
|
|
|
# If property is not None, check if the bios_property is supported
|
|
for property in properties:
|
|
if property not in bios_settings:
|
|
# not supported on this platform
|
|
msg = ('BIOS Property "' + property + '" is not'
|
|
' supported on this system.')
|
|
raise exception.IloCommandNotSupportedError(msg)
|
|
|
|
return headers, bios_uri, bios_settings
|
|
|
|
else:
|
|
msg = ('"links/BIOS" section in ComputerSystem/Oem/Hp'
|
|
' does not exist')
|
|
raise exception.IloCommandNotSupportedError(msg)
|
|
|
|
def _get_bios_settings_resource(self, data):
|
|
"""Get the BIOS settings resource."""
|
|
try:
|
|
bios_settings_uri = data['links']['Settings']['href']
|
|
except KeyError:
|
|
msg = ('BIOS Settings resource not found.')
|
|
raise exception.IloError(msg)
|
|
|
|
status, headers, bios_settings = self._rest_get(bios_settings_uri)
|
|
if status != 200:
|
|
msg = self._get_extended_error(bios_settings)
|
|
raise exception.IloError(msg)
|
|
|
|
return headers, bios_settings_uri, bios_settings
|
|
|
|
def _validate_if_patch_supported(self, headers, uri):
|
|
"""Check if the PATCH Operation is allowed on the resource."""
|
|
if not self._operation_allowed(headers, 'PATCH'):
|
|
msg = ('PATCH Operation not supported on the resource '
|
|
'"%s"' % uri)
|
|
raise exception.IloError(msg)
|
|
|
|
def _get_bios_setting(self, bios_property):
|
|
"""Retrieves bios settings of the server."""
|
|
headers, bios_uri, bios_settings = self._check_bios_resource([
|
|
bios_property])
|
|
return bios_settings[bios_property]
|
|
|
|
def _get_bios_hash_password(self, bios_password):
|
|
"""Get the hashed BIOS password."""
|
|
request_headers = dict()
|
|
if bios_password:
|
|
bios_password_hash = hashlib.sha256((bios_password.encode()).
|
|
hexdigest().upper())
|
|
request_headers['X-HPRESTFULAPI-AuthToken'] = bios_password_hash
|
|
return request_headers
|
|
|
|
def _change_bios_setting(self, properties):
|
|
"""Change the bios settings to specified values."""
|
|
keys = properties.keys()
|
|
# Check if the BIOS resource/property exists.
|
|
headers, bios_uri, settings = self._check_bios_resource(keys)
|
|
if not self._operation_allowed(headers, 'PATCH'):
|
|
headers, bios_uri, _ = self._get_bios_settings_resource(settings)
|
|
self._validate_if_patch_supported(headers, bios_uri)
|
|
|
|
request_headers = self._get_bios_hash_password(self.bios_password)
|
|
status, headers, response = self._rest_patch(bios_uri, request_headers,
|
|
properties)
|
|
if status >= 300:
|
|
msg = self._get_extended_error(response)
|
|
raise exception.IloError(msg)
|
|
|
|
def _change_secure_boot_settings(self, property, value):
|
|
"""Change secure boot settings on the server."""
|
|
system = self._get_host_details()
|
|
# find the BIOS URI
|
|
if ('links' not in system['Oem']['Hp'] or
|
|
'SecureBoot' not in system['Oem']['Hp']['links']):
|
|
msg = (' "SecureBoot" resource or feature is not '
|
|
'supported on this system')
|
|
raise exception.IloCommandNotSupportedError(msg)
|
|
|
|
secure_boot_uri = system['Oem']['Hp']['links']['SecureBoot']['href']
|
|
|
|
# Change the property required
|
|
new_secure_boot_settings = dict()
|
|
new_secure_boot_settings[property] = value
|
|
|
|
# perform the patch
|
|
status, headers, response = self._rest_patch(
|
|
secure_boot_uri, None, new_secure_boot_settings)
|
|
|
|
if status >= 300:
|
|
msg = self._get_extended_error(response)
|
|
raise exception.IloError(msg)
|
|
|
|
# Change the bios setting as a workaround to enable secure boot
|
|
# Can be removed when fixed for Gen9 snap2
|
|
val = self._get_bios_setting('CustomPostMessage')
|
|
val = val.rstrip() if val.endswith(" ") else val+" "
|
|
self._change_bios_setting({'CustomPostMessage': val})
|
|
|
|
def _validate_uefi_boot_mode(self):
|
|
"""Checks if the system is in uefi boot mode.
|
|
|
|
:return: 'True' if the boot mode is uefi else 'False'
|
|
:raises: IloError, on an error from iLO.
|
|
:raises: IloCommandNotSupportedError, if the command is not supported
|
|
on the server.
|
|
"""
|
|
boot_mode = self.get_current_boot_mode()
|
|
if boot_mode == 'UEFI':
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def get_product_name(self):
|
|
"""Gets the product name of the server.
|
|
|
|
:returns: server model name.
|
|
:raises: IloError, on an error from iLO.
|
|
"""
|
|
system = self._get_host_details()
|
|
return system['Model']
|
|
|
|
def get_secure_boot_mode(self):
|
|
"""Get the status of secure boot.
|
|
|
|
:returns: True, if enabled, else False
|
|
:raises: IloError, on an error from iLO.
|
|
:raises: IloCommandNotSupportedError, if the command is not supported
|
|
on the server.
|
|
"""
|
|
system = self._get_host_details()
|
|
|
|
if ('links' not in system['Oem']['Hp'] or
|
|
'SecureBoot' not in system['Oem']['Hp']['links']):
|
|
msg = ('"SecureBoot" resource or feature is not supported'
|
|
' on this system')
|
|
raise exception.IloCommandNotSupportedError(msg)
|
|
|
|
secure_boot_uri = system['Oem']['Hp']['links']['SecureBoot']['href']
|
|
|
|
# get the Secure Boot object
|
|
status, headers, secure_boot_settings = self._rest_get(secure_boot_uri)
|
|
|
|
if status >= 300:
|
|
msg = self._get_extended_error(secure_boot_settings)
|
|
raise exception.IloError(msg)
|
|
|
|
return secure_boot_settings['SecureBootCurrentState']
|
|
|
|
def set_secure_boot_mode(self, secure_boot_enable):
|
|
"""Enable/Disable secure boot on the server.
|
|
|
|
:param secure_boot_enable: True, if secure boot needs to be
|
|
enabled for next boot, else False.
|
|
:raises: IloError, on an error from iLO.
|
|
:raises: IloCommandNotSupportedError, if the command is not supported
|
|
on the server.
|
|
"""
|
|
self._change_secure_boot_settings('SecureBootEnable',
|
|
secure_boot_enable)
|
|
|
|
def reset_secure_boot_keys(self):
|
|
"""Reset secure boot keys to manufacturing defaults.
|
|
|
|
:raises: IloError, on an error from iLO.
|
|
:raises: IloCommandNotSupportedError, if the command is not supported
|
|
on the server.
|
|
"""
|
|
self._change_secure_boot_settings('ResetToDefaultKeys', True)
|
|
|
|
def clear_secure_boot_keys(self):
|
|
"""Reset all keys.
|
|
|
|
:raises: IloError, on an error from iLO.
|
|
:raises: IloCommandNotSupportedError, if the command is not supported
|
|
on the server.
|
|
"""
|
|
self._change_secure_boot_settings('ResetAllKeys', True)
|
|
|
|
def get_host_power_status(self):
|
|
"""Request the power state of the server.
|
|
|
|
:returns: Power State of the server, 'ON' or 'OFF'
|
|
:raises: IloError, on an error from iLO.
|
|
"""
|
|
|
|
data = self._get_host_details()
|
|
return data['Power'].upper()
|
|
|
|
def get_http_boot_url(self):
|
|
"""Request the http boot url from system in uefi boot mode.
|
|
|
|
:returns: URL for http boot
|
|
:raises: IloError, on an error from iLO.
|
|
:raises: IloCommandNotSupportedInBiosError, if the system is
|
|
in the bios boot mode.
|
|
"""
|
|
if(self._validate_uefi_boot_mode() is True):
|
|
return self._get_bios_setting('UefiShellStartupUrl')
|
|
else:
|
|
msg = 'get_http_boot_url is not supported in the BIOS boot mode'
|
|
raise exception.IloCommandNotSupportedInBiosError(msg)
|
|
|
|
def set_http_boot_url(self, url):
|
|
"""Set url to the UefiShellStartupUrl to the system in uefi boot mode.
|
|
|
|
:param url: URL for http boot
|
|
:raises: IloError, on an error from iLO.
|
|
:raises: IloCommandNotSupportedInBiosError, if the system is
|
|
in the bios boot mode.
|
|
"""
|
|
if(self._validate_uefi_boot_mode() is True):
|
|
self._change_bios_setting({'UefiShellStartupUrl': url})
|
|
else:
|
|
msg = 'set_http_boot_url is not supported in the BIOS boot mode'
|
|
raise exception.IloCommandNotSupportedInBiosError(msg)
|
|
|
|
def get_current_boot_mode(self):
|
|
"""Retrieves the current boot mode of the server.
|
|
|
|
:returns: Current boot mode, LEGACY or UEFI.
|
|
:raises: IloError, on an error from iLO.
|
|
"""
|
|
boot_mode = self._get_bios_setting('BootMode')
|
|
if boot_mode == 'LegacyBios':
|
|
boot_mode = 'legacy'
|
|
|
|
return boot_mode.upper()
|
|
|
|
def get_pending_boot_mode(self):
|
|
"""Retrieves the pending boot mode of the server.
|
|
|
|
Gets the boot mode to be set on next reset.
|
|
:returns: either LEGACY or UEFI.
|
|
:raises: IloError, on an error from iLO.
|
|
"""
|
|
headers, uri, bios_settings = self._check_bios_resource(['BootMode'])
|
|
_, _, settings = self._get_bios_settings_resource(bios_settings)
|
|
boot_mode = settings.get('BootMode')
|
|
if boot_mode == 'LegacyBios':
|
|
boot_mode = 'legacy'
|
|
return boot_mode.upper()
|
|
|
|
def set_pending_boot_mode(self, boot_mode):
|
|
"""Sets the boot mode of the system for next boot.
|
|
|
|
:param boot_mode: either 'uefi' or 'legacy'.
|
|
:raises: IloInvalidInputError, on an invalid input.
|
|
:raises: IloError, on an error from iLO.
|
|
:raises: IloCommandNotSupportedError, if the command is not supported
|
|
on the server.
|
|
"""
|
|
boot_mode = boot_mode.lower()
|
|
if boot_mode not in ['uefi', 'legacy']:
|
|
msg = 'Invalid Boot mode specified'
|
|
raise exception.IloInvalidInputError(msg)
|
|
|
|
boot_properties = {'BootMode': boot_mode}
|
|
|
|
if boot_mode == 'legacy':
|
|
boot_properties['BootMode'] = 'LegacyBios'
|
|
else:
|
|
# If Boot Mode is 'Uefi' set the UEFIOptimizedBoot first.
|
|
boot_properties['UefiOptimizedBoot'] = "Enabled"
|
|
|
|
# Change the Boot Mode
|
|
self._change_bios_setting(boot_properties)
|
|
|
|
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.
|
|
:raises: IloCommandNotSupportedError, if the command is not supported
|
|
on the server.
|
|
"""
|
|
acc_uri = '/rest/v1/AccountService/Accounts'
|
|
|
|
for status, hds, account, memberuri in self._get_collection(acc_uri):
|
|
if account['UserName'] == self.login:
|
|
mod_user = {}
|
|
mod_user['Password'] = password
|
|
status, headers, response = self._rest_patch(memberuri,
|
|
None, mod_user)
|
|
if status != 200:
|
|
msg = self._get_extended_error(response)
|
|
raise exception.IloError(msg)
|
|
return
|
|
|
|
msg = "iLO Account with specified username is not found."
|
|
raise exception.IloError(msg)
|
|
|
|
def _get_ilo_details(self):
|
|
"""Gets iLO details
|
|
|
|
:raises: IloError, on an error from iLO.
|
|
:raises: IloConnectionError, if iLO is not up after reset.
|
|
:raises: IloCommandNotSupportedError, if the command is not supported
|
|
on the server.
|
|
"""
|
|
reset_uri = '/rest/v1/Managers/1'
|
|
status, headers, manager = self._rest_get(reset_uri)
|
|
|
|
if status != 200:
|
|
msg = self._get_extended_error(manager)
|
|
raise exception.IloError(msg)
|
|
|
|
# verify expected type
|
|
mtype = self._get_type(manager)
|
|
if (mtype not in ['Manager.0', 'Manager.1']):
|
|
msg = "%s is not a valid Manager type " % mtype
|
|
raise exception.IloError(msg)
|
|
|
|
return manager, reset_uri
|
|
|
|
def reset_ilo(self):
|
|
"""Resets the iLO.
|
|
|
|
:raises: IloError, on an error from iLO.
|
|
:raises: IloConnectionError, if iLO is not up after reset.
|
|
:raises: IloCommandNotSupportedError, if the command is not supported
|
|
on the server.
|
|
"""
|
|
manager, reset_uri = self._get_ilo_details()
|
|
action = {'Action': 'Reset'}
|
|
|
|
# perform the POST
|
|
status, headers, response = self._rest_post(reset_uri, None, action)
|
|
|
|
if(status != 200):
|
|
msg = self._get_extended_error(response)
|
|
raise exception.IloError(msg)
|
|
|
|
# Check if the iLO is up again.
|
|
common.wait_for_ilo_after_reset(self)
|
|
|
|
def reset_bios_to_default(self):
|
|
"""Resets the BIOS settings to default values.
|
|
|
|
:raises: IloError, on an error from iLO.
|
|
:raises: IloCommandNotSupportedError, if the command is not supported
|
|
on the server.
|
|
"""
|
|
# Check if the BIOS resource if exists.
|
|
headers_bios, bios_uri, bios_settings = self._check_bios_resource()
|
|
# Get the BaseConfig resource.
|
|
try:
|
|
base_config_uri = bios_settings['links']['BaseConfigs']['href']
|
|
except KeyError:
|
|
msg = ("BaseConfigs resource not found. Couldn't apply the BIOS "
|
|
"Settings.")
|
|
raise exception.IloCommandNotSupportedError(msg)
|
|
|
|
# Check if BIOS resource supports patch, else get the settings
|
|
if not self._operation_allowed(headers_bios, 'PATCH'):
|
|
headers, bios_uri, _ = self._get_bios_settings_resource(
|
|
bios_settings)
|
|
self._validate_if_patch_supported(headers, bios_uri)
|
|
|
|
status, headers, config = self._rest_get(base_config_uri)
|
|
if status != 200:
|
|
msg = self._get_extended_error(config)
|
|
raise exception.IloError(msg)
|
|
|
|
new_bios_settings = {}
|
|
for cfg in config['BaseConfigs']:
|
|
default_settings = cfg.get('default', None)
|
|
if default_settings is not None:
|
|
new_bios_settings = default_settings
|
|
break
|
|
else:
|
|
msg = ("Default Settings not found in 'BaseConfigs' resource.")
|
|
raise exception.IloCommandNotSupportedError(msg)
|
|
request_headers = self._get_bios_hash_password(self.bios_password)
|
|
status, headers, response = self._rest_patch(bios_uri, request_headers,
|
|
new_bios_settings)
|
|
if status >= 300:
|
|
msg = self._get_extended_error(response)
|
|
raise exception.IloError(msg)
|
|
|
|
def _get_ilo_firmware_version(self):
|
|
"""Gets the ilo firmware version for server capabilities
|
|
|
|
:returns: a dictionary of iLO firmware version.
|
|
|
|
"""
|
|
|
|
manager, reset_uri = self._get_ilo_details()
|
|
ilo_firmware_version = manager['Firmware']['Current']['VersionString']
|
|
return {'ilo_firmware_version': ilo_firmware_version}
|
|
|
|
def get_server_capabilities(self):
|
|
"""Gets server properties which can be used for scheduling
|
|
|
|
:returns: a dictionary of hardware properties like firmware
|
|
versions, server model.
|
|
:raises: IloError, if iLO returns an error in command execution.
|
|
|
|
"""
|
|
capabilities = {}
|
|
system = self._get_host_details()
|
|
capabilities['server_model'] = system['Model']
|
|
rom_firmware_version = (
|
|
system['Oem']['Hp']['Bios']['Current']['VersionString'])
|
|
capabilities['rom_firmware_version'] = rom_firmware_version
|
|
capabilities.update(self._get_ilo_firmware_version())
|
|
try:
|
|
self.get_secure_boot_mode()
|
|
capabilities['secure_boot'] = 'true'
|
|
except exception.IloCommandNotSupportedError:
|
|
# If an error is raised dont populate the capability
|
|
# secure_boot
|
|
pass
|
|
return capabilities
|