小言_互联网的博客

SPDK配置文件从INI到JSON的转换

361人阅读  评论(0)

作者简介

王海亮 英特尔SPDK测试工程师

为什么SPDK要使用JSON格式配置文件?

众所周知,配置文件是软件应用中常用的初始化参数的配置方式,而配置文件的格式有很多种。不同的配置文件格式有不同的用户友好性,比如Windows较早的开发者喜欢使用INI,java工程师喜欢使用properties,通用的编程喜欢yaml、JSON等格式。

1.1

SPDK里INI配置文件回顾

SPDK测试代码里的配置文件主要使用了INI格式和JSON格式。

INI文件是一个无固定标准格式的配置文件。文件格式语法比较简单, 分为:parameter,p和comments。SPDK里一般命名为.conf或.conf.in。动态创建,动态删除。

例如,创建一个Malloc块设备的bdev.conf:


   
  1. [Malloc]
  2. NumberOfLuns 6
  3. LunSizeInMB 32

然后在shell脚本里,以-c参数加载上面的配置文件。例如运行bdevperf:

./spdk/test/bdev/bdevperf -c bdev.conf -q 256 -w read -o 196608 -t 5

SPDK测试和开发工程师们应该都很熟悉这种方式了。随着应用场合的变迁和扩大,INI格式也有其弊端,它只支持键值模式,而且只有一层分类,无嵌套。在处理例如对象(object)、记录(record)、结构体(struct)、字典(dictionary)、哈希表(hash table)、键值列表(keyed list),还有数组(array)、向量(vector)、列表(list),以及对象组成的数组等等时,显得非常不足,但JSON可以。这些特性使其逐步成为理想的数据交换语言。当今,JSON已被所有大型企业所采用,十大最受欢迎的 Web API 接口列表中多数是以 JSON格式开放数据的。JSON也在程序编码级别和文件存储上被广泛采用:在 Stack Overflow网站上,现在更多的是关于 JSON 的问题,而不是其他的数据交换格式。

现在SPDK逐步改为使用JSON-RPC格式的配置文件。从SPDK v20.07版本开始,我们进行了INI格式到JSON的转换工作。涉及到INI  conf解析的代码,运行时都会出现下面的提示:

conf.c: 613:spdk_conf_read: *ERROR*: INI configuration has been deprecated and will be removed in a future release. Please switch to JSON-RPC.

到SPDK v20.10,已经基本转换完成。在此过程中涉及到部分测试代码和脚本改动,原来的以INI格式配置文件为基础的测试方法,已经不能成功运行测试用例。为了方便SPDK用户迁移到JSON格式,我们适时地推出了这篇文章,希望提供一份有实用价值的参考。

1.2

JSON文件格式简介

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。

JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。



JSON的优点

  • 数据格式比较简单,易于读写,格式都是压缩的,占用带宽小。

  • 易于解析这种语言,客户端JavaScript可以简单的通过eval()进行JSON数据的读取(浏览器解析)。

  • 因为JSON格式能够直接为服务器端代码使用, 大大简化了服务器端和客户端的代码开发量, 但是完成的任务不变,且易于维护。能够被大多数后端语言支持。

JSON用于描述数据结构,有以下形式存在

  • 对象(object):一个对象以{开始,并以}结束。一个对象包含一系列未排序的名称/值对

  • 名称/值(collection):名称和值之间使用:隔开,一般的形式是:{name:value}

  • 值的有序列表(array):一个或者多个值用,分区后,使用[,]括起来就形成了这样的列表

  • 字符串:以""括起来的一串字符。

  • 数值:一系列0-9的数字组合,可以为负数或者小数。还可以用e或者E表示为指数形式。

  • 布尔值:表示为true或者false。

JSON格式更详细的描述可以参考RFC 4627。

二.SPDK里INI到JSON转换举例

SPDK里INI到JSON转换举例

在SPDK里,从INI带JSON的转换工作,主要涉及两部分:shell和C代码。

下面列举了常用的转换案例对照。

2.1

创建Malloc块设备

