cli通过Expand;GetPath,获取某Interface某些属性为特定值的资源树Path失败

// 此模板仅供参考,如果不适用可以修改

问题描述

方法一:

List + Expand

List用于获取子资源树集合,Expand用于将可访问的URI原地替换为该URI对应的响应体。用于获取指定父路径下特性属性如PortName等于ReqBody的某个属性值的子路径对象,再通过子路径对象获取对象另一个属性,如PortId

{
            "Uri": "/cli/v1/lswm/portenable",
            "Interfaces": [
                {
                    "Type": "Patch",
                    "Description": "Set lswm port enable",
                    "Usage": "ipmcset -t lswm -d portenable -v <PortName> <enable|disable> [PersistType]",
                    "Example": [
                        "ipmcset -t lswm -d portenable -v BMC_1 enable",
                        "ipmcset -t lswm -d portenable -v CAS_1 disable 1"
                    ],
                    "Confirm": {
                        "Condition": true,
                        "Description": "WARNING: The operation may have many adverse effects.\nDo you want to continue?[Y/N]:"
                    },
                    "ReqBody": {
                        "Type": "object",
                        "Required": true,
                        "Properties": {
                            "PortName": {
                                "Required": true,
                                "Type": "string",
                                "Description": "Port name, such as: BMC_1 MGMT CAS_1"
                            },
                            "Enable": {
                                "Required": true,
                                "Type": "string",
                                "Validator": [
                                    {
                                        "Type": "Enum",
                                        "Formula": [
                                            "disable",
                                            "enable"
                                        ]
                                    }
                                ],
                                "Description": "disable | enable"
                            },
                            "PersistType": {
                                "Required": false,
                                "Type": "integer",
                                "Validator": [
                                    {
                                        "Type": "Enum",
                                        "Formula":[
                                            0,
                                            1
                                        ]
                                    }
                                ],
                                "Description": "Persist type"
                            }
                        }
                    },
                    "RspBody": {
                        "Result": "${Statements/Result()}"
                    },
                    "Statements": {
                        "GetPathParams": {
                            "Input": "${ProcessingFlow[1]/Destination/PortsList}",
                            "Steps": [
                                {
                                    "Type": "Expand"
                                },
                                {
                                    "Type": "Script",
                                    "Formula": "get_path_params.lua"
                                }
                            ]
                        },
                        "Result": {
                            "Input": "${ReqBody/PersistType}",
                            "Steps": [
                                {
                                    "Type": "Script",
                                    "Formula": "if Input ~= nil then return ProcessingFlow[2].Destination.Result else return ProcessingFlow[3].Destination.Result end"
                                }
                            ]
                        }

                    },
                    "ProcessingFlow": [
                        {
                            "Type": "List",
                            "Path": "/bmc/kepler/Switch/1/Ports",
                            "Destination": {
                                "Members": "PortsList"
                            }
                        },
                        {
                            "Type": "Method",
                            "Path": "/bmc/kepler/Switch/1/Ports/${Statements/GetPathParams()}",
                            "Interface": "bmc.kepler.Switch.Port",
                            "Name": "SetPortEnable",
                            "Params": [
                                "${ReqBody/PortName}",
                                "${ReqBody/Enable}",
                                "${ReqBody/PersistType}"
                            ],
                            "Destination": {
                                "Result": "Result"
                            },
                            "CallIf": {
                                "${ReqBody/PersistType}": "#WITH"
                            }
                        },
                        {
                            "Type": "Method",
                            "Path": "/bmc/kepler/Switch/1/Ports/${Statements/GetPathParams()}",
                            "Interface": "bmc.kepler.Switch.Port",
                            "Name": "SetPortEnable",
                            "Params": [
                                "${ReqBody/PortName}",
                                "${ReqBody/Enable}",
                                0
                            ],
                            "Destination": {
                                "Result": "Result"
                            },
                            "CallIf": {
                                "${ReqBody/PersistType}": "#WITHOUT"
                            }
                        }
                    ],
                    "Echoes": [
                        "ipmcset/lswm_portenable",
                        ""
                    ]
                }
            ]
        },

get_path_params.lua脚本内容:
local log = require 'mc.logging'
local port_id = 0xffff
local port_name = ReqBody.PortName
log:error('%s----------------------------------------lsw', ReqBody.PortName)

