Skip to content

OpenCHAMI/csm-redfish-interface-emulator

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

45 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CSM Redfish Interface Emulator

The CSM Redfish Interface Emulator is based off DMTF's Redfish Interface Emulator and can emulate a redfish interface with static and dynamic resources.

Although this project is based on DMTF's Redfish Interface Emulator project, it deviates a bit from DMTF's project's functionality. DMTF's project's dynamic resources are meant to be fully generated and generic. The CSM Redfish Interface Emulator project takes this an uses it to create dynamic resources that sit under a static mockup to emulate specific BMCs.

Static resources are emulated by simply copying a redfish mockup into the ./mockups/<BMC_type> directory and specifying the <BMC_type> in the MOCKUPFOLDER environment variable. The emulator will automatically try to apply known dynamic resources to the mockup such as the AccountService using the default loader. All other URIs will answer to GET only. For more on static resources see the Creating a Static Mockup section.

Dynamic emulation is accomplished by creating custom python files for each dynamic resource. You can use the Update Service API as an example when creating a new dynamic resources.

New BMC types for emulation are created by loading a complete static mockup and overriding the resource URIs that you want to be dynamic.

Topics:

Running the emulator

This emulator can be run locally or as a docker image.

Docker

You can run a predefined emulator using ./Dockerfile and setting the MOCKUPFOLDER environment variable, their ./mockups/<BMC_type>/Dockerfile (i.e. ./mockups/EX325a/Dockerfile), or create a ./mockups/<BMC_type>/Dockerfile in your custom mockup directory.

NOTE: 'Dockerfile' assumes your custom static files are in place in the ./mockups/ directory.

NOTE: When using the emulator with a custom mockup that does not have a loader specified for it in ./src/api_emulator/resource_manager.py, the generic loader will be used to generate a best guess mockup using known dynamic resources.

Build the docker image.

docker build -t csm-rie:latest -f ./Dockerfile .

Set a local port for your BMC

BMC_PORT=5000

Run the docker image.

docker run -p ${BMC_PORT}:5000 -e MOCKUPFOLDER=<BMC_type> --rm csm-rie:latest

Locally

This method runs setup.sh which will copy the resources into a new directory, create a virtual environment, install the required packages, and run the emulator.

NOTE: When using the emulator with a custom mockup that does not have a loader specified for it in ./src/api_emulator/resource_manager.py, the generic loader will be used to generate a best guess mockup using known dynamic resources.

Set a local port for your BMC

BMC_PORT=5000

Run the setup.sh script.

./setup.sh -p ${BMC_PORT} -m <BMC_type> -w ../<WORK_DIR>

This will create a <WORK_DIR> directory outside of your csm-redfish-interface-emulator directory, copy the local csm-redfish-interface-emulator files and directories into the specified workspace directory, and run the emulator. All static mockups in csm-redfish-interface-emulator/mockups will be copied into <WORK_DIR>/api_emulator/redfish/static.

The setup.sh script can also be run without starting the emulator to just prepare the emulator files.

./setup.sh -p ${BMC_PORT} -w ../<WORK_DIR> -n

NOTE: If <WORK_DIR> already exists before running the setup.sh script, it will first get deleted.

Redfish Authorization

The emulator supports Basic and session authorization (X-Auth-Token). Initial accounts can be created by setting the AUTH_CONFIG environment variable. The expected format is :: where is one of Administrator, Operator, or ReadOnly as defined by DMTF as required default redfish roles.

These roles carry the following redfish privileges as defined by DMTF:

  • Administrator
    • Login
    • ConfigureManager
    • ConfigureUsers
    • ConfigureSelf
    • ConfigureComponents
  • Operator
    • Login
    • ConfigureSelf
    • ConfigureComponents
  • ReadOnly
    • Login
    • ConfigureSelf

Access to the emulators URIs are privilege based and are set per HTTP method for dynamic resources. Static resources (GET only) require Login privileges for access.

Any account specified by AUTH_CONFIG will be also added under the AccountService in the emulated redfish server and, if using the dynamic resource, can be manipulated (Add/Delete/Patch) using the AccountService. Actions under the AccountService will affect the emulator's authorization accounts.

Similarly session tokens can be created with SessionService actions if the emulator is using the dynamic resource. No sessions exist by default.

By default, if AUTH_CONFIG is empty or an invalid format, the emulator will have 3 accounts created:

  • root:root_password:Administrator
  • operator:operator_password:Operator
  • guest:guest_password:ReadOnly

Creating a new BMC type for emulation

