Blame tools/extra/packager/afu.py

Packit 534379
# Copyright(c) 2017, Intel Corporation
Packit 534379
#
Packit 534379
# Redistribution  and  use  in source  and  binary  forms,  with  or  without
Packit 534379
# modification, are permitted provided that the following conditions are met:
Packit 534379
#
Packit 534379
# * Redistributions of  source code  must retain the  above copyright notice,
Packit 534379
#  this list of conditions and the following disclaimer.
Packit 534379
# * Redistributions in binary form must reproduce the above copyright notice,
Packit 534379
#  this list of conditions and the following disclaimer in the documentation
Packit 534379
#   and/or other materials provided with the distribution.
Packit 534379
# * Neither the name  of Intel Corporation  nor the names of its contributors
Packit 534379
#   may be used to  endorse or promote  products derived  from this  software
Packit 534379
#   without specific prior written permission.
Packit 534379
#
Packit 534379
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
Packit 534379
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,  BUT NOT LIMITED TO,  THE
Packit 534379
# IMPLIED WARRANTIES OF  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
Packit 534379
# ARE DISCLAIMED.  IN NO EVENT  SHALL THE COPYRIGHT OWNER  OR CONTRIBUTORS BE
Packit 534379
# LIABLE  FOR  ANY  DIRECT,  INDIRECT,  INCIDENTAL,  SPECIAL,  EXEMPLARY,  OR
Packit 534379
# CONSEQUENTIAL  DAMAGES  (INCLUDING,  BUT  NOT LIMITED  TO,  PROCUREMENT  OF
Packit 534379
# SUBSTITUTE GOODS OR SERVICES;  LOSS OF USE,  DATA, OR PROFITS;  OR BUSINESS
Packit 534379
# INTERRUPTION)  HOWEVER CAUSED  AND ON ANY THEORY  OF LIABILITY,  WHETHER IN
Packit 534379
# CONTRACT,  STRICT LIABILITY,  OR TORT  (INCLUDING NEGLIGENCE  OR OTHERWISE)
Packit 534379
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,  EVEN IF ADVISED OF THE
Packit 534379
# POSSIBILITY OF SUCH DAMAGE.
Packit 534379
Packit 534379
import json
Packit 534379
import os
Packit 534379
import shutil
Packit 534379
import sys
Packit 534379
import utils
Packit 534379
import zipfile
Packit 534379
from metadata import metadata
Packit 534379
from gbs import GBS, GBS_EXT
Packit 534379
Packit 534379
# Update sys.path to include jsonschema folder from different locations
Packit 534379
try:
Packit 534379
    # pkgPATH1 : jsonschema search path for opae-sdk/tools/extra/packager
Packit 534379
    pkgPath1 = os.path.join(sys.path[0], 'jsonschema-2.3.0')
Packit 534379
Packit 534379
    # pkgPath2 : current packager script location
Packit 534379
    pkgPath2 = os.path.abspath(os.path.dirname(sys.argv[0]))
Packit 534379
    dirList = pkgPath2.split("/")
Packit 534379
    dirList = dirList[:-1]
Packit 534379
    pkgPath2 = "/".join(dirList)
Packit 534379
Packit 534379
    # pkgPath3 : jsonschema search path for current packager location
Packit 534379
    pkgPath3 = pkgPath2 + "/share/opae/python/jsonschema-2.3.0"
Packit 534379
Packit 534379
    sys.path.append(pkgPath1)
Packit 534379
    sys.path.append(pkgPath3)
Packit 534379
    from jsonschema import validators
Packit 534379
    from jsonschema import exceptions
Packit 534379
except ImportError:
Packit 534379
    print("jsonschema module has no validatiors() or exceptions()")
Packit 534379
    raise
Packit 534379
Packit 534379
filepath = os.path.dirname(os.path.realpath(__file__))
Packit 534379
schema_path = "schema/afu_schema_v01.json"
Packit 534379
if(zipfile.is_zipfile(filepath)):
Packit 534379
    archive = zipfile.ZipFile(filepath, 'r')
Packit 534379
    afu_schema = json.load(archive.open(schema_path, "r"))
Packit 534379
else:
Packit 534379
    afu_schema = json.load(open(filepath + "/" + schema_path, "r"))
Packit 534379
Packit 534379
ARCHIVE_FORMAT = "zip"
Packit 534379
ARCHIVE_EXT = ".zip"
Packit 534379
Packit 534379
Packit 534379
class AFU(object):
Packit 534379
    def __init__(self, afu_desc_file=None):
