Add feature sets to test with different available feature
* "full" is the default and provides all features * "minimum" only provides boot settings * "vmedia" is "minimum" plus virtual media and NICs Closes-Bug: #2046153 Change-Id: I57cff90b1327104f65264d3fb87dafc89f3be521
This commit is contained in:
parent
a654a033de
commit
0e18baab40
@ -8,6 +8,19 @@ except that the frontend protocol is Redfish rather than IPMI. The Redfish
|
||||
commands coming from the client are handled by one or more resource-specific
|
||||
drivers.
|
||||
|
||||
Feature sets
|
||||
------------
|
||||
|
||||
The emulator can be configured with different feature sets to emulate different
|
||||
hardware. The feature set is supplied either via the
|
||||
``SUSHY_EMULATOR_FEATURE_SET`` configuration variable or through the
|
||||
``--feature-set`` command line flag.
|
||||
|
||||
Supported feature sets are:
|
||||
* ``minimum`` - only Systems with Boot settings and no other optional fields.
|
||||
* ``vmedia`` - ``minimum`` plus Managers, VirtualMedia and EthernetInterfaces.
|
||||
* ``full`` - all features implemented in the emulator.
|
||||
|
||||
Systems resource
|
||||
----------------
|
||||
|
||||
|
5
releasenotes/notes/feature-set-42f2846a17f59424.yaml
Normal file
5
releasenotes/notes/feature-set-42f2846a17f59424.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Add a configuration options ``SUSHY_EMULATOR_FEATURE_SET`` to define which
|
||||
resources should be available. See the documentation for more details.
|
@ -98,6 +98,18 @@ class Application(flask.Flask):
|
||||
if auth_file and not isinstance(self.wsgi_app, RedfishAuthMiddleware):
|
||||
self.wsgi_app = RedfishAuthMiddleware(self.wsgi_app, auth_file)
|
||||
|
||||
feature_set = self.config.get('SUSHY_EMULATOR_FEATURE_SET', 'full')
|
||||
if feature_set not in ('full', 'vmedia', 'minimum'):
|
||||
raise RuntimeError(f"Invalid feature set {self.feature_set}")
|
||||
|
||||
@property
|
||||
def feature_set(self):
|
||||
return self.config.get('SUSHY_EMULATOR_FEATURE_SET', 'full')
|
||||
|
||||
def render_template(self, template_name, /, **params):
|
||||
params.setdefault('feature_set', self.feature_set)
|
||||
return flask.render_template(template_name, **params)
|
||||
|
||||
@property
|
||||
@memoize.memoize()
|
||||
def systems(self):
|
||||
@ -203,15 +215,18 @@ def all_exception_handler(message):
|
||||
@app.route('/redfish/v1/')
|
||||
@api_utils.returns_json
|
||||
def root_resource():
|
||||
return flask.render_template('root.json')
|
||||
return app.render_template('root.json')
|
||||
|
||||
|
||||
@app.route('/redfish/v1/Chassis')
|
||||
@api_utils.returns_json
|
||||
def chassis_collection_resource():
|
||||
if app.feature_set != "full":
|
||||
raise error.FeatureNotAvailable("Chassis")
|
||||
|
||||
app.logger.debug('Serving chassis list')
|
||||
|
||||
return flask.render_template(
|
||||
return app.render_template(
|
||||
'chassis_collection.json',
|
||||
manager_count=len(app.chassis.chassis),
|
||||
chassis=app.chassis.chassis)
|
||||
@ -220,6 +235,9 @@ def chassis_collection_resource():
|
||||
@app.route('/redfish/v1/Chassis/<identity>', methods=['GET', 'PATCH'])
|
||||
@api_utils.returns_json
|
||||
def chassis_resource(identity):
|
||||
if app.feature_set != "full":
|
||||
raise error.FeatureNotAvailable("Chassis")
|
||||
|
||||
chassis = app.chassis
|
||||
|
||||
uuid = chassis.uuid(identity)
|
||||
@ -241,7 +259,7 @@ def chassis_resource(identity):
|
||||
storage = []
|
||||
drives = []
|
||||
|
||||
return flask.render_template(
|
||||
return app.render_template(
|
||||
'chassis.json',
|
||||
identity=identity,
|
||||
name=chassis.name(identity),
|
||||
@ -272,6 +290,9 @@ def chassis_resource(identity):
|
||||
@app.route('/redfish/v1/Chassis/<identity>/Thermal', methods=['GET'])
|
||||
@api_utils.returns_json
|
||||
def thermal_resource(identity):
|
||||
if app.feature_set != "full":
|
||||
raise error.FeatureNotAvailable("Chassis")
|
||||
|
||||
chassis = app.chassis
|
||||
|
||||
uuid = chassis.uuid(identity)
|
||||
@ -286,7 +307,7 @@ def thermal_resource(identity):
|
||||
else:
|
||||
systems = []
|
||||
|
||||
return flask.render_template(
|
||||
return app.render_template(
|
||||
'thermal.json',
|
||||
chassis=identity,
|
||||
systems=systems
|
||||
@ -296,9 +317,12 @@ def thermal_resource(identity):
|
||||
@app.route('/redfish/v1/Managers')
|
||||
@api_utils.returns_json
|
||||
def manager_collection_resource():
|
||||
if app.feature_set == "minimum":
|
||||
raise error.FeatureNotAvailable("Managers")
|
||||
|
||||
app.logger.debug('Serving managers list')
|
||||
|
||||
return flask.render_template(
|
||||
return app.render_template(
|
||||
'manager_collection.json',
|
||||
manager_count=len(app.managers.managers),
|
||||
managers=app.managers.managers)
|
||||
@ -319,6 +343,9 @@ def jsonify(obj_type, obj_version, obj):
|
||||
@app.route('/redfish/v1/Managers/<identity>', methods=['GET'])
|
||||
@api_utils.returns_json
|
||||
def manager_resource(identity):
|
||||
if app.feature_set == "minimum":
|
||||
raise error.FeatureNotAvailable("Managers")
|
||||
|
||||
app.logger.debug('Serving resources for manager "%s"', identity)
|
||||
|
||||
manager = app.managers.get_manager(identity)
|
||||
@ -326,22 +353,11 @@ def manager_resource(identity):
|
||||
chassis = app.managers.get_managed_chassis(manager)
|
||||
|
||||
uuid = manager['UUID']
|
||||
return jsonify('Manager', 'v1_3_1', {
|
||||
result = {
|
||||
"Id": manager['Id'],
|
||||
"Name": manager.get('Name'),
|
||||
"UUID": uuid,
|
||||
"ServiceEntryPointUUID": manager.get('ServiceEntryPointUUID'),
|
||||
"ManagerType": "BMC",
|
||||
"Description": "Contoso BMC",
|
||||
"Model": "Joo Janta 200",
|
||||
"DateTime": datetime.now().strftime('%Y-%M-%dT%H:%M:%S+00:00'),
|
||||
"DateTimeLocalOffset": "+00:00",
|
||||
"Status": {
|
||||
"State": "Enabled",
|
||||
"Health": "OK"
|
||||
},
|
||||
"PowerState": "On",
|
||||
"FirmwareVersion": "1.00",
|
||||
"VirtualMedia": {
|
||||
"@odata.id": "/redfish/v1/Systems/%s/VirtualMedia" % systems[0],
|
||||
},
|
||||
@ -356,11 +372,27 @@ def manager_resource(identity):
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Chassis/%s" % ch
|
||||
}
|
||||
for ch in chassis
|
||||
for ch in chassis if app.feature_set == "full"
|
||||
]
|
||||
},
|
||||
"@odata.id": "/redfish/v1/Managers/%s" % uuid
|
||||
})
|
||||
"@odata.id": "/redfish/v1/Managers/%s" % uuid,
|
||||
}
|
||||
if app.feature_set == "full":
|
||||
result.update({
|
||||
"ServiceEntryPointUUID": manager.get('ServiceEntryPointUUID'),
|
||||
"Description": "Contoso BMC",
|
||||
"Model": "Joo Janta 200",
|
||||
"DateTime": datetime.now().strftime('%Y-%M-%dT%H:%M:%S+00:00'),
|
||||
"DateTimeLocalOffset": "+00:00",
|
||||
"Status": {
|
||||
"State": "Enabled",
|
||||
"Health": "OK"
|
||||
},
|
||||
"PowerState": "On",
|
||||
"FirmwareVersion": "1.00",
|
||||
})
|
||||
|
||||
return jsonify('Manager', 'v1_3_1', result)
|
||||
|
||||
|
||||
@app.route('/redfish/v1/Systems')
|
||||
@ -371,7 +403,7 @@ def system_collection_resource():
|
||||
|
||||
app.logger.debug('Serving systems list')
|
||||
|
||||
return flask.render_template(
|
||||
return app.render_template(
|
||||
'system_collection.json', system_count=len(systems), systems=systems)
|
||||
|
||||
|
||||
@ -390,7 +422,7 @@ def system_resource(identity):
|
||||
except error.NotSupportedError:
|
||||
return None
|
||||
|
||||
return flask.render_template(
|
||||
return app.render_template(
|
||||
'system.json',
|
||||
identity=identity,
|
||||
name=app.systems.name(identity),
|
||||
@ -414,6 +446,8 @@ def system_resource(identity):
|
||||
if not boot and not indicator_led_state:
|
||||
return ('PATCH only works for Boot and '
|
||||
'IndicatorLED elements'), 400
|
||||
if indicator_led_state and app.feature_set != "full":
|
||||
raise error.FeatureNotAvailable("IndicatorLED", code=400)
|
||||
|
||||
if boot:
|
||||
target = boot.get('BootSourceOverrideTarget')
|
||||
@ -492,9 +526,12 @@ def system_resource(identity):
|
||||
@api_utils.ensure_instance_access
|
||||
@api_utils.returns_json
|
||||
def ethernet_interfaces_collection(identity):
|
||||
if app.feature_set == "minimum":
|
||||
raise error.FeatureNotAvailable("EthernetInterfaces")
|
||||
|
||||
nics = app.systems.get_nics(identity)
|
||||
|
||||
return flask.render_template(
|
||||
return app.render_template(
|
||||
'ethernet_interfaces_collection.json', identity=identity,
|
||||
nics=nics)
|
||||
|
||||
@ -504,11 +541,14 @@ def ethernet_interfaces_collection(identity):
|
||||
@api_utils.ensure_instance_access
|
||||
@api_utils.returns_json
|
||||
def ethernet_interface(identity, nic_id):
|
||||
if app.feature_set == "minimum":
|
||||
raise error.FeatureNotAvailable("EthernetInterfaces")
|
||||
|
||||
nics = app.systems.get_nics(identity)
|
||||
|
||||
for nic in nics:
|
||||
if nic['id'] == nic_id:
|
||||
return flask.render_template(
|
||||
return app.render_template(
|
||||
'ethernet_interface.json', identity=identity, nic=nic)
|
||||
|
||||
raise error.NotFound()
|
||||
@ -519,9 +559,12 @@ def ethernet_interface(identity, nic_id):
|
||||
@api_utils.ensure_instance_access
|
||||
@api_utils.returns_json
|
||||
def processors_collection(identity):
|
||||
if app.feature_set != "full":
|
||||
raise error.FeatureNotAvailable("Processors")
|
||||
|
||||
processors = app.systems.get_processors(identity)
|
||||
|
||||
return flask.render_template(
|
||||
return app.render_template(
|
||||
'processors_collection.json', identity=identity,
|
||||
processors=processors)
|
||||
|
||||
@ -531,11 +574,14 @@ def processors_collection(identity):
|
||||
@api_utils.ensure_instance_access
|
||||
@api_utils.returns_json
|
||||
def processor(identity, processor_id):
|
||||
if app.feature_set != "full":
|
||||
raise error.FeatureNotAvailable("Processors")
|
||||
|
||||
processors = app.systems.get_processors(identity)
|
||||
|
||||
for proc in processors:
|
||||
if proc['id'] == processor_id:
|
||||
return flask.render_template(
|
||||
return app.render_template(
|
||||
'processor.json', identity=identity, processor=proc)
|
||||
|
||||
raise error.NotFound()
|
||||
@ -560,11 +606,14 @@ def system_reset_action(identity):
|
||||
@api_utils.ensure_instance_access
|
||||
@api_utils.returns_json
|
||||
def bios(identity):
|
||||
if app.feature_set != "full":
|
||||
raise error.FeatureNotAvailable("BIOS")
|
||||
|
||||
bios = app.systems.get_bios(identity)
|
||||
|
||||
app.logger.debug('Serving BIOS for system "%s"', identity)
|
||||
|
||||
return flask.render_template(
|
||||
return app.render_template(
|
||||
'bios.json',
|
||||
identity=identity,
|
||||
bios_current_attributes=json.dumps(bios, sort_keys=True, indent=6))
|
||||
@ -575,13 +624,15 @@ def bios(identity):
|
||||
@api_utils.ensure_instance_access
|
||||
@api_utils.returns_json
|
||||
def bios_settings(identity):
|
||||
if app.feature_set != "full":
|
||||
raise error.FeatureNotAvailable("BIOS")
|
||||
|
||||
if flask.request.method == 'GET':
|
||||
bios = app.systems.get_bios(identity)
|
||||
|
||||
app.logger.debug('Serving BIOS Settings for system "%s"', identity)
|
||||
|
||||
return flask.render_template(
|
||||
return app.render_template(
|
||||
'bios_settings.json',
|
||||
identity=identity,
|
||||
bios_pending_attributes=json.dumps(bios, sort_keys=True, indent=6))
|
||||
@ -601,6 +652,9 @@ def bios_settings(identity):
|
||||
@api_utils.ensure_instance_access
|
||||
@api_utils.returns_json
|
||||
def system_reset_bios(identity):
|
||||
if app.feature_set != "full":
|
||||
raise error.FeatureNotAvailable("BIOS")
|
||||
|
||||
app.systems.reset_bios(identity)
|
||||
|
||||
app.logger.info('BIOS for system "%s" reset', identity)
|
||||
@ -613,13 +667,15 @@ def system_reset_bios(identity):
|
||||
@api_utils.ensure_instance_access
|
||||
@api_utils.returns_json
|
||||
def secure_boot(identity):
|
||||
if app.feature_set != "full":
|
||||
raise error.FeatureNotAvailable("SecureBoot")
|
||||
|
||||
if flask.request.method == 'GET':
|
||||
secure = app.systems.get_secure_boot(identity)
|
||||
|
||||
app.logger.debug('Serving secure boot for system "%s"', identity)
|
||||
|
||||
return flask.render_template(
|
||||
return app.render_template(
|
||||
'secure_boot.json',
|
||||
identity=identity,
|
||||
secure_boot_enable=secure,
|
||||
@ -640,10 +696,13 @@ def secure_boot(identity):
|
||||
@api_utils.ensure_instance_access
|
||||
@api_utils.returns_json
|
||||
def simple_storage_collection(identity):
|
||||
if app.feature_set != "full":
|
||||
raise error.FeatureNotAvailable("SimpleStorage")
|
||||
|
||||
simple_storage_controllers = (
|
||||
app.systems.get_simple_storage_collection(identity))
|
||||
|
||||
return flask.render_template(
|
||||
return app.render_template(
|
||||
'simple_storage_collection.json', identity=identity,
|
||||
simple_storage_controllers=simple_storage_controllers)
|
||||
|
||||
@ -653,6 +712,9 @@ def simple_storage_collection(identity):
|
||||
@api_utils.ensure_instance_access
|
||||
@api_utils.returns_json
|
||||
def simple_storage(identity, simple_storage_id):
|
||||
if app.feature_set != "full":
|
||||
raise error.FeatureNotAvailable("SimpleStorage")
|
||||
|
||||
simple_storage_controllers = (
|
||||
app.systems.get_simple_storage_collection(identity))
|
||||
try:
|
||||
@ -660,8 +722,8 @@ def simple_storage(identity, simple_storage_id):
|
||||
except KeyError:
|
||||
app.logger.debug('"%s" Simple Storage resource was not found')
|
||||
raise error.NotFound()
|
||||
return flask.render_template('simple_storage.json', identity=identity,
|
||||
simple_storage=storage_controller)
|
||||
return app.render_template('simple_storage.json', identity=identity,
|
||||
simple_storage=storage_controller)
|
||||
|
||||
|
||||
@app.route('/redfish/v1/Systems/<identity>/Storage',
|
||||
@ -669,11 +731,14 @@ def simple_storage(identity, simple_storage_id):
|
||||
@api_utils.ensure_instance_access
|
||||
@api_utils.returns_json
|
||||
def storage_collection(identity):
|
||||
if app.feature_set != "full":
|
||||
raise error.FeatureNotAvailable("Storage")
|
||||
|
||||
uuid = app.systems.uuid(identity)
|
||||
|
||||
storage_col = app.storage.get_storage_col(uuid)
|
||||
|
||||
return flask.render_template(
|
||||
return app.render_template(
|
||||
'storage_collection.json', identity=identity,
|
||||
storage_col=storage_col)
|
||||
|
||||
@ -683,12 +748,15 @@ def storage_collection(identity):
|
||||
@api_utils.ensure_instance_access
|
||||
@api_utils.returns_json
|
||||
def storage(identity, storage_id):
|
||||
if app.feature_set != "full":
|
||||
raise error.FeatureNotAvailable("Storage")
|
||||
|
||||
uuid = app.systems.uuid(identity)
|
||||
storage_col = app.storage.get_storage_col(uuid)
|
||||
|
||||
for stg in storage_col:
|
||||
if stg['Id'] == storage_id:
|
||||
return flask.render_template(
|
||||
return app.render_template(
|
||||
'storage.json', identity=identity, storage=stg)
|
||||
|
||||
raise error.NotFound()
|
||||
@ -699,12 +767,15 @@ def storage(identity, storage_id):
|
||||
@api_utils.ensure_instance_access
|
||||
@api_utils.returns_json
|
||||
def drive_resource(identity, stg_id, drv_id):
|
||||
if app.feature_set != "full":
|
||||
raise error.FeatureNotAvailable("Storage")
|
||||
|
||||
uuid = app.systems.uuid(identity)
|
||||
drives = app.drives.get_drives(uuid, stg_id)
|
||||
|
||||
for drv in drives:
|
||||
if drv['Id'] == drv_id:
|
||||
return flask.render_template(
|
||||
return app.render_template(
|
||||
'drive.json', identity=identity, storage_id=stg_id, drive=drv)
|
||||
|
||||
raise error.NotFound()
|
||||
@ -715,6 +786,9 @@ def drive_resource(identity, stg_id, drv_id):
|
||||
@api_utils.ensure_instance_access
|
||||
@api_utils.returns_json
|
||||
def volumes_collection(identity, storage_id):
|
||||
if app.feature_set != "full":
|
||||
raise error.FeatureNotAvailable("Storage")
|
||||
|
||||
uuid = app.systems.uuid(identity)
|
||||
|
||||
if flask.request.method == 'GET':
|
||||
@ -729,7 +803,7 @@ def volumes_collection(identity, storage_id):
|
||||
else:
|
||||
vol_ids.append(vol_id)
|
||||
|
||||
return flask.render_template(
|
||||
return app.render_template(
|
||||
'volume_collection.json', identity=identity,
|
||||
storage_id=storage_id, volume_col=vol_ids)
|
||||
|
||||
@ -757,6 +831,9 @@ def volumes_collection(identity, storage_id):
|
||||
@api_utils.ensure_instance_access
|
||||
@api_utils.returns_json
|
||||
def volume(identity, stg_id, vol_id):
|
||||
if app.feature_set != "full":
|
||||
raise error.FeatureNotAvailable("Storage")
|
||||
|
||||
uuid = app.systems.uuid(identity)
|
||||
vol_col = app.volumes.get_volumes_col(uuid, stg_id)
|
||||
|
||||
@ -766,7 +843,7 @@ def volume(identity, stg_id, vol_id):
|
||||
if not vol_id:
|
||||
app.volumes.delete_volume(uuid, stg_id, vol)
|
||||
else:
|
||||
return flask.render_template(
|
||||
return app.render_template(
|
||||
'volume.json', identity=identity, storage_id=stg_id,
|
||||
volume=vol)
|
||||
|
||||
@ -776,44 +853,59 @@ def volume(identity, stg_id, vol_id):
|
||||
@app.route('/redfish/v1/Registries')
|
||||
@api_utils.returns_json
|
||||
def registry_file_collection():
|
||||
if app.feature_set != "full":
|
||||
raise error.FeatureNotAvailable("Registries")
|
||||
|
||||
app.logger.debug('Serving registry file collection')
|
||||
|
||||
return flask.render_template(
|
||||
return app.render_template(
|
||||
'registry_file_collection.json')
|
||||
|
||||
|
||||
@app.route('/redfish/v1/Registries/BiosAttributeRegistry.v1_0_0')
|
||||
@api_utils.returns_json
|
||||
def bios_attribute_registry_file():
|
||||
if app.feature_set != "full":
|
||||
raise error.FeatureNotAvailable("Registries")
|
||||
|
||||
app.logger.debug('Serving BIOS attribute registry file')
|
||||
|
||||
return flask.render_template(
|
||||
return app.render_template(
|
||||
'bios_attribute_registry_file.json')
|
||||
|
||||
|
||||
@app.route('/redfish/v1/Registries/Messages')
|
||||
@api_utils.returns_json
|
||||
def message_registry_file():
|
||||
if app.feature_set != "full":
|
||||
raise error.FeatureNotAvailable("Registries")
|
||||
|
||||
app.logger.debug('Serving message registry file')
|
||||
|
||||
return flask.render_template(
|
||||
return app.render_template(
|
||||
'message_registry_file.json')
|
||||
|
||||
|
||||
@app.route('/redfish/v1/Systems/Bios/BiosRegistry')
|
||||
@api_utils.returns_json
|
||||
def bios_registry():
|
||||
if app.feature_set != "full":
|
||||
raise error.FeatureNotAvailable("Registries")
|
||||
|
||||
app.logger.debug('Serving BIOS registry')
|
||||
|
||||
return flask.render_template('bios_registry.json')
|
||||
return app.render_template('bios_registry.json')
|
||||
|
||||
|
||||
@app.route('/redfish/v1/Registries/Messages/Registry')
|
||||
@api_utils.returns_json
|
||||
def message_registry():
|
||||
if app.feature_set != "full":
|
||||
raise error.FeatureNotAvailable("Registries")
|
||||
|
||||
app.logger.debug('Serving message registry')
|
||||
|
||||
return flask.render_template('message_registry.json')
|
||||
return app.render_template('message_registry.json')
|
||||
|
||||
|
||||
def parse_args():
|
||||
@ -843,6 +935,10 @@ def parse_args():
|
||||
type=str,
|
||||
help='SSL key to use for HTTPS. Can also be set'
|
||||
'via config variable SUSHY_EMULATOR_SSL_KEY.')
|
||||
parser.add_argument('--feature-set',
|
||||
type=str, choices=['full', 'vmedia', 'minimum'],
|
||||
help='Feature set to provide. Can also be set'
|
||||
'via config variable SUSHY_EMULATOR_FEATURE_SET.')
|
||||
backend_group = parser.add_mutually_exclusive_group()
|
||||
backend_group.add_argument('--os-cloud',
|
||||
type=str,
|
||||
@ -908,6 +1004,9 @@ def main():
|
||||
if args.ssl_key:
|
||||
app.config['SUSHY_EMULATOR_SSL_KEY'] = args.ssl_key
|
||||
|
||||
if args.feature_set:
|
||||
app.config['SUSHY_EMULATOR_FEATURE_SET'] = args.feature_set
|
||||
|
||||
ssl_context = None
|
||||
ssl_certificate = app.config.get('SUSHY_EMULATOR_SSL_CERT')
|
||||
ssl_key = app.config.get('SUSHY_EMULATOR_SSL_KEY')
|
||||
|
@ -4,21 +4,27 @@
|
||||
"Name": "Redvirt Service",
|
||||
"RedfishVersion": "1.5.0",
|
||||
"UUID": "85775665-c110-4b85-8989-e6162170b3ec",
|
||||
{% if feature_set == "full" %}
|
||||
"Chassis": {
|
||||
"@odata.id": "/redfish/v1/Chassis"
|
||||
},
|
||||
{% endif %}
|
||||
"Systems": {
|
||||
"@odata.id": "/redfish/v1/Systems"
|
||||
},
|
||||
{% if feature_set != "minimum" %}
|
||||
"Managers": {
|
||||
"@odata.id": "/redfish/v1/Managers"
|
||||
},
|
||||
{% endif %}
|
||||
{% if feature_set == "full" %}
|
||||
"Registries": {
|
||||
"@odata.id": "/redfish/v1/Registries"
|
||||
},
|
||||
"CertificateService": {
|
||||
"@odata.id": "/redfish/v1/CertificateService"
|
||||
},
|
||||
{% endif %}
|
||||
"@odata.id": "/redfish/v1/",
|
||||
"@Redfish.Copyright": "Copyright 2014-2016 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
|
||||
}
|
||||
|
@ -3,12 +3,14 @@
|
||||
"Id": {{ identity|string|tojson }},
|
||||
"Name": {{ name|string|tojson }},
|
||||
"UUID": {{ uuid|string|tojson }},
|
||||
{%- if feature_set == "full" %}
|
||||
"Manufacturer": "Sushy Emulator",
|
||||
"Status": {
|
||||
"State": "Enabled",
|
||||
"Health": "OK",
|
||||
"HealthRollUp": "OK"
|
||||
},
|
||||
{% endif %}
|
||||
{%- if power_state %}
|
||||
"PowerState": {{ power_state|string|tojson }},
|
||||
{%- endif %}
|
||||
@ -40,6 +42,7 @@
|
||||
"BootSourceOverrideEnabled": "Continuous"
|
||||
{%- endif %}
|
||||
},
|
||||
{%- if feature_set == "full" %}
|
||||
"ProcessorSummary": {
|
||||
{%- if total_cpus %}
|
||||
"Count": {{ total_cpus }},
|
||||
@ -70,9 +73,6 @@
|
||||
"Memory": {
|
||||
"@odata.id": {{ "/redfish/v1/Systems/%s/Memory"|format(identity)|tojson }}
|
||||
},
|
||||
"EthernetInterfaces": {
|
||||
"@odata.id": {{ "/redfish/v1/Systems/%s/EthernetInterfaces"|format(identity)|tojson }}
|
||||
},
|
||||
"SecureBoot": {
|
||||
"@odata.id": {{ "/redfish/v1/Systems/%s/SecureBoot"|format(identity)|tojson }}
|
||||
},
|
||||
@ -85,10 +85,17 @@
|
||||
{%- if indicator_led %}
|
||||
"IndicatorLED": {{ indicator_led|string|tojson }},
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{%- if feature_set != "minimum" %}
|
||||
"EthernetInterfaces": {
|
||||
"@odata.id": {{ "/redfish/v1/Systems/%s/EthernetInterfaces"|format(identity)|tojson }}
|
||||
},
|
||||
"VirtualMedia": {
|
||||
"@odata.id": {{ "/redfish/v1/Systems/%s/VirtualMedia"|format(identity)|tojson }}
|
||||
},
|
||||
{%- endif %}
|
||||
"Links": {
|
||||
{%- if feature_set == "full" %}
|
||||
"Chassis": [
|
||||
{%- for chassis_ in chassis %}
|
||||
{
|
||||
@ -96,6 +103,8 @@
|
||||
}{% if not loop.last %},{% endif %}
|
||||
{% endfor -%}
|
||||
],
|
||||
{% endif %}
|
||||
{%- if feature_set != "minimum" %}
|
||||
"ManagedBy": [
|
||||
{%- for manager in managers %}
|
||||
{
|
||||
@ -103,6 +112,7 @@
|
||||
}{% if not loop.last %},{% endif %}
|
||||
{% endfor -%}
|
||||
]
|
||||
{% endif %}
|
||||
},
|
||||
"Actions": {
|
||||
"#ComputerSystem.Reset": {
|
||||
|
@ -45,3 +45,10 @@ class BadRequest(FishyError):
|
||||
|
||||
def __init__(self, msg, code=400):
|
||||
super().__init__(msg, code)
|
||||
|
||||
|
||||
class FeatureNotAvailable(NotFound):
|
||||
"""Feature is not available."""
|
||||
|
||||
def __init__(self, feature, code=404):
|
||||
super().__init__(f"Feature {feature} not available", code=code)
|
||||
|
@ -36,6 +36,12 @@ class EmulatorTestCase(base.BaseTestCase):
|
||||
|
||||
super(EmulatorTestCase, self).setUp()
|
||||
|
||||
def set_feature_set(self, new_feature_set):
|
||||
main.app.config['SUSHY_EMULATOR_FEATURE_SET'] = new_feature_set
|
||||
self.addCleanup(
|
||||
lambda: main.app.config.pop('SUSHY_EMULATOR_FEATURE_SET', None)
|
||||
)
|
||||
|
||||
|
||||
class CommonTestCase(EmulatorTestCase):
|
||||
|
||||
@ -52,6 +58,22 @@ class CommonTestCase(EmulatorTestCase):
|
||||
self.assertEqual(200, response.status_code)
|
||||
self.assertEqual('RedvirtService', response.json['Id'])
|
||||
|
||||
def test_root_resource_only_vmedia(self):
|
||||
self.set_feature_set("vmedia")
|
||||
response = self.app.get('/redfish/v1/')
|
||||
self.assertEqual(200, response.status_code)
|
||||
self.assertEqual(
|
||||
{'Id', 'Name', 'RedfishVersion', 'UUID', 'Systems', 'Managers'},
|
||||
{x for x in response.json if not x.startswith('@')})
|
||||
|
||||
def test_root_resource_minimum(self):
|
||||
self.set_feature_set("minimum")
|
||||
response = self.app.get('/redfish/v1/')
|
||||
self.assertEqual(200, response.status_code)
|
||||
self.assertEqual(
|
||||
{'Id', 'Name', 'RedfishVersion', 'UUID', 'Systems'},
|
||||
{x for x in response.json if not x.startswith('@')})
|
||||
|
||||
|
||||
TEST_PASSWD = \
|
||||
b"admin:$2y$05$mYl8KMwM94l4LR/sw1teIeA6P2u8gfX16e8wvT7NmGgAM5r9jgLl."
|
||||
@ -210,6 +232,33 @@ class ManagersTestCase(EmulatorTestCase):
|
||||
response.json['Links']['ManagerForServers'])
|
||||
self.assertEqual([{'@odata.id': '/redfish/v1/Chassis/chassis0'}],
|
||||
response.json['Links']['ManagerForChassis'])
|
||||
self.assertEqual({'@odata.id': '/redfish/v1/Systems/xxx/VirtualMedia'},
|
||||
response.json['VirtualMedia'])
|
||||
|
||||
@patch_resource('managers')
|
||||
def test_manager_resource_get_reduced_feature_set(self, managers_mock):
|
||||
self.set_feature_set("vmedia")
|
||||
managers_mock = managers_mock.return_value
|
||||
managers_mock.managers = ['xxxx-yyyy-zzzz']
|
||||
managers_mock.get_manager.return_value = {
|
||||
'UUID': 'xxxx-yyyy-zzzz',
|
||||
'Name': 'name',
|
||||
'Id': 'xxxx-yyyy-zzzz',
|
||||
}
|
||||
managers_mock.get_managed_systems.return_value = ['xxx']
|
||||
managers_mock.get_managed_chassis.return_value = ['chassis0']
|
||||
|
||||
response = self.app.get('/redfish/v1/Managers/xxxx-yyyy-zzzz')
|
||||
|
||||
self.assertEqual(200, response.status_code, response.json)
|
||||
self.assertEqual('xxxx-yyyy-zzzz', response.json['Id'])
|
||||
self.assertEqual('xxxx-yyyy-zzzz', response.json['UUID'])
|
||||
self.assertNotIn('ServiceEntryPointUUID', response.json)
|
||||
self.assertEqual([{'@odata.id': '/redfish/v1/Systems/xxx'}],
|
||||
response.json['Links']['ManagerForServers'])
|
||||
self.assertNotIn('Chassis', response.json['Links'])
|
||||
self.assertEqual({'@odata.id': '/redfish/v1/Systems/xxx/VirtualMedia'},
|
||||
response.json['VirtualMedia'])
|
||||
|
||||
|
||||
class SystemsTestCase(EmulatorTestCase):
|
||||
@ -263,6 +312,81 @@ class SystemsTestCase(EmulatorTestCase):
|
||||
self.assertEqual(
|
||||
[{'@odata.id': '/redfish/v1/Chassis/chassis0'}],
|
||||
response.json['Links']['Chassis'])
|
||||
self.assertEqual(
|
||||
{'@odata.id': '/redfish/v1/Systems/xxxx-yyyy-zzzz/VirtualMedia'},
|
||||
response.json['VirtualMedia'])
|
||||
|
||||
@patch_resource('indicators')
|
||||
@patch_resource('chassis')
|
||||
@patch_resource('managers')
|
||||
@patch_resource('systems')
|
||||
def test_system_resource_get_reduced_feature_set(
|
||||
self, systems_mock, managers_mock, chassis_mock, indicators_mock):
|
||||
self.set_feature_set("vmedia")
|
||||
systems_mock = systems_mock.return_value
|
||||
systems_mock.uuid.return_value = 'zzzz-yyyy-xxxx'
|
||||
systems_mock.get_power_state.return_value = 'On'
|
||||
systems_mock.get_boot_device.return_value = 'Cd'
|
||||
systems_mock.get_boot_mode.return_value = 'Legacy'
|
||||
managers_mock.return_value.get_managers_for_system.return_value = [
|
||||
'aaaa-bbbb-cccc']
|
||||
|
||||
response = self.app.get('/redfish/v1/Systems/xxxx-yyyy-zzzz')
|
||||
|
||||
self.assertEqual(200, response.status_code)
|
||||
self.assertEqual('xxxx-yyyy-zzzz', response.json['Id'])
|
||||
self.assertEqual('zzzz-yyyy-xxxx', response.json['UUID'])
|
||||
self.assertEqual('On', response.json['PowerState'])
|
||||
self.assertNotIn('IndicatorLED', response.json)
|
||||
self.assertNotIn('MemorySummary', response.json)
|
||||
self.assertNotIn('ProcessorSummary', response.json)
|
||||
self.assertNotIn('BiosVersion', response.json)
|
||||
self.assertNotIn('Bios', response.json)
|
||||
self.assertEqual(
|
||||
'Cd', response.json['Boot']['BootSourceOverrideTarget'])
|
||||
self.assertEqual(
|
||||
'Legacy', response.json['Boot']['BootSourceOverrideMode'])
|
||||
self.assertEqual(
|
||||
[{'@odata.id': '/redfish/v1/Managers/aaaa-bbbb-cccc'}],
|
||||
response.json['Links']['ManagedBy'])
|
||||
self.assertNotIn('Chassis', response.json['Links'])
|
||||
self.assertEqual(
|
||||
{'@odata.id': '/redfish/v1/Systems/xxxx-yyyy-zzzz/VirtualMedia'},
|
||||
response.json['VirtualMedia'])
|
||||
|
||||
@patch_resource('indicators')
|
||||
@patch_resource('chassis')
|
||||
@patch_resource('managers')
|
||||
@patch_resource('systems')
|
||||
def test_system_resource_get_minimum_feature_set(
|
||||
self, systems_mock, managers_mock, chassis_mock, indicators_mock):
|
||||
self.set_feature_set("minimum")
|
||||
systems_mock = systems_mock.return_value
|
||||
systems_mock.uuid.return_value = 'zzzz-yyyy-xxxx'
|
||||
systems_mock.get_power_state.return_value = 'On'
|
||||
systems_mock.get_boot_device.return_value = 'Cd'
|
||||
systems_mock.get_boot_mode.return_value = 'Legacy'
|
||||
managers_mock.return_value.get_managers_for_system.return_value = [
|
||||
'aaaa-bbbb-cccc']
|
||||
|
||||
response = self.app.get('/redfish/v1/Systems/xxxx-yyyy-zzzz')
|
||||
|
||||
self.assertEqual(200, response.status_code)
|
||||
self.assertEqual('xxxx-yyyy-zzzz', response.json['Id'])
|
||||
self.assertEqual('zzzz-yyyy-xxxx', response.json['UUID'])
|
||||
self.assertEqual('On', response.json['PowerState'])
|
||||
self.assertNotIn('IndicatorLED', response.json)
|
||||
self.assertNotIn('MemorySummary', response.json)
|
||||
self.assertNotIn('ProcessorSummary', response.json)
|
||||
self.assertNotIn('BiosVersion', response.json)
|
||||
self.assertNotIn('Bios', response.json)
|
||||
self.assertEqual(
|
||||
'Cd', response.json['Boot']['BootSourceOverrideTarget'])
|
||||
self.assertEqual(
|
||||
'Legacy', response.json['Boot']['BootSourceOverrideMode'])
|
||||
self.assertNotIn('ManagedBy', response.json['Links'])
|
||||
self.assertNotIn('Chassis', response.json['Links'])
|
||||
self.assertNotIn('VirtualMedia', response.json)
|
||||
|
||||
@patch_resource('systems')
|
||||
def test_system_resource_patch(self, systems_mock):
|
||||
@ -336,6 +460,18 @@ class SystemsTestCase(EmulatorTestCase):
|
||||
json=data)
|
||||
self.assertEqual(500, response.status_code)
|
||||
|
||||
@patch_resource('indicators')
|
||||
@patch_resource('systems')
|
||||
def test_system_indicator_reduced_feature_set(self, systems_mock,
|
||||
indicators_mock):
|
||||
self.set_feature_set("vmedia")
|
||||
systems_mock.return_value.uuid.return_value = self.uuid
|
||||
|
||||
data = {'IndicatorLED': 'Off'}
|
||||
response = self.app.patch('/redfish/v1/Systems/xxxx-yyyy-zzzz',
|
||||
json=data)
|
||||
self.assertEqual(400, response.status_code)
|
||||
|
||||
|
||||
@patch_resource('systems')
|
||||
class BiosTestCase(EmulatorTestCase):
|
||||
|
Loading…
Reference in New Issue
Block a user