New BMC types can be added for emulation. This can be done by using a completely static mockup or a mix of static and dynamic resources. The following outlines the overall process.

Creating a static mockup

A static mockup is a directory tree that is an exact copy of the desired BMC type's redfish tree starting at the service base, /redfish/v1. Each directory has an index.json file that contains a copy of what is returned by a GET to that resource in the redfish tree. For example:

EX235a:
- Systems
-- Node0
...
--- index.json
-- index.json
- index.json

DMTF has a tool that can be run against a real BMC to walk the entire redfish tree and create a mockup, the Redfish Mockup Creator.

The Redfish Mockup Creator can be run locally. For example, to create the EX325a mockup I used x9000c3s3b0 on loki by:

  • Opening a ssh tunnel to x9000c3s3b0
> ssh -L 7443:x3000c0s24b0:443 -N [email protected]
  • In another window, run the Redfish Mockup Creator
Redfish-Mockup-Creator> python ./redfishMockupCreator.py --u <username> --p <password> --A basic --r localhost:7443 -S -D ./EX325a

NOTE: DMTF's Redfish Mockup Creator only follows "@odata.id", "Uri", or "[email protected]" links to go deeper. Because of this "@Redfish.ActionInfo" URIs such as '/redfish/v1/Systems/Node0/ResetActionInfo' do not get automatically captured. To complete the mockup, they either need to be manually copied or you need to checkout the Redfish Mockup Creator and add 'or item == "@Redfish.ActionInfo"' here and here before running it.

The static resource tree for a BMC type needs to be placed in csm-redfish-interface-emulator/mockups/<BMC_type> such that the index.json file in ./<BMC_type> is for the resource base, /redfish/v1.

Creating dynamic resources

To create an emulator with dynamic resources for a new BMC type you can either:

  1. Create no new files and let the default loader, loader.py, use its best guess at selecting the correct dynamic resources for your mockup.
  2. Create a <BMC_type>_loader.py file where the functions for setting up the dynamic resources will live. The hpe_cray_ex235a_loader.py is an example of this.

A new loader and dynamic resource files may be required if your mockup requires any non-standard modifications that are not handled by the default loader.

Default Loader

The default loader will automatically be used for <BMC_type> not matched in resource_manager.py. It uses a best guess approach to determine which known dynamic resources to use for the mockup depending on which URI's are available in the static mockup. If the default loader can't determine which dynamic resource to use, it will not set one up and that set of URI's will continue to be Static, GET only, resources.

The default loader is the parent Loader python class that is used as the base for all <bmc_type>_loaders.

The Loader class currently sets up dynamic resources for:

  • Power Limit (init_power_limit())
    • Generic Power Limit Schema
      • GET/PATCH /redfish/v1/Chassis/{sys_id}/Power
    • HPE Cray EX Power Limit Schema
      • GET/PATCH /redfish/v1/Chassis/{sys_id}/Controls/{control_id}
      • PATCH /redfish/v1/Chassis/{sys_id}/Controls.Deep
    • Apollo 6500 Power Limit Schema
      • GET /redfish/v1/Chassis/{sys_id}/Power/AccPowerService/PowerLimit
      • POST /redfish/v1/Chassis/{sys_id}/Power/AccPowerService/PowerLimit/Actions/HpeServerAccPowerLimit.ConfigurePowerLimit
  • System Power Actions (init_system_reset())
    • Generic
      • GET /redfish/v1/Systems/{sys_id}
      • GET/POST /redfish/v1/Systems/{sys_id}/Actions/ComputerSystem.Reset
  • Chassis Power Actions (init_chassis_reset())
    • Generic
      • GET /redfish/v1/Chassis/{chassis_id}
      • GET/POST /redfish/v1/Chassis/{chassis_id}/Actions/Chassis.Reset
  • Manager Power Actions (init_manager_reset())
    • Generic
      • GET /redfish/v1/Managers/{manager_id}
      • GET/POST /redfish/v1/Managers/{manager_id}/Actions/Manager.Reset
  • Update Service (init_update_service())
    • Generic
      • GET /redfish/v1/UpdateService/FirmwareInventory/{target_id}
      • POST /redfish/v1/UpdateService/SimpleUpdate
      • GET/PATCH /redfish/v1/UpdateService/FirmwareInventory/Config
  • Account Service (init_account_service())
    • Generic
      • GET/POST /redfish/v1/AccountService/Accounts
      • GET/PATCH/DELETE /redfish/v1/AccountService/Accounts/{id}
  • Session Service (init_session_service())
    • Generic
      • GET/POST /redfish/v1/SessionService/Sessions
      • GET/DELETE /redfish/v1/AccountService/Sessions/{id}
  • Certificate Service (init_certificate_service())
    • HPE Cray EX Schema
      • POST /redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate
  • Manager Network Protocol (init_manager_network_protocol())
    • Generic
      • GET/PATCH /redfish/v1/Managers/{manager_id}/NetworkProtocol
  • Event Service (init_event_service())
    • Generic
      • GET/PATCH /redfish/v1/EventService
      • GET/POST /redfish/v1/EventService/Subscriptions
      • GET/PATCH/DELETE /redfish/v1/EventService/Subscriptions/{sub_id}
  • Redfish Event Generation Schemas (get_event_template())
    • Generic
    • Intel
    • Gigabyte
    • HPE Cray
    • Proliant iLO

