# 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