Packit 534379
        self.afu_json = {}
Packit 534379
        self.metadata_len = 0
Packit 534379
        self.afu_desc_file = afu_desc_file
Packit 534379
        if afu_desc_file:
Packit 534379
            self.load_afu_desc_file(afu_desc_file)
Packit 534379
Packit 534379
    @classmethod
Packit 534379
    def create_afu_from_gbs(cls, gbs):
Packit 534379
        afu = cls()
Packit 534379
Packit 534379
        afu.afu_json = gbs.gbs_info
Packit 534379
        afu.metadata_len = gbs.metadata_len
Packit 534379
Packit 534379
        return afu
Packit 534379
Packit 534379
    def load_afu_desc_file(self, afu_desc_file):
Packit 534379
        if os.path.exists(afu_desc_file):
Packit 534379
            self.afu_desc_file = os.path.abspath(afu_desc_file)
Packit 534379
            self.afu_dir = os.path.dirname(afu_desc_file)
Packit 534379
        else:
Packit 534379
            raise Exception("Cannot find {0}".format(afu_desc_file))
Packit 534379
Packit 534379
        self.afu_json = json.load(open(self.afu_desc_file, "r"))
Packit 534379
        self.compat_update()
Packit 534379
Packit 534379
        if not self.validate():
Packit 534379
            raise Exception("Accelerator description file failed validation!")
Packit 534379
Packit 534379
    # Load AFU JSON file given an open file handle
Packit 534379
    def load_afu_desc_file_hdl(self, afu_desc_file_hdl):
Packit 534379
        self.afu_json = json.load(afu_desc_file_hdl)
Packit 534379
        self.compat_update()
Packit 534379
Packit 534379
        if not self.validate():
Packit 534379
            raise Exception("Accelerator description file failed validation!")
Packit 534379
Packit 534379
    # Update/rename fields as needed to maintain backward compatibility
Packit 534379
    def compat_update(self):
Packit 534379
        try:
Packit 534379
            afu_ifc = self.afu_json['afu-image']['afu-top-interface']
Packit 534379
            # The interface 'class' used to be called 'name'.
Packit 534379
            # Maintain compatibility with older AFUs.
Packit 534379
            if ('name' in afu_ifc):
Packit 534379
                afu_ifc['class'] = afu_ifc.pop('name')
Packit 534379
        except KeyError as e:
Packit 534379
            None
Packit 534379
Packit 534379
    def validate(self, packaging=False):
Packit 534379
        if self.afu_json == {}:
Packit 534379
            return False
Packit 534379
        try:
Packit 534379
            validators.validate(self.afu_json, afu_schema)
Packit 534379
        except exceptions.ValidationError as ve:
Packit 534379
            print("JSON schema error at {0}: {1}".format(
Packit 534379
                str(list(ve.path)), str(ve.message)))
Packit 534379
            return False
Packit 534379
Packit 534379
        # If emitting a GBS file do some extra validation beyond the schema.
Packit 534379
        if packaging:
Packit 534379
            # User clocks can be "auto" in the source JSON in order to
Packit 534379
            # set the frequency to the actual achieved speed.  When
Packit 534379
            # creating the GBS, the frequencies must be numbers.
Packit 534379
            for clock in ['clock-frequency-high', 'clock-frequency-low']:
Packit 534379
                if clock in self.afu_json['afu-image']:
Packit 534379
                    f = self.afu_json['afu-image'][clock]
Packit 534379
                    if not isinstance(f, (int, float)):
Packit 534379
                        print("JSON schema error at {0}: {1}").format(
Packit 534379
                            "afu-image/" + clock, "expected number")
Packit 534379
                        raise Exception("Accelerator description file " +
Packit 534379
                                        "failed validation!")
Packit 534379
Packit 534379
        return True
Packit 534379
Packit 534379
    def update_afu_json(self, key_values):
Packit 534379
        try:
Packit 534379
            for value in key_values:
Packit 534379
                # Colon separates key and value
Packit 534379
                curr_val = value.split(':')
Packit 534379
                curr_val[1] = utils.convert_to_native_type(curr_val[1])
Packit 534379
                if self.afu_json:
Packit 534379
                    # Compatibility support for old scripts that set
Packit 534379
                    # interface-uuid, assuming it would be found in
Packit 534379
                    # afu-image.  After all scripts are updated, this
Packit 534379
                    # check can be removed.
Packit 534379
                    if curr_val[0] == 'interface-uuid':
Packit 534379
                        curr_val[0] = 'afu-image/interface-uuid'
