Skip to content

Commit

Permalink
Merge pull request #1 from mvazquezc/migration-to-new-repo
Browse files Browse the repository at this point in the history
Move fakefish from telco-operations repo
  • Loading branch information
rhjanders authored Apr 28, 2022
2 parents 17c2738 + bf3a044 commit 69c6348
Show file tree
Hide file tree
Showing 19 changed files with 693 additions and 0 deletions.
19 changes: 19 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
REGISTRY ?= quay.io
IMAGE_NAMESPACE ?= mavazque
IMAGE_NAME ?= fakefish
IMAGE_URL ?= $(REGISTRY)/$(IMAGE_NAMESPACE)/$(IMAGE_NAME)
TAG ?= latest

.PHONY: build-dell build-custom pre-reqs

default: pre-reqs build-custom

build-dell:
podman build . -f dell_scripts/Containerfile -t ${IMAGE_URL}:${TAG}

build-custom: pre-reqs
podman build . -f custom_scripts/Containerfile -t ${IMAGE_URL}:${TAG}

.SILENT:
pre-reqs:
if [ $(shell find custom_scripts/ -name "*.sh" | grep -Ec "mountcd.sh|poweroff.sh|poweron.sh|unmountcd.sh|bootfromcdonce.sh") -ne 5 ];then echo 'Missing custom scripts or bad naming';exit 1;fi
171 changes: 171 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# FakeFish

> **WARNING**: The work exposed here is not supported in any way by Red Hat, this is the result of exploratory work. Use at your own risk.
`FakeFish` is a flask based app that exposes a RedFish-like API with a set of limited endpoints that allow the deployment of OpenShift nodes via the Metal3 operator on hardware that doesn't support RedFish or doesn't follow the RedFish standard.

The way it works is by running a set of scripts that interact with the hardware using vendor tools/other methods while exposing a fake RedFish API that Metal3 can query.

The [app/](./app/) directory contains the FakeFish application. Inside the `app` directory we can find the [custom_scripts](./app/custom_scripts/) folder where we need to create scripts:

|Script|What should it do?|
|------|----------------|
|`poweron.sh`|Power on the server|
|`poweroff.sh`|Power off the server|
|`bootfromcdonce.sh`|Set server to boot from virtual CD once|
|`mountcd.sh`|Mount the iso received in the server's virtual CD|
|`unmountcd.sh`|Unmount the iso from the server's virtual CD|

The script names must match above naming, you can check the [dell_scripts](./dell_scripts/) folder to find example scripts with the correct naming.

> **NOTE**: Dell scripts linked above are only meant to show how someone could implement the required scripts to make custom hardware compatible with FakeFish. Dell hardware is supported by the `idrac-virtualmedia` provider in Metal3. **These scripts are unsupported/unmaintained** and should be taken as a reference, nothing else.
Users need to implement their own scripts, we will not maintain/add providers to this project.

A [Containerfile](./custom_scripts/Containerfile) is included, so users can build their own container image out of it.

## Building your own FakeFish container image

1. Place your custom scripts inside the [custom_scripts](./custom_scripts/) folder.
2. Run the build command:

> **NOTE**: Check the Makefile vars to customize the output container image naming and tag.
~~~sh
make build-custom
~~~

## Usage

You need a FakeFish process for each server you plan to install. Think of FakeFish like if it was a custom implementation of a BMC for that specific server.

Since you will be potentially running multiple FakeFish instances, you will make use of an environment variable to configure in which port a given FakeFish instance listens on. On top of that, you need to do a bind mount for the folder containing the scripts for managing that specific server.

An example can be found below:

> **NOTE**: Every container is mapped to a single BMC, but if more hosts are required, different ports can be used (9001, 9002,...)

```sh
podman run -p 9000:9000 -e PORT=9000 -v $PWD/dell_scripts:/opt/fakefish/custom_scripts:z quay.io/mavazque/fakefish:v0
sudo firewall-cmd --add-port=9000/tcp
```

Then, in the `install-config.yaml` file, it is required to specify the IP where the container is running instead of the 'real' BMC:

```yaml
bmcAddress: redfish-virtualmedia://192.168.1.10:9000/redfish/v1/Systems/1
```

## Logs

In a successful execution you should see something like this in the logs:

- Starting FakeFish