for  _, port_obj in pairs(Input) do
    log:error('%s------------------lsw----------------------%s', port_obj.PortName, tostring(port_obj.LogicPortId))
    if string.upper(port_obj.PortName) == string.upper(port_name) then
        port_id = tonumber(port_obj.LogicPortId)
        log:error('Get lsw Port%s successfully-----------------', tostring(port_id))
    end
end
log:error('Get lsw Port%s successfully', tostring(port_id))
return port_id

方法二

通过PortName设法获取到Path,MDB提供公共方法GetPath,用于获取执行Interface并且某些属性为特定值的资源树Path,配置如下:

{

            "Uri": "/cli/v1/lswm/portenable",

            "Interfaces": [

                {

                    "Type": "Patch",

                    "Description": "Set lswm port enable",

                    "Usage": "ipmcset -t lswm -d portenable -v <PortName> <enable|disable> [PersistType]",

                    "Example": [

                        "ipmcset -t lswm -d portenable -v BMC_1 enable",

                        "ipmcset -t lswm -d portenable -v CAS_1 disable 1"

                    ],

                    "Confirm": {

                        "Condition": true,

                        "Description": "WARNING: The operation may have many adverse effects.\nDo you want to continue?[Y/N]:"

                    },

                    "ReqBody": {

                        "Type": "object",

                        "Required": true,

                        "Properties": {

                            "PortName": {

                                "Required": true,

                                "Type": "string",

                                "Description": "Port name, such as: BMC_1 MGMT CAS_1"

                            },

                            "Enable": {

                                "Required": true,

                                "Type": "string",

                                "Validator": [

                                    {

                                        "Type": "Enum",

                                        "Formula": [

                                            "disable",

                                            "enable"

                                        ]

                                    }

                                ],

                                "Description": "disable | enable"

                            },

                            "PersistType": {

                                "Required": false,

                                "Type": "integer",

                                "Validator": [

                                    {

                                        "Type": "Enum",

                                        "Formula":[

                                            0,

                                            1

                                        ]

                                    }

                                ],

                                "Description": "Persist type"

                            }

                        }

                    },

                    "RspBody": {

                        "Result": "${Statements/Result()}"

                    },

                    "Statements": {

                        "Result": {

                            "Input": "${ReqBody/PersistType}",

                            "Steps": [

                                {

                                    "Type": "Script",

                                    "Formula": "if Input ~= nil then return ProcessingFlow[2].Destination.Result else return ProcessingFlow[3].Destination.Result end"

                                }

                            ]

                        }




                    },

                    "ProcessingFlow": [

                        {

                            "Type": "Method",

                            "Path": "/bmc/kepler/MdbService",

                            "Interface": "bmc.kepler.Mdb",

                            "Name": "GetPath",

                            "Params": [

                                "bmc.kepler.Switch.Port",    

                                {

                                    "PortName": "${ReqBody/PortName}"

                                }

                            ],

                            "Destination": {

                                "Path": "Path"

                            }

                        },

                        {

                            "Type": "Method",

                            "Path": "${ProcessingFlow[1]/Destination/Path}",

                            "Interface": "bmc.kepler.Switch.Port",

                            "Name": "SetPortEnable",

                            "Params": [

                                "${ReqBody/PortName}",

                                "${ReqBody/Enable}",

                                "${ReqBody/PersistType}"

                            ],

                            "Destination": {

                                "Result": "Result"

                            },

                            "CallIf": {

                                "${ProcessingFlow[1]/Destination/Path}": "#WITH",

                                "${ReqBody/PersistType}": "#WITH"

                            }

                        },

                        {

                            "Type": "Method",

                            "Path": "${ProcessingFlow[1]/Destination/Path}",

                            "Interface": "bmc.kepler.Switch.Port",

                            "Name": "SetPortEnable",

                            "Params": [

                                "${ReqBody/PortName}",

                                "${ReqBody/Enable}",

                                0

                            ],

                            "Destination": {

                                "Result": "Result"

                            },

                            "CallIf": {

                                "${ProcessingFlow[1]/Destination/Path}": "#WITH",

                                "${ReqBody/PersistType}": "#WITHOUT"

                            }

                        }

                    ],

                    "Echoes": [

                        "ipmcset/lswm_portenable",

                        ""

                    ]

                }

            ]

        },

但是执行对应IPMC均失败,且无日志打印

资源树查询及命令执行结果:

