node.py (ironic-17.0.2) | : | node.py (ironic-17.0.3) | ||
---|---|---|---|---|
skipping to change at line 1335 | skipping to change at line 1335 | |||
cdict = api.request.context.to_policy_values() | cdict = api.request.context.to_policy_values() | |||
target_dict = dict(cdict) | target_dict = dict(cdict) | |||
owner = node.get('owner') | owner = node.get('owner') | |||
lessee = node.get('lessee') | lessee = node.get('lessee') | |||
if owner: | if owner: | |||
target_dict['node.owner'] = owner | target_dict['node.owner'] = owner | |||
if lessee: | if lessee: | |||
target_dict['node.lessee'] = lessee | target_dict['node.lessee'] = lessee | |||
# Scrub the dictionary's contents down to what was requested. | ||||
api_utils.sanitize_dict(node, fields) | ||||
# NOTE(tenbrae): the 'show_password' policy setting name exists for | # NOTE(tenbrae): the 'show_password' policy setting name exists for | |||
# legacy purposes and can not be changed. Changing it will | # legacy purposes and can not be changed. Changing it will | |||
# cause upgrade problems for any operators who have | # cause upgrade problems for any operators who have | |||
# customized the value of this field | # customized the value of this field | |||
# NOTE(TheJulia): These methods use policy.check and normally return | # NOTE(TheJulia): These methods use policy.check and normally return | |||
# False in a noauth or password auth based situation, because the | # False in a noauth or password auth based situation, because the | |||
# effective caller doesn't match the policy check rule. | # effective caller doesn't match the policy check rule. | |||
show_driver_secrets = policy.check("show_password", cdict, target_dict) | show_driver_secrets = policy.check("show_password", cdict, target_dict) | |||
show_instance_secrets = policy.check("show_instance_secrets", | show_instance_secrets = policy.check("show_instance_secrets", | |||
cdict, target_dict) | cdict, target_dict) | |||
skipping to change at line 1356 | skipping to change at line 1359 | |||
# but until we have auditing clarity, it might not be a big deal. | # but until we have auditing clarity, it might not be a big deal. | |||
# Determine if we need to do the additional checks. Keep in mind | # Determine if we need to do the additional checks. Keep in mind | |||
# nova integrated with ironic is API read heavy, so it is ideal | # nova integrated with ironic is API read heavy, so it is ideal | |||
# to keep the policy checks for say system-member based roles to | # to keep the policy checks for say system-member based roles to | |||
# a minimum as they are likely the regular API users as well. | # a minimum as they are likely the regular API users as well. | |||
# Also, the default for the filter_threshold is system-member. | # Also, the default for the filter_threshold is system-member. | |||
evaluate_additional_policies = not policy.check_policy( | evaluate_additional_policies = not policy.check_policy( | |||
"baremetal:node:get:filter_threshold", | "baremetal:node:get:filter_threshold", | |||
target_dict, cdict) | target_dict, cdict) | |||
node_keys = node.keys() | ||||
if evaluate_additional_policies: | if evaluate_additional_policies: | |||
# NOTE(TheJulia): The net effect of this is that by default, | # NOTE(TheJulia): The net effect of this is that by default, | |||
# at least matching common/policy.py defaults. is these should | # at least matching common/policy.py defaults. is these should | |||
# be stripped out. | # be stripped out. | |||
if not policy.check("baremetal:node:get:last_error", | if ('last_error' in node_keys | |||
target_dict, cdict): | and not policy.check("baremetal:node:get:last_error", | |||
target_dict, cdict)): | ||||
# Guard the last error from being visible as it can contain | # Guard the last error from being visible as it can contain | |||
# hostnames revealing infrastucture internal details. | # hostnames revealing infrastucture internal details. | |||
node['last_error'] = ('** Value Redacted - Requires ' | node['last_error'] = ('** Value Redacted - Requires ' | |||
'baremetal:node:get:last_error ' | 'baremetal:node:get:last_error ' | |||
'permission. **') | 'permission. **') | |||
if not policy.check("baremetal:node:get:reservation", | if ('reservation' in node_keys | |||
target_dict, cdict): | and not policy.check("baremetal:node:get:reservation", | |||
target_dict, cdict)): | ||||
# Guard conductor names from being visible. | # Guard conductor names from being visible. | |||
node['reservation'] = ('** Redacted - requires baremetal:' | node['reservation'] = ('** Redacted - requires baremetal:' | |||
'node:get:reservation permission. **') | 'node:get:reservation permission. **') | |||
if not policy.check("baremetal:node:get:driver_internal_info", | if ('driver_internal_info' in node_keys | |||
target_dict, cdict): | and not policy.check("baremetal:node:get:driver_internal_info", | |||
target_dict, cdict)): | ||||
# Guard conductor names from being visible. | # Guard conductor names from being visible. | |||
node['driver_internal_info'] = { | node['driver_internal_info'] = { | |||
'content': '** Redacted - Requires baremetal:node:get:' | 'content': '** Redacted - Requires baremetal:node:get:' | |||
'driver_internal_info permission. **'} | 'driver_internal_info permission. **'} | |||
if not policy.check("baremetal:node:get:driver_info", | ||||
target_dict, cdict): | if 'driver_info' in node_keys: | |||
if (evaluate_additional_policies | ||||
and not policy.check("baremetal:node:get:driver_info", | ||||
target_dict, cdict)): | ||||
# Guard infrastructure intenral details from being visible. | # Guard infrastructure intenral details from being visible. | |||
node['driver_info'] = { | node['driver_info'] = { | |||
'content': '** Redacted - requires baremetal:node:get:' | 'content': '** Redacted - requires baremetal:node:get:' | |||
'driver_info permission. **'} | 'driver_info permission. **'} | |||
if not show_driver_secrets: | ||||
node['driver_info'] = strutils.mask_dict_password( | ||||
node['driver_info'], "******") | ||||
if not show_driver_secrets and node.get('driver_info'): | if not show_instance_secrets and 'instance_info' in node_keys: | |||
node['driver_info'] = strutils.mask_dict_password( | ||||
node['driver_info'], "******") | ||||
if not show_instance_secrets and node.get('instance_info'): | ||||
node['instance_info'] = strutils.mask_dict_password( | node['instance_info'] = strutils.mask_dict_password( | |||
node['instance_info'], "******") | node['instance_info'], "******") | |||
# NOTE(tenbrae): agent driver may store a swift temp_url on the | # NOTE(tenbrae): agent driver may store a swift temp_url on the | |||
# instance_info, which shouldn't be exposed to non-admin users. | # instance_info, which shouldn't be exposed to non-admin users. | |||
# Now that ironic supports additional policies, we need to hide | # Now that ironic supports additional policies, we need to hide | |||
# it here, based on this policy. | # it here, based on this policy. | |||
# Related to bug #1613903 | # Related to bug #1613903 | |||
if node['instance_info'].get('image_url'): | if node['instance_info'].get('image_url'): | |||
node['instance_info']['image_url'] = "******" | node['instance_info']['image_url'] = "******" | |||
if node.get('driver_internal_info', {}).get('agent_secret_token'): | if node.get('driver_internal_info', {}).get('agent_secret_token'): | |||
node['driver_internal_info']['agent_secret_token'] = "******" | node['driver_internal_info']['agent_secret_token'] = "******" | |||
update_state_in_older_versions(node) | if 'provision_state' in node_keys: | |||
# Update legacy state data for provision state, but only if | ||||
# the key is present. | ||||
update_state_in_older_versions(node) | ||||
hide_fields_in_newer_versions(node) | hide_fields_in_newer_versions(node) | |||
api_utils.sanitize_dict(node, fields) | ||||
show_states_links = ( | show_states_links = ( | |||
api_utils.allow_links_node_states_and_driver_properties()) | api_utils.allow_links_node_states_and_driver_properties()) | |||
show_portgroups = api_utils.allow_portgroups_subcontrollers() | show_portgroups = api_utils.allow_portgroups_subcontrollers() | |||
show_volume = api_utils.allow_volume() | show_volume = api_utils.allow_volume() | |||
if not show_volume: | if not show_volume: | |||
node.pop('volume', None) | node.pop('volume', None) | |||
if not show_portgroups: | if not show_portgroups: | |||
node.pop('portgroups', None) | node.pop('portgroups', None) | |||
if not show_states_links: | if not show_states_links: | |||
skipping to change at line 1710 | skipping to change at line 1722 | |||
_("The sort_key value %(key)s is an invalid field for " | _("The sort_key value %(key)s is an invalid field for " | |||
"sorting") % {'key': sort_key}) | "sorting") % {'key': sort_key}) | |||
marker_obj = None | marker_obj = None | |||
if marker: | if marker: | |||
marker_obj = objects.Node.get_by_uuid(api.request.context, | marker_obj = objects.Node.get_by_uuid(api.request.context, | |||
marker) | marker) | |||
# The query parameters for the 'next' URL | # The query parameters for the 'next' URL | |||
parameters = {} | parameters = {} | |||
possible_filters = { | ||||
'maintenance': maintenance, | ||||
'chassis_uuid': chassis_uuid, | ||||
'associated': associated, | ||||
'provision_state': provision_state, | ||||
'driver': driver, | ||||
'resource_class': resource_class, | ||||
'fault': fault, | ||||
'conductor_group': conductor_group, | ||||
'owner': owner, | ||||
'lessee': lessee, | ||||
'project': project, | ||||
'description_contains': description_contains, | ||||
'retired': retired, | ||||
'instance_uuid': instance_uuid | ||||
} | ||||
filters = {} | ||||
for key, value in possible_filters.items(): | ||||
if value is not None: | ||||
filters[key] = value | ||||
nodes = objects.Node.list(api.request.context, limit, marker_obj, | ||||
sort_key=sort_key, sort_dir=sort_dir, | ||||
filters=filters) | ||||
# Special filtering on results based on conductor field | ||||
if conductor: | ||||
nodes = self._filter_by_conductor(nodes, conductor) | ||||
parameters = {'sort_key': sort_key, 'sort_dir': sort_dir} | ||||
if associated: | ||||
parameters['associated'] = associated | ||||
if maintenance: | ||||
parameters['maintenance'] = maintenance | ||||
if retired: | ||||
parameters['retired'] = retired | ||||
if detail is not None: | ||||
parameters['detail'] = detail | ||||
if instance_uuid: | if instance_uuid: | |||
# NOTE(rloo) if instance_uuid is specified, the other query | ||||
# parameters are ignored. Since there can be at most one node that | ||||
# has this instance_uuid, we do not want to generate a 'next' link. | ||||
nodes = self._get_nodes_by_instance(instance_uuid) | ||||
# NOTE(rloo) if limit==1 and len(nodes)==1 (see | # NOTE(rloo) if limit==1 and len(nodes)==1 (see | |||
# Collection.has_next()), a 'next' link will | # Collection.has_next()), a 'next' link will | |||
# be generated, which we don't want. | # be generated, which we don't want. | |||
# NOTE(TheJulia): This is done after the query as | ||||
# instance_uuid is a unique constraint in the DB | ||||
# and we cannot pass a limit of 0 to sqlalchemy | ||||
# and expect a response. | ||||
limit = 0 | limit = 0 | |||
else: | ||||
possible_filters = { | ||||
'maintenance': maintenance, | ||||
'chassis_uuid': chassis_uuid, | ||||
'associated': associated, | ||||
'provision_state': provision_state, | ||||
'driver': driver, | ||||
'resource_class': resource_class, | ||||
'fault': fault, | ||||
'conductor_group': conductor_group, | ||||
'owner': owner, | ||||
'lessee': lessee, | ||||
'project': project, | ||||
'description_contains': description_contains, | ||||
'retired': retired, | ||||
} | ||||
filters = {} | ||||
for key, value in possible_filters.items(): | ||||
if value is not None: | ||||
filters[key] = value | ||||
nodes = objects.Node.list(api.request.context, limit, marker_obj, | ||||
sort_key=sort_key, sort_dir=sort_dir, | ||||
filters=filters) | ||||
# Special filtering on results based on conductor field | ||||
if conductor: | ||||
nodes = self._filter_by_conductor(nodes, conductor) | ||||
parameters = {'sort_key': sort_key, 'sort_dir': sort_dir} | ||||
if associated: | ||||
parameters['associated'] = associated | ||||
if maintenance: | ||||
parameters['maintenance'] = maintenance | ||||
if retired: | ||||
parameters['retired'] = retired | ||||
if detail is not None: | ||||
parameters['detail'] = detail | ||||
return node_list_convert_with_links(nodes, limit, | return node_list_convert_with_links(nodes, limit, | |||
url=resource_url, | url=resource_url, | |||
fields=fields, | fields=fields, | |||
**parameters) | **parameters) | |||
def _get_nodes_by_instance(self, instance_uuid): | ||||
"""Retrieve a node by its instance uuid. | ||||
It returns a list with the node, or an empty list if no node is found. | ||||
""" | ||||
try: | ||||
node = objects.Node.get_by_instance_uuid(api.request.context, | ||||
instance_uuid) | ||||
return [node] | ||||
except exception.InstanceNotFound: | ||||
return [] | ||||
def _check_names_acceptable(self, names, error_msg): | def _check_names_acceptable(self, names, error_msg): | |||
"""Checks all node 'name's are acceptable, it does not return a value. | """Checks all node 'name's are acceptable, it does not return a value. | |||
This function will raise an exception for unacceptable names. | This function will raise an exception for unacceptable names. | |||
:param names: list of node names to check | :param names: list of node names to check | |||
:param error_msg: error message in case of exception.ClientSideError, | :param error_msg: error message in case of exception.ClientSideError, | |||
should contain %(name)s placeholder. | should contain %(name)s placeholder. | |||
:raises: exception.NotAcceptable | :raises: exception.NotAcceptable | |||
:raises: exception.ClientSideError | :raises: exception.ClientSideError | |||
End of changes. 16 change blocks. | ||||
73 lines changed or deleted | 70 lines changed or added |