Extensions

Purpose

RacksDB offers the possibility to define custom extensions of vendor schema with additional objects or additional properties to existing objects. RacksDB ensures at load time the database content fully respect the structure defined by the combination of the vendor schema and the defined extensions.

The additional data is automatically available through the CLI, Python library and REST API without further modification.

This feature can be used to enrich RacksDB database with data useful to your applications without requiring another data source, with the advantage of being fully integrated with the other data defined in RacksDB.

Path

The extensions must be defined in a plain file formated in YAML language.

By default, RacksDB loads extensions defined in file /etc/racksdb/extensions.yml. If this file does not exist, RacksDB silently ignores it and just loads the built-in schema without modification.

RacksDB can load extensions at the path of your choice, at the condition of specifying this path in racksdb command options and library load() arguments. For this reason, it is highly recommended to use the default path.

Content

The syntax of the extension file is roughly similar to the schema file.

The YAML extension file can contain 2 keys at its root:

_content

Mapping (aka: hash or associative array) to extend the definition of the object at database root.

_objects

Mapping to extend the objects definitions.

The format of _content is an object definition. The format of _objects is a mapping whose keys are objects names and values are objects definitions.

Objects Definitions

Objects definitions are mappings with the following expected keys:

description (optional)

String to describe the purpose of the object.

properties

Mapping of objects properties. The keys are the names of the properties. The values are mapping with the following expected keys:

type

The property type, as described below.

description

String to describe the purpose and content of the property.

key (optional)

Boolean to determine if the property is a key. Default value is false.

default (optional)

Default value of the property. When the type is an object, the special value :recursive can be used to make the default value of the property the corresponding object with all its defaults properties, recursively.

example (optional)

Example value of the property.

optional (optional)

Boolean to determine if property is optional. Default value is false.

Properties Type

The properties types are expressed with the following notation:

Example

This is a simple example of an extension definition file:

_content:
  properties:
    power:
      type: list[:Ups]
      optional: true

_objects:
  Ups:
    properties:
      capacity:
        type: ~watts
  RackType:
    properties:
      weight:
        type: int
        optional: true

With this extension:

  • The power key is added at the root of RacksDB database structure. It can contain an optional list of Ups object.

  • The new Ups object is defined, as it does not exist in RacksDB vendor structure. This object contains a property named capacity using the ~watts defined type.

  • The RackType object is extended with an optional weight integer attribute.

Data Visibility

When RacksDB is installed with packages, this example extension is installed in path /usr/share/doc/racksdb/examples/extensions with fake values. This example is used to illustrate data visibility of custom extensions.

The custom objects and properties data can then be explored with RacksDB interfaces:

$ racksdb --db /usr/share/doc/racksdb/examples/extensions/db \
          --ext /usr/share/doc/racksdb/examples/extensions/extensions.yml \
          racks --format json | \
          jq  '.[] | { name: .name, weight: .type.weight}'

This reports the weight of every racks:

{
  "name": "R1-A01",
  "weight": 128
}
{
  "name": "R1-A02",
  "weight": 128
}
{
  "name": "R1-A03",
  "weight": 128
}
…

Print the capacity of every UPS :

>>> from racksdb import RacksDB
>>> db = RacksDB.load(
...          db="/usr/share/doc/racksdb/examples/extensions/db",
...          ext="/usr/share/doc/racksdb/examples/extensions/extensions.yml"
...      )
>>> db.power
[<racksdb.generic.db.RacksDBUps object at 0x7f7c3e7c2bf0>,
 <racksdb.generic.db.RacksDBUps object at 0x7f7c3e7c2c50>]
>>> for power in db.power:
...     print(f"capacity: {power.capacity}")
...
capacity: 250000
capacity: 1600000

Launch racksdb-web to get REST API server:

$ racksdb-web --db /usr/share/doc/racksdb/examples/extensions/db \
              --ext /usr/share/doc/racksdb/examples/extensions/extensions.yml

Request with HTTP client:

$ curl http://localhost:5000/racks | \
  jq  '.[] | { name: .name, weight: .type.weight}'

This reports the weight of every racks:

{
  "name": "R1-A01",
  "weight": 128
}
{
  "name": "R1-A02",
  "weight": 128
}
{
  "name": "R1-A03",
  "weight": 128
}
…