Thiago Paiva dca44ab0dd Optimizations for setting boot device
By default, OneView doesn't provide on its API a facility to set one-time boot
on managed ServerHardware. This patch aims to allow it through bypassing OneView
and using iLO's REST interface directly. Also, some optimization is made to not
try to set again the boot device if the selected one is already the primary.

Change-Id: I1163048b6edf8778c3a84414804b73eef2c0fd5f
2016-07-22 17:06:19 -03:00

227 lines
9.7 KiB
Python

# (c) Copyright 2015 Hewlett Packard Enterprise Development LP
# Copyright 2015 Universidade Federal de Campina Grande
#
# 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.
import json
import requests
from oneview_client import exceptions
def rest_op(operation, host, suburi, request_headers, request_body,
x_auth_token, enforce_SSL=True):
url = 'https://' + host + suburi
if request_headers is None:
request_headers = dict()
# if X-Auth-Token specified, supply it instead of basic auth
if x_auth_token is not None:
request_headers['X-Auth-Token'] = x_auth_token
if operation == "GET":
response = requests.get(url, headers=request_headers,
verify=enforce_SSL)
return_value = (response.status_code, response.headers,
response.json())
elif operation == "DELETE":
response = requests.delete(url, headers=request_headers,
verify=enforce_SSL)
return_value = (response.status_code, response.headers,
response.json())
elif operation == "PATCH":
response = requests.patch(url, data=json.dumps(request_body),
headers=request_headers, verify=enforce_SSL)
return_value = (response.status_code, response.headers,
response.json())
return return_value
# REST GET
def rest_get(host, suburi, request_headers, x_auth_token, enforce_SSL=True):
return rest_op('GET', host, suburi, request_headers, None, x_auth_token,
enforce_SSL=enforce_SSL)
# NOTE: be prepared for various HTTP responses including 500, 404, etc.
# REST PATCH
def rest_patch(host, suburi, request_headers, request_body, x_auth_token,
enforce_SSL=True):
return rest_op('PATCH', host, suburi, request_headers, request_body,
x_auth_token, enforce_SSL=enforce_SSL)
# NOTE: be prepared for various HTTP responses including 500, 404, etc.
# REST DELETE
def rest_delete(host, suburi, request_headers, x_auth_token, enforce_SSL=True):
return rest_op('DELETE', host, suburi, request_headers, None, x_auth_token,
enforce_SSL=enforce_SSL)
# NOTE: be prepared for various HTTP responses including 500, 404, etc.
# NOTE: response may be an ExtendedError or may be empty
# this is a generator that returns collection members
def collection(host, collection_uri, request_headers, x_auth_token,
enforce_SSL=True):
# get the collection
status, headers, thecollection = rest_get(host,
collection_uri,
request_headers,
x_auth_token,
enforce_SSL)
while status < 300:
# verify expected type
# NOTE: Because of the Redfish standards effort, we have versioned
# many things at 0 in anticipation of them being ratified for version 1
# at some point. So this code makes the (unguarranteed) assumption
# throughout that version 0 and 1 are both legitimate at this point.
# Don't write code requiring version 0 as we will bump to version 1 at
# some point.
# hint: don't limit to version 0 here as we will rev to 1.0 at some
# point hopefully with minimal changes
assert(get_type(thecollection) == 'Collection.0' or
get_type(thecollection) == 'Collection.1')
# 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.
# A client may wish to pass in a boolean flag favoring the href links
# vs. the Items in case a collection contains both.
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']
# Read up on Python generator functions to understand what this
# does.
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 = rest_get(host,
memberuri['href'],
request_headers,
x_auth_token,
enforce_SSL)
# Read up on Python generator functions to understand what this
# does.
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_page = str(thecollection['links']['NextPage']['page'])
next_link_uri = collection_uri + '?page=' + next_page
status, headers, thecollection = rest_get(host,
next_link_uri,
request_headers,
x_auth_token,
enforce_SSL)
# else we are finished iterating the collection
else:
break
else:
raise exceptions.IloException("HTTP %s" % status)
def get_mac_from_ilo(host_ip, x_auth_token, nic_index=0, allow_insecure=False):
# for each system in the systems collection at /rest/v1/Systems
ilo_hardware = collection(host_ip, '/rest/v1/Systems', None,
x_auth_token, enforce_SSL=not allow_insecure)
for status, headers, system, member_uri in ilo_hardware:
# verify expected type
# hint: don't limit to version 0 here as we will rev to 1.0 at
# some point hopefully with minimal changes
assert(get_type(system) == 'ComputerSystem.0' or
get_type(system) == 'ComputerSystem.1')
if 'HostMACAddress' not in system['HostCorrelation']:
raise exceptions.IloException(
'NIC resource does not contain "MacAddress" property'
)
else:
# Can have more than a link
return system['HostCorrelation']['HostMACAddress'][nic_index]
def set_onetime_boot(host_ip, x_auth_token, boot_target, allow_insecure=False):
# for each system in the systems collection at /rest/v1/Systems
ilo_hardware = collection(host_ip, '/rest/v1/Systems', None,
x_auth_token, enforce_SSL=not allow_insecure)
for status, headers, system, member_uri in ilo_hardware:
# verify expected type
# hint: don't limit to version 0 here as we will rev to 1.0 at
# some point hopefully with minimal changes
assert(get_type(system) == 'ComputerSystem.0' or
get_type(system) == 'ComputerSystem.1')
if boot_target not in system["Boot"]["BootSourceOverrideSupported"]:
raise exceptions.IloException(
"ERROR: %s is not a supported boot option.\n" % boot_target)
else:
body = {"Boot": {"BootSourceOverrideTarget": boot_target}}
headers = {"Content-Type": "application/json"}
status_code, _, response = rest_patch(host_ip, member_uri, headers,
body, x_auth_token,
not allow_insecure)
if status_code != 200:
raise exceptions.IloException(response)
def ilo_logout(host_ip, x_auth_token):
sessions = collection(host_ip, '/rest/v1/sessions', None, x_auth_token,
enforce_SSL=False)
for status, headers, member, member_uri in sessions:
if member.get('Oem').get('Hp').get('MySession') is True:
status, headers, member = rest_delete(host_ip, member_uri, None,
x_auth_token, False)
if status != 200:
message = "iLO returned HTTP %s" % status
raise exceptions.IloException(message)
return status, member
# return the type of an object (down to the major version, skipping minor, and
# errata)
def get_type(obj):
typever = obj['Type']
typesplit = typever.split('.')
return typesplit[0] + '.' + typesplit[1]