Merge "JSON conversion followup change"
This commit is contained in:
commit
56595161fd
|
@ -77,11 +77,13 @@ def convert_with_links(rpc_allocation, fields=None, sanitize=True):
|
||||||
'owner'),
|
'owner'),
|
||||||
list_fields=('candidate_nodes', 'traits')
|
list_fields=('candidate_nodes', 'traits')
|
||||||
)
|
)
|
||||||
api_utils.populate_node_uuid(rpc_allocation, allocation,
|
try:
|
||||||
raise_notfound=False)
|
api_utils.populate_node_uuid(rpc_allocation, allocation)
|
||||||
|
except exception.NodeNotFound:
|
||||||
|
allocation['node_uuid'] = None
|
||||||
|
|
||||||
if fields is not None:
|
if fields is not None:
|
||||||
api_utils.check_for_invalid_fields(fields, allocation.keys())
|
api_utils.check_for_invalid_fields(fields, set(allocation))
|
||||||
|
|
||||||
if sanitize:
|
if sanitize:
|
||||||
allocation_sanitize(allocation, fields)
|
allocation_sanitize(allocation, fields)
|
||||||
|
|
|
@ -37,7 +37,8 @@ def list_convert_with_links(items, item_name, limit, url=None, fields=None,
|
||||||
:param fields:
|
:param fields:
|
||||||
Optional fields to use for sanitize function
|
Optional fields to use for sanitize function
|
||||||
:param sanitize_func:
|
:param sanitize_func:
|
||||||
Optional sanitize function run on each item
|
Optional sanitize function run on each item, item changes will be
|
||||||
|
done in-place
|
||||||
:param key_field:
|
:param key_field:
|
||||||
Key name for building next URL
|
Key name for building next URL
|
||||||
:param kwargs:
|
:param kwargs:
|
||||||
|
|
|
@ -40,8 +40,7 @@ METRICS = metrics_utils.get_metrics_logger(__name__)
|
||||||
|
|
||||||
DEFAULT_RETURN_FIELDS = ['uuid', 'name']
|
DEFAULT_RETURN_FIELDS = ['uuid', 'name']
|
||||||
|
|
||||||
INTERFACE_NAMES = list(
|
INTERFACE_NAMES = list(conductor_steps.DEPLOYING_INTERFACE_PRIORITY)
|
||||||
conductor_steps.DEPLOYING_INTERFACE_PRIORITY.keys())
|
|
||||||
|
|
||||||
STEP_SCHEMA = {
|
STEP_SCHEMA = {
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
|
|
|
@ -221,7 +221,7 @@ class DriverPassthruController(rest.RestController):
|
||||||
@method.expose()
|
@method.expose()
|
||||||
@method.body('data')
|
@method.body('data')
|
||||||
@args.validate(driver_name=args.string, method=args.string)
|
@args.validate(driver_name=args.string, method=args.string)
|
||||||
def _default(self, driver_name, method=None, data=None):
|
def _default(self, driver_name, method, data=None):
|
||||||
"""Call a driver API extension.
|
"""Call a driver API extension.
|
||||||
|
|
||||||
:param driver_name: name of the driver to call.
|
:param driver_name: name of the driver to call.
|
||||||
|
@ -229,8 +229,6 @@ class DriverPassthruController(rest.RestController):
|
||||||
implementation.
|
implementation.
|
||||||
:param data: body of data to supply to the specified method.
|
:param data: body of data to supply to the specified method.
|
||||||
"""
|
"""
|
||||||
if not method:
|
|
||||||
raise exception.MissingArgument('method')
|
|
||||||
|
|
||||||
cdict = api.request.context.to_policy_values()
|
cdict = api.request.context.to_policy_values()
|
||||||
policy.authorize('baremetal:driver:vendor_passthru', cdict, cdict)
|
policy.authorize('baremetal:driver:vendor_passthru', cdict, cdict)
|
||||||
|
|
|
@ -67,7 +67,7 @@ EVENTS_SCHEMA = {
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
'properties': {
|
'properties': {
|
||||||
'event': {'type': 'string',
|
'event': {'type': 'string',
|
||||||
'enum': list(EVENT_VALIDATORS.keys())},
|
'enum': list(EVENT_VALIDATORS)},
|
||||||
},
|
},
|
||||||
'required': ['event'],
|
'required': ['event'],
|
||||||
'additionalProperties': True,
|
'additionalProperties': True,
|
||||||
|
@ -80,7 +80,7 @@ EVENTS_SCHEMA = {
|
||||||
|
|
||||||
|
|
||||||
def events_valid(name, value):
|
def events_valid(name, value):
|
||||||
'''Validator for events'''
|
"""Validator for events"""
|
||||||
|
|
||||||
for event in value['events']:
|
for event in value['events']:
|
||||||
validator = EVENT_VALIDATORS[event['event']]
|
validator = EVENT_VALIDATORS[event['event']]
|
||||||
|
|
|
@ -1277,13 +1277,6 @@ def node_sanitize(node, fields):
|
||||||
node['driver_info'] = strutils.mask_dict_password(
|
node['driver_info'] = strutils.mask_dict_password(
|
||||||
node['driver_info'], "******")
|
node['driver_info'], "******")
|
||||||
|
|
||||||
# NOTE(derekh): mask ssh keys for the ssh power driver.
|
|
||||||
# As this driver is deprecated masking here (opposed to strutils)
|
|
||||||
# is simpler, and easier to backport. This can be removed along
|
|
||||||
# with support for the ssh power driver.
|
|
||||||
if node['driver_info'].get('ssh_key_contents'):
|
|
||||||
node['driver_info']['ssh_key_contents'] = "******"
|
|
||||||
|
|
||||||
if not show_instance_secrets and node.get('instance_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'], "******")
|
||||||
|
|
|
@ -226,7 +226,7 @@ def object_to_dict(obj, created_at=True, updated_at=True, uuid=True,
|
||||||
return to_dict
|
return to_dict
|
||||||
|
|
||||||
|
|
||||||
def populate_node_uuid(obj, to_dict, raise_notfound=True):
|
def populate_node_uuid(obj, to_dict):
|
||||||
"""Look up the node referenced in the object and populate a dict.
|
"""Look up the node referenced in the object and populate a dict.
|
||||||
|
|
||||||
The node is fetched with the object ``node_id`` attribute and the
|
The node is fetched with the object ``node_id`` attribute and the
|
||||||
|
@ -236,23 +236,15 @@ def populate_node_uuid(obj, to_dict, raise_notfound=True):
|
||||||
object to get the node_id attribute
|
object to get the node_id attribute
|
||||||
:param to_dict:
|
:param to_dict:
|
||||||
dict to populate with a ``node_uuid`` value
|
dict to populate with a ``node_uuid`` value
|
||||||
:param raise_notfound:
|
|
||||||
If ``True`` raise a NodeNotFound exception if the node doesn't exist
|
|
||||||
otherwise set the dict ``node_uuid`` value to None.
|
|
||||||
:raises:
|
:raises:
|
||||||
exception.NodeNotFound if raise_notfound and the node is not found
|
exception.NodeNotFound if the node is not found
|
||||||
"""
|
"""
|
||||||
if not obj.node_id:
|
if not obj.node_id:
|
||||||
to_dict['node_uuid'] = None
|
to_dict['node_uuid'] = None
|
||||||
return
|
return
|
||||||
try:
|
to_dict['node_uuid'] = objects.Node.get_by_id(
|
||||||
to_dict['node_uuid'] = objects.Node.get_by_id(
|
api.request.context,
|
||||||
api.request.context,
|
obj.node_id).uuid
|
||||||
obj.node_id).uuid
|
|
||||||
except exception.NodeNotFound:
|
|
||||||
if raise_notfound:
|
|
||||||
raise
|
|
||||||
to_dict['node_uuid'] = None
|
|
||||||
|
|
||||||
|
|
||||||
def replace_node_uuid_with_id(to_dict):
|
def replace_node_uuid_with_id(to_dict):
|
||||||
|
@ -343,7 +335,7 @@ def patched_validate_with_schema(patched_dict, schema, validator=None):
|
||||||
:raises: exception.Invalid if validation fails
|
:raises: exception.Invalid if validation fails
|
||||||
"""
|
"""
|
||||||
schema_fields = schema['properties']
|
schema_fields = schema['properties']
|
||||||
for field in set(patched_dict.keys()):
|
for field in set(patched_dict):
|
||||||
if field not in schema_fields:
|
if field not in schema_fields:
|
||||||
patched_dict.pop(field, None)
|
patched_dict.pop(field, None)
|
||||||
if not validator:
|
if not validator:
|
||||||
|
@ -385,7 +377,7 @@ def sanitize_dict(to_sanitize, fields):
|
||||||
if fields is None:
|
if fields is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
for key in set(to_sanitize.keys()):
|
for key in set(to_sanitize):
|
||||||
if key not in fields and key != 'links':
|
if key not in fields and key != 'links':
|
||||||
to_sanitize.pop(key, None)
|
to_sanitize.pop(key, None)
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,9 @@ def expose(status_code=None):
|
||||||
pecan.response.status = 500
|
pecan.response.status = 500
|
||||||
|
|
||||||
def _empty():
|
def _empty():
|
||||||
|
# This is for a pecan workaround originally in WSME,
|
||||||
|
# but the original issue description is in an issue tracker
|
||||||
|
# that is now offline
|
||||||
pecan.request.pecan['content_type'] = None
|
pecan.request.pecan['content_type'] = None
|
||||||
pecan.response.content_type = None
|
pecan.response.content_type = None
|
||||||
|
|
||||||
|
|
|
@ -100,7 +100,7 @@ def uuid_or_name(name, value):
|
||||||
if value is None:
|
if value is None:
|
||||||
return
|
return
|
||||||
if (not utils.is_valid_logical_name(value)
|
if (not utils.is_valid_logical_name(value)
|
||||||
and not uuidutils.is_uuid_like(value)):
|
and not uuidutils.is_uuid_like(value)):
|
||||||
raise exception.InvalidParameterValue(
|
raise exception.InvalidParameterValue(
|
||||||
_('Expected UUID or name for %s: %s') % (name, value))
|
_('Expected UUID or name for %s: %s') % (name, value))
|
||||||
return value
|
return value
|
||||||
|
@ -271,7 +271,9 @@ def types(*types):
|
||||||
:param: types one or more types to use for the isinstance test
|
:param: types one or more types to use for the isinstance test
|
||||||
:returns: validator function which takes name and value arguments
|
:returns: validator function which takes name and value arguments
|
||||||
"""
|
"""
|
||||||
return functools.partial(_validate_types, types=tuple(types))
|
# Replace None with the None type
|
||||||
|
types = tuple((type(None) if tp is None else tp) for tp in types)
|
||||||
|
return functools.partial(_validate_types, types=types)
|
||||||
|
|
||||||
|
|
||||||
def _apply_validator(name, value, val_functions):
|
def _apply_validator(name, value, val_functions):
|
||||||
|
@ -352,7 +354,7 @@ def validate(*args, **kwargs):
|
||||||
elif param.default == inspect.Parameter.empty:
|
elif param.default == inspect.Parameter.empty:
|
||||||
# no argument was provided, and there is no default
|
# no argument was provided, and there is no default
|
||||||
# in the parameter, so this is a mandatory argument
|
# in the parameter, so this is a mandatory argument
|
||||||
raise exception.InvalidParameterValue(
|
raise exception.MissingParameterValue(
|
||||||
_('Missing mandatory parameter: %s') % param.name)
|
_('Missing mandatory parameter: %s') % param.name)
|
||||||
|
|
||||||
if param_positional:
|
if param_positional:
|
||||||
|
@ -388,7 +390,8 @@ patch = schema({
|
||||||
'op': {'type': 'string', 'enum': ['add', 'replace', 'remove']},
|
'op': {'type': 'string', 'enum': ['add', 'replace', 'remove']},
|
||||||
'value': {}
|
'value': {}
|
||||||
},
|
},
|
||||||
'additionalProperties': False
|
'additionalProperties': False,
|
||||||
|
'required': ['op', 'path']
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
"""Validate a patch API operation"""
|
"""Validate a patch API operation"""
|
||||||
|
|
|
@ -774,19 +774,6 @@ class UnknownArgument(ClientSideError):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class MissingArgument(ClientSideError):
|
|
||||||
def __init__(self, argname, msg=''):
|
|
||||||
self.argname = argname
|
|
||||||
super(MissingArgument, self).__init__(msg)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def faultstring(self):
|
|
||||||
return _('Missing argument: "%(argname)s"%(msg)s') % {
|
|
||||||
'argname': self.argname,
|
|
||||||
'msg': self.msg and ": " + self.msg or ""
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class UnknownAttribute(ClientSideError):
|
class UnknownAttribute(ClientSideError):
|
||||||
def __init__(self, fieldname, attributes, msg=''):
|
def __init__(self, fieldname, attributes, msg=''):
|
||||||
self.fieldname = fieldname
|
self.fieldname = fieldname
|
||||||
|
|
|
@ -363,7 +363,7 @@ class TestListDrivers(base.BaseApiTest):
|
||||||
|
|
||||||
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
|
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
|
||||||
error = json.loads(response.json['error_message'])
|
error = json.loads(response.json['error_message'])
|
||||||
self.assertEqual('Missing argument: "method"',
|
self.assertEqual('Missing mandatory parameter: method',
|
||||||
error['faultstring'])
|
error['faultstring'])
|
||||||
|
|
||||||
@mock.patch.object(rpcapi.ConductorAPI,
|
@mock.patch.object(rpcapi.ConductorAPI,
|
||||||
|
|
|
@ -2198,18 +2198,6 @@ class TestListNodes(test_api_base.BaseApiTest):
|
||||||
mock_vdi.assert_called_once_with(mock.ANY, mock.ANY,
|
mock_vdi.assert_called_once_with(mock.ANY, mock.ANY,
|
||||||
node.uuid, 'test-topic')
|
node.uuid, 'test-topic')
|
||||||
|
|
||||||
def test_ssh_creds_masked(self):
|
|
||||||
driver_info = {"ssh_password": "password", "ssh_key_contents": "key"}
|
|
||||||
node = obj_utils.create_test_node(self.context,
|
|
||||||
chassis_id=self.chassis.id,
|
|
||||||
driver_info=driver_info)
|
|
||||||
data = self.get_json(
|
|
||||||
'/nodes/%s' % node.uuid,
|
|
||||||
headers={api_base.Version.string: str(api_v1.max_version())})
|
|
||||||
|
|
||||||
self.assertEqual("******", data["driver_info"]["ssh_password"])
|
|
||||||
self.assertEqual("******", data["driver_info"]["ssh_key_contents"])
|
|
||||||
|
|
||||||
@mock.patch.object(rpcapi.ConductorAPI, 'get_indicator_state')
|
@mock.patch.object(rpcapi.ConductorAPI, 'get_indicator_state')
|
||||||
def test_get_indicator_state(self, mock_gis):
|
def test_get_indicator_state(self, mock_gis):
|
||||||
node = obj_utils.create_test_node(self.context)
|
node = obj_utils.create_test_node(self.context)
|
||||||
|
|
|
@ -858,14 +858,6 @@ class TestNodeIdent(base.TestCase):
|
||||||
'node_uuid': '1be26c0b-03f2-4d2e-ae87-c02d7f33c123'
|
'node_uuid': '1be26c0b-03f2-4d2e-ae87-c02d7f33c123'
|
||||||
}, d)
|
}, d)
|
||||||
|
|
||||||
# not found, don't raise
|
|
||||||
mock_gbi.side_effect = exception.NodeNotFound(node=port.node_id)
|
|
||||||
d = {}
|
|
||||||
utils.populate_node_uuid(port, d, raise_notfound=False)
|
|
||||||
self.assertEqual({
|
|
||||||
'node_uuid': None
|
|
||||||
}, d)
|
|
||||||
|
|
||||||
# not found, raise exception
|
# not found, raise exception
|
||||||
mock_gbi.side_effect = exception.NodeNotFound(node=port.node_id)
|
mock_gbi.side_effect = exception.NodeNotFound(node=port.node_id)
|
||||||
d = {}
|
d = {}
|
||||||
|
|
|
@ -597,7 +597,7 @@ class ValidateTypesTest(BaseTest):
|
||||||
|
|
||||||
def test_types(self):
|
def test_types(self):
|
||||||
|
|
||||||
@args.validate(foo=args.types(type(None), dict, str))
|
@args.validate(foo=args.types(None, dict, str))
|
||||||
def doit(foo):
|
def doit(foo):
|
||||||
return foo
|
return foo
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue