SoftLayer 如何解析新订购服务的 resourceTableId
结论
不要通过当前账期的 invoiceItems[].resourceTableId 获取资源 ID。这个字段属于发票行数据,在发票正式生成或结算前可能一直是 null。
应先读取对应 SoftLayer_Billing_Item 的 resourceTableId。如果顶层 Billing Item 表示套餐或容器,还要继续检查它的子 Billing Items。资源创建完成并与 Billing Item 建立关联后,resourceTableId 才能稳定返回。
为什么新服务的发票行没有资源 ID
getNextInvoiceTopLevelBillingItems 返回当前有效的计费项目,其中的 invoiceItems 可能包含尚未正式开票的待结算行。
SoftLayer_Invoice_Item.resourceTableId 更像是开票时保存的资源引用,不适合用来实时发现资源。因此,新订单可能处于以下状态:
- Billing Item 和待结算 invoice line 已经创建。
- 主机名、域名、备注等计费信息已经写入。
- 服务仍在 provisioning,或者计费记录尚未与实际资源完成关联。
invoiceItems[].resourceTableId暂时是null。
历史账期的发票行已经完成结算,数据也已固化,因此通常能看到 resourceTableId。
下面三个 ID 的含义不同:
billingItem.id:计费项目 ID。invoiceItem.id:发票行 ID。resourceTableId:实际服务资源 ID,例如 Virtual Guest ID 或 Bare Metal Server ID。
前两个 ID 都不能当作资源 ID 使用。
直接读取 Billing Item 的 resourceTableId
将 resourceTableId 添加到顶层 Billing Item 的 object mask 中,不要只在 invoiceItems 中请求这个字段:
GET /rest/v3.1/SoftLayer_Account/getNextInvoiceTopLevelBillingItems.json
?objectMask=mask[
id,
categoryCode,
hostName,
domainName,
resourceTableId,
invoiceItems[
id,
createDate,
hostName,
domainName,
resourceTableId,
notes
]
]
返回结果可能如下:
[
{
"id": 123456789,
"categoryCode": "guest_core",
"hostName": "example-vsi",
"domainName": "example.com",
"resourceTableId": 987654321,
"invoiceItems": [
{
"id": 456789012,
"createDate": "2026-09-01T10:20:30+08:00",
"resourceTableId": null
}
]
}
]
这里应该使用顶层的 987654321,不需要等待 invoiceItems[0].resourceTableId 补齐。
也可以根据 Billing Item ID 单独查询,确认资源关联是否已经建立:
GET /rest/v3.1/SoftLayer_Billing_Item/123456789/getObject.json
?objectMask=mask[
id,
categoryCode,
hostName,
domainName,
resourceTableId
]
检查子 Billing Items
getNextInvoiceTopLevelBillingItems 只返回顶层计费项目。对于某些产品,顶层项目表示套餐、父级服务或组合订单,实际的资源引用可能保存在子 Billing Item 中。
可以在请求中同时加入子项目:
GET /rest/v3.1/SoftLayer_Account/getNextInvoiceTopLevelBillingItems.json
?objectMask=mask[
id,
categoryCode,
hostName,
domainName,
resourceTableId,
children[
id,
categoryCode,
hostName,
domainName,
resourceTableId
]
]
解析时先检查顶层 resourceTableId。如果为空,再检查 children:
def find_resource_ids(billing_item):
results = []
resource_id = billing_item.get("resourceTableId")
if resource_id is not None:
results.append({
"billingItemId": billing_item["id"],
"categoryCode": billing_item.get("categoryCode"),
"resourceTableId": resource_id,
})
for child in billing_item.get("children", []):
child_resource_id = child.get("resourceTableId")
if child_resource_id is not None:
results.append({
"billingItemId": child["id"],
"categoryCode": child.get("categoryCode"),
"resourceTableId": child_resource_id,
})
return results
取得资源 ID 后,可以按照产品类型查询实际对象。例如:
GET /rest/v3.1/SoftLayer_Virtual_Guest/987654321/getObject.json
如果是 Bare Metal Server,则使用对应的 Hardware Server 服务:
GET /rest/v3.1/SoftLayer_Hardware_Server/987654321/getObject.json
Billing Item 本身也返回 null 时
如果顶层和子级 Billing Item 的 resourceTableId 都为空,通常表示资源仍未完成 provisioning,或者计费项目与资源之间的关联尚未写入。此时没有可靠的本地算法可以只根据 invoice line 推导资源表主键。
可以按以下顺序处理:
- 保存
billingItem.id,将它作为跟踪订单的依据。 - 查询 Billing Item 的 provisioning 状态或关联的 provisioning transaction。
- 等待订单完成,然后重新读取 Billing Item。
- 从 Billing Item 或它的子项目中获取
resourceTableId。 - 如果控制台已经显示资源创建完成,但该字段长时间仍为空,应向 IBM Cloud Classic Infrastructure 支持提供 Billing Item ID 和订单信息,请后台检查资源关联。
还可以查询账户下的 Virtual Guests 或 Hardware,再通过它们关联的 Billing Item ID 进行反向匹配。Billing Item ID 比 hostName 和 domainName 更可靠,因为主机名可能重复、被修改,或者在重装后继续使用。
注意事项
- 不要依赖
invoiceItems[].resourceTableId查找当前账期内刚创建的资源。 - 不要将
invoiceItem.id或billingItem.id直接传给SoftLayer_Virtual_Guest。 - 顶层 Billing Item 未必是实际资源对应的计费项目。对于组合产品,还需要检查
children。 categoryCode可以帮助判断资源类型,但不同产品的计费结构可能不同,不应只根据单个 category 硬编码。- 如果服务仍在 provisioning,可以轮询 Billing Item,但要设置合理的间隔、超时和失败处理,避免高频调用。
- Billing Item 的
resourceTableId尚未建立时,通过主机名推断资源只能作为临时兜底,无法保证唯一性。
备注:内容仅供参考。