Service bmc.kepler.lsw_main:
└─/bmc
  └─/bmc/kepler
    ├─/bmc/kepler/Debug
    │ └─/bmc/kepler/Debug/LswMainDebug
    ├─/bmc/kepler/IpmiCmds
    │ └─/bmc/kepler/IpmiCmds/30
    │   └─/bmc/kepler/IpmiCmds/30/93
    │     └─/bmc/kepler/IpmiCmds/30/93/SetOtherSmmPortEnable
    ├─/bmc/kepler/Switch
    │ └─/bmc/kepler/Switch/1
    │   └─/bmc/kepler/Switch/1/Ports
    │     ├─/bmc/kepler/Switch/1/Ports/1
    │     ├─/bmc/kepler/Switch/1/Ports/10
    │     ├─/bmc/kepler/Switch/1/Ports/11
    │     ├─/bmc/kepler/Switch/1/Ports/12
    │     ├─/bmc/kepler/Switch/1/Ports/13
    │     ├─/bmc/kepler/Switch/1/Ports/14
    │     ├─/bmc/kepler/Switch/1/Ports/15
    │     ├─/bmc/kepler/Switch/1/Ports/16
    │     ├─/bmc/kepler/Switch/1/Ports/17
    │     ├─/bmc/kepler/Switch/1/Ports/18
    │     ├─/bmc/kepler/Switch/1/Ports/19
    │     ├─/bmc/kepler/Switch/1/Ports/2
    │     ├─/bmc/kepler/Switch/1/Ports/20
    │     ├─/bmc/kepler/Switch/1/Ports/21
    │     ├─/bmc/kepler/Switch/1/Ports/22
    │     ├─/bmc/kepler/Switch/1/Ports/23
    │     ├─/bmc/kepler/Switch/1/Ports/24
    │     ├─/bmc/kepler/Switch/1/Ports/25
    │     ├─/bmc/kepler/Switch/1/Ports/26
    │     ├─/bmc/kepler/Switch/1/Ports/27
    │     ├─/bmc/kepler/Switch/1/Ports/28
    │     ├─/bmc/kepler/Switch/1/Ports/29
    │     ├─/bmc/kepler/Switch/1/Ports/3
    │     ├─/bmc/kepler/Switch/1/Ports/30
    │     ├─/bmc/kepler/Switch/1/Ports/31
    │     ├─/bmc/kepler/Switch/1/Ports/4
    │     ├─/bmc/kepler/Switch/1/Ports/5
    │     ├─/bmc/kepler/Switch/1/Ports/6
    │     ├─/bmc/kepler/Switch/1/Ports/7
    │     ├─/bmc/kepler/Switch/1/Ports/8
    │     └─/bmc/kepler/Switch/1/Ports/9
    └─/bmc/kepler/lsw_main
      └─/bmc/kepler/lsw_main/MicroComponent
        └─/bmc/kepler/lsw_main/MicroComponent/Debug

~ ~ # busctl --user introspect bmc.kepler.lsw_main /bmc/kepler/Switch/1/Ports/10 bmc.kepler.Switch.Port
NAME                   TYPE      SIGNATURE RESULT/VALUE FLAGS
.SetPortAutonegEnable  method    a{ss}ss   s            -
.SetPortEnable         method    a{ss}ssu  s            -
.SetPortLoopMode       method    a{ss}ss   s            -
.SetPortSpeed          method    a{ss}ss   s            -
.LogicPortId           property  u         10           emits-change
.PortName              property  s         "BMC_1"      emits-change
~ ~ # ipmcset -t lswm -d portenable -v BMC_1 enable 1
WARNING: The operation may have many adverse effects.
Do you want to continue?[Y/N]:Y
userdata: NULL

答案内容由AI生成,仅供参考:

:white_check_mark: 最终结论

:red_exclamation_mark: 根本原因GetPathFilter 参数未正确序列化为字符串,导致查询失败,返回空路径,后续方法无法执行。

:white_check_mark: 解决方案:将 Filter 参数格式化为 转义后的 JSON 字符串,例如 "{\\\"PortName\\\": \\\"BMC_1\\\"}",并传入 GetPath

:white_check_mark: 推荐做法:使用 Statements + Script 动态生成 Filter,提高可维护性和灵活性。

点击此处查看详细分析解答

CLI 通过 ExpandGetPath 获取资源树 Path 失败问题分析与解决方案

根据您提供的信息,您在使用 CLI 接口通过两种方法(List + ExpandGetPath)动态获取指定 PortName 对应的 D-Bus 路径时均失败,且无日志输出。以下结合系统架构、接口行为和配置逻辑进行全面分析,并提供可行解决方案。