The Loader class also tries to search the static mockup for serial numbers to randomize them with randomize(). MAC addresses are also modified either with a random MAC or using the Mountain scheme based on the given xname.

Creating a New Loader

A new loader file may be required if your mockup requires any non-standard modifications that are not handled by the default loader. This file is usually named <BMC_type>_loader.py that lives in the ./src/api_emulator directory and contains code to create a subclass of Loader.

You can override any of the methods in the Loader class or create new ones to add customizations. hpe_cray_ex325a_loader.py is an example of this.

Add a call to your new loader in resource_manager.py

If you have customizations needed for your new <BMC_type>, you'll likely also need to create new dynamic resources

Dynamic Resources

Dynamic resources are the backend handlers for any URI you want to answer to more than just GET requests. They are generally named _api.py and live in the ./src/api_emulator/redfish directory. For example the generic power control resource is power_control_api.py and the HPE Cray EX variant is hpe_cray_ex_power_control_api.py.

Your new dynamic resource files should contain classes for the collection (if it isn't a singleton) and members. These classes should be subclasses of flask_restful.Resource to be attached to a URI.

These new subclass should define methods for get(), put(), post(), patch(), and delete(). The 'request' variable comes from flask and contains all of the information about the request (method, payload, path, etc) and can be used within the method.

    # HTTP GET
    def get(self, ch_id):
        logging.info('%s %s called' % (self.apiName, request.method))
        try:
            # Find the entry with the correct value for Id
            resp = error_404_response(request.path)
            if ch_id in members:
                resp = members[ch_id], 200
        except Exception:
            traceback.print_exc()
            resp = simple_error_response('Server encountered an unexpected Error', 500)
        return resp

    # HTTP PATCH
    #
    # Apply power limit to the specified Control
    def patch(self, ch_id):
        logging.info('%s %s called' % (self.apiName, request.method))
        raw_dict = request.get_json(force=True)
        try:
            resp = error_404_response(request.path)
            if ch_id in members:
                if 'PowerControl' in raw_dict and 'PowerControl' in members[ch_id] and \
                   raw_dict['PowerControl'] == len(members[ch_id]['PowerControl']):
                    resp = applyPatch(raw_dict['PowerControl'], ch_id)
                else:
                    resp = simple_error_response('Invalid paramters for PATCH', 400)
        except Exception:
            traceback.print_exc()
            resp = simple_error_response('Server encountered an unexpected Error', 500)
        return resp

For authorization, define method_decorators with entries for each http method specifying auth.auth_required(priv=<required_privilages>) as the decorator. Specify the required redfish privileges that a user would need to perform that type of request on your resource. You can either list all required privileges, 'priv={Privilege.Login, Privilege.ConfigureComponents}' or just the highest one needed, 'priv={Privilege.ConfigureComponents}'. Generally, GETs require 'Privilege.Login' and all others require 'Privilege.ConfigureComponents'. The privileges are listed in redfish_auth.py and are those defined in the redfish spec.

method_decorators = {'get':    [auth.auth_required(priv={Privilege.Login})],
                     'post':   [auth.auth_required(priv={Privilege.ConfigureComponents})],
                     'put':    [auth.auth_required(priv={Privilege.ConfigureComponents})],
                     'patch':  [auth.auth_required(priv={Privilege.ConfigureComponents})],
                     'delete': [auth.auth_required(priv={Privilege.ConfigureComponents})]}

Behavior controls

A few of the existing dynamic resources have 'hidden' config URIs used for configuring response behavior for the dynamic resource on the fly. The update_service_api.py file is an example of this. It provides a 'hidden' (not displayed in /redfish/v1/UpdateService/FirmwareInventory['Members']) URI. This URI is used to view (GET) currently configured behavior or set (PATCH) new behavior for the '/redfish/v1/UpdateService/SimpleUpdate' URI. For an emulated EX235a, this looks like:

{
  "Id": "UpdateServiceConfigInfo",
  "Name": "UpdateServiceConfig",
  "Description": "Use PATCH operations to set the below values to affect Update Service actions.",
  "Parameters": [
    {
      "DataType": "StringArray",
      "Name": "Fail",
      "Description": "List of targets that will fail update actions.",
      "AllowableValues": [
        "Node0.HPCNet3",
        "BMC",
        "Node0.HPCNet0",
        "FPGA2",
        "Node0.HPCNet1",
        "Node0.AccVBIOS",
        "Bootloader",
        "Node0.HPCNet2",
        "Node0.AccUC",
        "Recovery",
        "FPGA0",
        "Node0.BIOS",
        "FPGA1",
        "Node0.ManagementEthernet"
      ]
    },
    {
      "DataType": "Int",
      "Name": "Hang",
      "Description": "Amount of time in seconds to Hang."
    },
    {
      "DataType": "Int",
      "Name": "UpdateTime",
      "Description": "Amount of time in seconds to for updates to take."
    }
  ],
  "CurrentValues": {
    "UpdateTime": 10,
    "Fail": [
      "BMC"
    ],
    "Hang": 0
  }
}

You may wish to create a resource such as this for each of your dynamic resource to make testing more configurable.

Templates for dynamic resources

Some dynamic resources such as the EventService generate responses (i.e. Redfish events) that are BMCType specific. Templates are used for these resources. The templates exist in the src/api_emulator/redfish/templates/ directory. The EX235a emulator uses hpe_cray_ex_events.py for generating redfish events for the EventService. Which templates to use are generally set by the <BMC_type>_loader.py when response format can vary between BMC type.

Existing static mockup files:

Existing dynamic resources:

  • Power Control
    • Generic - power_control_api.py
      • GET/PATCH /redfish/v1/Chassis/<system_id>/Power
    • HPE Cray EX - hpe_cray_ex_power_control_api.py
      • GET/PATCH /redfish/v1/Chassis/<system_id>/Controls/<control_id>
      • PATCH /redfish/v1/Chassis/<system_id>/Controls.Deep
    • Proliant iLO - proliant_ilo_power_control_api.py
      • GET /redfish/v1/Chassis/<system_id>/Power/AccPowerService/PowerLimit
      • POST /redfish/v1/Chassis/<system_id>/Power/AccPowerService/PowerLimit/Actions/HpeServerAccPowerLimit.ConfigurePowerLimit
  • Computer System Power Actions - computer_systems_api.py
    • GET /redfish/v1/Systems/<system_id>
    • POST /redfish/v1/Systems/<system_id>/Actions/ComputerSystem.Reset
  • Chassis Power Actions - chassis_api.py
    • GET /redfish/v1/Chassis/<chassis_id>
    • GET/POST /redfish/v1/Chassis/<chassis_id>/Actions/Chassis.Reset
  • Manager Power Actions - manager_api.py
    • GET /redfish/v1/Managers/<manager_id>
    • GET/POST /redfish/v1/Managers/<manager_id>/Actions/Manager.Reset
  • Firmware Update - update_service_api.py
    • GET /redfish/v1/UpdateService/FirmwareInventory/<target_id>
    • POST /redfish/v1/UpdateService/SimpleUpdate
    • GET/PATCH /redfish/v1/UpdateService/FirmwareInventory/Config
  • EventService - event_service_api.py, event_generator.py
    • GET/PATCH /redfish/v1/EventService
    • GET/POST /redfish/v1/EventService/Subscriptions
    • GET/PATCH/DELETE /redfish/v1/EventService/Subscriptions/
  • Event templates
  • Account Service - account_service_api.py
    • GET/POST /redfish/v1/AccountService/Accounts
    • GET/PATCH/DELETE /redfish/v1/AccountService/Accounts/
  • Session Service - session_service_api.py
    • GET/POST /redfish/v1/SessionService/Sessions
    • GET/DELETE /redfish/v1/AccountService/Sessions/
  • Certificate Service - hpe_cray_ex_certificate_service_api.py
    • POST /redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate
  • Manager Network Protocol - manager_network_protocol_api.py
    • GET/PATCH /redfish/v1/Managers/<manager_id>/NetworkProtocol

Emulator to Loader Map

About

CSM implementation of redfish interface emulators

Resources

License

Code of conduct

Stars

Watchers

Forks

Packages

 
 
 

Languages

  • Python 91.7%
  • Dockerfile 6.5%
  • Shell 1.8%