Packit 534379
Packit 534379
                    # Slash separates key hierarchy
Packit 534379
                    key = curr_val[0].split('/')
Packit 534379
                    if len(key) > 1:
Packit 534379
                        # Walk key hierarchy
Packit 534379
                        afu = self.afu_json
Packit 534379
                        for k in key[:-1]:
Packit 534379
                            if k not in afu:
Packit 534379
                                # Intermediate key not present, add it.
Packit 534379
                                afu[k] = dict()
Packit 534379
                            afu = afu[k]
Packit 534379
Packit 534379
                        # Add the new value
Packit 534379
                        afu[key[-1]] = curr_val[1]
Packit 534379
                    else:
Packit 534379
                        # Old method didn't support key hierarchy.  Search
Packit 534379
                        # for the key either in afu-image or at top-level.
Packit 534379
                        # If not found, assume top-level.
Packit 534379
                        if key[0] in self.afu_json:
Packit 534379
                            self.afu_json[key[0]] = curr_val[1]
Packit 534379
                        elif key[0] in self.afu_json['afu-image']:
Packit 534379
                            self.afu_json['afu-image'][key[0]] = curr_val[1]
Packit 534379
                        else:
Packit 534379
                            self.afu_json[key[0]] = curr_val[1]
Packit 534379
        except IndexError as e:
Packit 534379
            print(e)
Packit 534379
            raise Exception(
Packit 534379
                "Invalid <key>:<value> pair passed using --set-value")
Packit 534379
Packit 534379
        if not self.validate():
Packit 534379
            raise Exception(
Packit 534379
                'AFU metadata validation failed after updating metadata '
Packit 534379
                ' with values provided with --set_value')
Packit 534379
Packit 534379
    def create_gbs(self, rbf_file, gbs_file, key_values=None):
Packit 534379
        if key_values:
Packit 534379
            self.update_afu_json(key_values)
Packit 534379
Packit 534379
        # Set the expected magic number if it hasn't already been set
Packit 534379
        if 'magic-no' not in self.afu_json['afu-image']:
Packit 534379
            self.afu_json['afu-image']['magic-no'] = 0x1d1f8680
Packit 534379
Packit 534379
        self.validate(packaging=True)
Packit 534379
        gbs = GBS.create_gbs_from_afu_info(rbf_file, self.afu_json)
Packit 534379
        return gbs.write_gbs(gbs_file)
Packit 534379
Packit 534379
    # Dump AFU JSON to string
Packit 534379
    def dumps(self):
Packit 534379
        return json.dumps(self.afu_json, indent=3)
Packit 534379
Packit 534379
    def package(self, rbf_file, sw_dir, doc_dir, package_name):
Packit 534379
        image_dir = os.path.join(utils.get_work_dir(), "image_0")
Packit 534379
        if not os.path.exists(image_dir):
Packit 534379
            os.makedirs(image_dir)
Packit 534379
Packit 534379
        gbs_name = os.path.splitext(
Packit 534379
            os.path.basename(
Packit 534379
                self.afu_desc_file))[0] + GBS_EXT
Packit 534379
        gbs_path = os.path.join(image_dir, gbs_name)
Packit 534379
        self.create_gbs(rbf_file, gbs_path)
Packit 534379
Packit 534379
        shutil.copyfile(
Packit 534379
            self.afu_desc_file, os.path.join(
Packit 534379
                image_dir, os.path.basename(
Packit 534379
                    self.afu_desc_file)))
Packit 534379
Packit 534379
        package_dir = os.path.join(utils.get_work_dir(), "package")
Packit 534379
        shutil.make_archive(
Packit 534379
            os.path.join(
Packit 534379
                package_dir,
Packit 534379
                "image_0"),
Packit 534379
            ARCHIVE_FORMAT,
Packit 534379
            image_dir)
Packit 534379
        if sw_dir:
Packit 534379
            shutil.make_archive(
Packit 534379
                os.path.join(
Packit 534379
                    package_dir,
Packit 534379
                    "sw"),
Packit 534379
                ARCHIVE_FORMAT,
Packit 534379
                sw_dir)
Packit 534379
        if doc_dir:
Packit 534379
            shutil.make_archive(
Packit 534379
                os.path.join(
Packit 534379
                    package_dir,
Packit 534379
                    "docs"),
Packit 534379
                ARCHIVE_FORMAT,
Packit 534379
                doc_dir)
Packit 534379
Packit 534379
        shutil.make_archive(package_name, ARCHIVE_FORMAT, package_dir)
Packit 534379
        shutil.rmtree(utils.get_work_dir())