```sh
$ podman run -p 9000:9000 -e PORT=9000 -v $PWD/dell_scripts:/opt/fakefish/custom_scripts:z quay.io/mavazque/fakefish:v0
* Serving Flask app 'fakefish' (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on all addresses.
WARNING: This is a development server. Do not use it in a production deployment.
* Running on https://10.19.3.25:9000/ (Press CTRL+C to quit)
```

- Provisioning Logs

```sh
10.19.3.23 - - [20/Apr/2022 13:17:09] "GET /redfish/v1/Systems/1 HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:17:09] "GET /redfish/v1/Systems/1 HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:17:09] "GET /redfish/v1/Systems/1/BIOS HTTP/1.1" 404 -
10.19.3.23 - - [20/Apr/2022 13:17:09] "GET /redfish/v1/Systems/1 HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:17:09] "GET /redfish/v1/Systems/1 HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:17:09] "GET /redfish/v1/Systems/1 HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:17:24] "GET /redfish/v1/Systems/1 HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:17:24] "GET /redfish/v1/Systems/1 HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:17:24] "GET /redfish/v1/Systems/1 HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:18:24] "GET /redfish/v1/Systems/1 HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:18:24] "GET /redfish/v1/Systems/1 HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:18:24] "GET /redfish/v1/Systems/1 HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:18:27] "GET /redfish/v1/Systems/1 HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:18:27] "GET /redfish/v1/Systems/1 HTTP/1.1" 200 -
Security Alert: Certificate is invalid - self signed certificate
Continuing execution. Use -S option for racadm to stop execution on certificate-related errors.
Server is already powered OFF.
10.19.3.23 - - [20/Apr/2022 13:18:32] "POST /redfish/v1/Systems/1/Actions/ComputerSystem.Reset HTTP/1.1" 204 -
10.19.3.23 - - [20/Apr/2022 13:18:33] "GET /redfish/v1/Systems/1 HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:18:33] "GET /redfish/v1/Systems/1 HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:18:33] "GET /redfish/v1/Systems/1 HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:18:33] "GET /redfish/v1/Managers/1 HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:18:33] "GET /redfish/v1/Managers/1/VirtualMedia HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:18:33] "GET /redfish/v1/Managers/1/VirtualMedia/Cd HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:18:33] "GET /redfish/v1/Systems/1 HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:18:33] "GET /redfish/v1/Systems/1 HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:18:33] "GET /redfish/v1/Managers/1 HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:18:34] "GET /redfish/v1/Managers/1/VirtualMedia HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:18:34] "GET /redfish/v1/Managers/1/VirtualMedia/Cd HTTP/1.1" 200 -
Security Alert: Certificate is invalid - self signed certificate
Continuing execution. Use -S option for racadm to stop execution on certificate-related errors.
Disable Remote File Started. Please check status using -s
option to know Remote File Share is ENABLED or DISABLED.
Security Alert: Certificate is invalid - self signed certificate
Continuing execution. Use -S option for racadm to stop execution on certificate-related errors.
Remote Image is now Configured
ShareName http://10.19.3.23:6180/redfish/boot-dc055836-d26c-4256-ba6c-222e8d4559be.iso
10.19.3.23 - - [20/Apr/2022 13:18:48] "POST /redfish/v1/Managers/1/VirtualMedia/Cd/Actions/VirtualMedia.InsertMedia HTTP/1.1" 204 -
10.19.3.23 - - [20/Apr/2022 13:18:48] "GET /redfish/v1/Systems/1 HTTP/1.1" 200 -
Security Alert: Certificate is invalid - self signed certificate
Continuing execution. Use -S option for racadm to stop execution on certificate-related errors.
[Key=iDRAC.Embedded.1#VirtualMedia.1]
Object value modified successfully
Security Alert: Certificate is invalid - self signed certificate
Continuing execution. Use -S option for racadm to stop execution on certificate-related errors.
[Key=iDRAC.Embedded.1#ServerBoot.1]
Object value modified successfully
10.19.3.23 - - [20/Apr/2022 13:18:58] "PATCH /redfish/v1/Systems/1 HTTP/1.1" 204 -
10.19.3.23 - - [20/Apr/2022 13:18:58] "GET /redfish/v1/Systems/1 HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:18:58] "GET /redfish/v1/Systems/1 HTTP/1.1" 200 -
Security Alert: Certificate is invalid - self signed certificate
Continuing execution. Use -S option for racadm to stop execution on certificate-related errors.
Server power operation successful
10.19.3.23 - - [20/Apr/2022 13:19:04] "POST /redfish/v1/Systems/1/Actions/ComputerSystem.Reset HTTP/1.1" 204 -
10.19.3.23 - - [20/Apr/2022 13:19:05] "GET /redfish/v1/Systems/1 HTTP/1.1" 200 -
```