例如:spdk/examples/blob/hello_world里,原来的配置文件是hello_blob.conf:


   
  1. [Malloc]
  2. NumberOfLuns 1
  3. LunSizeInMB 16
  4. BlockSize 512

现在改为hello_blob.json:


   
  1. {
  2. "subsystems": [
  3. {
  4. "subsystem": "bdev",
  5. "config": [
  6. {
  7. "method": "bdev_malloc_create",
  8. "params": {
  9. "name": "Malloc0",
  10. "num_blocks": 32768,
  11. "block_size": 512
  12. }
  13. }
  14. ]
  15. }
  16. ]
  17. }

请注意,这里使用了RPC方法“bdev_malloc_create”创建malloc块设备。

SPDK使用的其他RPC方法请参考命令./scripts/rpc.py -h

调用方法变化:

examples/blob/hello_world/hello_blob hello_blob.conf

变为

examples/blob/hello_world/hello_blob hello_blob.json

如果是NumberOfLuns 3,


   
  1. [Malloc]
  2. NumberOfLuns 3
  3. LunSizeInMB 300
  4. BlockSize 512

要使用循环创建,于是变为:


   
  1. local size= 300 # MB
  2. local block_size= 512
  3. local config
  4. local malloc malloc_devs= 3
  5. for (( malloc = 0; malloc < malloc_devs; malloc++ )); do
  6. config+=(
  7. "$(
  8. cat <<-JSON
  9. {
  10. "method ": "bdev_malloc_create ",
  11. "params ": {
  12. "name ": "Malloc$malloc ",
  13. "num_blocks ": $(( (size << 20) / block_size )),
  14. "block_size ": 512
  15. }
  16. }
  17. JSON
  18. )"
  19. )
  20. done

2.2

 创建PCIe NVMe块设备

原来INI格式:


   
  1. [Nvme]
  2. TransportID " trtype :PCIe traddr :0000 :5f :00.0" Nvme0

现在改为:


   
  1. {
  2. "method": "bdev_nvme_attach_controller",
  3. "params": {
  4. "trtype": "PCIe",
  5. "name": "Nvme0",
  6. "traddr": "0000:5f:00.0"
  7. }
  8. }

调用方式

INI格式:

spdk/test/bdev/bdevperf/bdevperf -m 0x10 -i 1 -c $testdir/bdevperf.conf -q 128 -o 4096 -w write -t 1 

对应修改为JSON格式:

spdk/test/bdev/bdevperf/bdevperf -m 0x10 -i 1 --json $testdir/bdevperf.json -q 128 -o 4096 -w write -t 1 

2.3

加载NVMe-oF控制器

INI格式:


   
  1. [Nvme]
  2. TransportID \"trtype:$TEST_TRANSPORT adrfam:IPv4 subnqn:nqn.2016-06.io.spdk:cnode1 traddr:$NVMF_FIRST_TARGET_IP trsvcid:$NVMF_PORT\" Nvme0

对应JSON格式修改为:


   
  1. function gen_nvmf_target_json()
  2. {
  3. local subsystem config=()
  4. for subsystem in "${@:-1}"; do
  5. config+=(
  6. "$(
  7. cat <<-EOF
  8. {
  9. "params ": {
  10. "name ": "Nvme$subsystem ",
  11. "trtype ": "$TEST_TRANSPORT ",
  12. "traddr ": "$NVMF_FIRST_TARGET_IP ",
  13. "adrfam ": "ipv4 ",
  14. "trsvcid ": "$NVMF_PORT ",
  15. "subnqn ": "nqn. 2016- 06.io.spdk:cnode$subsystem "
  16. },
  17. "method ": "bdev_nvme_attach_controller "
  18. }
  19. EOF
  20. )"
  21. )
  22. done
  23. jq . <<-JSON
  24. {
  25. "subsystems": [
  26. {
  27. "subsystem": "bdev",
  28. "config": [
  29. $(IFS= ","; printf '%s\n' "${config[*]}")
  30. ]
  31. }
  32. ]
  33. }
  34. JSON
  35. }

调用方式

INI格式:

spdk/test/bdev/bdevperf/bdevperf -m 0x10 -i 1 -c $testdir/bdevperf.conf -q 128 -o 4096 -w write -t 1 

