python-redfish/samples/HpRestfulApiExamples.py
DanHP 79352bdfb1 ReSync initial commit
ReSync initial commit
2015-03-23 13:11:05 -05:00

1516 lines
73 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.
"""
Provides examples of using the HP RESTful API on iLO for common use cases. This is for tutorial/example purposes only.
---------------------------------------------------------------------------------------------------------------------
IMPORTANT!!!
---------------------------------------------------------------------------------------------------------------------
When developing a client for the HP RESTful API, be sure to not code based upon assumptions that are not guaranteed.
Search for, and note any 'NOTE' comments in this code to read about ways to avoid incorrect assumptions.
The reason avoiding these assumptions is so important is that implementations may vary across systems and firmware
versions, and we want your code to work consistently.
---------------------------------------------------------------------------------------------------------------------
STARTING ASSUMPTIONS
---------------------------------------------------------------------------------------------------------------------
On URIs:
The HP RESTful API is a "hypermedia API" by design. This is to avoid building in restrictive assumptions to the
data model that will make it difficult to adapt to future hardware implementations. A hypermedia API avoids these
assumptions by making the data model discoverable via links between resources.
A URI should be treated by the client as opaque, and thus should not be attempted to be understood or deconstructed
by the client. Only specific top level URIs (any URI in this sample code) may be assumed, and even these may be
absent based upon the implementation (e.g. there might be no /rest/v1/Systems collection on something that doesn't
have compute nodes.)
The other URIs must be discovered dynamically by following href links. This is because the API will eventually be
implemented on a system that breaks any existing data model "shape" assumptions we may make now. In particular,
clients should not make assumptions about the URIs for the resource members of a collection. For instance, the URI of
a collection member will NOT always be /rest/v1/.../collection/1, or 2. On Moonshot a System collection member might be
/rest/v1/Systems/C1N1.
This sounds very complicated, but in reality (as these examples demonstrate), if you are looking for specific items,
the traversal logic isn't too complicated.
On Resource Model Traversal:
Although the resources in the data model are linked together, because of cross link references between resources,
a client may not assume the resource model is a tree. It is a graph instead, so any crawl of the data model should
keep track of visited resources to avoid an infinite traversal loop.
A reference to another resource is any property called "href" no matter where it occurs in a resource.
An external reference to a resource outside the data model is referred to by a property called "extref". Any
resource referred to by extref should not be assumed to follow the conventions of the API.
On Resource Versions:
Each resource has a "Type" property with a value of the format Tyepname.x.y.z where
* x = major version - incrementing this is a breaking change to the schema
* y = minor version - incrementing this is a non-breaking additive change to the schema
* z = errata - non-breaking change
Because all resources are versioned and schema also have a version, it is possible to design rules for "nearest"
match (e.g. if you are interacting with multiple services using a common batch of schema files). The mechanism
is not prescribed, but a client should be prepared to encounter both older and newer versions of resource types.
On HTTP POST to create:
WHen POSTing to create a resource (e.g. create an account or session) the guarantee is that a successful response
includes a "Location" HTTP header indicating the resource URI of the newly created resource. The POST may also
include a representation of the newly created object in a JSON response body but may not. Do not assume the response
body, but test it. It may also be an ExtendedError object.
HTTP REDIRECT:
All clients must correctly handle HTTP redirect. We (or Redfish) may eventually need to use redirection as a way
to alias portions of the data model.
FUTURE: Asynchronous tasks
In the future some operations may start asynchonous tasks. In this case, the client should recognized and handle
HTTP 202 if needed and the 'Location' header will point to a resource with task information and status.
JSON-SCHEMA:
The json-schema available at /rest/v1/Schemas governs the content of the resources, but keep in mind:
* not every property in the schema is implemented in every implementation.
* some properties are schemed to allow both null and anotehr type like string or integer.
Robust client code should check both the existence and type of interesting properties and fail gracefully if
expectations are not met.
GENERAL ADVICE:
Clients should always be prepared for:
* unimplemented properties (e.g. a property doesn't apply in a particular case)
* null values in some cases if the value of a property is not currently known due to system conditions
* HTTP status codes other than 200 OK. Can your code handle an HTTP 500 Internal Server Error with no other info?
* URIs are case insensitive
* HTTP header names are case insensitive
* JSON Properties and Enum values are case sensitive
* A client should be tolerant of any set of HTTP headers the service returns
"""
__author__ = 'HP'
import ssl
import urllib2
from urlparse import urlparse
import httplib
import base64
import json
import hashlib
import gzip
import StringIO
import sys
# REST operation generic handler
def rest_op(operation, host, suburi, request_headers, request_body, iLO_loginname, iLO_password, x_auth_token=None, enforce_SSL=True):
url = urlparse('https://' + host + suburi)
#url = urlparse('http://' + 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
# else use iLO_loginname/iLO_password and Basic Auth
elif iLO_loginname is not None and iLO_password is not None:
request_headers['Authorization'] = "BASIC " + base64.b64encode(iLO_loginname + ":" + iLO_password)
redir_count = 4
while redir_count:
conn = None
if url.scheme == 'https':
# New in Python 2.7.9, SSL enforcement is defaulted on, but can be opted-out of.
# The below case is the Opt-Out condition and should be used with GREAT caution.
if( sys.version_info.major == 2 and
sys.version_info.minor == 7 and
sys.version_info.micro >= 9 and
enforce_SSL == False):
cont=ssl.SSLContext(ssl.PROTOCOL_TLSv1)
cont.verify_mode = ssl.CERT_NONE
conn = httplib.HTTPSConnection(host=url.netloc, strict=True, context=cont)
else:
conn = httplib.HTTPSConnection(host=url.netloc, strict=True)
elif url.scheme == 'http':
conn = httplib.HTTPConnection(host=url.netloc, strict=True)
else:
assert(False)
conn.request(operation, url.path, headers=request_headers, body=json.dumps(request_body))
resp = conn.getresponse()
body = resp.read()
# 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(headers['location'])
redir_count -= 1
else:
break
response = dict()
try:
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:
pass
# return empty
pass
return resp.status, headers, response
# REST GET
def rest_get(host, suburi, request_headers, iLO_loginname, iLO_password):
return rest_op('GET', host, suburi, request_headers, None, iLO_loginname, iLO_password)
# NOTE: be prepared for various HTTP responses including 500, 404, etc.
# REST PATCH
def rest_patch(server, suburi, request_headers, request_body, iLO_loginname, iLO_password):
if not isinstance(request_headers, dict): request_headers = dict()
request_headers['Content-Type'] = 'application/json'
return rest_op('PATCH', server, suburi, request_headers, request_body, iLO_loginname, iLO_password)
# NOTE: be prepared for various HTTP responses including 500, 404, 202 etc.
# REST PUT
def rest_put(host, suburi, request_headers, request_body, iLO_loginname, iLO_password):
if not isinstance(request_headers, dict): request_headers = dict()
request_headers['Content-Type'] = 'application/json'
return rest_op('PUT', host, suburi, request_headers, request_body, iLO_loginname, iLO_password)
# NOTE: be prepared for various HTTP responses including 500, 404, 202 etc.
# REST POST
def rest_post(host, suburi, request_headers, request_body, iLO_loginname, iLO_password):
if not isinstance(request_headers, dict): request_headers = dict()
request_headers['Content-Type'] = 'application/json'
return rest_op('POST', host, suburi, request_headers, request_body, iLO_loginname, iLO_password)
# NOTE: don't assume any newly created resource is included in the response. Only the Location header matters.
# the response body may be the new resource, it may be an ExtendedError, or it may be empty.
# REST DELETE
def rest_delete(host, suburi, request_headers, iLO_loginname, iLO_password):
return rest_op('DELETE', host, suburi, request_headers, None, iLO_loginname, iLO_password)
# 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, iLO_loginname, iLO_password):
# get the collection
status, headers, thecollection = rest_get(host, collection_uri, request_headers, iLO_loginname, iLO_password)
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, iLO_loginname, iLO_password)
# 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_link_uri = collection_uri + '?page=' + str(thecollection['links']['NextPage']['page'])
status, headers, thecollection = rest_get(host, next_link_uri, request_headers, iLO_loginname, iLO_password)
# else we are finished iterating the collection
else:
break
# 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]
# checks HTTP response headers for specified operation (e.g. 'GET' or 'PATCH')
def operation_allowed(headers_dict, operation):
if 'allow' in headers_dict:
if headers_dict['allow'].find(operation) != -1:
return True
return False
# Message registry support
message_registries = {}
# 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.
def render_extended_error_message_list(extended_error):
messages = []
if isinstance(extended_error, dict):
if 'Type' in extended_error and extended_error['Type'].startswith('ExtendedError.'):
for msg in extended_error['Messages']:
MessageID = msg['MessageID']
x = MessageID.split('.')
registry = x[0]
msgkey = x[len(x) - 1]
# if the correct message registry is loaded, do string resolution
if registry in message_registries:
if registry in message_registries and msgkey in message_registries[registry]['Messages']:
msg_dict = message_registries[registry]['Messages'][msgkey]
msg_str = MessageID + ': ' + msg_dict['Message']
for argn in range(0, msg_dict['NumberOfArgs']):
subst = '%' + str(argn+1)
msg_str = msg_str.replace(subst, str(msg['MessageArgs'][argn]))
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('No Message Registry Info: '+ str(msg))
return messages
# Print a list of decoded messages from the extended_error using the message registries
def print_extended_error(extended_error):
messages = render_extended_error_message_list(extended_error)
msgcnt = 0
for msg in messages:
print('\t' + msg)
msgcnt += 1
if msgcnt == 0: # add a spacer
print
# noinspection PyPep8Naming
def ex1_change_bios_setting(host, bios_property, value, iLO_loginname, iLO_password, bios_password):
print('EXAMPLE 1: Change a BIOS setting')
# for each system in the systems collection at /rest/v1/Systems
for status, headers, system, memberuri in collection(host, '/rest/v1/Systems', None, iLO_loginname, iLO_password):
# 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')
# find the BIOS URI
if 'links' not in system['Oem']['Hp']:
print('\t"links/BIOS" section in ComputerSystem/Oem/Hp does not exist')
return
bios_uri = system['Oem']['Hp']['links']['BIOS']['href']
# get the BIOS object
status, headers, bios_settings = rest_get(host, bios_uri, None, iLO_loginname, iLO_password)
# check to make sure the bios_property is supported
# if not, its OK to PATCH it but it will generate an error if not implemented and waste everyone's time
if bios_property not in bios_settings:
# not supported on this platform
print('\tBIOS Property "' + bios_property + '" is not supported on this system')
return
# if this BIOS resource doesn't support PATCH, go get the Settings, which should
if not operation_allowed(headers, 'PATCH'): # this is GET-only
bios_uri = bios_settings['links']['Settings']['href']
status, headers, bios_settings = rest_get(host, bios_uri, None, iLO_loginname, iLO_password)
assert(operation_allowed(headers, 'PATCH')) # this allows PATCH
# we don't need to PATCH back everything, just the one bios_property we want to change
new_bios_settings = dict()
new_bios_settings[bios_property] = value
request_headers = dict()
if bios_password:
bios_password_hash = hashlib.sha256(bios_password.encode()).hexdigest().upper()
request_headers['X-HPRESTFULAPI-AuthToken'] = bios_password_hash
# perform the patch
print('PATCH ' + json.dumps(new_bios_settings) + ' to ' + bios_uri)
status, headers, response = rest_patch(host, bios_uri, request_headers, new_bios_settings, iLO_loginname, iLO_password)
print('PATCH response = ' + str(status))
print_extended_error(response)
assert(status < 300)
# point made...quit
break
# noinspection PyPep8Naming
def ex2_reset_server(host, iLO_loginname, iLO_password):
print('EXAMPLE 2: Reset a server')
# for each system in the systems collection at /rest/v1/Systems
for status, headers, system, memberuri in collection(host, '/rest/v1/Systems', None, iLO_loginname, iLO_password):
# 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')
# verify it supports POST
assert(operation_allowed(headers, 'POST'))
action = dict()
action['Action'] = 'Reset'
action['ResetType'] = 'ForceRestart'
# perform the POST action
print('POST ' + json.dumps(action) + ' to ' + memberuri)
status, headers, response = rest_post(host, memberuri, None, action, iLO_loginname, iLO_password)
print('POST response = ' + str(status))
print_extended_error(response)
# point made...quit
break
# noinspection PyPep8Naming
def ex3_enable_secure_boot(host, secure_boot_enable, iLO_loginname, iLO_password):
print('EXAMPLE 3: Enable UEFI Secure Boot')
# for each system in the systems collection at /rest/v1/Systems
for status, headers, system, memberuri in collection(host, '/rest/v1/Systems', None, iLO_loginname, iLO_password):
# 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')
# find the BIOS URI
if 'links' not in system['Oem']['Hp'] or 'SecureBoot' not in system['Oem']['Hp']['links']:
print('\t"SecureBoot" resource or feature is not supported on this system')
return
secure_boot_uri = system['Oem']['Hp']['links']['SecureBoot']['href']
# get the Secure Boot object
status, headers, secure_boot_settings = rest_get(host, secure_boot_uri, None, iLO_loginname, iLO_password)
# if the BIOS doesn't support PATCH, go get the Settings, which should
if not operation_allowed(headers, 'PATCH'): # this is GET-only
secure_boot_uri = secure_boot_settings['links']['Settings']['href']
status, headers, boot_settings = rest_get(host, secure_boot_uri, None, iLO_loginname, iLO_password)
assert(operation_allowed(headers, 'PATCH')) # this allows PATCH
# we don't need to PATCH back everything, just the one property we want to change
new_secure_boot_settings = dict()
new_secure_boot_settings['SecureBootEnable'] = secure_boot_enable
# perform the patch
print('PATCH ' + json.dumps(new_secure_boot_settings) + ' to ' + secure_boot_uri)
status, headers, response = rest_patch(host, secure_boot_uri, None, new_secure_boot_settings, iLO_loginname, iLO_password)
print('PATCH response = ' + str(status))
print_extended_error(response)
assert(status < 300)
# point made...quit
break
# noinspection PyPep8Naming
def ex4_bios_revert_default(host, iLO_loginname, iLO_password, default_overrides=None):
if not default_overrides: default_overrides = {}
print('EXAMPLE 4: Revert BIOS Settings to default')
# for each system in the systems collection at /rest/v1/Systems
for status, headers, system, memberuri in collection(host, '/rest/v1/Systems', None, iLO_loginname, iLO_password):
# 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')
# find the BIOS URI
if 'links' not in system['Oem']['Hp'] or 'BIOS' not in system['Oem']['Hp']['links']:
print('\tBIOS Settings resource or feature is not supported on this system')
return
bios_uri = system['Oem']['Hp']['links']['BIOS']['href']
# get the BIOS object
status, headers, bios_settings = rest_get(host, bios_uri, None, iLO_loginname, iLO_password)
# if the BIOS doesn't support PUT, go get the Settings, which should
if not operation_allowed(headers, 'PUT'): # this is GET-only
if 'Settings' not in bios_settings['links']:
print('No BIOS settings resources allow PUT')
return
bios_uri = bios_settings['links']['Settings']['href']
status, headers, bios_settings = rest_get(host, bios_uri, None, iLO_loginname, iLO_password)
assert(operation_allowed(headers, 'PUT')) # this allows PUT
# we don't need to PUT back everything, just the one property we want to change
new_bios_settings = dict()
new_bios_settings['BaseConfig'] = 'default'
# preserve the Type property from the existing BIOS settings to avoid an error
new_bios_settings['Type'] = bios_settings['Type']
# add in any caller-supplied override properties
for override in default_overrides:
new_bios_settings[override] = default_overrides[override]
# perform the patch
print('PUT ' + json.dumps(new_bios_settings) + ' to ' + bios_uri)
status, headers, response = rest_put(host, bios_uri, None, new_bios_settings, iLO_loginname, iLO_password)
print('PUT response = ' + str(status))
print_extended_error(response)
assert(status < 300)
# point made...quit
break
# noinspection PyPep8Naming
def ex5_change_boot_order(host, iLO_loginname, iLO_password, bios_password):
print('EXAMPLE 5: Change Boot Order (UEFI)')
# for each system in the systems collection at /rest/v1/Systems
for status, headers, system, memberuri in collection(host, '/rest/v1/Systems', None, iLO_loginname, iLO_password):
# 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')
# find the BIOS URI
if 'links' not in system['Oem']['Hp'] or 'BIOS' not in system['Oem']['Hp']['links']:
print('\tBIOS Settings resource or feature is not supported on this system')
return
bios_uri = system['Oem']['Hp']['links']['BIOS']['href']
# get the BIOS object
status, headers, bios_settings = rest_get(host, bios_uri, None, iLO_loginname, iLO_password)
# get the BOOT object
if 'Boot' not in bios_settings['links']:
print('\t"links" section in Bios settings does not have a Boot order resource')
return
boot_uri = bios_settings['links']['Boot']['href']
status, headers, boot_settings = rest_get(host, boot_uri, None, iLO_loginname, iLO_password)
# if the BIOS doesn't support PATCH, go get the Settings, which should
if not operation_allowed(headers, 'PATCH'): # this is GET-only
boot_uri = boot_settings['links']['Settings']['href']
status, headers, boot_settings = rest_get(host, boot_uri, None, iLO_loginname, iLO_password)
assert(operation_allowed(headers, 'PATCH')) # this allows PATCH
# we don't need to PATCH back everything, just the one property we want to change
new_boot_settings = dict()
new_boot_settings['PersistentBootConfigOrder'] = boot_settings['PersistentBootConfigOrder']
# TODO - rearrange new_boot_settings['PersistentBootConfigOrder'] with the desired order
# supply the BIOS setup iLO_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
# perform the patch
print('PATCH ' + json.dumps(new_boot_settings) + ' to ' + boot_uri)
status, headers, response = rest_patch(host, boot_uri, request_headers, new_boot_settings, iLO_loginname, iLO_password)
print('PATCH response = ' + str(status))
print_extended_error(response)
assert(status < 300)
# point made...quit
break
# noinspection PyPep8Naming
def ex6_change_temporary_boot_order(host, boottarget, iLO_loginname, iLO_password):
print('EXAMPLE 6: Change temporary boot order (one time boot or temporary override)')
# for each system in the systems collection at /rest/v1/Systems
for status, headers, system, memberuri in collection(host, '/rest/v1/Systems', None, iLO_loginname, iLO_password):
# 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')
# verify it supports PATCH
assert(operation_allowed(headers, 'PATCH'))
# verify the requested boot target is supported
if boottarget in system['Boot']['BootSourceOverrideSupported']:
# build a PATCH payload to change to the requested boot target
boot = dict()
boot['Boot'] = dict()
boot['Boot']['BootSourceOverrideTarget'] = boottarget
# perform the POST action
print('PATCH ' + json.dumps(boot) + ' to ' + memberuri)
status, headers, response = rest_patch(host, memberuri, None, boot, iLO_loginname, iLO_password)
print('PATCH response = ' + str(status))
print_extended_error(response)
else: # target not in supported list
print('\tBootSourceOverrideTarget value "' + boottarget + '" is not supported. Valid values are:')
for tgt in system['Boot']['BootSourceOverrideSupported']:
print('\t\t' + tgt)
# point made...quit
break
# noinspection PyPep8Naming
def ex7_find_iLO_MAC_address(host, iLO_loginname, iLO_password):
print("EXAMPLE 7: Find iLO's MAC Addresses")
# for each system in the systems collection at /rest/v1/Systems
for status, headers, manager, memberuri in collection(host, '/rest/v1/Managers', None, iLO_loginname, iLO_password):
# 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(manager) == 'Manager.0' or get_type(manager) == 'Manager.1')
# for each system in the systems collection at /rest/v1/Systems
for status, headers, nic, memberuri in collection(host, manager['links']['EthernetNICs']['href'], None, iLO_loginname, iLO_password):
# 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(nic) == 'EthernetNetworkInterface.0' or get_type(nic) == 'EthernetNetworkInterface.1')
if 'MacAddress' not in nic:
print('\tNIC resource does not contain "MacAddress" property')
else:
print('\t' + manager['Model'] + ' ' + nic['Name'] + ' = ' + nic['MacAddress'] + '\t(' + nic['Status']['State'] + ')')
# noinspection PyPep8Naming
def ex8_add_iLO_user_account(host, iLO_loginname, iLO_password, new_iLO_loginname, new_iLO_username, new_iLO_password, irc=False, cfg=False, vm=False, usercfg=False, vpr=False):
print('EXAMPLE 8: Create an iLO User Account')
# get the URI of the Accounts collection (not standardized)
status, headers, obj = rest_get(host, '/rest/v1/AccountService', None, iLO_loginname, iLO_password)
assert(status == 200)
account_collection = obj['links']['Accounts']['href']
# build up a new account object to create
# iLO has two user account properties:
# Login name = the string used as the user identity to log in - we use this for 'UserName'
# User name = the friendly (or full) name of the user
# Potentially easy to reverse, so be careful - use the iLO account login name as 'UserName' in the API
user = {'UserName': new_iLO_loginname, 'Password': new_iLO_password, 'Oem': {}}
# Supply the full name as LoginName
user['Oem']['Hp'] = {}
user['Oem']['Hp']['LoginName'] = new_iLO_username # again this is tricky: LoginName gets the friendly user name
# plug in the requested privileges, by default you get LoginPriv and nothing else
user['Oem']['Hp']['Privileges'] = {}
user['Oem']['Hp']['Privileges']['RemoteConsolePriv'] = irc
user['Oem']['Hp']['Privileges']['iLOConfigPriv'] = cfg
user['Oem']['Hp']['Privileges']['VirtualMediaPriv'] = vm
user['Oem']['Hp']['Privileges']['UserConfigPriv'] = usercfg
user['Oem']['Hp']['Privileges']['VirtualPowerAndResetPriv'] = vpr
# create the account
print('POST ' + json.dumps(user) + ' to ' + account_collection)
status, headers, response = rest_post(host, account_collection, None, user, iLO_loginname, iLO_password)
print('POST response = ' + str(status))
print_extended_error(response)
if status == 201:
# this is the new account URI
new_account_uri = headers['location'] # HTTP headers are not case sensitive
print('Account ' + new_account_uri + ' created')
# get the new account resource
# it is possible that a future version of iLO will simply return the new account resource in the create response
status, headers, acct = rest_get(host, urlparse(new_account_uri).path, None, iLO_loginname, iLO_password)
assert(status == 200)
#print('Account info: ' + json.dumps(acct, indent=4))
# demonstration of how to remove the account using the Location header
#status, headers, response = rest_delete(host, urlparse(new_account_uri).path, None, iLO_loginname, iLO_password)
#assert(status == 200)
#print('Account ' + new_account_uri + ' removed')
# noinspection PyPep8Naming
def ex9_modify_iLO_user_account(host, iLO_loginname, iLO_password, iLO_login_name_to_modify, new_loginname=None, new_username=None, new_password=None, irc=None, cfg=None, vm=None, usercfg=None, vpr=None):
print('EXAMPLE 9: Modify an iLO user account')
# get the URI of the Accounts collection (not standardized)
status, headers, obj = rest_get(host, '/rest/v1/AccountService', None, iLO_loginname, iLO_password)
assert(status == 200)
account_collection = obj['links']['Accounts']['href']
# for each system in the systems collection at /rest/v1/Systems
for status, headers, account, memberuri in collection(host, account_collection, None, iLO_loginname, iLO_password):
# iLO has two user account properties:
# Login name = the string used as the user identity to log in - we use this for 'UserName'
# User name = the friendly (or full) name of the user
# Potentially easy to reverse, so be careful - use the iLO account login name as 'UserName' in the API
if account['UserName'] == iLO_login_name_to_modify:
# Cannot read/modify/write same object because iLO will reject non-PATCHable properties
# i.e. if I just change 'iLO_password' and PATCH back I get an error identifying 'Description' as non-PATCHable
# this resource handler is a little picky about passing in empty objects, so only add what we need by
# assembling what's actually used.
mod_user = {}
mod_user_oemhp = {}
mod_user_oemhp_privs = {}
# if new loginname or password specified
if new_password: mod_user['Password'] = new_password
if new_loginname: mod_user['UserName'] = new_loginname
# if different username specified
if new_username: mod_user_oemhp['LoginName'] = new_username
# if different privileges were requested (None = no change)
if irc != None: mod_user_oemhp_privs['RemoteConsolePriv'] = irc
if vm != None: mod_user_oemhp_privs['VirtualMediaPriv'] = vm
if cfg != None: mod_user_oemhp_privs['iLOConfigPriv'] = cfg
if usercfg != None: mod_user_oemhp_privs['UserConfigPriv'] = usercfg
if vpr != None: mod_user_oemhp_privs['VirtualPowerAndResetPriv'] = vpr
# component assembly
if len(mod_user_oemhp_privs):
mod_user_oemhp['Privileges'] = mod_user_oemhp_privs
if len(mod_user_oemhp):
mod_user['Oem'] = {'Hp': mod_user_oemhp}
# patch the account
status, headers, response = rest_patch(host, memberuri, None, mod_user, iLO_loginname, iLO_password)
# Warning, if you don't change anything, you will get an HTTP 400 back
#assert(status == 200)
if status == 200:
print('Account ' + memberuri + ' account modified')
else:
print('Account ' + memberuri + ' account not modified. HTTP Status = ' + str(status))
print_extended_error(response)
return
print('Account not found')
# noinspection PyPep8Naming
def ex10_remove_iLO_account(host, iLO_loginname, iLO_password, iLO_loginname_to_remove):
print('EXAMPLE 10: Remove an iLO account')
# get the URI of the Accounts collection (not standardized)
status, headers, obj = rest_get(host, '/rest/v1/AccountService', None, iLO_loginname, iLO_password)
assert(status == 200)
account_collection = obj['links']['Accounts']['href']
# for each system in the systems collection at /rest/v1/Systems
for status, headers, account, memberuri in collection(host, account_collection, None, iLO_loginname, iLO_password):
# iLO has two user account properties:
# Login name = the string used as the user identity to log in - we use this for 'UserName'
# User name = the friendly (or full) name of the user
# Potentially easy to reverse, so be careful - use the iLO account login name as 'UserName' in the API
if account['UserName'] == iLO_loginname_to_remove:
# demonstration of how to remove the account
status, headers, response = rest_delete(host, memberuri, None, iLO_loginname, iLO_password)
print_extended_error(response)
assert(status == 200)
print('Account ' + memberuri + ' removed')
return
print('Account not found')
# noinspection PyPep8Naming
def ex11_dump_iLO_NIC(host, iLO_loginname, iLO_password):
print('EXAMPLE 11: Get iLO NIC state')
# for each system in the systems collection at /rest/v1/Systems
for status, headers, manager, memberuri in collection(host, '/rest/v1/Managers', None, iLO_loginname, iLO_password):
# 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(manager) == 'Manager.0' or get_type(manager) == 'Manager.1')
# for each system in the systems collection at /rest/v1/Systems
for status, headers, nic, memberuri in collection(host, manager['links']['EthernetNICs']['href'], None, iLO_loginname, iLO_password):
# 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(nic) == 'EthernetNetworkInterface.0' or get_type(nic) == 'EthernetNetworkInterface.1')
if nic['Status']['State'] == 'Enabled':
print('\t' + nic['Name'])
if 'MacAddress' not in nic:
print('\tNo MacAddress information available (no "MacAddress" property in NIC resource)')
else:
print('\tMAC: ' + str(nic['MacAddress']))
print('\tSpeed: ' + str(nic['SpeedMbps']))
print('\tAutosense: ' + str(nic['Autosense']))
print('\tFull Duplex: ' + str(nic['FullDuplex']))
if 'FQDN' not in nic:
print('\tNo FQDN information available')
else:
print('\tFQDN: ' + str(nic['FQDN']))
for addr in nic['IPv4Addresses']:
print('\tIPv4 Address: ' + addr['Address'] + ' from ' + addr['AddressOrigin'])
if 'IPv6Addresses' not in nic:
print('\tIPv6Addresses information not available')
else:
for addr in nic['IPv6Addresses']:
print('\tIPv6 Address: ' + addr['Address'] + ' from ' + addr['AddressOrigin'])
#print(json.dumps(nic, indent=4))
def ex12_sessions(host, iLO_loginname, iLO_password):
print('EXAMPLE 12: Create/Use/Delete a user session')
# build up a new session object to create
# iLO has two user account properties:
# Login name = the string used as the user identity to log in - we use this for 'UserName'
# User name = the friendly (or full) name of the user
# Potentially easy to reverse, so be careful - use the iLO account login name as 'UserName' in the API
new_session = {"UserName": iLO_loginname, "Password": iLO_password}
# create the session
print('POST ' + json.dumps(new_session) + ' to /rest/v1/Sessions')
status, headers, response = rest_post(host, '/rest/v1/Sessions', None, new_session, iLO_loginname, iLO_password)
print('POST response = ' + str(status) + ', ' + str(response))
assert(status == 201)
# this is the new account URI
session_uri = headers['location'] # iLO returns lower case header names, be careful of casing. HTTP says headers are case insensitive.
session_uri = urlparse(session_uri).path
print('\tSession ' + session_uri + ' created')
x_auth_token = headers['x-auth-token']
# get the new session resource
# This could be returned on the create operation instead of the ExtendedError created result
# Instead of iLO_loginname/iLO_password here, use the newly supplied x_auth_token and the low level form of rest_op
request_headers = {'X-Auth-Token': x_auth_token}
status, headers, session = rest_op('GET', host, session_uri, request_headers, None, None, None, x_auth_token)
assert(status == 200)
assert(session['Oem']['Hp']['MySession'] == True) # this flag is used for "whoami"
#print('Session info: ' + json.dumps(session, indent=4))
print('\tLogged in from IP ' + session['Oem']['Hp']['UserIP'] + ' using session token ' + x_auth_token)
# demonstration of how to log out
status, headers, response = rest_delete(host, session_uri, None, iLO_loginname, iLO_password)
assert(status == 200)
print('\tSession ' + session_uri + ' logged out')
# noinspection PyPep8Naming
def ex13_set_uid_light(host, uid, iLO_loginname, iLO_password):
print('EXAMPLE 13: Set UID Light on or off')
# for each system in the systems collection at /rest/v1/Systems
for status, headers, system, memberuri in collection(host, '/rest/v1/Systems', None, iLO_loginname, iLO_password):
# 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')
# verify it supports POST
assert(operation_allowed(headers, 'PATCH'))
uid_state = dict()
if uid:
uid_state['IndicatorLED'] = 'Lit'
else:
uid_state['IndicatorLED'] = 'Off'
# perform the POST action
print('PATCH ' + json.dumps(uid_state) + ' to ' + memberuri)
status, headers, response = rest_patch(host, memberuri, None, uid_state, iLO_loginname, iLO_password)
print('PATCH response = ' + str(status))
print_extended_error(response)
assert(status < 300)
# point made...quit
break
# noinspection PyPep8Naming
def ex14_computer_details(host, iLO_loginname, iLO_password):
print('EXAMPLE 14: Dump host computer details')
# for each system in the systems collection at /rest/v1/Systems
for status, headers, system, memberuri in collection(host, '/rest/v1/Systems', None, iLO_loginname, iLO_password):
# 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')
# wrap these values in str() because some that are normally strings can also be null (None in Python)
print('\tManufacturer: ' + str(system['Manufacturer']))
print('\tModel: ' + str(system['Model']))
print('\tSerial Number: ' + str(system['SerialNumber']))
if 'VirtualSerialNumber' in system:
print('\tVirtual Serial Number: ' + str(system['VirtualSerialNumber']))
else:
print('\tVirtual Serial Number information not available on system resource')
print('\tUUID: ' + str(system['UUID']))
if 'VirtualUUID' in system['Oem']['Hp']:
print('\tVirtualUUID: ' + str(system['Oem']['Hp']['VirtualUUID']))
else:
print('\tVirtualUUID not available system resource')
if 'AssetTag' in system:
print('\tAsset Tag: ' + system['AssetTag'])
else:
print('\tNo Asset Tag information on system resource')
print('\tBIOS Version: ' + system['Bios']['Current']['VersionString'])
print('\tMemory: ' + str(system['Memory']['TotalSystemMemoryGB']) + ' GB')
print('\tProcessors: ' + str(system['Processors']['Count']) + ' x ' + str(system['Processors']['ProcessorFamily']))
if 'Status' not in system or 'Health' not in system['Status']:
print('\tStatus/Health information not available in system resource')
else:
print('\tHealth: ' + str(system['Status']['Health']))
if 'HostCorrelation' in system:
if 'HostFQDN' in system['HostCorrelation']:
print('\tHost FQDN: ' + system['HostCorrelation']['HostFQDN'])
if 'HostMACAddress' in system['HostCorrelation']:
for mac in system['HostCorrelation']['HostMACAddress']:
print('\tHost MAC Address: ' + str(mac))
if 'HostName' in system['HostCorrelation']:
print('\tHost Name: ' + system['HostCorrelation']['HostName'])
if 'IPAddress' in system['HostCorrelation']:
for ip in system['HostCorrelation']['IPAddress']:
print('\tHost IP Address: ' + str(ip))
# point made...quit
break
# Mount an ISO to virtual media and optionally specify whether it should be the boot target for the next server reset.
# if iso_url is left out, it unmounts
# if boot_on_next_server_reset is left out the option is not set one way or the other
# noinspection PyPep8Naming
def ex15_mount_virtual_media_dvd_iso(host, iLO_loginname, iLO_password, iso_url = None, boot_on_next_server_reset = None):
print('EXAMPLE 15: Mount iLO Virtual Media DVD ISO from URL')
# for each system in the systems collection at /rest/v1/Systems
for status, headers, manager, memberuri in collection(host, '/rest/v1/Managers', None, iLO_loginname, iLO_password):
# 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(manager) == 'Manager.0' or get_type(manager) == 'Manager.1')
if 'VirtualMedia' not in manager['links']:
print('\tVirtualMedia not available in manager links')
continue
# for each system in the systems collection at /rest/v1/Systems
for status, headers, vm, memberuri in collection(host, manager['links']['VirtualMedia']['href'], None, iLO_loginname, iLO_password):
# 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(vm) == 'VirtualMedia.0' or get_type(vm) == 'VirtualMedia.1')
if 'DVD' in vm['MediaTypes']:
mount = {'Image': iso_url}
# only add this if we are mounting and user specified something to set
if iso_url is not None and boot_on_next_server_reset is not None:
mount['Oem'] = {'Hp': {'BootOnNextServerReset': boot_on_next_server_reset}}
# perform the patch
print('PATCH ' + json.dumps(mount) + ' to ' + memberuri)
status, headers, response = rest_patch(host, memberuri, None, mount, iLO_loginname, iLO_password)
print('PATCH response = ' + str(status))
print_extended_error(response)
if status == 400:
#this will respond with 400 if the vm state is already in this state assert(status < 300)
pass
else:
assert(status < 300)
# finished
break
break
# noinspection PyPep8Naming
def ex16_set_server_asset_tag(host, iLO_loginname, iLO_password, asset_tag):
print('EXAMPLE 16: Set Computer Asset Tag')
# for each system in the systems collection at /rest/v1/Systems
for status, headers, system, memberuri in collection(host, '/rest/v1/Systems', None, iLO_loginname, iLO_password):
# 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')
a = {'AssetTag': asset_tag}
# perform the patch
print('PATCH ' + json.dumps(a) + ' to ' + memberuri)
status, headers, response = rest_patch(host, memberuri, None, a, iLO_loginname, iLO_password)
print('PATCH response = ' + str(status))
print_extended_error(response)
assert(status < 300)
# point made...quit
break
# noinspection PyPep8Naming
def ex17_reset_iLO(host, iLO_loginname, iLO_password):
print('EXAMPLE 17: Reset iLO')
# for each system in the systems collection at /rest/v1/Systems
for status, headers, manager, memberuri in collection(host, '/rest/v1/Managers', None, iLO_loginname, iLO_password):
# 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(manager) == 'Manager.0' or get_type(manager) == 'Manager.1')
action = {'Action': 'Reset'}
# perform the POST
print('POST ' + json.dumps(action) + ' to ' + memberuri)
status, headers, response = rest_post(host, memberuri, None, action, iLO_loginname, iLO_password)
print_extended_error(response)
assert(status == 200)
break
# noinspection PyPep8Naming
def ex18_get_iLO_NIC(host, iLO_loginname, iLO_password, get_active=True):
print("EXAMPLE 18: Get iLO's NIC configuration")
# for each system in the systems collection at /rest/v1/Systems
for status, headers, manager, memberuri in collection(host, '/rest/v1/Managers', None, iLO_loginname, iLO_password):
# 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(manager) == 'Manager.0' or get_type(manager) == 'Manager.1')
# for each system in the systems collection at /rest/v1/Systems
for status, headers, nic, memberuri in collection(host, manager['links']['EthernetNICs']['href'], None, iLO_loginname, iLO_password):
# 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(nic) == 'EthernetNetworkInterface.0' or get_type(nic) == 'EthernetNetworkInterface.1')
if get_active and nic['Status']['State'] == 'Enabled':
# this is the active NIC
return memberuri, nic
elif get_active == False and nic['Status']['State'] == 'Disabled':
# this is the inactive NIC
return memberuri, nic
def ex19_set_active_iLO_nic(host, iLO_loginname, iLO_password, shared_nic=False):
print("EXAMPLE 19: Set the active iLO NIC")
# for each system in the systems collection at /rest/v1/Systems
for status, headers, manager, memberuri in collection(host, '/rest/v1/Managers', None, iLO_loginname, iLO_password):
# 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(manager) == 'Manager.0' or get_type(manager) == 'Manager.1')
selected_nic_uri = None
# for each system in the systems collection at /rest/v1/Systems
for status, headers, nic, memberuri in collection(host, manager['links']['EthernetNICs']['href'], None, iLO_loginname, iLO_password):
# 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(nic) == 'EthernetNetworkInterface.0' or get_type(nic) == 'EthernetNetworkInterface.1')
# this is a little complex but we are just looking for the dedicated vs. shared NIC without
# assuming the order or URIs in the NICs collection.
try:
if nic['Oem']['Hp']['SupportsFlexibleLOM'] == True and shared_nic == True:
# this is the shared NIC
selected_nic_uri = memberuri
break
except KeyError:
pass
try:
if nic['Oem']['Hp']['SupportsLOM'] == True and shared_nic == True:
# this is the shared NIC
selected_nic_uri = memberuri
break
except KeyError:
pass
if not shared_nic:
selected_nic_uri = memberuri
break
elif not selected_nic_uri:
print('\tShared NIC is not supported')
break
# we should have found the desired NIC
if selected_nic_uri:
# build the request header
request = {'Oem': {'Hp': {'NICEnabled': True}}}
# perform the PATCH
print('PATCH ' + json.dumps(request) + ' to ' + memberuri)
status, headers, response = rest_patch(host, selected_nic_uri, None, request, iLO_loginname, iLO_password)
print_extended_error(response)
# this will require an iLO reset to take effect (see ex17_reset_iLO)
def ex20_dump_Integrated_Management_Log(host, iLO_loginname, iLO_password):
print('EXAMPLE 20: Dump Integrated Management Log')
# for each system in the systems collection at /rest/v1/Systems
for status, headers, system, memberuri in collection(host, '/rest/v1/Systems', None, iLO_loginname, iLO_password):
# 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 'Logs' not in system['links']:
print('\tLogs not available on system resource')
return
logsuri = system['links']['Logs']['href']
for status, headers, log, memberuri in collection(host, logsuri, None, iLO_loginname, iLO_password):
assert(get_type(log) == 'LogService.0' or get_type(log) == 'LogService.1')
entries_collection_array = log['links']['Entries']
for entries_collection in entries_collection_array:
entries_collection_uri = entries_collection['href']
for status, headers, log_entry, log_entry_uri in collection(host, entries_collection_uri, None, iLO_loginname, iLO_password):
print(log_entry['Severity'] + ': Class ' + str(log_entry['Oem']['Hp']['Class']) + ' / Code ' + str(log_entry['Oem']['Hp']['Code']) + ':\t' + log_entry['Message'])
# example only - don't iterate all systems
break
def ex21_dump_iLO_event_log(host, iLO_loginname, iLO_password):
print('EXAMPLE 21: Dump iLO Event Log')
# for each manager in the managers collection at /rest/v1/Managers
for status, headers, manager, memberuri in collection(host, '/rest/v1/Managers', None, iLO_loginname, iLO_password):
# 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(manager) == 'Manager.0' or get_type(manager) == 'Manager.1')
if 'Logs' not in manager['links']:
print('\tLogs not available on manager resource')
return
logsuri = manager['links']['Logs']['href']
for status, headers, log, memberuri in collection(host, logsuri, None, iLO_loginname, iLO_password):
assert(get_type(log) == 'LogService.0' or get_type(log) == 'LogService.1')
entries_collection_array = log['links']['Entries']
for entries_collection in entries_collection_array:
entries_collection_uri = entries_collection['href']
for status, headers, log_entry, log_entry_uri in collection(host, entries_collection_uri, None, iLO_loginname, iLO_password):
print(log_entry['Message'])
#status, headers, response = rest_get(host, iml, None, iLO_loginname, iLO_password)
# example only - don't iterate all managers
break
def ex22_clear_Integrated_Management_Log(host, iLO_loginname, iLO_password):
print('EXAMPLE 20: Clear Integrated Management Log')
# for each system in the systems collection at /rest/v1/Systems
for status, headers, system, memberuri in collection(host, '/rest/v1/Systems', None, iLO_loginname, iLO_password):
# 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 'Logs' not in system['links']:
print('\tLogs not available on system resource')
return
logsuri = system['links']['Logs']['href']
for status, headers, log, memberuri in collection(host, logsuri, None, iLO_loginname, iLO_password):
assert(get_type(log) == 'LogService.0' or get_type(log) == 'LogService.1')
action = {'Action': 'ClearLog'}
# perform the POST
print('POST ' + json.dumps(action) + ' to ' + memberuri)
status, headers, response = rest_post(host, memberuri, None, action, iLO_loginname, iLO_password)
print_extended_error(response)
assert(status == 200)
break
# example only - don't iterate all systems
break
def ex23_clear_iLO_event_log(host, iLO_loginname, iLO_password):
print('EXAMPLE 20: Clear iLO Event Log')
# for each manager in the managers collection at /rest/v1/Managers
for status, headers, manager, memberuri in collection(host, '/rest/v1/Managers', None, iLO_loginname, iLO_password):
# 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(manager) == 'Manager.0' or get_type(manager) == 'Manager.1')
if 'Logs' not in manager['links']:
print('\tLogs not available on manager resource')
return
logsuri = manager['links']['Logs']['href']
for status, headers, log, memberuri in collection(host, logsuri, None, iLO_loginname, iLO_password):
assert(get_type(log) == 'LogService.0' or get_type(log) == 'LogService.1')
action = {'Action': 'ClearLog'}
# perform the POST
print('POST ' + json.dumps(action) + ' to ' + memberuri)
status, headers, response = rest_post(host, memberuri, None, action, iLO_loginname, iLO_password)
print_extended_error(response)
assert(status == 200)
break
# example only - don't iterate all managers
break
def ex24_configure_SNMP(host, iLO_loginname, iLO_password, snmp_mode, snmp_alerts):
print("EXAMPLE 24: Configure iLO SNMP Settings")
# for each system in the systems collection at /rest/v1/Systems
for status, headers, manager, memberuri in collection(host, '/rest/v1/Managers', None, iLO_loginname, iLO_password):
# 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(manager) == 'Manager.0' or get_type(manager) == 'Manager.1')
# get the Network Service resource
status, headers, network_service = rest_get(host, manager['links']['NetworkService']['href'], None, iLO_loginname, iLO_password)
assert(get_type(network_service) == 'ManagerNetworkService.0' or get_type(network_service) == 'ManagerNetworkService.1')
# get the SNMP resource
if 'SNMPService' not in network_service['links']:
print('\tSNMPService not found in manager network service links')
continue
status, headers, snmp_service = rest_get(host, network_service['links']['SNMPService']['href'], None, iLO_loginname, iLO_password)
assert(get_type(snmp_service) == 'SnmpService.0' or get_type(snmp_service) == 'SnmpService.1')
config = {'Mode': snmp_mode, 'AlertsEnabled': snmp_alerts}
# perform the POST
print('PATCH ' + json.dumps(config) + ' to ' + network_service['links']['SNMPService']['href'])
status, headers, response = rest_patch(host, network_service['links']['SNMPService']['href'], None, config, iLO_loginname, iLO_password)
print_extended_error(response)
assert(status == 200)
def ex25_get_schema(host, iLO_loginname, iLO_password, schema_prefix):
# This could be augmented to return a specific language
print("EXAMPLE 25: Find and return schema " + schema_prefix)
# for each system in the systems collection at /rest/v1/Systems
for status, headers, schemaptr, memberuri in collection(host, '/rest/v1/Schemas', None, iLO_loginname, iLO_password):
# 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(schemaptr) == 'SchemaFile.0' or get_type(schemaptr) == 'SchemaFile.1')
if schemaptr['Schema'].startswith(schema_prefix):
for location in schemaptr['Location']:
# this is an extref rather than an href because the actual registries/schemas lie outside the data model
extref_uri = location['Uri']['extref']
status, headers, schema = rest_get(host, extref_uri, None, iLO_loginname, iLO_password)
if status == 200:
print('\tFound ' + schema_prefix + ' at ' + extref_uri)
return extref_uri, schema
else:
print('\t' + schema_prefix + ' not found at ' + extref_uri)
return None, None
print('Registry ' + schema_prefix + ' not found.')
def ex26_get_registry(host, iLO_loginname, iLO_password, registry_prefix):
# This could be augmented to return a specific language
print("EXAMPLE 26: Find and return registry " + registry_prefix)
# for each system in the systems collection at /rest/v1/Systems
for status, headers, registryptr, memberuri in collection(host, '/rest/v1/Registries', None, iLO_loginname, iLO_password):
# 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(registryptr) == 'SchemaFile.0' or get_type(registryptr) == 'SchemaFile.1')
if 'Schema' in registryptr and registryptr['Schema'].startswith(registry_prefix):
for location in registryptr['Location']:
# this is an extref rather than an href because the actual registries/schemas lie outside the data model
extref_uri = location['Uri']['extref']
status, headers, registry = rest_get(host, extref_uri, None, iLO_loginname, iLO_password)
if status == 200:
print('\tFound ' + registry_prefix + ' at ' + extref_uri)
return extref_uri, registry
else:
print('\t' + registry_prefix + ' not found at ' + extref_uri)
return None, None
print('Registry ' + registry_prefix + ' not found.')
return None, None
def ex27_set_iLO_timezone(host, olson_timezone, iLO_loginname, iLO_password):
# this only works if iLO is NOT configured to take time settings from DHCP v4 or v6
print("EXAMPLE 27: Set iLO's Timezone")
print("\tNOTE: This only works if iLO is NOT configured to take time settings from DHCP v4 or v6")
# for each system in the systems collection at /rest/v1/Systems
for status, headers, manager, memberuri in collection(host, '/rest/v1/Managers', None, iLO_loginname, iLO_password):
# 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(manager) == 'Manager.0' or get_type(manager) == 'Manager.1')
# for each system in the systems collection at /rest/v1/Systems
status, headers, datetime = rest_get(host, manager['Oem']['Hp']['links']['DateTimeService']['href'], None, iLO_loginname, iLO_password)
# print current time zone
print('\tCurrent iLO timezone: ' + datetime['TimeZone']['Name'])
# find time zone from list
for tz in datetime['TimeZoneList']:
if tz['Name'].startswith(olson_timezone):
request = {'TimeZone': {'Index': tz['Index']}}
print('PATCH ' + json.dumps(request) + ' to ' + manager['Oem']['Hp']['links']['DateTimeService']['href'])
status, headers, response = rest_patch(host, manager['Oem']['Hp']['links']['DateTimeService']['href'], None, request, iLO_loginname, iLO_password)
print_extended_error(response)
if status == 200:
print('\tiLO TimeZone set to ' + tz['Name'])
return
print('\tTimeZone value not found')
def ex28_set_iLO_NTP_servers(host, ntp_servers, iLO_loginname, iLO_password):
print("EXAMPLE 28: Set iLO's NTP Servers")
# for each system in the systems collection at /rest/v1/Systems
for status, headers, manager, memberuri in collection(host, '/rest/v1/Managers', None, iLO_loginname, iLO_password):
# 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(manager) == 'Manager.0' or get_type(manager) == 'Manager.1')
# for each system in the systems collection at /rest/v1/Systems
status, headers, datetime = rest_get(host, manager['Oem']['Hp']['links']['DateTimeService']['href'], None, iLO_loginname, iLO_password)
# print current time zone
print('\tCurrent iLO Date/Time Settings: ' + json.dumps(datetime['ConfigurationSettings']))
print('\tCurrent iLO NTP Servers: ' + json.dumps(datetime['NTPServers']))
request = {'StaticNTPServers': ntp_servers}
print('PATCH ' + json.dumps(request) + ' to ' + manager['Oem']['Hp']['links']['DateTimeService']['href'])
status, headers, response = rest_patch(host, manager['Oem']['Hp']['links']['DateTimeService']['href'], None, request, iLO_loginname, iLO_password)
print_extended_error(response)
print('\tChanges in pending settings require an iLO reset to become active.')
def ex29_get_PowerMetrics_Average(host, iLO_loginname, iLO_password):
# https://<host>/rest/v1/Chassis/{item}/PowerMetrics#/PowerMetrics/AverageConsumedWatts
print("EXAMPLE 29: Report PowerMetrics Average Watts")
# for each chassis in the chassis collection at /rest/v1/Chassis
for status, headers, chassis, memberuri in collection(host, '/rest/v1/Chassis', None, iLO_loginname, iLO_password):
# 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(chassis) == 'Chassis.0' or get_type(chassis) == 'Chassis.1')
# for each chassis in the chassis collection at /rest/v1/Chassis
status, headers, pwrmetric = rest_get(host, chassis['links']['PowerMetrics']['href'], None, iLO_loginname, iLO_password)
# 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(pwrmetric) == 'PowerMetrics.0' or get_type(pwrmetric) == 'PowerMetrics.1')
if ('PowerMetrics' not in pwrmetric or
'AverageConsumedWatts' not in pwrmetric['PowerMetrics'] or
'IntervalInMin' not in pwrmetric['PowerMetrics']):
print('\tPowerMetrics resource does not contain "AverageConsumedWatts" or "IntervalInMin" property')
else:
print('\t' + chassis['Model'] + ' AverageConsumedWatts = ' + str(pwrmetric['PowerMetrics']['AverageConsumedWatts']) + ' watts over a ' + str(pwrmetric['PowerMetrics']['IntervalInMin']) + ' minute moving average' )
# Run the tests
# commonly needed function values (typically would be passed in by argument)
host = 'hostname'
iLO_loginname = 'username'
iLO_password = 'password'
bios_password = None
print('Tutorial Examples 0.9.12 BETA for HP RESTful API')
print('Copyright 2002-2014 Hewlett-Packard Development Company, L.P.')
print('For more information see www.hp.com/go/restfulapi')
print('Uncomment the ex* functions in the script file to see use case examples')
# comment this out to get to the test cases - we just don't want to start messing with any network endpoints
# without deliberate alteration to enable.
print('NOTE: Remove the sys.exit call here to run the test cases.')
sys.exit(-1)
if False:
# Get the message registries (we could just iterate through but for convenience and clarity, we'll just grab 2 of them)
# This is optional, but nice if you want to turn error structures into nice strings
reguri, message_registries['Base'] = ex26_get_registry(host, iLO_loginname, iLO_password, 'Base')
reguri, message_registries['iLO'] = ex26_get_registry(host, iLO_loginname, iLO_password, 'iLO')
if False:
ex1_change_bios_setting(host, 'AdminName', 'Mr. Rest', iLO_loginname, iLO_password, bios_password)
ex2_reset_server(host, iLO_loginname, iLO_password)
ex3_enable_secure_boot(host, False, iLO_loginname, iLO_password)
ex4_bios_revert_default(host, iLO_loginname, iLO_password)
ex5_change_boot_order(host, iLO_loginname, iLO_password, bios_password)
if False:
ex6_change_temporary_boot_order(host, 'Pxe', iLO_loginname, iLO_password)
ex6_change_temporary_boot_order(host, 'Hdd', iLO_loginname, iLO_password)
if False:
ex7_find_iLO_MAC_address(host, iLO_loginname, iLO_password)
# iLO User Account Modification
if False:
# Create new account
ex8_add_iLO_user_account(host, iLO_loginname, iLO_password, 'jjackson', 'John Jackson', 'newpassword', irc=True, cfg=True, vm=True, usercfg=True, vpr=True)
# change login name, user name, and password
ex9_modify_iLO_user_account(host, iLO_loginname, iLO_password, 'jjackson', new_loginname='jjohnson', new_username='Jack Johnson', new_password='adifferentpassword') # change user/pass info
# Remove some privileges
ex9_modify_iLO_user_account(host, iLO_loginname, iLO_password, 'jjohnson', irc=False, vm=False, usercfg=False)
# Remove the account
ex10_remove_iLO_account(host, iLO_loginname, iLO_password, 'jjohnson')
if False:
ex11_dump_iLO_NIC(host, iLO_loginname, iLO_password)
ex12_sessions(host, iLO_loginname, iLO_password)
ex13_set_uid_light(host, True, iLO_loginname, iLO_password) # light it up
ex13_set_uid_light(host, False, iLO_loginname, iLO_password) # turn it off
ex14_computer_details(host, iLO_loginname, iLO_password)
# virtual media
if False:
# unmount (should fail with HTTP 400 of nothing is currently mounted)
ex15_mount_virtual_media_dvd_iso(host, iLO_loginname, iLO_password) # unmount
# mount ISO - replace this with a valid URI to an ISO file
iso_uri = 'http://someuri/dvdimg.iso'
ex15_mount_virtual_media_dvd_iso(host, iLO_loginname, iLO_password, iso_uri, boot_on_next_server_reset = True) # mount
# unmount again
ex15_mount_virtual_media_dvd_iso(host, iLO_loginname, iLO_password) # unmount
if False:
ex16_set_server_asset_tag(host, iLO_loginname, iLO_password, 'SampleAssetTag')
if False:
ex17_reset_iLO(host, iLO_loginname, iLO_password)
# Find iLO NIC (active/inactive, dedicated/shared)
if False:
# find and return the uri and resource of the currently active iLO NIC
nic_uri, content = ex18_get_iLO_NIC(host, iLO_loginname, iLO_password)
print ('Active\t' + nic_uri + ": " + json.dumps(content))
# set the Shared Network Port active
ex19_set_active_iLO_nic(host, iLO_loginname, iLO_password, shared_nic=True)
# set the Dedicated Network Port active
ex19_set_active_iLO_nic(host, iLO_loginname, iLO_password, shared_nic=False)
# Log dump and clearing
if False:
# dump both logs
ex20_dump_Integrated_Management_Log(host, iLO_loginname, iLO_password)
ex21_dump_iLO_event_log(host, iLO_loginname, iLO_password)
# clear both logs
ex22_clear_Integrated_Management_Log(host, iLO_loginname, iLO_password)
ex23_clear_iLO_event_log(host, iLO_loginname, iLO_password)
# SNMP Configuration
if False:
ex24_configure_SNMP(host, iLO_loginname, iLO_password, snmp_mode='Agentless', snmp_alerts=False)
# Get Schema
if False:
# getting all the schema from iLO is pretty slow - this is an example of how to get one by name
schema_uri, schema = ex25_get_schema(host, iLO_loginname, iLO_password, 'ComputerSystem')
# iLO TimeZone
if False:
ex27_set_iLO_timezone(host, 'America/Chicago', iLO_loginname, iLO_password)
# iLO NTP Servers
if False:
ex28_set_iLO_NTP_servers(host, ['192.168.0.1', '192.168.0.2'], iLO_loginname, iLO_password)
# Get average watts consumed.
if False:
ex29_get_PowerMetrics_Average(host, iLO_loginname, iLO_password)