- Deprovisioning Logs

```sh
10.19.3.23 - - [20/Apr/2022 13:23:29] "GET /redfish/v1/Systems/1 HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:23:29] "GET /redfish/v1/Managers/1 HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:23:29] "GET /redfish/v1/Managers/1/VirtualMedia HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:23:29] "GET /redfish/v1/Managers/1/VirtualMedia/Cd HTTP/1.1" 200 -
Security Alert: Certificate is invalid - self signed certificate
Continuing execution. Use -S option for racadm to stop execution on certificate-related errors.
Disable Remote File Started. Please check status using -s
option to know Remote File Share is ENABLED or DISABLED.
10.19.3.23 - - [20/Apr/2022 13:23:55] "POST /redfish/v1/Managers/1/VirtualMedia/Cd/Actions/VirtualMedia.EjectMedia HTTP/1.1" 204 -
10.19.3.23 - - [20/Apr/2022 13:23:55] "GET /redfish/v1/Systems/1 HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:23:55] "GET /redfish/v1/Managers/1 HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:23:55] "GET /redfish/v1/Managers/1/VirtualMedia HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:23:55] "GET /redfish/v1/Managers/1/VirtualMedia/Cd HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:23:55] "GET /redfish/v1/Systems/1 HTTP/1.1" 200 -
10.19.3.23 - - [20/Apr/2022 13:23:56] "GET /redfish/v1/Systems/1 HTTP/1.1" 200 -
Security Alert: Certificate is invalid - self signed certificate
Continuing execution. Use -S option for racadm to stop execution on certificate-related errors.
Server power operation successful
10.19.3.23 - - [20/Apr/2022 13:24:09] "POST /redfish/v1/Systems/1/Actions/ComputerSystem.Reset HTTP/1.1" 204 -
10.19.3.23 - - [20/Apr/2022 13:24:10] "GET /redfish/v1/Systems/1 HTTP/1.1" 200 -
```
154 changes: 154 additions & 0 deletions app/fakefish.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
#!/usr/bin/env python3
# coding=utf-8

import flask
import json
import os
import requests
import subprocess
from datetime import datetime
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

app = flask.Flask(__name__)

try:
app.config.from_object('settings')
config = app.config
except ImportError:
config = {'PORT': os.environ.get('PORT', 9000)}

debug = config['DEBUG'] if 'DEBUG' in list(config) else True
port = int(config['PORT']) if 'PORT' in list(config) else 9000

@app.route('/redfish/v1/')
def root_resource():
return flask.render_template('root.json')

@app.route('/redfish/v1/Managers')
def manager_collection_resource():
return flask.render_template('managers.json')

@app.route('/redfish/v1/Systems')
def system_collection_resource():
return flask.render_template('systems.json')

@app.route('/redfish/v1/Systems/1', methods=['GET', 'PATCH'])
def system_resource():
if flask.request.method == 'GET':
return flask.render_template(
'fake_system.json',
power_state=power_state,
)
else:
app.logger.info('patch request')
boot = flask.request.json.get('Boot')
if not boot:
return ('PATCH only works for Boot'), 400
if boot:
target = boot.get('BootSourceOverrideTarget')
mode = boot.get('BootSourceOverrideMode')
if not target and not mode:
return ('Missing the BootSourceOverrideTarget and/or '
'BootSourceOverrideMode element', 400)
else:
app.logger.info('Running script that sets boot from VirtualCD once')
try:
subprocess.check_call(['custom_scripts/bootfromcdonce.sh'])
except subprocess.CalledProcessError as e:
return ('Failed to set boot from virtualcd once', 400)

return '', 204

@app.route('/redfish/v1/Systems/1/EthernetInterfaces', methods=['GET'])
def manage_interfaces():
return flask.render_template('fake_interfaces.json')