对应修改为JSON格式:

spdk/test/bdev/bdevperf/bdevperf -m 0x10 -i 1 --json <(gen_nvmf_target_json) -q 128 -o 4096 -w write -t 1 

2.4

创建iSCSI启动器

INI格式:


   
  1. [ iSCSI_Initiator]
  2. URL iscsi: //$TARGET_IP/iqn.2016-06.io.spdk:disk1/0 iSCSI0

对应修改为JSON格式:


   
  1. function initiator_json_config() {
  2. # Prepare config file for iSCSI initiator
  3. jq . <<- JSON
  4. {
  5. "subsystems": [
  6. {
  7. "subsystem": "bdev",
  8. "config": [
  9. {
  10. "method": "bdev_iscsi_create",
  11. "params": {
  12. "name": "iSCSI0",
  13. "url": "iscsi://$TARGET_IP/iqn.2016-06.io.spdk:disk1/0",
  14. "initiator_iqn": "iqn.2016-06.io.spdk:disk1/0"
  15. }
  16. }${*:+,$*}
  17. ]
  18. }
  19. ]
  20. }
  21. JSON
  22. }

调用方式

INI格式:

spdk/test/bdev/bdevperf/bdevperf -c $testdir/bdev.conf -q 128 -o 4096 -w verify -t 5 -s 512

对应修改为JSON格式:

spdk/test/bdev/bdevperf/bdevperf" --json <(initiator_json_config) -q 128 -o 4096 -w verify -t 5 -s 512

2.5

创建AIO块设备

INI格式:


   
  1. [AIO]
  2. AIO ${tmp_file} ${bdevname} 512

对应修改为JSON格式:


   
  1. jq . < "$testdir/config"
  2. {
  3. "subsystems": [
  4. {
  5. "subsystem": "bdev",
  6. "config": [
  7. {
  8. "method": "bdev_aio_create",
  9. "params": {
  10. "name": "${bdevname}",
  11. "block_size": 512,
  12. "filename": "${tmp_file}"
  13. }
  14. }
  15. ]
  16. }
  17. ]
  18. }
  19. JSON

此外,也可以用RPC方法接收来INI格式参数,例如bdev_set_options


   
  1. [Bdev]
  2. BdevIoPoolSize 128
  3. BdevIoCacheSize 1
  4. [AIO]
  5. AIO $testdir/aio.bdev aio0 4096

在shell里调用,并使用save_config RPC方法来保存为JSON文件:

1.  # Minimal number of bdev io pool (128) and cache (1)  

2.  $rpc_py bdev_set_options --bdev-io-pool-size 128 --bdev-io-cache-size 1  

3.  $rpc_py framework_start_init  

4.  $rpc_py save_config > $testdir/bdevperf.json

2.6

创建OCF块设备

例如:INI里将Nvme0n1分成8个小块,Nvme0n1p0 - Nvme0n1p7,一个OCF块建立在两个Nvme小块之上。


   
  1. [OCF]
  2. OCF PT_Nvme pt Nvme0n1p0 Nvme0n1p1
  3. OCF WT_Nvme wt Nvme0n1p2 Nvme0n1p3
  4. OCF WB_Nvme0 wb Nvme0n1p4 Nvme0n1p5
  5. OCF WB_Nvme1 wb Nvme0n1p6 Nvme0n1p7

JSON里用bdev_split_create RPC方法,也将nvme0n1分成8个小块,但需要增加"mode","cache_bdev_name"和"core_bdev_name"参数。

