2a70e6e765
Some big changes here: Rename connection.py to server.py Refactor about half of server.py into a new types.py module which builds classes for each resource type, and auto-builds links to fetch sub-resources from each type. Add examples/walk-chassis.py to demonstrate how to use the Root and Chassis classes to walk all the objects returned from /rest/v1/chassis/ Import oslo_log and start using it (more to do here, it's not working quite yet).
198 lines
6.1 KiB
Python
198 lines
6.1 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.
|
|
|
|
|
|
"""
|
|
Redfish Resource Types
|
|
"""
|
|
|
|
import base64
|
|
import gzip
|
|
import hashlib
|
|
import httplib
|
|
import json
|
|
import ssl
|
|
import StringIO
|
|
import sys
|
|
import urllib2
|
|
from urlparse import urlparse
|
|
|
|
from oslo_log import log as logging
|
|
from redfish import exception
|
|
|
|
LOG = logging.getLogger('redfish')
|
|
|
|
|
|
class Base(object):
|
|
def __init__(self, obj, connection=None):
|
|
self._conn = connection
|
|
"""handle to the redfish connection"""
|
|
|
|
self._attrs = []
|
|
"""list of discovered attributes"""
|
|
|
|
self._links = []
|
|
"""list of linked resources"""
|
|
|
|
# parse the individual resources, appending them to
|
|
# the list of object attributes
|
|
for k in obj.keys():
|
|
ref = k.lower()
|
|
if ref in ["links", "oem", "items"]:
|
|
continue
|
|
setattr(self, ref, obj[k])
|
|
self._attrs.append(ref)
|
|
|
|
# make sure the required attributes are present
|
|
if not getattr(self, 'name', False):
|
|
raise ObjectLoadException(
|
|
"Failed to load object. Reason: could not determine name.")
|
|
if not getattr(self, 'type', False):
|
|
raise ObjectLoadException(
|
|
"Failed to load object. Reason: could not determine type.")
|
|
|
|
if getattr(self, 'serviceversion', False):
|
|
self.type = self.type.replace('.' + self.serviceversion, '')
|
|
else:
|
|
# TODO: use a regex here to strip and store the version
|
|
# instead of assuming it is 7 chars long
|
|
self.type = self.type[:-7]
|
|
|
|
# Lastly, parse the 'links' resource.
|
|
# Note that this may have different nested structure, depending on
|
|
# what type of resource this is, or what vendor it is.
|
|
# subclasses may follow this by parsing other resources / collections
|
|
self._parse_links(obj)
|
|
|
|
def _parse_links(self, obj):
|
|
"""Map linked resources to getter functions
|
|
|
|
The root resource returns a dict of links to top-level resources
|
|
"""
|
|
def getter(connection, href):
|
|
def _get():
|
|
return connection.rest_get(href, {})
|
|
return _get
|
|
|
|
for k in obj['links']:
|
|
ref = "get_" + k.lower()
|
|
self._links.append(ref)
|
|
href = obj['links'][k]['href']
|
|
setattr(self, ref, getter(self._conn, href))
|
|
|
|
def __repr__(self):
|
|
"""Return this object's _attrs as a dict"""
|
|
res = {}
|
|
for a in self._attrs:
|
|
res[a] = getattr(self, a)
|
|
return res
|
|
|
|
def __str__(self):
|
|
"""Return the string representation of this object's _attrs"""
|
|
return json.dumps(self.__repr__())
|
|
|
|
|
|
class BaseCollection(Base):
|
|
"""Base class for collection types"""
|
|
def __init__(self, obj, connection=None):
|
|
super(BaseCollection, self).__init__(obj, connection=connection)
|
|
self._parse_items(obj)
|
|
self._attrs.append('items')
|
|
|
|
def _parse_links(self, obj):
|
|
"""links are special on a chassis; dont parse them"""
|
|
pass
|
|
|
|
def _parse_items(self, obj):
|
|
"""Map linked items to getter methods
|
|
|
|
The chassis resource returns a list of items and corresponding
|
|
link data in a separate entity.
|
|
"""
|
|
def getter(connection, href):
|
|
def _get():
|
|
return connection.rest_get(href, {})
|
|
return _get
|
|
|
|
self.items = []
|
|
self._item_getters = []
|
|
|
|
if 'links' in obj and 'Member' in obj['links']:
|
|
# NOTE: this assumes the lists are ordered the same
|
|
counter = 0
|
|
for item in obj['links']['Member']:
|
|
self.items.append(obj['Items'][counter])
|
|
self._item_getters.append(
|
|
getter(self._conn, item['href']))
|
|
counter+=1
|
|
elif 'Items' in obj:
|
|
# TODO: find an example of this format and make sure it works
|
|
for item in obj['Items']:
|
|
if 'links' in item and 'self' in item['links']:
|
|
href = item['links']['self']['href']
|
|
self.items.append(item)
|
|
|
|
# TODO: implement paging support
|
|
# if 'links' in obj and 'NextPage' in obj['links']:
|
|
# next_page = THIS_URI + '?page=' + str(obj['links']['NextPage']['page'])
|
|
# do something with next_page URI
|
|
|
|
def __iter__(self):
|
|
for getter in self._item_getters:
|
|
yield getter()
|
|
|
|
|
|
class Root(Base):
|
|
"""Root '/' resource class"""
|
|
def _parse_links(self, obj):
|
|
"""Map linked resources to getter functions
|
|
|
|
The root resource returns a dict of links to top-level resources
|
|
|
|
TODO: continue implementing customizations for top-level resources
|
|
|
|
"""
|
|
mapping = {
|
|
'Systems': Systems,
|
|
'Chassis': Chassis,
|
|
'Managers': Base,
|
|
'Schemas': Base,
|
|
'Registries': Base,
|
|
'Tasks': Base,
|
|
'AccountService': Base,
|
|
'Sessions': Base,
|
|
'EventService': Base,
|
|
}
|
|
|
|
def getter(connection, href, type):
|
|
def _get():
|
|
return mapping[type](connection.rest_get(href, {}), self._conn)
|
|
return _get
|
|
|
|
for k in obj['links']:
|
|
ref = "get_" + k.lower()
|
|
self._links.append(ref)
|
|
href = obj['links'][k]['href']
|
|
setattr(self, ref, getter(self._conn, href, k))
|
|
|
|
|
|
class Chassis(BaseCollection):
|
|
"""Chassis resource class"""
|
|
def __len__(self):
|
|
return len(self.items)
|
|
|
|
|
|
class Systems(Base):
|
|
pass
|