Spdk/scripts/sma.py
Konrad Sztyber b5678ba805 sma: wait until SPDK process is responding
Most of the devices need to send RPCs during initialization (e.g. create
a transport), so we need to take care that they're initialized only
after we are certain that the SPDK process is listening.

The mechanisim is similar to the `waitforlisten` function used in our
test scripts - it sends a series of `rpc_get_methods` calls and times
out after a period of time if no response is received.  For now, the
timeout is hardcoded to 60s.

Signed-off-by: Konrad Sztyber <konrad.sztyber@intel.com>
Change-Id: Iddadc04ad4c486d2894bc40e1a899a9d204400fc
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/11802
Community-CI: Broadcom CI <spdk-ci.pdl@broadcom.com>
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Tomasz Zawadzki <tomasz.zawadzki@intel.com>
2022-05-20 09:16:54 +00:00

127 lines
4.0 KiB
Python
Executable File

#!/usr/bin/env python3
from argparse import ArgumentParser
import importlib
import logging
import os
import signal
import sys
import threading
import time
import yaml
sys.path.append(os.path.dirname(__file__) + '/../python')
import spdk.sma as sma # noqa
import spdk.rpc.client as rpcclient # noqa
def parse_config(path):
if path is None:
return {}
with open(path, 'r') as cfgfile:
config = yaml.load(cfgfile, Loader=yaml.FullLoader)
return {**config} if config is not None else {}
def parse_argv():
parser = ArgumentParser(description='Storage Management Agent command line interface')
parser.add_argument('--address', '-a', help='IP address to listen on')
parser.add_argument('--socket', '-s', help='SPDK RPC socket')
parser.add_argument('--port', '-p', type=int, help='IP port to listen on')
parser.add_argument('--config', '-c', help='Path to config file')
defaults = {'address': 'localhost',
'socket': '/var/tmp/spdk.sock',
'port': 8080}
# Merge the default values, config file, and the command-line
args = vars(parser.parse_args())
config = parse_config(args.get('config'))
for argname, argvalue in defaults.items():
if args.get(argname) is not None:
if config.get(argname) is not None:
logging.info(f'Overriding "{argname}" value from command-line')
config[argname] = args[argname]
if config.get(argname) is None:
config[argname] = argvalue
return config
def get_build_client(sock):
def build_client():
return rpcclient.JSONRPCClient(sock)
return build_client
def register_devices(agent, devices, config):
for device_config in config.get('devices') or []:
name = device_config.get('name')
device_manager = next(filter(lambda s: s.name == name, devices), None)
if device_manager is None:
logging.error(f'Couldn\'t find device: {name}')
sys.exit(1)
logging.info(f'Registering device: {name}')
device_manager.init(device_config.get('params'))
agent.register_device(device_manager)
def load_plugins(plugins, client):
devices = []
for plugin in plugins:
module = importlib.import_module(plugin)
for device in getattr(module, 'devices', []):
logging.debug(f'Loading external device: {plugin}.{device.__name__}')
devices.append(device(client))
return devices
def wait_for_listen(client, timeout):
start = time.monotonic()
while True:
try:
with client() as _client:
_client.call('rpc_get_methods')
# If we got here, the process is responding to RPCs
break
except rpcclient.JSONRPCException:
logging.debug('The SPDK process is not responding for {}s'.format(
int(time.monotonic() - start)))
if time.monotonic() > start + timeout:
logging.error('Timed out while waiting for SPDK process to respond')
sys.exit(1)
time.sleep(1)
def run(agent):
event = threading.Event()
def signal_handler(signum, frame):
event.set()
for signum in [signal.SIGTERM, signal.SIGINT]:
signal.signal(signum, signal_handler)
agent.start()
event.wait()
agent.stop()
if __name__ == '__main__':
logging.basicConfig(level=os.environ.get('SMA_LOGLEVEL', 'WARNING').upper())
config = parse_argv()
client = get_build_client(config['socket'])
# Wait until the SPDK process starts responding to RPCs
wait_for_listen(client, timeout=60.0)
agent = sma.StorageManagementAgent(config['address'], config['port'])
devices = [sma.NvmfTcpDeviceManager(client)]
devices += load_plugins(config.get('plugins') or [], client)
devices += load_plugins(filter(None, os.environ.get('SMA_PLUGINS', '').split(':')),
client)
register_devices(agent, devices, config)
run(agent)