于是改为:


   
  1. ocf_names[ 1]=PT_Nvme ocf_modes[ 1]=pt
  2. ocf_names[ 2]=WT_Nvme ocf_modes[ 2]=wt
  3. ocf_names[ 3]=WB_Nvme 0 ocf_modes[ 3]=wb
  4. ocf_names[ 4]=WB_Nvme1 ocf_modes[ 4]=wb
  5. config+=(
  6. "$(
  7. cat <<- JSON
  8. {
  9. "method ": "bdev_split_create ",
  10. "params ": {
  11. "base_bdev ": "Nvme0n1 ",
  12. "split_count ": 8,
  13. "split_size_mb ": 101
  14. }
  15. }
  16. JSON
  17. )"
  18. )
  19. for ((d = 0, c = 1; d <= ${ #ocf_names[@]} + 2; d += 2, c++)); do
  20. config+=(
  21. "$(
  22. cat <<- JSON
  23. {
  24. "method ": "bdev_ocf_create ",
  25. "params ": {
  26. "name ": "${ocf_names[c]} ",
  27. "mode ": "${ocf_modes[c]} ",
  28. "cache_bdev_name ": "Nvme0n1p$d ",
  29. "core_bdev_name ": "Nvme0n1p$((d + 1)) "
  30. }
  31. }
  32. JSON
  33. )"
  34. )
  35. done

以上保存为modes.conf,调用方法不变:

LD_PRELOAD=examples/bdev/fio_plugin/fio_plugin test/ocf/integrity/test.fio--

ioengine=spdk_bdev --filename=PT_Nvme:WT_Nvme:WB_Nvme0:WB_Nvme1--spdk_json_conf="$curdir/modes.conf" --thread=1

更多请参考本公众号前期微信文章????缓存助力存储加速–OCF与SPDK介绍及用法

2.7

Fio_plugin的修改

和fio工具一样,运行fio_plugin也需要job文件。只需要把job文件里指定的spdk_conf 由bdev.conf.in改为bdev.json:


   
  1. [global]
  2. ioengine=spdk_bdev
  3. spdk_conf=/home/qwan/scripts/fio_job/full_speed/spdk_bdev.conf
  4. #这里改为
  5. spdk_json_conf=/home/qwan/scripts/fio_job/full_speed/spdk_bdev.json

C代码里,增加相应的"spdk_json_conf"操作项:


   
  1. static struct fio_option options[] = {
  2. {
  3. .name = "spdk_conf",
  4. .lname = "SPDK configuration file",
  5. .type = FIO_OPT_STR_STORE,
  6. .off1 = offsetof( struct spdk_fio_options, conf),
  7. .help = "A SPDK configuration file",
  8. .category = FIO_OPT_C_ENGINE,
  9. . group = FIO_OPT_G_INVALID,
  10. },
  11. {
  12. .name = "spdk_json_conf",
  13. .lname = "SPDK JSON configuration file",
  14. .type = FIO_OPT_STR_STORE,
  15. .off1 = offsetof( struct spdk_fio_options, json_conf),
  16. .help = "A SPDK JSON configuration file",
  17. .category = FIO_OPT_C_ENGINE,
  18. . group = FIO_OPT_G_INVALID,
  19. },

调用方法不变:

$ LD_PRELOAD=spdk/examples/nvme/fio_plugin/fio_plugin /usr/src/fio/fio  example_config.job

2.8

上述JSON的三种使用方法小结

(1)C代码里app参数-c改为-j,并且opts. config_file改为opts.json_config_file。

例如:

opts.config_file = cli_context->config_file;

改为

opts.json_config_file = cli_context->config_file;

2.1节使用了这种方法。

(2)使用EAL参数--json调用。

例如:

spdk/test/bdev/bdevperf/bdevperf -m 0x10 -i 1 --json <(gen_nvmf_target_json) -q 128 -o 4096 -w write -t 1 

还可以直接加文件名,app --json $testdir/bdevperf.json

2.2、2.3、2.4、2.6、2.7节使用了这种方法。

(3)使用RPC方法save_config保存JSON文件

例如:

$rpc_py save_config > $testdir/bdevperf.json

2.5节使用了这种方法。

因为JSON本质上是RPC的调用,所以我们推荐这种方法。此外,如果熟悉了RPC操作,还可以按RPC的命令来填充和检查JSON文件。

转载须知

DPDK与SPDK开源社区公众号文章转载声明

推荐阅读

缓存助力存储加速–OCF与SPDK介绍及用法

分享、在看与点赞,至少点亮一个吧


转载:https://blog.csdn.net/weixin_37097605/article/details/112301001
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场