The directory containing the interface protobuf files recently changed. Updating the documentation to point to the new directory. Change-Id: I8c1586e7aee024a12867bc8c32cc7ad57868339b Signed-off-by: John Kariuki <John.K.Kariuki@intel.com> Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/16350 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Reviewed-by: Tomasz Zawadzki <tomasz.zawadzki@intel.com> Reviewed-by: Konrad Sztyber <konrad.sztyber@intel.com>
144 lines
5.5 KiB
Markdown
144 lines
5.5 KiB
Markdown
# Storage Management Agent {#sma}
|
|
|
|
Storage Management Agent (SMA) is a service providing a gRPC interface for
|
|
orchestrating SPDK applications. It's a standalone application that allows
|
|
users to create and manage various types of devices (e.g. NVMe, virtio-blk,
|
|
etc.). The major difference between SMA's API and the existing SPDK-RPC
|
|
interface is that it's designed to abstract the low level details exposed by
|
|
SPDK-RPCs, which enables it to be more easily consumed by orchestration
|
|
frameworks, such as k8s or OpenStack. This is especially important for
|
|
deployments on IPUs (Infrastructure Processing Unit), which usually require a
|
|
lot of hardware-specific options.
|
|
|
|
## Interface
|
|
|
|
The interface is defined in a protobuf files located in `proto`
|
|
directory. The generic interface common to all types of devices is defined in
|
|
`sma.proto` file, while device-specific options are defined in their separate
|
|
files (e.g. `nvme.proto` for NVMe).
|
|
|
|
Currently, the interface consists of four methods. Additionally, it defines two
|
|
main types of objects: volumes and devices. A volume is a representation of
|
|
some storage media. It is equivalent to a SPDK bdev and/or an NVMe namespace
|
|
and can exist even if it's not presented to the host system. A device is
|
|
usually a virtual/physical PCIe function that is exposed to a host. It is
|
|
capable of presenting one or more volumes (depending on the type of the device)
|
|
to a host.
|
|
|
|
The following sections provide a high-level description of each method. For
|
|
more details, consult the protobuf definitions.
|
|
|
|
### CreateDevice
|
|
|
|
This method creates a device. If a device with given parameters already exists,
|
|
it becomes a no-op and returns a handle to that device.
|
|
|
|
Input:
|
|
|
|
- `volume`: Volume parameters describing a volume to immediately attach to the
|
|
created device. This field may be optional for some device types (e.g. NVMe),
|
|
while it may be required for others (e.g. virtio-blk).
|
|
- `params`: Device-specific parameters. The type of this structure determines
|
|
the type of device to create.
|
|
|
|
Output:
|
|
|
|
- `handle`: Opaque handle identifying the device.
|
|
|
|
### DeleteDevice
|
|
|
|
This method deletes a device. Volumes that are still attached to a device being
|
|
deleted will be automatically detached.
|
|
|
|
Input:
|
|
|
|
- `handle`: Device handle obtained from `CreateDevice`.
|
|
|
|
### AttachVolume
|
|
|
|
This method creates a volume and attaches it to a device exposing it to the
|
|
host. It might lead to establishing a connection to remote storage target.
|
|
However, this is not always the case, even if the volume is remote. For
|
|
instance, if a volume describes an NVMe namespace, it might already be connected
|
|
if another volume on the same subsystem was created previously. It may be
|
|
unsupported by some types of devices (e.g. virtio-blk).
|
|
|
|
Input:
|
|
|
|
- `volume`: Parameters describing the volume to attach. The type of this
|
|
structure determines the method to create it (e.g. direct NVMe-oF connection,
|
|
NVMe-oF through discovery service, iSCSI, etc.).
|
|
- `device_handle`: Device handle obtained from `CreateDevice`.
|
|
|
|
### DetachVolume
|
|
|
|
This method detaches a volume from a device making it unavailable to the host.
|
|
It may be unsupported by some types of devices (e.g. virtio-blk).
|
|
|
|
Input:
|
|
|
|
- `volume_id`: Volume UUID/GUID.
|
|
- `device_handle`: Device handle obtained from `CreateDevice`.
|
|
|
|
## Running and Configuration
|
|
|
|
In order to run SMA, SPDK needs to be configured with the `--with-sma` flag.
|
|
Then, SMA can be started using a script located in `scripts/sma.py`. It
|
|
requires a YAML configuration file that specifies which types of devices to
|
|
service, as well as several other options (e.g. listen address, SPDK-RPC socket,
|
|
etc.). Device types not listed in the configuration will be disabled and it
|
|
won't be possible to manage them. Below is an example configuration enabling
|
|
two device types (NVMe/vfiouser and vhost-blk):
|
|
|
|
```yaml
|
|
address: 'localhost'
|
|
socket: '/var/tmp/spdk.sock'
|
|
port: 8080
|
|
devices:
|
|
- name: 'vfiouser'
|
|
params:
|
|
root_path: '/var/tmp/vfiouser'
|
|
bus: 'bus0'
|
|
address: '127.0.0.1'
|
|
port: 4444
|
|
- name: 'vhost-blk'
|
|
```
|
|
|
|
## Plugins
|
|
|
|
SMA provides a way to load external plugins implementing support for specific
|
|
device types. A plugin will be loaded if it's specified in the `SMA_PLUGINS`
|
|
environment variable (multiple plugins are separated with a colon) or if it's
|
|
specified in the `plugins` section of the config file. For example, the
|
|
following two methods are equivalent:
|
|
|
|
```sh
|
|
$ SMA_PLUGINS=plugin1:plugin2 scripts/sma.py
|
|
|
|
$ cat sma.yaml
|
|
address: 'localhost'
|
|
port: 8080
|
|
plugins:
|
|
- 'plugin1'
|
|
- 'plugin2'
|
|
devices:
|
|
- name: 'device-from-plugin1'
|
|
- name: 'device-from-plugin2'
|
|
$ scripts/sma.py -c sma.yaml
|
|
```
|
|
|
|
Each plugin needs to be in the python search path (either in one of the default
|
|
directories or added to `PYTHONPATH`).
|
|
|
|
A plugin is required to define a global variable called `devices` storing a list
|
|
of classes deriving from `spdk.sma.DeviceManager`. This base class define the
|
|
interface each device needs to implement. Additionally, each DeviceManager
|
|
needs to define a unique name that will be used to identify it in config file as
|
|
well as the name of the protocol it supports. There can be many DeviceManagers
|
|
supporting the same protocol, but only one can be active at a time. The name of
|
|
the protocol shall match the type specified in `CreateDeviceRequest.params`
|
|
(e.g. "nvme", "virtio_blk", etc.), as it'll be used to select the DeviceManager
|
|
to handle a gRPC request. Finally, a DeviceManager needs to implement the
|
|
`own_device()` method returning a boolean value indicating whether a given
|
|
device handle is owned by that DeviceManager.
|