python-redfish/redfish/types.py
Devananda van der Veen 2a70e6e765 Refactoring and adding Types
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).
2015-04-04 08:44:22 -07:00

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