:magnifying_glass_tilted_left: 问题总结

  • 目标:根据请求体中的 PortName(如 "BMC_1"),查找其对应的 D-Bus 路径(如 /bmc/kepler/Switch/1/Ports/10)。
  • 尝试方法
    1. 使用 List + Expand 遍历所有端口对象,匹配 PortName
    2. 使用 GetPath 方法直接查询满足条件的路径。
  • 现象
    • 命令执行返回失败,无有效日志打印
    • busctl introspect 确认目标接口存在,PortName 属性可读。
    • 端口对象路径为 /bmc/kepler/Switch/1/Ports/:ID,符合动态 URI 模式。

:puzzle_piece: 关键知识点梳理(基于 Context 信息)

1. GetPath 方法行为说明

来自文档 Document Chunks (DC) 的方法定义:

`GetPath`
根据Interface下的属性值查找满足条件的资源树对象路径

**参数**
- `Interface`: String \- 资源树对象路径下的接口
- `Filter`: String \- 属性名属性值键值对的json字符串
- `IgnoreCase`: Boolean \- 支持忽略属性值大小写

**返回值**
- `Path`: String \- 返回满足查找条件且按字典序排序的第一个资源树对象路径
- `Service`: String \- 返回所在的服务名称

:warning: 关键点

  • Filter 参数必须是 JSON 字符串,而不是 JSON 对象。
  • 错误地传入 { "PortName": "${ReqBody/PortName}" }(作为对象)会导致解析失败,GetPath 找不到匹配路径。

2. 动态路径结构支持

  • 实体 bmc/kepler/Switch/:id/Ports/:ID 是一个动态路径模板:id:ID 为运行时替换的占位符。
  • GetPath 支持基于接口 bmc.kepler.Switch.Port 和属性 PortName 查询实际路径,前提是输入格式正确。

:cross_mark: 失败原因分析

方法一:List + Expand 失败原因

  • Expand 的作用 是将 URI 替换为该 URI 的响应体(即对象数据),但 List 只返回路径列表,不自动加载属性。
  • 必须通过 Expand 显式展开各个路径以获取其属性(如 PortNameLogicPortId)。
  • 问题可能出现在:
    • Expand 是否成功获取了属性?
    • get_path_params.lua 脚本是否执行?是否有 权限或路径错误导致脚本未被调用
    • Lua 脚本中使用 log:error() 但日志未输出,说明脚本未被执行或日志等级未打开

:warning: 可能原因:ProcessingFlow[1]Expand 后的数据未正确传递给脚本,或 Input 结构非预期(仅路径字符串数组而非对象数组)。


方法二:GetPath 失败的根本原因(重点)

配置错误:Filter 参数类型错误

当前配置:

"Params": [
    "bmc.kepler.Switch.Port",    
    {
        "PortName": "${ReqBody/PortName}"
    }
]

这是将一个 JSON 对象 直接传入 GetPath(Filter),但 Filter 要求是 字符串类型的 JSON 序列化文本

:white_check_mark: 正确方式应为

"Params": [
    "bmc.kepler.Switch.Port",
    "{\\\"PortName\\\": \\\"${ReqBody/PortName}\\\"}",
    true
]

或者在 Statements 中预处理:

"Statements": {
  "Filter": {
    "Steps": [
      {
        "Type": "Script",
        "Formula": "return string.format('{\"PortName\":\"%s\"}', ReqBody.PortName)"
      }
    ]
  }
}

然后引用:

"Params": [
    "bmc.kepler.Switch.Port",
    "${Statements/Filter()}",
    true
]

:red_circle: 原因结论:GetPathFilter 类型错误而无法匹配任何对象,返回空路径,后续方法调用失败,且未触发日志输出


:white_check_mark: 推荐解决方案

:white_check_mark: 方案一:修复 GetPath 调用(推荐)

修改 ProcessingFlow[1],确保 Filter 为合法 JSON 字符串:

{
  "Type": "Method",
  "Path": "/bmc/kepler/MdbService",
  "Interface": "bmc.kepler.Mdb",
  "Name": "GetPath",
  "Params": [
    "bmc.kepler.Switch.Port",
    "{\\\"PortName\\\": \\\"${ReqBody/PortName}\\\"}",
    true
  ],
  "Destination": {
    "Path": "Path"
  }
}