@app.route('/redfish/v1/Managers/1', methods=['GET'])
def manager_resource():
return flask.render_template(
'fake_manager.json',
date_time=datetime.now().strftime('%Y-%M-%dT%H:%M:%S+00:00'),
)

@app.route('/redfish/v1/Systems/1/Actions/ComputerSystem.Reset',
methods=['POST'])
def system_reset_action():
reset_type = flask.request.json.get('ResetType')
global power_state
if reset_type == 'On':
app.logger.info('Running script that powers on the server')
try:
subprocess.check_call(['custom_scripts/poweron.sh'])
except subprocess.CalledProcessError as e:
return ('Failed to poweron the server', 400)
power_state = 'On'
else:
app.logger.info('Running script that powers off the server')
try:
subprocess.check_call(['custom_scripts/poweroff.sh'])
except subprocess.CalledProcessError as e:
return ('Failed to poweroff the server', 400)
power_state = 'Off'

return '', 204


@app.route('/redfish/v1/Managers/1/VirtualMedia', methods=['GET'])
def virtualmedia_collection_resource():
return flask.render_template('virtualmedias.json')

@app.route('/redfish/v1/Managers/1/VirtualMedia/Cd', methods=['GET'])
def virtualmedia_cd_resource():
return flask.render_template(
'virtualmedia_cd.json',
inserted=inserted,
image_url=image_url,
)

@app.route('/redfish/v1/Managers/1/VirtualMedia/Cd/Actions/VirtualMedia.InsertMedia',
methods=['POST'])
def virtualmedia_insert():
image = flask.request.json.get('Image')
if not image:
return('POST only works for Image'), 400
else:
global inserted
global image_url
inserted = True
image_url = image
app.logger.info('Running script that mounts cd with iso %s', image)
try:
subprocess.check_call(['custom_scripts/mountcd.sh', image_url])
except subprocess.CalledProcessError as e:
return ('Failed to mount virtualcd', 400)
return '', 204

@app.route('/redfish/v1/Managers/1/VirtualMedia/Cd/Actions/VirtualMedia.EjectMedia',
methods=['POST'])
def virtualmedia_eject():
global inserted
global image_url
inserted = False
image_url = ''
app.logger.info('Running script that unmounts cd')
try:
subprocess.check_call(['custom_scripts/unmountcd.sh'])
except subprocess.CalledProcessError as e:
return ('Failed to unmount virtualcd', 400)
return '', 204


def run():
"""
"""
app.run(host='0.0.0.0', port=port, debug=False, ssl_context='adhoc')


if __name__ == '__main__':

inserted = False
image_url = ''
power_state = 'On'
run()
13 changes: 13 additions & 0 deletions app/templates/fake_interfaces.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"@odata.type": "#EthernetInterfaceCollection.EthernetInterfaceCollection",
"Name": "Ethernet Interface Collection",
"Description": "Fake NICs",
"[email protected]": 0,
"Members": [

],
"Oem": {},
"@odata.context": "/redfish/v1/$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection",
"@odata.id": "/redfish/v1/Systems/1"
}

37 changes: 37 additions & 0 deletions app/templates/fake_manager.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"@Redfish.Copyright": "Copyright 2014-2017 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright.",
"@odata.context": "/redfish/v1/$metadata#Manager.Manager",
"@odata.id": "/redfish/v1/Managers/1",
"@odata.type": "#Manager.v1_3_1.Manager",
"DateTime": {{ date_time|string|tojson }},
"DateTimeLocalOffset": "+00:00",
"Description": "FakeFish BMC",
"FirmwareVersion": "1.00",
"Id": "1",
"Links": {
"ManagerForChassis": [
{
"@odata.id": "/redfish/v1/Chassis/fake-chassis"
}
],
"ManagerForServers": [
{
"@odata.id": "/redfish/v1/Systems/1"
}
]
},
"ManagerType": "BMC",
"Model": "Palc 2000",
"Name": "FakeFish Manager",
"PowerState": "On",
"ServiceEntryPointUUID": null,
"Status": {
"Health": "OK",
"State": "Enabled"
},
"UUID": "1",
"VirtualMedia": {
"@odata.id": "/redfish/v1/Managers/1/VirtualMedia"
}
}

Loading

0 comments on commit 69c6348

Please sign in to comment.