Endpoint Discovery¶
来自 Catalog 的 Endpoint¶
在从 keystone 认证返回的 token 中可以找到 {service-catalog}。
如果使用 v3 认证,catalog 将位于顶级 token 对象的 catalog 属性中。例如
{
"token": {
"catalog": {}
}
}
如果使用 v2 认证,它将位于顶级 access 对象的 serviceCatalog 属性中。例如
{
"access": {
"serviceCatalog": {}
}
}
在两种情况下,catalog 内容本身都是对象的列表。每个对象都有两个主要键与发现相关
type匹配
{service-type}endpoints该服务对应的 endpoint 对象列表
此外,为了向后兼容,可能需要检查以下键。
name匹配
{service-name}id匹配
{service-id}
endpoint 列表的格式取决于是否使用了 v2 或 v3 认证。对于两个版本,每个 endpoint 对象都有一个 region 键,如果提供了 {region-name},则该键应与其匹配。
在 v2 认证中,endpoint 对象有三个键 publicURL、internalURL 和 adminURL。用户请求的 {interface} 的 endpoint 位于名称与 {interface} 加上字符串 URL 匹配的键中。
在 v3 认证中,endpoint 对象有一个 url,如果 interface 的值与 {interface} 匹配,则该 url 就是正在请求的 endpoint。
带有 Catalog 的 Token 示例¶
V3 Catalog 对象
{
"token": {
"catalog": [
{
"endpoints": [
{
"id": "39dc322ce86c4111b4f06c2eeae0841b",
"interface": "public",
"region": "RegionOne",
"url": "https://identity.example.com"
},
{
"id": "ec642f27474842e78bf059f6c48f4e99",
"interface": "internal",
"region": "RegionOne",
"url": "https://identity.example.com"
},
{
"id": "c609fc430175452290b62a4242e8a7e8",
"interface": "admin",
"region": "RegionOne",
"url": "https://identity.example.com"
}
],
"id": "4363ae44bdf34a3981fde3b823cb9aa2",
"type": "identity",
"name": "keystone"
}
],
}
V2 Catalog 对象
{
"access": {
"serviceCatalog": [
{
"endpoints_links": [],
"endpoints": [
{
"adminURL": "https://identity.example.com/v2.0",
"region": "RegionOne",
"publicURL": "https://identity.example.com/v2.0",
"internalURL": "https://identity.example.com/v2.0",
"id": "4deb4d0504a044a395d4480741ba628c"
}
],
"type": "identity",
"name": "keystone"
},
]
}
}
Endpoint Discovery 算法¶
如果提供了
{endpoint-version}并且{service-type}以v[0-9]+$后缀结尾,并且{endpoint-version}不匹配该后缀(请参阅 比较主版本),则停止。返回一个错误,说明用户请求了版本化的{service-type}别名和一个不兼容的{endpoint-version}。在
{service-catalog}中找到与请求的{service-type}匹配的对象(请参阅 匹配候选条目)。如果提供了
{service-name}并且剩余的对象具有name字段,则仅保留name与{service-name}匹配的对象。注意
Keystone v3 之前的 catalog 不具有 name 字段。如果未请求
{be-strict}并且 catalog 不具有name字段,则应忽略{service-name}。如果提供了
{service-id}并且剩余的对象具有id字段,则仅保留id与{service-id}匹配的对象。注意
Keystone v2 的 catalog 不具有 id 字段。如果未请求
{be-strict}并且 catalog 不具有id字段,则应忽略{service-id}。
剩余的对象列表是 {candidate-catalog-objects}。如果此列表为空,则返回一个错误,说明没有匹配 {service-type} 和 {service-name} 的 endpoint。
使用
{candidate-catalog-objects}生成{candidate-endpoints}列表。对于{candidate-catalog-objects}中的每个 endpoint 对象如果 v2,如果对于给定的任何
{interface}值,都没有形式为{interface}URL的键,则丢弃该 endpoint。如果 v3,如果
interface不匹配任何给定的{interface}值,则丢弃该 endpoint。
如果没有剩余的 endpoint,则返回一个错误,说明没有匹配任何
{interface}值的 endpoint,最好包括找到的接口列表。对于剩余的
{candidate-endpoints}中的每个 endpoint,如果提供了{region_name}并且它不匹配region或region_id中的任何一个,则丢弃该 endpoint。如果没有剩余的 endpoint,则返回一个错误,说明没有匹配
{region_name}的 endpoint,最好包括找到的区域列表。从剩余的候选 endpoint 集合中,找到与请求的
{service-type}最佳匹配的 endpoint(请参阅 查找最佳服务类型匹配的 Endpoint)。从剩余的候选 endpoint 集合中,找到与最佳可用的请求
{interface}最佳匹配的 endpoint:按照{interface}列表的偏好顺序,返回与第一个具有任何匹配 endpoint 的{interface}匹配的所有 endpoint。
剩余的 {candidate-endpoints} 匹配请求。如果有多个 endpoint,则使用第一个,但向用户发出警告,说明剩余的 endpoint 超过一个。如果请求了 {be-strict},则返回一个错误,其中包含列表中每个 endpoint 的信息。
注意
如果存在多个剩余的 endpoint,提出错误会更正确,但 keystoneauth 库返回第一个,更改它会破坏大量现有用户。如果编写一个全新的库或一个新的主要版本,其中行为更改是可以接受的,那么如果剩余的 endpoint 超过一个,最好在此处提出错误。
如果 v2,
{catalog-endpoint}是{interface}URL的值。如果 v3,
{catalog-endpoint}是url的值。
匹配候选条目¶
对于 catalog 中的每个条目
如果条目的类型与请求的
{service-type}匹配,则它是候选条目。如果请求的类型是来自 OpenStack 服务类型权威机构 的官方类型,该类型具有别名,并且其中一个别名与条目的类型匹配,则它是候选条目。
如果请求的类型是来自 OpenStack 服务类型权威机构 的官方类型的别名,并且条目的类型与官方类型匹配,则它是候选条目。
如果请求的类型是来自 OpenStack 服务类型权威机构 的官方类型的别名,该类型具有别名,并且条目的类型与其中一个别名匹配,并且提供了
{endpoint-version},并且找到的别名以v[0-9]+$后缀结尾,并且{endpoint-version}与后缀中的版本匹配(请参阅 比较主版本),则它是候选条目。
查找最佳服务类型匹配的 Endpoint¶
给定一个已匹配其他标准的候选 endpoint 列表
检查候选 endpoint 列表,查看其中一个是否与请求的
{service-type}匹配。如果有任何是精确匹配,则返回它们。如果请求的
{service-type}是具有别名的 OpenStack 服务类型权威机构 中的官方类型
{endpoint-version}已提供
查找以
v[0-9]+$形式结尾的别名。如果有任何别名具有与{endpoint-version}匹配的后缀(请参阅 比较主版本),则在候选 endpoint 列表中查找这些别名。如果有任何匹配,则返回它们。如果请求的
{service-type}是 OpenStack 服务类型权威机构 中的官方类型,该类型具有别名
{endpoint-version}未提供
按列出的顺序检查每个别名,查看它是否有来自候选 endpoint 的匹配 endpoint。返回与具有匹配 endpoint 的第一个别名匹配的 endpoint。
如果请求的
{service-type}是 OpenStack 服务类型权威机构 中的官方类型的别名
{endpoint-version}已提供
查找以
v[0-9]+$形式结尾的别名。如果有任何别名具有与{endpoint-version}匹配的后缀(请参阅 比较主版本),则在候选 endpoint 列表中查找这些别名。返回与最高匹配版本的别名匹配的 endpoint。
如果没有匹配的 endpoint,则返回一个错误。
注意
的情况是
请求了一个别名
没有提供
{endpoint-version}catalog 中有一个不同的别名
是不安全的,因此有意将其视为缺少匹配的 endpoint。许多别名都带有隐含的版本,因此在没有用户请求的 {endpoint-version} 的情况下,返回与明确请求的不同 endpoint 很有可能不是用户期望的 endpoint。
比较主版本¶
在比较主版本时,有一个 required 和一个 candidate
required是用户请求的。candidate是正在测试的可能版本。
为了适合,候选版本 必须与 所需版本 具有相同的 major 版本号,并且 minor 版本号至少相等:候选版本 3.3 与 所需版本 3.1 匹配,但 4.1 不匹配。
在所有情况下,应丢弃前导的 ‘v’ 字符串。
仅包含单个数字的版本号应归一化为
.0。也就是说,版本号2应被视为2.0。如果
所需版本是字符串latest或不包含任何值,则候选版本匹配。如果
所需版本是一个范围,则任何大于或等于第一个值且小于或等于第二个值的候选版本都匹配。相等性按照上述规则进行判断。大于和小于的判断按预期进行:首先比较第一个数字,如果第一个数字匹配,则比较第二个数字。因此,{所需版本}为2,4时,匹配2、2.3、3、4和4.7。{所需版本}为2.1,4.0时,匹配2.3、3、4和4.7,但不匹配2。如果
所需版本是一个没有最大值的范围,则最大值应被视为latest。
发现示例¶
例如,给定以下目录
{
"token": {
"catalog": [
{
"endpoints": [
{
"interface": "public",
"region": "RegionOne",
"url": "https://block-storage.example.com/v3"
}
],
"id": "4363ae44bdf34a3981fde3b823cb9aa3",
"type": "volumev3",
"name": "cinder"
},
{
"endpoints": [
{
"interface": "public",
"region": "RegionOne",
"url": "https://block-storage.example.com/v2"
}
],
"id": "4363ae44bdf34a3981fde3b823cb9aa2",
"type": "volumev2",
"name": "cinder"
}
],
}
那么以下
service_type = 'block-storage'
# block-storage is not found, get list of aliases
# volumev3 is found, return it
service_type = 'volumev2'
# volumev2 not an official type in authority, but is in catalog
# return volumev2 entry
service_type = 'volume'
# volume not in authority or catalog
# volume is an alias of block-storage
# block-storage is not found. Return error.
service_type = 'volume'
api_version = 2
# volume not in authority or catalog
# volume is an alias of block-storage
# block-storage is not found.
# volumev2 is an alias of block-storage and ends with v2 which matches
# api_version of 2
# return volumev2
给定以下目录
{
"token": {
"catalog": [
{
"endpoints": [
{
"interface": "public",
"region": "RegionOne",
"url": "https://block-storage.example.com"
}
],
"id": "4363ae44bdf34a3981fde3b823cb9aa3",
"type": "block-storage",
"name": "cinder"
}
],
}
那么以下
service_type = 'block-storage'
# block-storage is found, return it
service_type = 'volumev2'
# volumev2 not in authority, is an alias for block-storage
# block-storage is in the catalog, return it
service_type = 'volumev2'
api_version = '3'
# volumev2 ends with a version suffix of v2 which does not match 3
# return an error before even fetching the catalog
给定以下目录
{
"token": {
"catalog": [
{
"endpoints": [
{
"interface": "public",
"region": "RegionOne",
"url": "https://block-storage.example.com"
}
],
"id": "4363ae44bdf34a3981fde3b823cb9aa3",
"type": "block-storage",
"name": "cinder"
},
{
"endpoints": [
{
"interface": "public",
"region": "RegionOne",
"url": "https://block-storage.example.com/v2"
},
{
"interface": "internal",
"region": "RegionOne",
"url": "https://block-storage.example.int/v2"
}
],
"id": "4363ae44bdf34a3981fde3b823cb9aa2",
"type": "volumev2",
"name": "cinder"
}
],
}
那么以下
service_type = 'block-storage'
interface = ['internal', 'public']
# block-storage is found
# block-storage does not have internal, but has public
# return block-storage public
service_type = 'volumev2'
interface = ['internal', 'public']
# volumev2 not an official type in authority, but is in catalog
# volumev2 has an internal interface
# return volumev2 internal entry