:white_check_mark: 说明:

  • \\\" 是 JSON 中表示双引号的方式。
  • 第三个参数 true 表示启用忽略大小写(可选)。
  • 此方式无需额外脚本,简洁高效。

:white_check_mark: 方案二:使用 Statements 生成 Filter(更灵活)

"Statements": {
  "MakeFilter": {
    "Steps": [
      {
        "Type": "Script",
        "Formula": "return string.format('{\\\"PortName\\\": \\\"%s\\\"}', ReqBody.PortName)"
      }
    ]
  },
  "Result": {
    "Input": "${ReqBody/PersistType}",
    "Steps": [
      {
        "Type": "Script",
        "Formula": "if Input ~= nil then return ProcessingFlow[2].Destination.Result else return ProcessingFlow[3].Destination.Result end"
      }
    ]
  }
},
"ProcessingFlow": [
  {
    "Type": "Method",
    "Path": "/bmc/kepler/MdbService",
    "Interface": "bmc.kepler.Mdb",
    "Name": "GetPath",
    "Params": [
      "bmc.kepler.Switch.Port",
      "${Statements/MakeFilter()}",
      true
    ],
    "Destination": {
      "Path": "Path"
    }
  },
  {
    "Type": "Method",
    "Path": "${ProcessingFlow[1]/Destination/Path}",
    "Interface": "bmc.kepler.Switch.Port",
    "Name": "SetPortEnable",
    "Params": [
      "${ReqBody/Enable}",
      "${ReqBody/PersistType}"
    ],
    "Destination": {
      "Result": "Result"
    },
    "CallIf": {
      "${ProcessingFlow[1]/Destination/Path}": "#WITH",
      "${ReqBody/PersistType}": "#WITH"
    }
  },
  {
    "Type": "Method",
    "Path": "${ProcessingFlow[1]/Destination/Path}",
    "Interface": "bmc.kepler.Switch.Port",
    "Name": "SetPortEnable",
    "Params": [
      "${ReqBody/Enable}",
      0
    ],
    "Destination": {
      "Result": "Result"
    },
    "CallIf": {
      "${ProcessingFlow[1]/Destination/Path}": "#WITH",
      "${ReqBody/PersistType}": "#WITHOUT"
    }
  }
]

:white_check_mark: 方案三:修复 List + Expand 流程(备用)

确保 Expand 能展开对象内容,并传递完整对象数组给 Lua 脚本:

"ProcessingFlow": [
  {
    "Type": "List",
    "Path": "/bmc/kepler/Switch/1/Ports",
    "Destination": {
      "Members": "PortPaths"
    }
  },
  {
    "Type": "Expand",
    "Input": "${ProcessingFlow[0]/Destination/PortPaths}",
    "Destination": {
      "Members": "PortObjects"
    }
  },
  {
    "Type": "Script",
    "Formula": "get_path_params.lua",
    "Input": "${ProcessingFlow[1]/Destination/PortObjects}",
    "Destination": {
      "Result": "LogicPortId"
    }
  }
]

并在 Lua 脚本中确保正确访问属性:

for _, obj in ipairs(Input) do
    if obj.PortName and string.upper(obj.PortName) == string.upper(ReqBody.PortName) then
        return tonumber(obj.LogicPortId)
    end
end
return nil

:warning: 注意:Expand 性能较差,适用于小规模集合。


:memo: 建议调试措施

  1. 启用调试日志

    • 确保 mc.logging 输出到日志文件或控制台。
    • 添加 log:error("Step reached") 测试脚本执行情况。
  2. 手动测试 GetPath

    busctl --user call bmc.kepler.Mdb /bmc/kepler/MdbService bmc.kepler.Mdb GetPath \
      ssa{sv} "bmc.kepler.Switch.Port" '{\"PortName\": \"BMC_1\"}' true
    
  3. 验证路径是否存在

    busctl --user introspect bmc.kepler.lsw_main /bmc/kepler/Switch/1/Ports/10
    

:white_check_mark: 最终结论

:red_exclamation_mark: 根本原因GetPathFilter 参数未正确序列化为字符串,导致查询失败,返回空路径,后续方法无法执行。

:white_check_mark: 解决方案:将 Filter 参数格式化为 转义后的 JSON 字符串,例如 "{\\\"PortName\\\": \\\"BMC_1\\\"}",并传入 GetPath

:white_check_mark: 推荐做法:使用 Statements + Script 动态生成 Filter,提高可维护性和灵活性。


如仍失败,请开启完整调试日志并检查 GetPath 是否返回有效路径。