本文地址:http://blog.csdn.net/spch2008/article/details/9712463
先前做QoS功能时,对于RESOURCE_ATTRIBUTE_MAP了解甚少,都是靠模仿者来做的。
本文针对quantum\api\v2\attributs.py中的port做相应的解释,为求简便,省略了部分属性。
'ports': {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True,
'primary_key': True},
'admin_state_up': {'allow_post': True, 'allow_put': True,
'default': True,
'convert_to': convert_to_boolean,
'is_visible': True},
'mac_address': {'allow_post': True, 'allow_put': False,
'default': ATTR_NOT_SPECIFIED,
'validate': {'type:mac_address': None},
'enforce_policy': True,
'is_visible': True},
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True,
'is_visible': True},
},
而资源属性映射的主要应用在quantum\api\v2\base.py中,结合该文件对资源属性映射进行分析。
1. allow_post 与资源创建有关。比如要新建一个端口,则会检测allow_post
if is_create: # POST
for attr, attr_vals in attr_info.iteritems():
if attr_vals['allow_post']:
if ('default' not in attr_vals and
attr not in res_dict):
msg = _("Failed to parse request. Required "
"attribute '%s' not specified") % attr
raise webob.exc.HTTPBadRequest(msg)
res_dict[attr] = res_dict.get(attr,
attr_vals.get('default'))
else:
if attr in res_dict:
msg = _("Attribute '%s' not allowed in POST") % attr
raise webob.exc.HTTPBadRequest(msg)
rest_dict是用户提交的创建信息。如果allow_post为True,则要求用户提交的信息中包含属性与值,比如name:spch2008。
如果允许提交信息,但用户却没有给定,如果给该属性配置了默认值“default"则取默认值,否则报错。如果不允许用户提交,而用户提交了该信息,就会报错。
2. default 如上所述,当需要用户提交创建信息,但用户没有提交时,使用默认信息。
3.allow_put 与资源更新有关。即创建以后,是否允许用户进行修改。
else: # PUT
for attr, attr_vals in attr_info.iteritems():
if attr in res_dict and not attr_vals['allow_put']:
msg = _("Cannot update read-only attribute %s") % attr
raise webob.exc.HTTPBadRequest(msg)
如果用户提交的更改内容不允许更改,则会报错。
4.validate 验证用户给定信息是否有效
#rule 字典键值 type:uuid
for rule in attr_vals['validate']:
res = attributes.validators[rule](res_dict[attr],
attr_vals['validate'][rule])
if res:
msg_dict = dict(attr=attr, reason=res)
msg = _("Invalid input for %(attr)s. "
"Reason: %(reason)s.") % msg_dict
raise webob.exc.HTTPBadRequest(msg)
在attribute.py中定义了许多验证器
validators = {'type:dict': _validate_dict,
'type:dict_or_none': _validate_dict_or_none,
'type:ip_address': _validate_ip_address,
'type:mac_address': _validate_mac_address,
'type:uuid': _validate_uuid,}
验证器对应相应的验证函数
def _validate_uuid(data, valid_values=None):
if not uuidutils.is_uuid_like(data):
msg = _("'%s' is not a valid UUID") % data
LOG.debug(msg)
return msg
取得对应的验证函数,验证信息是否有效。
5.convert_to将用户提交的数据转换成相应类型
if 'convert_to' in attr_vals:
res_dict[attr] = attr_vals['convert_to'](res_dict[attr])
上述admin_state_up中要求转换成bool类型,对应于attribute.py中的函数
def convert_to_boolean(data):
if isinstance(data, basestring):
val = data.lower()
if val == "true" or val == "1":
return True
if val == "false" or val == "0":
return False
elif isinstance(data, bool):
return data
elif isinstance(data, int):
if data == 0:
return False
elif data == 1:
return True
msg = _("'%s' cannot be converted to boolean") % data
raise q_exc.InvalidInput(error_message=msg)
5. is_visible 是否允许用户查看该属性
def _is_visible(self, attr):
attr_val = self._attr_info.get(attr)
return attr_val and attr_val['is_visible']
如果该属性不允许用户查看,则将查询结果中的该属性屏蔽掉。
6.required_by_policy 从字面上理解就是权限认证的时候需要该属性
首先看一个quantum port-show 命令
root@nova-controller:/etc/quantum# quantum port-show c5975e89-c3cb-4cce-a751-5931258c42d1
+----------------+-----------------------------------------------------------------------------------+
| Field | Value |
+----------------+-----------------------------------------------------------------------------------+
| admin_state_up | True |
| device_id | dhcp23842b6d-1b21-5315-86af-417875e3667e-404daf26-f0a9-46a2-8fa9-dafaf112d221 |
| device_owner | network:dhcp |
| fixed_ips | {"subnet_id": "262350ee-c1fd-426f-a6a5-23a90cabbf5f", "ip_address": "10.10.10.2"} |
| id | c5975e89-c3cb-4cce-a751-5931258c42d1 |
| mac_address | fa:16:3e:4e:3e:12 |
| name | spch2008 |
| network_id | 404daf26-f0a9-46a2-8fa9-dafaf112d221 |
| status | ACTIVE |
| tenant_id | 9b320e2822da4461967232e938d92d18 |
+----------------+-----------------------------------------------------------------------------------+
由于quantumclient支持使用名字进行查询,如下:
root@nova-controller:/etc/quantum# quantum port-show spch2008
+----------------+-----------------------------------------------------------------------------------+
| Field | Value |
+----------------+-----------------------------------------------------------------------------------+
| admin_state_up | True |
| device_id | dhcp23842b6d-1b21-5315-86af-417875e3667e-404daf26-f0a9-46a2-8fa9-dafaf112d221 |
| device_owner | network:dhcp |
| fixed_ips | {"subnet_id": "262350ee-c1fd-426f-a6a5-23a90cabbf5f", "ip_address": "10.10.10.2"} |
| id | c5975e89-c3cb-4cce-a751-5931258c42d1 |
| mac_address | fa:16:3e:4e:3e:12 |
| name | spch2008 |
| network_id | 404daf26-f0a9-46a2-8fa9-dafaf112d221 |
| status | ACTIVE |
| tenant_id | 9b320e2822da4461967232e938d92d18 |
+----------------+-----------------------------------------------------------------------------------+
当使用名字的时候,需要到控制节点查询名字对应的id,然后在使用id进行一次查询,得到结果。可以在quantumclient\quantum\v2_0\__init__.py中
找到证据。ShowCommand类的get_data方法,调用find_resourceid_by_name_or_id,会使用list(相当于port-list)取得所有名字为spch2008的项,
如果返回结果大于1,则报错,这样得到id后,相当于port-show id。如果用户给定的是id,仍需要list去数据库中找是否包含此项。
由于只想取得id,不需要端口的其它信息,所以调用的时候指定参数fields='id'(data = obj_lister(name=name, fields='id')),由于指定了field,会过滤掉其它
内容,只保留id。但是,上一权限认证介绍过,权限认证需要相应的信息,如tenant_id等,如果没有这些验证信息,可能导致权限验证失败。所以呢,required_by_policy
字段,保证该属性在权限认证的时候存在,而最终返回给用户的时候删掉。
quantumclient通过上述方式提交的url如下:
由于quantumclient调用的是list方法去取得名字对应的id,所以到base.py中查看list对应的方法index,进而转到_items,一下为_items涉及的语句。
original_fields, fields_to_add = self._do_field_list(
api_common.list_args(request, 'fields'))
filters = api_common.get_filters(request, self._attr_info,
['fields', 'sort_key', 'sort_dir',
'limit', 'marker', 'page_reverse'])
kwargs = {'filters': filters,
'fields': original_fields}
实际上通过_do_field_list扩展了original_fields,将权限认证需要的信息加入其中,这样,最终取得的结果包含它们。
def _do_field_list(self, original_fields):
fields_to_add = None
if original_fields:
fields_to_add = [attr for attr in self._policy_attrs
if attr not in original_fields]
original_fields.extend(self._policy_attrs)
return original_fields, fields_to_add
_policy_attrs为权限认证需要的信息,如果original_fields中没有,加入fields_to_add中,最终扩展到original_fields。之所以要保留
fields_to_add是因为这一部分是我们增加的,用户并不需要,等权限认证结束后,需要将这些属性去除,然后将结果返回给用户。
看一下如何去除属性,_view方法。调用该函数时,fields_to_add对应于fields_to_strip。
def _view(self, data, fields_to_strip=None):
if not fields_to_strip:
fields_to_strip = []
return dict(item for item in data.iteritems()
if (self._is_visible(item[0]) and
item[0] not in fields_to_strip))
主要功能是:判断是否允许显示,是否要被剔除。
7.enforce_policy 修改属性的时候,需要进行验证,验证用户是否有权限进行修改。
上一篇,介绍_build_match_rule时候,省略了这部分。
def _build_match_rule(action, target):
match_rule = policy.RuleCheck('rule', action)
resource, is_write = get_resource_and_action(action)
if is_write:
# assigning to variable with short name for improving readability
res_map = attributes.RESOURCE_ATTRIBUTE_MAP
if resource in res_map:
for attribute_name in res_map[resource]:
if _is_attribute_explicitly_set(attribute_name,
res_map[resource],
target):
attribute = res_map[resource][attribute_name]
if 'enforce_policy' in attribute and is_write:
attr_rule = policy.RuleCheck('rule', '%s:%s' %
(action, attribute_name))
match_rule = policy.AndCheck([match_rule, attr_rule])
return match_rule
如果要更改属性切该属性有enforce_policy限制,则需要进行验证。
比如action 对应于 create_port,而attribute_name 对应于mac_address,这样,创建一个RuleCheck,对这个RuleCheck包装一下,
建立一个AndCheck,即要满足二者,才放行。
"create_port:mac_address": [["rule:admin_or_network_owner"]]
policy.json数据格式如上所示。