From 878a32e07f81e4bdd64edff3ef8ce23ce82a1bbe Mon Sep 17 00:00:00 2001 From: Aija Jaunteva Date: Mon, 6 Aug 2018 15:49:05 +0300 Subject: [PATCH] Add support for loading resources from archive file As Redfish Message registries can be reside inside archives, added support to load resource from JSON files in archive. Currently supporting only ZIP archives, support for other archive types can be added as need arises or specification is clarified. The Redfish specification does not detail which types of archives need to be supported, but gives ZIP as an example. Change-Id: I3609df39c68f2149c1ff1a6818af7168bbd02df0 Story: 2001791 Task: 23062 --- sushy/exceptions.py | 4 + sushy/resources/base.py | 69 +++++++++++++++++- .../tests/unit/json_samples/TestRegistry.zip | Bin 0 -> 622 bytes sushy/tests/unit/resources/test_base.py | 45 +++++++++++- 4 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 sushy/tests/unit/json_samples/TestRegistry.zip diff --git a/sushy/exceptions.py b/sushy/exceptions.py index 21531c72..e6deb372 100644 --- a/sushy/exceptions.py +++ b/sushy/exceptions.py @@ -57,6 +57,10 @@ class InvalidParameterValueError(SushyError): 'Valid values are: %(valid_values)s') +class ArchiveParsingError(SushyError): + message = 'Failed parsing archive "%(path)s": %(error)s' + + class HTTPError(SushyError): """Basic exception for HTTP errors""" diff --git a/sushy/resources/base.py b/sushy/resources/base.py index 8bc46d35..e858ae91 100644 --- a/sushy/resources/base.py +++ b/sushy/resources/base.py @@ -16,7 +16,10 @@ import abc import collections import copy +import io +import json import logging +import zipfile import six @@ -244,13 +247,69 @@ class MappedField(Field): adapter=mapping.get) +@six.add_metaclass(abc.ABCMeta) +class AbstractJsonReader(object): + + def set_connection(self, connector, path): + """Sets mandatory connection parameters + + :param connector: A Connector instance + :param path: path of the resource + """ + self._conn = connector + self._path = path + + @abc.abstractmethod + def get_json(self): + """Based on data source get data and parse to JSON""" + + +class JsonFileReader(AbstractJsonReader): + """Gets the data from JSON file given by path""" + + def get_json(self): + """Gets JSON file from URI directly""" + return self._conn.get(path=self._path).json() + + +class JsonArchiveReader(AbstractJsonReader): + """Gets the data from JSON file in archive""" + + def __init__(self, archive_file): + """Initializes the reader + + :param archive_file: file name of JSON file in archive + """ + self._archive_file = archive_file + + def get_json(self): + """Gets JSON file from archive. Currently supporting ZIP only""" + + data = self._conn.get(path=self._path) + if data.headers.get('content-type') == 'application/zip': + try: + archive = zipfile.ZipFile(io.BytesIO(data.content)) + return json.loads(archive.read(self._archive_file) + .decode(encoding='utf-8')) + except (zipfile.BadZipfile, ValueError) as e: + raise exceptions.ArchiveParsingError( + path=self._path, error=e) + else: + LOG.error('Support for %(type)s not implemented', + {'type': data.headers['content-type']}) + + @six.add_metaclass(abc.ABCMeta) class ResourceBase(object): redfish_version = None """The Redfish version""" - def __init__(self, connector, path='', redfish_version=None): + def __init__(self, + connector, + path='', + redfish_version=None, + reader=JsonFileReader()): """A class representing the base of any Redfish resource Invokes the ``refresh()`` method of resource for the first @@ -259,6 +318,8 @@ class ResourceBase(object): :param path: sub-URI path to the resource. :param redfish_version: The version of Redfish. Used to construct the object according to schema of the given version. + :param reader: Reader to use to fetch JSON data. Defaults to + JsonFileReader """ self._conn = connector self._path = path @@ -269,6 +330,9 @@ class ResourceBase(object): # attribute values are fetched. self._is_stale = True + reader.set_connection(connector, path) + self._reader = reader + self.refresh() def _parse_attributes(self): @@ -299,7 +363,8 @@ class ResourceBase(object): if not self._is_stale and not force: return - self._json = self._conn.get(path=self._path).json() + self._json = self._reader.get_json() + LOG.debug('Received representation of %(type)s %(path)s: %(json)s', {'type': self.__class__.__name__, 'path': self._path, 'json': self._json}) diff --git a/sushy/tests/unit/json_samples/TestRegistry.zip b/sushy/tests/unit/json_samples/TestRegistry.zip new file mode 100644 index 0000000000000000000000000000000000000000..565c7c88648262e4bbc7bd9738514bf72d74accc GIT binary patch literal 622 zcmWIWW@Zs#-~hrowQRl&P_UbYfq|DnfgvQdxJ1uL&pk_tK5GT~Xw zgf>^jm5F+yFI?O;C9J!+!%l`AZaWpUZ*}ae7cEot*cvb2G&lGm6t+g4$2`;RTzW`C zrbl$b-0)AYEI66CWkmcfU#<06s*6L4bT5~q0e%_J7SJx_&H{E@@ z?)a*|DTd0XGwy3md;9$A1Hnv5^{aKY)@N>-<&^C`r~GNgq0MZ+H{H1PZ--lRoy&%h zBF|GxF1$P7bJCyb)~!5~%6$8ZhL4h~=StbQv~u5<`^Hmaf1v1&T-O@`SqAyuLf*g+ z4@%7Uw*ZtoAW?i4)MDG8pq4(p;QcfI=+qCx4-ps!Y0p9E!xxK&EEn;L~07Y?tHzSh> kGa~wt