Volume targets/connectors Project Scoped RBAC
This patch adds project scoped access, as part of the work to delineate system and project scope access. Adds policies: * baremetal:volume:list_all * baremetal:volume:list * baremetal:volume:view_target_properties Change-Id: I898310b515195b7065a3b1c7998ef3f29f5e8747
This commit is contained in:
parent
e9dfe5ddaa
commit
e870bd34d0
@ -66,8 +66,12 @@ Supported Endpoints
|
|||||||
* /nodes
|
* /nodes
|
||||||
* /nodes/<uuid>/ports
|
* /nodes/<uuid>/ports
|
||||||
* /nodes/<uuid>/portgroups
|
* /nodes/<uuid>/portgroups
|
||||||
|
* /nodes/<uuid>/volume/connectors
|
||||||
|
* /nodes/<uuid>/volume/targets
|
||||||
* /ports
|
* /ports
|
||||||
* /portgroups
|
* /portgroups
|
||||||
|
* /volume/connectors
|
||||||
|
* /volume/targets
|
||||||
|
|
||||||
How Project Scoped Works
|
How Project Scoped Works
|
||||||
------------------------
|
------------------------
|
||||||
@ -146,7 +150,7 @@ More information is available on these fields in :doc:`/configuration/policy`.
|
|||||||
Pratical differences
|
Pratical differences
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
Most users, upon implementing the use of ``system`` scoped authenticaiton,
|
Most users, upon implementing the use of ``system`` scoped authentication
|
||||||
should not notice a difference as long as their authentication token is
|
should not notice a difference as long as their authentication token is
|
||||||
properly scoped to ``system`` and with the appropriate role for their
|
properly scoped to ``system`` and with the appropriate role for their
|
||||||
access level. For most users who used a ``baremetal`` project,
|
access level. For most users who used a ``baremetal`` project,
|
||||||
@ -154,7 +158,7 @@ or other custom project via a custom policy file, along with a custom
|
|||||||
role name such as ``baremetal_admin``, this will require changing
|
role name such as ``baremetal_admin``, this will require changing
|
||||||
the user to be a ``system`` scoped user with ``admin`` privilges.
|
the user to be a ``system`` scoped user with ``admin`` privilges.
|
||||||
|
|
||||||
The most noticable difference for API consumers is the HTTP 403 access
|
The most noticeable difference for API consumers is the HTTP 403 access
|
||||||
code is now mainly a HTTP 404 access code. The access concept has changed
|
code is now mainly a HTTP 404 access code. The access concept has changed
|
||||||
from "Does the user user broadly has access to the API?" to
|
from "Does the user user broadly has access to the API?" to
|
||||||
"Does user have access to the node, and then do they have access
|
"Does user have access to the node, and then do they have access
|
||||||
|
@ -1787,6 +1787,110 @@ def check_port_list_policy(portgroup=False, parent_node=None,
|
|||||||
return owner
|
return owner
|
||||||
|
|
||||||
|
|
||||||
|
def check_volume_list_policy(parent_node=None):
|
||||||
|
"""Check if the specified policy authorizes this request on a port.
|
||||||
|
|
||||||
|
:param parent_node: The UUID of a node, if any, to apply a policy
|
||||||
|
check to as well before applying other policy
|
||||||
|
check operations.
|
||||||
|
|
||||||
|
:raises: HTTPForbidden if the policy forbids access.
|
||||||
|
:return: owner that should be used for list query, if needed
|
||||||
|
"""
|
||||||
|
|
||||||
|
cdict = api.request.context.to_policy_values()
|
||||||
|
|
||||||
|
# No node is associated with this request, yet.
|
||||||
|
rpc_node = None
|
||||||
|
conceal_linked_node = None
|
||||||
|
|
||||||
|
if parent_node:
|
||||||
|
try:
|
||||||
|
rpc_node = objects.Node.get_by_uuid(api.request.context,
|
||||||
|
parent_node)
|
||||||
|
conceal_linked_node = rpc_node.uuid
|
||||||
|
except exception.NotFound:
|
||||||
|
raise exception.NodeNotFound(node=parent_node)
|
||||||
|
if parent_node:
|
||||||
|
try:
|
||||||
|
check_owner_policy(
|
||||||
|
'node', 'baremetal:node:get',
|
||||||
|
rpc_node.owner, rpc_node.lessee,
|
||||||
|
conceal_node=conceal_linked_node)
|
||||||
|
except exception.NotAuthorized:
|
||||||
|
if parent_node:
|
||||||
|
# This should likely never be hit, because
|
||||||
|
# the existence of a parent node should
|
||||||
|
# trigger the node not found exception to be
|
||||||
|
# explicitly raised.
|
||||||
|
raise exception.NodeNotFound(
|
||||||
|
node=parent_node)
|
||||||
|
raise
|
||||||
|
|
||||||
|
try:
|
||||||
|
policy.authorize('baremetal:volume:list_all',
|
||||||
|
cdict, api.request.context)
|
||||||
|
except exception.HTTPForbidden:
|
||||||
|
owner = cdict.get('project_id')
|
||||||
|
if not owner:
|
||||||
|
raise
|
||||||
|
policy.authorize('baremetal:volume:list',
|
||||||
|
cdict, api.request.context)
|
||||||
|
return owner
|
||||||
|
|
||||||
|
|
||||||
|
def check_volume_policy_and_retrieve(policy_name, vol_ident, target=False):
|
||||||
|
"""Check if the specified policy authorizes this request on a port.
|
||||||
|
|
||||||
|
:param: policy_name: Name of the policy to check.
|
||||||
|
:param: vol_ident: The name, uuid, or other valid ID value to find
|
||||||
|
a port or portgroup by.
|
||||||
|
:param: target: Boolean value to indicate if the check is for a volume
|
||||||
|
target or connector. Default value is False, implying
|
||||||
|
connector.
|
||||||
|
|
||||||
|
:raises: HTTPForbidden if the policy forbids access.
|
||||||
|
:raises: VolumeConnectorNotFound if the node is not found.
|
||||||
|
:raises: VolumeTargetNotFound if the node is not found.
|
||||||
|
:return: RPC port identified by port_ident associated node
|
||||||
|
"""
|
||||||
|
context = api.request.context
|
||||||
|
cdict = context.to_policy_values()
|
||||||
|
owner = None
|
||||||
|
lessee = None
|
||||||
|
try:
|
||||||
|
if not target:
|
||||||
|
rpc_vol = objects.VolumeConnector.get(context, vol_ident)
|
||||||
|
else:
|
||||||
|
rpc_vol = objects.VolumeTarget.get(context, vol_ident)
|
||||||
|
except (exception.VolumeConnectorNotFound, exception.VolumeTargetNotFound):
|
||||||
|
# don't expose non-existence of port unless requester
|
||||||
|
# has generic access to policy
|
||||||
|
raise
|
||||||
|
|
||||||
|
target_dict = dict(cdict)
|
||||||
|
try:
|
||||||
|
rpc_node = objects.Node.get_by_id(context, rpc_vol.node_id)
|
||||||
|
owner = rpc_node['owner']
|
||||||
|
lessee = rpc_node['lessee']
|
||||||
|
except exception.NodeNotFound:
|
||||||
|
pass
|
||||||
|
target_dict = dict(cdict)
|
||||||
|
target_dict['node.owner'] = owner
|
||||||
|
target_dict['node.lessee'] = lessee
|
||||||
|
try:
|
||||||
|
policy.authorize('baremetal:node:get', target_dict, context)
|
||||||
|
except exception.NotAuthorized:
|
||||||
|
if not target:
|
||||||
|
raise exception.VolumeConnectorNotFound(connector=vol_ident)
|
||||||
|
else:
|
||||||
|
raise exception.VolumeTargetNotFound(target=vol_ident)
|
||||||
|
|
||||||
|
policy.authorize(policy_name, target_dict, context)
|
||||||
|
|
||||||
|
return rpc_vol, rpc_node
|
||||||
|
|
||||||
|
|
||||||
def allow_build_configdrive():
|
def allow_build_configdrive():
|
||||||
"""Check if building configdrive is allowed.
|
"""Check if building configdrive is allowed.
|
||||||
|
|
||||||
|
@ -111,7 +111,8 @@ class VolumeConnectorsController(rest.RestController):
|
|||||||
def _get_volume_connectors_collection(self, node_ident, marker, limit,
|
def _get_volume_connectors_collection(self, node_ident, marker, limit,
|
||||||
sort_key, sort_dir,
|
sort_key, sort_dir,
|
||||||
resource_url=None,
|
resource_url=None,
|
||||||
fields=None, detail=None):
|
fields=None, detail=None,
|
||||||
|
project=None):
|
||||||
limit = api_utils.validate_limit(limit)
|
limit = api_utils.validate_limit(limit)
|
||||||
sort_dir = api_utils.validate_sort_dir(sort_dir)
|
sort_dir = api_utils.validate_sort_dir(sort_dir)
|
||||||
|
|
||||||
@ -135,13 +136,15 @@ class VolumeConnectorsController(rest.RestController):
|
|||||||
node = api_utils.get_rpc_node(node_ident)
|
node = api_utils.get_rpc_node(node_ident)
|
||||||
connectors = objects.VolumeConnector.list_by_node_id(
|
connectors = objects.VolumeConnector.list_by_node_id(
|
||||||
api.request.context, node.id, limit, marker_obj,
|
api.request.context, node.id, limit, marker_obj,
|
||||||
sort_key=sort_key, sort_dir=sort_dir)
|
sort_key=sort_key, sort_dir=sort_dir,
|
||||||
|
project=project)
|
||||||
else:
|
else:
|
||||||
connectors = objects.VolumeConnector.list(api.request.context,
|
connectors = objects.VolumeConnector.list(api.request.context,
|
||||||
limit,
|
limit,
|
||||||
marker_obj,
|
marker_obj,
|
||||||
sort_key=sort_key,
|
sort_key=sort_key,
|
||||||
sort_dir=sort_dir)
|
sort_dir=sort_dir,
|
||||||
|
project=project)
|
||||||
return list_convert_with_links(connectors, limit,
|
return list_convert_with_links(connectors, limit,
|
||||||
url=resource_url,
|
url=resource_url,
|
||||||
fields=fields,
|
fields=fields,
|
||||||
@ -156,7 +159,7 @@ class VolumeConnectorsController(rest.RestController):
|
|||||||
sort_dir=args.string, fields=args.string_list,
|
sort_dir=args.string, fields=args.string_list,
|
||||||
detail=args.boolean)
|
detail=args.boolean)
|
||||||
def get_all(self, node=None, marker=None, limit=None, sort_key='id',
|
def get_all(self, node=None, marker=None, limit=None, sort_key='id',
|
||||||
sort_dir='asc', fields=None, detail=None):
|
sort_dir='asc', fields=None, detail=None, project=None):
|
||||||
"""Retrieve a list of volume connectors.
|
"""Retrieve a list of volume connectors.
|
||||||
|
|
||||||
:param node: UUID or name of a node, to get only volume connectors
|
:param node: UUID or name of a node, to get only volume connectors
|
||||||
@ -179,7 +182,8 @@ class VolumeConnectorsController(rest.RestController):
|
|||||||
:raises: InvalidParameterValue if sort key is invalid for sorting.
|
:raises: InvalidParameterValue if sort key is invalid for sorting.
|
||||||
:raises: InvalidParameterValue if both fields and detail are specified.
|
:raises: InvalidParameterValue if both fields and detail are specified.
|
||||||
"""
|
"""
|
||||||
api_utils.check_policy('baremetal:volume:get')
|
project = api_utils.check_volume_list_policy(
|
||||||
|
parent_node=self.parent_node_ident)
|
||||||
|
|
||||||
if fields is None and not detail:
|
if fields is None and not detail:
|
||||||
fields = _DEFAULT_RETURN_FIELDS
|
fields = _DEFAULT_RETURN_FIELDS
|
||||||
@ -191,7 +195,7 @@ class VolumeConnectorsController(rest.RestController):
|
|||||||
resource_url = 'volume/connectors'
|
resource_url = 'volume/connectors'
|
||||||
return self._get_volume_connectors_collection(
|
return self._get_volume_connectors_collection(
|
||||||
node, marker, limit, sort_key, sort_dir, resource_url=resource_url,
|
node, marker, limit, sort_key, sort_dir, resource_url=resource_url,
|
||||||
fields=fields, detail=detail)
|
fields=fields, detail=detail, project=project)
|
||||||
|
|
||||||
@METRICS.timer('VolumeConnectorsController.get_one')
|
@METRICS.timer('VolumeConnectorsController.get_one')
|
||||||
@method.expose()
|
@method.expose()
|
||||||
@ -210,13 +214,15 @@ class VolumeConnectorsController(rest.RestController):
|
|||||||
:raises: VolumeConnectorNotFound if no volume connector exists with
|
:raises: VolumeConnectorNotFound if no volume connector exists with
|
||||||
the specified UUID.
|
the specified UUID.
|
||||||
"""
|
"""
|
||||||
api_utils.check_policy('baremetal:volume:get')
|
|
||||||
|
rpc_connector, _ = api_utils.check_volume_policy_and_retrieve(
|
||||||
|
'baremetal:volume:get',
|
||||||
|
connector_uuid,
|
||||||
|
target=False)
|
||||||
|
|
||||||
if self.parent_node_ident:
|
if self.parent_node_ident:
|
||||||
raise exception.OperationNotPermitted()
|
raise exception.OperationNotPermitted()
|
||||||
|
|
||||||
rpc_connector = objects.VolumeConnector.get_by_uuid(
|
|
||||||
api.request.context, connector_uuid)
|
|
||||||
return convert_with_links(rpc_connector, fields=fields)
|
return convert_with_links(rpc_connector, fields=fields)
|
||||||
|
|
||||||
@METRICS.timer('VolumeConnectorsController.post')
|
@METRICS.timer('VolumeConnectorsController.post')
|
||||||
@ -238,7 +244,23 @@ class VolumeConnectorsController(rest.RestController):
|
|||||||
same UUID already exists
|
same UUID already exists
|
||||||
"""
|
"""
|
||||||
context = api.request.context
|
context = api.request.context
|
||||||
api_utils.check_policy('baremetal:volume:create')
|
owner = None
|
||||||
|
lessee = None
|
||||||
|
raise_node_not_found = False
|
||||||
|
node_uuid = connector.get('node_uuid')
|
||||||
|
|
||||||
|
try:
|
||||||
|
node = api_utils.replace_node_uuid_with_id(connector)
|
||||||
|
owner = node.owner
|
||||||
|
lessee = node.lessee
|
||||||
|
except exception.NotFound:
|
||||||
|
raise_node_not_found = True
|
||||||
|
api_utils.check_owner_policy('node', 'baremetal:volume:create',
|
||||||
|
owner, lessee=lessee, conceal_node=False)
|
||||||
|
|
||||||
|
if raise_node_not_found:
|
||||||
|
raise exception.InvalidInput(fieldname='node_uuid',
|
||||||
|
value=node_uuid)
|
||||||
|
|
||||||
if self.parent_node_ident:
|
if self.parent_node_ident:
|
||||||
raise exception.OperationNotPermitted()
|
raise exception.OperationNotPermitted()
|
||||||
@ -247,8 +269,6 @@ class VolumeConnectorsController(rest.RestController):
|
|||||||
if not connector.get('uuid'):
|
if not connector.get('uuid'):
|
||||||
connector['uuid'] = uuidutils.generate_uuid()
|
connector['uuid'] = uuidutils.generate_uuid()
|
||||||
|
|
||||||
node = api_utils.replace_node_uuid_with_id(connector)
|
|
||||||
|
|
||||||
new_connector = objects.VolumeConnector(context, **connector)
|
new_connector = objects.VolumeConnector(context, **connector)
|
||||||
|
|
||||||
notify.emit_start_notification(context, new_connector, 'create',
|
notify.emit_start_notification(context, new_connector, 'create',
|
||||||
@ -294,7 +314,11 @@ class VolumeConnectorsController(rest.RestController):
|
|||||||
volume connector is not powered off.
|
volume connector is not powered off.
|
||||||
"""
|
"""
|
||||||
context = api.request.context
|
context = api.request.context
|
||||||
api_utils.check_policy('baremetal:volume:update')
|
|
||||||
|
rpc_connector, rpc_node = api_utils.check_volume_policy_and_retrieve(
|
||||||
|
'baremetal:volume:update',
|
||||||
|
connector_uuid,
|
||||||
|
target=False)
|
||||||
|
|
||||||
if self.parent_node_ident:
|
if self.parent_node_ident:
|
||||||
raise exception.OperationNotPermitted()
|
raise exception.OperationNotPermitted()
|
||||||
@ -307,9 +331,6 @@ class VolumeConnectorsController(rest.RestController):
|
|||||||
"%(uuid)s.") % {'uuid': str(value)}
|
"%(uuid)s.") % {'uuid': str(value)}
|
||||||
raise exception.InvalidUUID(message=message)
|
raise exception.InvalidUUID(message=message)
|
||||||
|
|
||||||
rpc_connector = objects.VolumeConnector.get_by_uuid(context,
|
|
||||||
connector_uuid)
|
|
||||||
|
|
||||||
connector_dict = rpc_connector.as_dict()
|
connector_dict = rpc_connector.as_dict()
|
||||||
# NOTE(smoriya):
|
# NOTE(smoriya):
|
||||||
# 1) Remove node_id because it's an internal value and
|
# 1) Remove node_id because it's an internal value and
|
||||||
@ -370,14 +391,14 @@ class VolumeConnectorsController(rest.RestController):
|
|||||||
volume connector is not powered off.
|
volume connector is not powered off.
|
||||||
"""
|
"""
|
||||||
context = api.request.context
|
context = api.request.context
|
||||||
api_utils.check_policy('baremetal:volume:delete')
|
|
||||||
|
|
||||||
|
rpc_connector, rpc_node = api_utils.check_volume_policy_and_retrieve(
|
||||||
|
'baremetal:volume:delete',
|
||||||
|
connector_uuid,
|
||||||
|
target=False)
|
||||||
if self.parent_node_ident:
|
if self.parent_node_ident:
|
||||||
raise exception.OperationNotPermitted()
|
raise exception.OperationNotPermitted()
|
||||||
|
|
||||||
rpc_connector = objects.VolumeConnector.get_by_uuid(context,
|
|
||||||
connector_uuid)
|
|
||||||
rpc_node = objects.Node.get_by_id(context, rpc_connector.node_id)
|
|
||||||
notify.emit_start_notification(context, rpc_connector, 'delete',
|
notify.emit_start_notification(context, rpc_connector, 'delete',
|
||||||
node_uuid=rpc_node.uuid)
|
node_uuid=rpc_node.uuid)
|
||||||
with notify.handle_error_notification(context, rpc_connector,
|
with notify.handle_error_notification(context, rpc_connector,
|
||||||
|
@ -27,6 +27,7 @@ from ironic.api import method
|
|||||||
from ironic.common import args
|
from ironic.common import args
|
||||||
from ironic.common import exception
|
from ironic.common import exception
|
||||||
from ironic.common.i18n import _
|
from ironic.common.i18n import _
|
||||||
|
from ironic.common import policy
|
||||||
from ironic import objects
|
from ironic import objects
|
||||||
|
|
||||||
METRICS = metrics_utils.get_metrics_logger(__name__)
|
METRICS = metrics_utils.get_metrics_logger(__name__)
|
||||||
@ -119,9 +120,22 @@ class VolumeTargetsController(rest.RestController):
|
|||||||
super(VolumeTargetsController, self).__init__()
|
super(VolumeTargetsController, self).__init__()
|
||||||
self.parent_node_ident = node_ident
|
self.parent_node_ident = node_ident
|
||||||
|
|
||||||
|
def _redact_target_properties(self, target):
|
||||||
|
# Filters what could contain sensitive information. For iSCSI
|
||||||
|
# volumes this can include iscsi connection details which may
|
||||||
|
# be sensitive.
|
||||||
|
redacted = ('** Value redacted: Requires permission '
|
||||||
|
'baremetal:volume:view_target_properties '
|
||||||
|
'access. Permission denied. **')
|
||||||
|
redacted_message = {
|
||||||
|
'redacted_contents': redacted
|
||||||
|
}
|
||||||
|
target.properties = redacted_message
|
||||||
|
|
||||||
def _get_volume_targets_collection(self, node_ident, marker, limit,
|
def _get_volume_targets_collection(self, node_ident, marker, limit,
|
||||||
sort_key, sort_dir, resource_url=None,
|
sort_key, sort_dir, resource_url=None,
|
||||||
fields=None, detail=None):
|
fields=None, detail=None,
|
||||||
|
project=None):
|
||||||
limit = api_utils.validate_limit(limit)
|
limit = api_utils.validate_limit(limit)
|
||||||
sort_dir = api_utils.validate_sort_dir(sort_dir)
|
sort_dir = api_utils.validate_sort_dir(sort_dir)
|
||||||
|
|
||||||
@ -134,7 +148,6 @@ class VolumeTargetsController(rest.RestController):
|
|||||||
raise exception.InvalidParameterValue(
|
raise exception.InvalidParameterValue(
|
||||||
_("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})
|
||||||
|
|
||||||
node_ident = self.parent_node_ident or node_ident
|
node_ident = self.parent_node_ident or node_ident
|
||||||
|
|
||||||
if node_ident:
|
if node_ident:
|
||||||
@ -145,12 +158,19 @@ class VolumeTargetsController(rest.RestController):
|
|||||||
node = api_utils.get_rpc_node(node_ident)
|
node = api_utils.get_rpc_node(node_ident)
|
||||||
targets = objects.VolumeTarget.list_by_node_id(
|
targets = objects.VolumeTarget.list_by_node_id(
|
||||||
api.request.context, node.id, limit, marker_obj,
|
api.request.context, node.id, limit, marker_obj,
|
||||||
sort_key=sort_key, sort_dir=sort_dir)
|
sort_key=sort_key, sort_dir=sort_dir, project=project)
|
||||||
else:
|
else:
|
||||||
targets = objects.VolumeTarget.list(api.request.context,
|
targets = objects.VolumeTarget.list(api.request.context,
|
||||||
limit, marker_obj,
|
limit, marker_obj,
|
||||||
sort_key=sort_key,
|
sort_key=sort_key,
|
||||||
sort_dir=sort_dir)
|
sort_dir=sort_dir,
|
||||||
|
project=project)
|
||||||
|
cdict = api.request.context.to_policy_values()
|
||||||
|
if not policy.check_policy('baremetal:volume:view_target_properties',
|
||||||
|
cdict, cdict):
|
||||||
|
for target in targets:
|
||||||
|
self._redact_target_properties(target)
|
||||||
|
|
||||||
return list_convert_with_links(targets, limit,
|
return list_convert_with_links(targets, limit,
|
||||||
url=resource_url,
|
url=resource_url,
|
||||||
fields=fields,
|
fields=fields,
|
||||||
@ -165,7 +185,7 @@ class VolumeTargetsController(rest.RestController):
|
|||||||
sort_dir=args.string, fields=args.string_list,
|
sort_dir=args.string, fields=args.string_list,
|
||||||
detail=args.boolean)
|
detail=args.boolean)
|
||||||
def get_all(self, node=None, marker=None, limit=None, sort_key='id',
|
def get_all(self, node=None, marker=None, limit=None, sort_key='id',
|
||||||
sort_dir='asc', fields=None, detail=None):
|
sort_dir='asc', fields=None, detail=None, project=None):
|
||||||
"""Retrieve a list of volume targets.
|
"""Retrieve a list of volume targets.
|
||||||
|
|
||||||
:param node: UUID or name of a node, to get only volume targets
|
:param node: UUID or name of a node, to get only volume targets
|
||||||
@ -180,6 +200,8 @@ class VolumeTargetsController(rest.RestController):
|
|||||||
:param fields: Optional, a list with a specified set of fields
|
:param fields: Optional, a list with a specified set of fields
|
||||||
of the resource to be returned.
|
of the resource to be returned.
|
||||||
:param detail: Optional, whether to retrieve with detail.
|
:param detail: Optional, whether to retrieve with detail.
|
||||||
|
:param project: Optional, an associated node project (owner,
|
||||||
|
or lessee) to filter the query upon.
|
||||||
|
|
||||||
:returns: a list of volume targets, or an empty list if no volume
|
:returns: a list of volume targets, or an empty list if no volume
|
||||||
target is found.
|
target is found.
|
||||||
@ -188,8 +210,8 @@ class VolumeTargetsController(rest.RestController):
|
|||||||
:raises: InvalidParameterValue if sort key is invalid for sorting.
|
:raises: InvalidParameterValue if sort key is invalid for sorting.
|
||||||
:raises: InvalidParameterValue if both fields and detail are specified.
|
:raises: InvalidParameterValue if both fields and detail are specified.
|
||||||
"""
|
"""
|
||||||
api_utils.check_policy('baremetal:volume:get')
|
project = api_utils.check_volume_list_policy(
|
||||||
|
parent_node=self.parent_node_ident)
|
||||||
if fields is None and not detail:
|
if fields is None and not detail:
|
||||||
fields = _DEFAULT_RETURN_FIELDS
|
fields = _DEFAULT_RETURN_FIELDS
|
||||||
|
|
||||||
@ -202,7 +224,8 @@ class VolumeTargetsController(rest.RestController):
|
|||||||
sort_key, sort_dir,
|
sort_key, sort_dir,
|
||||||
resource_url=resource_url,
|
resource_url=resource_url,
|
||||||
fields=fields,
|
fields=fields,
|
||||||
detail=detail)
|
detail=detail,
|
||||||
|
project=project)
|
||||||
|
|
||||||
@METRICS.timer('VolumeTargetsController.get_one')
|
@METRICS.timer('VolumeTargetsController.get_one')
|
||||||
@method.expose()
|
@method.expose()
|
||||||
@ -220,13 +243,20 @@ class VolumeTargetsController(rest.RestController):
|
|||||||
node.
|
node.
|
||||||
:raises: VolumeTargetNotFound if no volume target with this UUID exists
|
:raises: VolumeTargetNotFound if no volume target with this UUID exists
|
||||||
"""
|
"""
|
||||||
api_utils.check_policy('baremetal:volume:get')
|
|
||||||
|
rpc_target, _ = api_utils.check_volume_policy_and_retrieve(
|
||||||
|
'baremetal:volume:get',
|
||||||
|
target_uuid,
|
||||||
|
target=True)
|
||||||
|
|
||||||
if self.parent_node_ident:
|
if self.parent_node_ident:
|
||||||
raise exception.OperationNotPermitted()
|
raise exception.OperationNotPermitted()
|
||||||
|
|
||||||
rpc_target = objects.VolumeTarget.get_by_uuid(
|
cdict = api.request.context.to_policy_values()
|
||||||
api.request.context, target_uuid)
|
if not policy.check_policy('baremetal:volume:view_target_properties',
|
||||||
|
cdict, cdict):
|
||||||
|
self._redact_target_properties(rpc_target)
|
||||||
|
|
||||||
return convert_with_links(rpc_target, fields=fields)
|
return convert_with_links(rpc_target, fields=fields)
|
||||||
|
|
||||||
@METRICS.timer('VolumeTargetsController.post')
|
@METRICS.timer('VolumeTargetsController.post')
|
||||||
@ -248,7 +278,23 @@ class VolumeTargetsController(rest.RestController):
|
|||||||
UUID exists
|
UUID exists
|
||||||
"""
|
"""
|
||||||
context = api.request.context
|
context = api.request.context
|
||||||
api_utils.check_policy('baremetal:volume:create')
|
raise_node_not_found = False
|
||||||
|
node = None
|
||||||
|
owner = None
|
||||||
|
lessee = None
|
||||||
|
node_uuid = target.get('node_uuid')
|
||||||
|
try:
|
||||||
|
node = api_utils.replace_node_uuid_with_id(target)
|
||||||
|
owner = node.owner
|
||||||
|
lessee = node.lessee
|
||||||
|
except exception.NotFound:
|
||||||
|
raise_node_not_found = True
|
||||||
|
api_utils.check_owner_policy('node', 'baremetal:volume:create',
|
||||||
|
owner, lessee=lessee,
|
||||||
|
conceal_node=False)
|
||||||
|
if raise_node_not_found:
|
||||||
|
raise exception.InvalidInput(fieldname='node_uuid',
|
||||||
|
value=node_uuid)
|
||||||
|
|
||||||
if self.parent_node_ident:
|
if self.parent_node_ident:
|
||||||
raise exception.OperationNotPermitted()
|
raise exception.OperationNotPermitted()
|
||||||
@ -256,9 +302,6 @@ class VolumeTargetsController(rest.RestController):
|
|||||||
# NOTE(hshiina): UUID is mandatory for notification payload
|
# NOTE(hshiina): UUID is mandatory for notification payload
|
||||||
if not target.get('uuid'):
|
if not target.get('uuid'):
|
||||||
target['uuid'] = uuidutils.generate_uuid()
|
target['uuid'] = uuidutils.generate_uuid()
|
||||||
|
|
||||||
node = api_utils.replace_node_uuid_with_id(target)
|
|
||||||
|
|
||||||
new_target = objects.VolumeTarget(context, **target)
|
new_target = objects.VolumeTarget(context, **target)
|
||||||
|
|
||||||
notify.emit_start_notification(context, new_target, 'create',
|
notify.emit_start_notification(context, new_target, 'create',
|
||||||
@ -301,7 +344,10 @@ class VolumeTargetsController(rest.RestController):
|
|||||||
volume target is not powered off.
|
volume target is not powered off.
|
||||||
"""
|
"""
|
||||||
context = api.request.context
|
context = api.request.context
|
||||||
api_utils.check_policy('baremetal:volume:update')
|
|
||||||
|
api_utils.check_volume_policy_and_retrieve('baremetal:volume:update',
|
||||||
|
target_uuid,
|
||||||
|
target=True)
|
||||||
|
|
||||||
if self.parent_node_ident:
|
if self.parent_node_ident:
|
||||||
raise exception.OperationNotPermitted()
|
raise exception.OperationNotPermitted()
|
||||||
@ -327,6 +373,10 @@ class VolumeTargetsController(rest.RestController):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
if target_dict['node_uuid'] != rpc_node.uuid:
|
if target_dict['node_uuid'] != rpc_node.uuid:
|
||||||
|
|
||||||
|
# TODO(TheJulia): I guess the intention is to
|
||||||
|
# permit the mapping to be changed
|
||||||
|
# should we even allow this at all?
|
||||||
rpc_node = objects.Node.get(
|
rpc_node = objects.Node.get(
|
||||||
api.request.context, target_dict['node_uuid'])
|
api.request.context, target_dict['node_uuid'])
|
||||||
except exception.NodeNotFound as e:
|
except exception.NodeNotFound as e:
|
||||||
@ -374,7 +424,10 @@ class VolumeTargetsController(rest.RestController):
|
|||||||
volume target is not powered off.
|
volume target is not powered off.
|
||||||
"""
|
"""
|
||||||
context = api.request.context
|
context = api.request.context
|
||||||
api_utils.check_policy('baremetal:volume:delete')
|
|
||||||
|
api_utils.check_volume_policy_and_retrieve('baremetal:volume:delete',
|
||||||
|
target_uuid,
|
||||||
|
target=True)
|
||||||
|
|
||||||
if self.parent_node_ident:
|
if self.parent_node_ident:
|
||||||
raise exception.OperationNotPermitted()
|
raise exception.OperationNotPermitted()
|
||||||
|
@ -112,7 +112,18 @@ SYSTEM_OR_OWNER_READER = (
|
|||||||
'(' + SYSTEM_READER + ') or (' + PROJECT_OWNER_READER + ')'
|
'(' + SYSTEM_READER + ') or (' + PROJECT_OWNER_READER + ')'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
SYSTEM_MEMBER_OR_OWNER_LESSEE_ADMIN = (
|
||||||
|
'(' + SYSTEM_MEMBER + ') or (' + PROJECT_OWNER_ADMIN + ') or (' + PROJECT_LESSEE_ADMIN + ')' # noqa
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Special purpose aliases for things like "ability to access the API
|
||||||
|
# as a reader, or permission checking that does not require node
|
||||||
|
# owner relationship checking
|
||||||
API_READER = ('role:reader')
|
API_READER = ('role:reader')
|
||||||
|
TARGET_PROPERTIES_READER = (
|
||||||
|
'(' + SYSTEM_READER + ') or (role:admin)'
|
||||||
|
)
|
||||||
|
|
||||||
default_policies = [
|
default_policies = [
|
||||||
# Legacy setting, don't remove. Likely to be overridden by operators who
|
# Legacy setting, don't remove. Likely to be overridden by operators who
|
||||||
@ -1339,9 +1350,40 @@ roles.
|
|||||||
|
|
||||||
volume_policies = [
|
volume_policies = [
|
||||||
policy.DocumentedRuleDefault(
|
policy.DocumentedRuleDefault(
|
||||||
name='baremetal:volume:get',
|
name='baremetal:volume:list_all',
|
||||||
check_str=SYSTEM_READER,
|
check_str=SYSTEM_READER,
|
||||||
scope_types=['system'],
|
scope_types=['system', 'project'],
|
||||||
|
description=('Retrieve a list of all Volume connector and target '
|
||||||
|
'records'),
|
||||||
|
operations=[
|
||||||
|
{'path': '/volume/connectors', 'method': 'GET'},
|
||||||
|
{'path': '/volume/targets', 'method': 'GET'},
|
||||||
|
{'path': '/nodes/{node_ident}/volume/connectors', 'method': 'GET'},
|
||||||
|
{'path': '/nodes/{node_ident}/volume/targets', 'method': 'GET'}
|
||||||
|
],
|
||||||
|
deprecated_rule=deprecated_volume_get,
|
||||||
|
deprecated_reason=deprecated_volume_reason,
|
||||||
|
deprecated_since=versionutils.deprecated.WALLABY
|
||||||
|
),
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='baremetal:volume:list',
|
||||||
|
check_str=API_READER,
|
||||||
|
scope_types=['system', 'project'],
|
||||||
|
description='Retrieve a list of Volume connector and target records',
|
||||||
|
operations=[
|
||||||
|
{'path': '/volume/connectors', 'method': 'GET'},
|
||||||
|
{'path': '/volume/targets', 'method': 'GET'},
|
||||||
|
{'path': '/nodes/{node_ident}/volume/connectors', 'method': 'GET'},
|
||||||
|
{'path': '/nodes/{node_ident}/volume/targets', 'method': 'GET'}
|
||||||
|
],
|
||||||
|
deprecated_rule=deprecated_volume_get,
|
||||||
|
deprecated_reason=deprecated_volume_reason,
|
||||||
|
deprecated_since=versionutils.deprecated.WALLABY
|
||||||
|
),
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='baremetal:volume:get',
|
||||||
|
check_str=SYSTEM_OR_PROJECT_READER,
|
||||||
|
scope_types=['system', 'project'],
|
||||||
description='Retrieve Volume connector and target records',
|
description='Retrieve Volume connector and target records',
|
||||||
operations=[
|
operations=[
|
||||||
{'path': '/volume', 'method': 'GET'},
|
{'path': '/volume', 'method': 'GET'},
|
||||||
@ -1360,8 +1402,8 @@ volume_policies = [
|
|||||||
),
|
),
|
||||||
policy.DocumentedRuleDefault(
|
policy.DocumentedRuleDefault(
|
||||||
name='baremetal:volume:create',
|
name='baremetal:volume:create',
|
||||||
check_str=SYSTEM_MEMBER,
|
check_str=SYSTEM_MEMBER_OR_OWNER_LESSEE_ADMIN,
|
||||||
scope_types=['system'],
|
scope_types=['system', 'project'],
|
||||||
description='Create Volume connector and target records',
|
description='Create Volume connector and target records',
|
||||||
operations=[
|
operations=[
|
||||||
{'path': '/volume/connectors', 'method': 'POST'},
|
{'path': '/volume/connectors', 'method': 'POST'},
|
||||||
@ -1373,8 +1415,8 @@ volume_policies = [
|
|||||||
),
|
),
|
||||||
policy.DocumentedRuleDefault(
|
policy.DocumentedRuleDefault(
|
||||||
name='baremetal:volume:delete',
|
name='baremetal:volume:delete',
|
||||||
check_str=SYSTEM_MEMBER,
|
check_str=SYSTEM_MEMBER_OR_OWNER_LESSEE_ADMIN,
|
||||||
scope_types=['system'],
|
scope_types=['system', 'project'],
|
||||||
description='Delete Volume connector and target records',
|
description='Delete Volume connector and target records',
|
||||||
operations=[
|
operations=[
|
||||||
{'path': '/volume/connectors/{volume_connector_id}',
|
{'path': '/volume/connectors/{volume_connector_id}',
|
||||||
@ -1388,8 +1430,8 @@ volume_policies = [
|
|||||||
),
|
),
|
||||||
policy.DocumentedRuleDefault(
|
policy.DocumentedRuleDefault(
|
||||||
name='baremetal:volume:update',
|
name='baremetal:volume:update',
|
||||||
check_str=SYSTEM_MEMBER,
|
check_str=SYSTEM_OR_OWNER_MEMBER_AND_LESSEE_ADMIN,
|
||||||
scope_types=['system'],
|
scope_types=['system', 'project'],
|
||||||
description='Update Volume connector and target records',
|
description='Update Volume connector and target records',
|
||||||
operations=[
|
operations=[
|
||||||
{'path': '/volume/connectors/{volume_connector_id}',
|
{'path': '/volume/connectors/{volume_connector_id}',
|
||||||
@ -1401,6 +1443,21 @@ volume_policies = [
|
|||||||
deprecated_reason=deprecated_volume_reason,
|
deprecated_reason=deprecated_volume_reason,
|
||||||
deprecated_since=versionutils.deprecated.WALLABY
|
deprecated_since=versionutils.deprecated.WALLABY
|
||||||
),
|
),
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='baremetal:volume:view_target_properties',
|
||||||
|
check_str=TARGET_PROPERTIES_READER,
|
||||||
|
scope_types=['system', 'project'],
|
||||||
|
description='Ability to view volume target properties',
|
||||||
|
operations=[
|
||||||
|
{'path': '/volume/connectors/{volume_connector_id}',
|
||||||
|
'method': 'GET'},
|
||||||
|
{'path': '/volume/targets/{volume_target_id}',
|
||||||
|
'method': 'GET'}
|
||||||
|
],
|
||||||
|
deprecated_rule=deprecated_volume_update,
|
||||||
|
deprecated_reason=deprecated_volume_reason,
|
||||||
|
deprecated_since=versionutils.deprecated.WALLABY
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -714,7 +714,8 @@ class Connection(object, metaclass=abc.ABCMeta):
|
|||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def get_volume_connector_list(self, limit=None, marker=None,
|
def get_volume_connector_list(self, limit=None, marker=None,
|
||||||
sort_key=None, sort_dir=None):
|
sort_key=None, sort_dir=None,
|
||||||
|
project=None):
|
||||||
"""Return a list of volume connectors.
|
"""Return a list of volume connectors.
|
||||||
|
|
||||||
:param limit: Maximum number of volume connectors to return.
|
:param limit: Maximum number of volume connectors to return.
|
||||||
@ -723,6 +724,8 @@ class Connection(object, metaclass=abc.ABCMeta):
|
|||||||
:param sort_key: Attribute by which results should be sorted.
|
:param sort_key: Attribute by which results should be sorted.
|
||||||
:param sort_dir: Direction in which results should be sorted.
|
:param sort_dir: Direction in which results should be sorted.
|
||||||
(asc, desc)
|
(asc, desc)
|
||||||
|
:param project: The associated node project to search with.
|
||||||
|
:returns: a list of :class:`VolumeConnector` objects
|
||||||
:returns: A list of volume connectors.
|
:returns: A list of volume connectors.
|
||||||
:raises: InvalidParameterValue If sort_key does not exist.
|
:raises: InvalidParameterValue If sort_key does not exist.
|
||||||
"""
|
"""
|
||||||
@ -750,7 +753,7 @@ class Connection(object, metaclass=abc.ABCMeta):
|
|||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def get_volume_connectors_by_node_id(self, node_id, limit=None,
|
def get_volume_connectors_by_node_id(self, node_id, limit=None,
|
||||||
marker=None, sort_key=None,
|
marker=None, sort_key=None,
|
||||||
sort_dir=None):
|
sort_dir=None, project=None):
|
||||||
"""List all the volume connectors for a given node.
|
"""List all the volume connectors for a given node.
|
||||||
|
|
||||||
:param node_id: The integer node ID.
|
:param node_id: The integer node ID.
|
||||||
@ -760,6 +763,8 @@ class Connection(object, metaclass=abc.ABCMeta):
|
|||||||
:param sort_key: Attribute by which results should be sorted
|
:param sort_key: Attribute by which results should be sorted
|
||||||
:param sort_dir: Direction in which results should be sorted
|
:param sort_dir: Direction in which results should be sorted
|
||||||
(asc, desc)
|
(asc, desc)
|
||||||
|
:param project: The associated node project to search with.
|
||||||
|
:returns: a list of :class:`VolumeConnector` objects
|
||||||
:returns: A list of volume connectors.
|
:returns: A list of volume connectors.
|
||||||
:raises: InvalidParameterValue If sort_key does not exist.
|
:raises: InvalidParameterValue If sort_key does not exist.
|
||||||
"""
|
"""
|
||||||
@ -813,7 +818,8 @@ class Connection(object, metaclass=abc.ABCMeta):
|
|||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def get_volume_target_list(self, limit=None, marker=None,
|
def get_volume_target_list(self, limit=None, marker=None,
|
||||||
sort_key=None, sort_dir=None):
|
sort_key=None, sort_dir=None,
|
||||||
|
project=None):
|
||||||
"""Return a list of volume targets.
|
"""Return a list of volume targets.
|
||||||
|
|
||||||
:param limit: Maximum number of volume targets to return.
|
:param limit: Maximum number of volume targets to return.
|
||||||
@ -822,6 +828,8 @@ class Connection(object, metaclass=abc.ABCMeta):
|
|||||||
:param sort_key: Attribute by which results should be sorted.
|
:param sort_key: Attribute by which results should be sorted.
|
||||||
:param sort_dir: direction in which results should be sorted.
|
:param sort_dir: direction in which results should be sorted.
|
||||||
(asc, desc)
|
(asc, desc)
|
||||||
|
:param project: The associated node project to search with.
|
||||||
|
:returns: a list of :class:`VolumeConnector` objects
|
||||||
:returns: A list of volume targets.
|
:returns: A list of volume targets.
|
||||||
:raises: InvalidParameterValue if sort_key does not exist.
|
:raises: InvalidParameterValue if sort_key does not exist.
|
||||||
"""
|
"""
|
||||||
@ -849,7 +857,7 @@ class Connection(object, metaclass=abc.ABCMeta):
|
|||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def get_volume_targets_by_node_id(self, node_id, limit=None,
|
def get_volume_targets_by_node_id(self, node_id, limit=None,
|
||||||
marker=None, sort_key=None,
|
marker=None, sort_key=None,
|
||||||
sort_dir=None):
|
sort_dir=None, project=None):
|
||||||
"""List all the volume targets for a given node.
|
"""List all the volume targets for a given node.
|
||||||
|
|
||||||
:param node_id: The integer node ID.
|
:param node_id: The integer node ID.
|
||||||
@ -859,6 +867,8 @@ class Connection(object, metaclass=abc.ABCMeta):
|
|||||||
:param sort_key: Attribute by which results should be sorted
|
:param sort_key: Attribute by which results should be sorted
|
||||||
:param sort_dir: direction in which results should be sorted
|
:param sort_dir: direction in which results should be sorted
|
||||||
(asc, desc)
|
(asc, desc)
|
||||||
|
:param project: The associated node project to search with.
|
||||||
|
:returns: a list of :class:`VolumeConnector` objects
|
||||||
:returns: A list of volume targets.
|
:returns: A list of volume targets.
|
||||||
:raises: InvalidParameterValue if sort_key does not exist.
|
:raises: InvalidParameterValue if sort_key does not exist.
|
||||||
"""
|
"""
|
||||||
@ -866,7 +876,7 @@ class Connection(object, metaclass=abc.ABCMeta):
|
|||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def get_volume_targets_by_volume_id(self, volume_id, limit=None,
|
def get_volume_targets_by_volume_id(self, volume_id, limit=None,
|
||||||
marker=None, sort_key=None,
|
marker=None, sort_key=None,
|
||||||
sort_dir=None):
|
sort_dir=None, project=None):
|
||||||
"""List all the volume targets for a given volume id.
|
"""List all the volume targets for a given volume id.
|
||||||
|
|
||||||
:param volume_id: The UUID of the volume.
|
:param volume_id: The UUID of the volume.
|
||||||
|
@ -169,6 +169,20 @@ def add_portgroup_filter_by_node_project(query, value):
|
|||||||
| (models.Node.lessee == value))
|
| (models.Node.lessee == value))
|
||||||
|
|
||||||
|
|
||||||
|
def add_volume_conn_filter_by_node_project(query, value):
|
||||||
|
query = query.join(models.Node,
|
||||||
|
models.VolumeConnector.node_id == models.Node.id)
|
||||||
|
return query.filter((models.Node.owner == value)
|
||||||
|
| (models.Node.lessee == value))
|
||||||
|
|
||||||
|
|
||||||
|
def add_volume_target_filter_by_node_project(query, value):
|
||||||
|
query = query.join(models.Node,
|
||||||
|
models.VolumeTarget.node_id == models.Node.id)
|
||||||
|
return query.filter((models.Node.owner == value)
|
||||||
|
| (models.Node.lessee == value))
|
||||||
|
|
||||||
|
|
||||||
def add_portgroup_filter(query, value):
|
def add_portgroup_filter(query, value):
|
||||||
"""Adds a portgroup-specific filter to a query.
|
"""Adds a portgroup-specific filter to a query.
|
||||||
|
|
||||||
@ -1235,9 +1249,12 @@ class Connection(api.Connection):
|
|||||||
% addresses)
|
% addresses)
|
||||||
|
|
||||||
def get_volume_connector_list(self, limit=None, marker=None,
|
def get_volume_connector_list(self, limit=None, marker=None,
|
||||||
sort_key=None, sort_dir=None):
|
sort_key=None, sort_dir=None, project=None):
|
||||||
|
query = model_query(models.VolumeConnector)
|
||||||
|
if project:
|
||||||
|
query = add_volume_conn_filter_by_node_project(query, project)
|
||||||
return _paginate_query(models.VolumeConnector, limit, marker,
|
return _paginate_query(models.VolumeConnector, limit, marker,
|
||||||
sort_key, sort_dir)
|
sort_key, sort_dir, query)
|
||||||
|
|
||||||
def get_volume_connector_by_id(self, db_id):
|
def get_volume_connector_by_id(self, db_id):
|
||||||
query = model_query(models.VolumeConnector).filter_by(id=db_id)
|
query = model_query(models.VolumeConnector).filter_by(id=db_id)
|
||||||
@ -1256,8 +1273,10 @@ class Connection(api.Connection):
|
|||||||
|
|
||||||
def get_volume_connectors_by_node_id(self, node_id, limit=None,
|
def get_volume_connectors_by_node_id(self, node_id, limit=None,
|
||||||
marker=None, sort_key=None,
|
marker=None, sort_key=None,
|
||||||
sort_dir=None):
|
sort_dir=None, project=None):
|
||||||
query = model_query(models.VolumeConnector).filter_by(node_id=node_id)
|
query = model_query(models.VolumeConnector).filter_by(node_id=node_id)
|
||||||
|
if project:
|
||||||
|
add_volume_conn_filter_by_node_project(query, project)
|
||||||
return _paginate_query(models.VolumeConnector, limit, marker,
|
return _paginate_query(models.VolumeConnector, limit, marker,
|
||||||
sort_key, sort_dir, query)
|
sort_key, sort_dir, query)
|
||||||
|
|
||||||
@ -1315,9 +1334,12 @@ class Connection(api.Connection):
|
|||||||
raise exception.VolumeConnectorNotFound(connector=ident)
|
raise exception.VolumeConnectorNotFound(connector=ident)
|
||||||
|
|
||||||
def get_volume_target_list(self, limit=None, marker=None,
|
def get_volume_target_list(self, limit=None, marker=None,
|
||||||
sort_key=None, sort_dir=None):
|
sort_key=None, sort_dir=None, project=None):
|
||||||
|
query = model_query(models.VolumeTarget)
|
||||||
|
if project:
|
||||||
|
query = add_volume_target_filter_by_node_project(query, project)
|
||||||
return _paginate_query(models.VolumeTarget, limit, marker,
|
return _paginate_query(models.VolumeTarget, limit, marker,
|
||||||
sort_key, sort_dir)
|
sort_key, sort_dir, query)
|
||||||
|
|
||||||
def get_volume_target_by_id(self, db_id):
|
def get_volume_target_by_id(self, db_id):
|
||||||
query = model_query(models.VolumeTarget).filter_by(id=db_id)
|
query = model_query(models.VolumeTarget).filter_by(id=db_id)
|
||||||
@ -1334,15 +1356,20 @@ class Connection(api.Connection):
|
|||||||
raise exception.VolumeTargetNotFound(target=uuid)
|
raise exception.VolumeTargetNotFound(target=uuid)
|
||||||
|
|
||||||
def get_volume_targets_by_node_id(self, node_id, limit=None, marker=None,
|
def get_volume_targets_by_node_id(self, node_id, limit=None, marker=None,
|
||||||
sort_key=None, sort_dir=None):
|
sort_key=None, sort_dir=None,
|
||||||
|
project=None):
|
||||||
query = model_query(models.VolumeTarget).filter_by(node_id=node_id)
|
query = model_query(models.VolumeTarget).filter_by(node_id=node_id)
|
||||||
|
if project:
|
||||||
|
add_volume_target_filter_by_node_project(query, project)
|
||||||
return _paginate_query(models.VolumeTarget, limit, marker, sort_key,
|
return _paginate_query(models.VolumeTarget, limit, marker, sort_key,
|
||||||
sort_dir, query)
|
sort_dir, query)
|
||||||
|
|
||||||
def get_volume_targets_by_volume_id(self, volume_id, limit=None,
|
def get_volume_targets_by_volume_id(self, volume_id, limit=None,
|
||||||
marker=None, sort_key=None,
|
marker=None, sort_key=None,
|
||||||
sort_dir=None):
|
sort_dir=None, project=None):
|
||||||
query = model_query(models.VolumeTarget).filter_by(volume_id=volume_id)
|
query = model_query(models.VolumeTarget).filter_by(volume_id=volume_id)
|
||||||
|
if project:
|
||||||
|
query = add_volume_target_filter_by_node_project(query, project)
|
||||||
return _paginate_query(models.VolumeTarget, limit, marker, sort_key,
|
return _paginate_query(models.VolumeTarget, limit, marker, sort_key,
|
||||||
sort_dir, query)
|
sort_dir, query)
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ class VolumeConnector(base.IronicObject,
|
|||||||
# @object_base.remotable_classmethod
|
# @object_base.remotable_classmethod
|
||||||
@classmethod
|
@classmethod
|
||||||
def list(cls, context, limit=None, marker=None,
|
def list(cls, context, limit=None, marker=None,
|
||||||
sort_key=None, sort_dir=None):
|
sort_key=None, sort_dir=None, project=None):
|
||||||
"""Return a list of VolumeConnector objects.
|
"""Return a list of VolumeConnector objects.
|
||||||
|
|
||||||
:param context: security context
|
:param context: security context
|
||||||
@ -116,13 +116,15 @@ class VolumeConnector(base.IronicObject,
|
|||||||
:param marker: pagination marker for large data sets
|
:param marker: pagination marker for large data sets
|
||||||
:param sort_key: column to sort results by
|
:param sort_key: column to sort results by
|
||||||
:param sort_dir: direction to sort. "asc" or "desc".
|
:param sort_dir: direction to sort. "asc" or "desc".
|
||||||
|
:param project: The associated node project to search with.
|
||||||
:returns: a list of :class:`VolumeConnector` objects
|
:returns: a list of :class:`VolumeConnector` objects
|
||||||
:raises: InvalidParameterValue if sort_key does not exist
|
:raises: InvalidParameterValue if sort_key does not exist
|
||||||
"""
|
"""
|
||||||
db_connectors = cls.dbapi.get_volume_connector_list(limit=limit,
|
db_connectors = cls.dbapi.get_volume_connector_list(limit=limit,
|
||||||
marker=marker,
|
marker=marker,
|
||||||
sort_key=sort_key,
|
sort_key=sort_key,
|
||||||
sort_dir=sort_dir)
|
sort_dir=sort_dir,
|
||||||
|
project=project)
|
||||||
return cls._from_db_object_list(context, db_connectors)
|
return cls._from_db_object_list(context, db_connectors)
|
||||||
|
|
||||||
# NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
|
# NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
|
||||||
@ -131,7 +133,7 @@ class VolumeConnector(base.IronicObject,
|
|||||||
# @object_base.remotable_classmethod
|
# @object_base.remotable_classmethod
|
||||||
@classmethod
|
@classmethod
|
||||||
def list_by_node_id(cls, context, node_id, limit=None, marker=None,
|
def list_by_node_id(cls, context, node_id, limit=None, marker=None,
|
||||||
sort_key=None, sort_dir=None):
|
sort_key=None, sort_dir=None, project=None):
|
||||||
"""Return a list of VolumeConnector objects related to a given node ID.
|
"""Return a list of VolumeConnector objects related to a given node ID.
|
||||||
|
|
||||||
:param context: security context
|
:param context: security context
|
||||||
@ -140,6 +142,8 @@ class VolumeConnector(base.IronicObject,
|
|||||||
:param marker: pagination marker for large data sets
|
:param marker: pagination marker for large data sets
|
||||||
:param sort_key: column to sort results by
|
:param sort_key: column to sort results by
|
||||||
:param sort_dir: direction to sort. "asc" or "desc".
|
:param sort_dir: direction to sort. "asc" or "desc".
|
||||||
|
:param project: The associated node project to search with.
|
||||||
|
:returns: a list of :class:`VolumeConnector` objects
|
||||||
:returns: a list of :class:`VolumeConnector` objects
|
:returns: a list of :class:`VolumeConnector` objects
|
||||||
:raises: InvalidParameterValue if sort_key does not exist
|
:raises: InvalidParameterValue if sort_key does not exist
|
||||||
"""
|
"""
|
||||||
@ -148,7 +152,8 @@ class VolumeConnector(base.IronicObject,
|
|||||||
limit=limit,
|
limit=limit,
|
||||||
marker=marker,
|
marker=marker,
|
||||||
sort_key=sort_key,
|
sort_key=sort_key,
|
||||||
sort_dir=sort_dir)
|
sort_dir=sort_dir,
|
||||||
|
project=project)
|
||||||
return cls._from_db_object_list(context, db_connectors)
|
return cls._from_db_object_list(context, db_connectors)
|
||||||
|
|
||||||
# NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
|
# NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
|
||||||
|
@ -107,7 +107,7 @@ class VolumeTarget(base.IronicObject,
|
|||||||
# @object_base.remotable_classmethod
|
# @object_base.remotable_classmethod
|
||||||
@classmethod
|
@classmethod
|
||||||
def list(cls, context, limit=None, marker=None,
|
def list(cls, context, limit=None, marker=None,
|
||||||
sort_key=None, sort_dir=None):
|
sort_key=None, sort_dir=None, project=None):
|
||||||
"""Return a list of VolumeTarget objects.
|
"""Return a list of VolumeTarget objects.
|
||||||
|
|
||||||
:param context: security context
|
:param context: security context
|
||||||
@ -115,13 +115,16 @@ class VolumeTarget(base.IronicObject,
|
|||||||
:param marker: pagination marker for large data sets
|
:param marker: pagination marker for large data sets
|
||||||
:param sort_key: column to sort results by
|
:param sort_key: column to sort results by
|
||||||
:param sort_dir: direction to sort. "asc" or "desc".
|
:param sort_dir: direction to sort. "asc" or "desc".
|
||||||
|
:param project: The associated node project to search with.
|
||||||
|
:returns: a list of :class:`VolumeConnector` objects
|
||||||
:returns: a list of :class:`VolumeTarget` objects
|
:returns: a list of :class:`VolumeTarget` objects
|
||||||
:raises: InvalidParameterValue if sort_key does not exist
|
:raises: InvalidParameterValue if sort_key does not exist
|
||||||
"""
|
"""
|
||||||
db_targets = cls.dbapi.get_volume_target_list(limit=limit,
|
db_targets = cls.dbapi.get_volume_target_list(limit=limit,
|
||||||
marker=marker,
|
marker=marker,
|
||||||
sort_key=sort_key,
|
sort_key=sort_key,
|
||||||
sort_dir=sort_dir)
|
sort_dir=sort_dir,
|
||||||
|
project=project)
|
||||||
return cls._from_db_object_list(context, db_targets)
|
return cls._from_db_object_list(context, db_targets)
|
||||||
|
|
||||||
# NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
|
# NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
|
||||||
@ -130,7 +133,7 @@ class VolumeTarget(base.IronicObject,
|
|||||||
# @object_base.remotable_classmethod
|
# @object_base.remotable_classmethod
|
||||||
@classmethod
|
@classmethod
|
||||||
def list_by_node_id(cls, context, node_id, limit=None, marker=None,
|
def list_by_node_id(cls, context, node_id, limit=None, marker=None,
|
||||||
sort_key=None, sort_dir=None):
|
sort_key=None, sort_dir=None, project=None):
|
||||||
"""Return a list of VolumeTarget objects related to a given node ID.
|
"""Return a list of VolumeTarget objects related to a given node ID.
|
||||||
|
|
||||||
:param context: security context
|
:param context: security context
|
||||||
@ -139,6 +142,8 @@ class VolumeTarget(base.IronicObject,
|
|||||||
:param marker: pagination marker for large data sets
|
:param marker: pagination marker for large data sets
|
||||||
:param sort_key: column to sort results by
|
:param sort_key: column to sort results by
|
||||||
:param sort_dir: direction to sort. "asc" or "desc".
|
:param sort_dir: direction to sort. "asc" or "desc".
|
||||||
|
:param project: The associated node project to search with.
|
||||||
|
:returns: a list of :class:`VolumeConnector` objects
|
||||||
:returns: a list of :class:`VolumeTarget` objects
|
:returns: a list of :class:`VolumeTarget` objects
|
||||||
:raises: InvalidParameterValue if sort_key does not exist
|
:raises: InvalidParameterValue if sort_key does not exist
|
||||||
"""
|
"""
|
||||||
@ -147,7 +152,8 @@ class VolumeTarget(base.IronicObject,
|
|||||||
limit=limit,
|
limit=limit,
|
||||||
marker=marker,
|
marker=marker,
|
||||||
sort_key=sort_key,
|
sort_key=sort_key,
|
||||||
sort_dir=sort_dir)
|
sort_dir=sort_dir,
|
||||||
|
project=project)
|
||||||
return cls._from_db_object_list(context, db_targets)
|
return cls._from_db_object_list(context, db_targets)
|
||||||
|
|
||||||
# NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
|
# NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
|
||||||
@ -156,7 +162,7 @@ class VolumeTarget(base.IronicObject,
|
|||||||
# @object_base.remotable_classmethod
|
# @object_base.remotable_classmethod
|
||||||
@classmethod
|
@classmethod
|
||||||
def list_by_volume_id(cls, context, volume_id, limit=None, marker=None,
|
def list_by_volume_id(cls, context, volume_id, limit=None, marker=None,
|
||||||
sort_key=None, sort_dir=None):
|
sort_key=None, sort_dir=None, project=None):
|
||||||
"""Return a list of VolumeTarget objects related to a given volume ID.
|
"""Return a list of VolumeTarget objects related to a given volume ID.
|
||||||
|
|
||||||
:param context: security context
|
:param context: security context
|
||||||
@ -174,7 +180,8 @@ class VolumeTarget(base.IronicObject,
|
|||||||
limit=limit,
|
limit=limit,
|
||||||
marker=marker,
|
marker=marker,
|
||||||
sort_key=sort_key,
|
sort_key=sort_key,
|
||||||
sort_dir=sort_dir)
|
sort_dir=sort_dir,
|
||||||
|
project=project)
|
||||||
return cls._from_db_object_list(context, db_targets)
|
return cls._from_db_object_list(context, db_targets)
|
||||||
|
|
||||||
# NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
|
# NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
|
||||||
|
@ -391,6 +391,13 @@ class TestRBACProjectScoped(TestACLBase):
|
|||||||
node_id=owned_node['id'],
|
node_id=owned_node['id'],
|
||||||
name='magicfoo',
|
name='magicfoo',
|
||||||
address='01:03:09:ff:01:01')
|
address='01:03:09:ff:01:01')
|
||||||
|
db_utils.create_test_volume_target(
|
||||||
|
uuid='a265e2f0-e97f-4177-b1c0-8298add53086',
|
||||||
|
node_id=owned_node['id'])
|
||||||
|
db_utils.create_test_volume_connector(
|
||||||
|
uuid='65ea0296-219b-4635-b0c8-a6e055da878d',
|
||||||
|
node_id=owned_node['id'],
|
||||||
|
connector_id='iqn.2012-06.org.openstack.magic')
|
||||||
|
|
||||||
# Leased nodes
|
# Leased nodes
|
||||||
leased_node = db_utils.create_test_node(
|
leased_node = db_utils.create_test_node(
|
||||||
|
@ -1315,9 +1315,9 @@ volume_connectors_post_admin:
|
|||||||
path: '/v1/volume/connectors'
|
path: '/v1/volume/connectors'
|
||||||
method: post
|
method: post
|
||||||
headers: *admin_headers
|
headers: *admin_headers
|
||||||
assert_status: 400
|
assert_status: 201
|
||||||
body: &volume_connector_body
|
body: &volume_connector_body
|
||||||
node_uuid: 68a552fb-dcd2-43bf-9302-e4c93287be16
|
node_uuid: 1be26c0b-03f2-4d2e-ae87-c02d7f33c123
|
||||||
type: ip
|
type: ip
|
||||||
connector_id: 192.168.1.100
|
connector_id: 192.168.1.100
|
||||||
deprecated: true
|
deprecated: true
|
||||||
@ -1349,7 +1349,7 @@ volume_volume_connector_id_get_member:
|
|||||||
path: '/v1/volume/connectors/{volume_connector_ident}'
|
path: '/v1/volume/connectors/{volume_connector_ident}'
|
||||||
method: get
|
method: get
|
||||||
headers: *member_headers
|
headers: *member_headers
|
||||||
assert_status: 403
|
assert_status: 404
|
||||||
deprecated: true
|
deprecated: true
|
||||||
|
|
||||||
volume_volume_connector_id_get_observer:
|
volume_volume_connector_id_get_observer:
|
||||||
@ -1375,7 +1375,7 @@ volume_volume_connector_id_patch_member:
|
|||||||
method: patch
|
method: patch
|
||||||
headers: *member_headers
|
headers: *member_headers
|
||||||
body: *connector_patch_body
|
body: *connector_patch_body
|
||||||
assert_status: 403
|
assert_status: 404
|
||||||
deprecated: true
|
deprecated: true
|
||||||
|
|
||||||
volume_volume_connector_id_patch_observer:
|
volume_volume_connector_id_patch_observer:
|
||||||
@ -1397,7 +1397,7 @@ volume_volume_connector_id_delete_member:
|
|||||||
path: '/v1/volume/connectors/{volume_connector_ident}'
|
path: '/v1/volume/connectors/{volume_connector_ident}'
|
||||||
method: delete
|
method: delete
|
||||||
headers: *member_headers
|
headers: *member_headers
|
||||||
assert_status: 403
|
assert_status: 404
|
||||||
deprecated: true
|
deprecated: true
|
||||||
|
|
||||||
volume_volume_connector_id_delete_observer:
|
volume_volume_connector_id_delete_observer:
|
||||||
@ -1437,11 +1437,11 @@ volume_targets_post_admin:
|
|||||||
path: '/v1/volume/targets'
|
path: '/v1/volume/targets'
|
||||||
method: post
|
method: post
|
||||||
headers: *admin_headers
|
headers: *admin_headers
|
||||||
assert_status: 400
|
assert_status: 201
|
||||||
body: &volume_target_body
|
body: &volume_target_body
|
||||||
node_uuid: 68a552fb-dcd2-43bf-9302-e4c93287be16
|
node_uuid: 1be26c0b-03f2-4d2e-ae87-c02d7f33c123
|
||||||
volume_type: iscsi
|
volume_type: iscsi
|
||||||
boot_index: 0
|
boot_index: 4
|
||||||
volume_id: 'test-id'
|
volume_id: 'test-id'
|
||||||
deprecated: true
|
deprecated: true
|
||||||
|
|
||||||
@ -1472,7 +1472,7 @@ volume_volume_target_id_get_member:
|
|||||||
path: '/v1/volume/targets/{volume_target_ident}'
|
path: '/v1/volume/targets/{volume_target_ident}'
|
||||||
method: get
|
method: get
|
||||||
headers: *member_headers
|
headers: *member_headers
|
||||||
assert_status: 403
|
assert_status: 404
|
||||||
deprecated: true
|
deprecated: true
|
||||||
|
|
||||||
volume_volume_target_id_get_observer:
|
volume_volume_target_id_get_observer:
|
||||||
@ -1493,12 +1493,12 @@ volume_volume_target_id_patch_admin:
|
|||||||
assert_status: 503
|
assert_status: 503
|
||||||
deprecated: true
|
deprecated: true
|
||||||
|
|
||||||
volume_volume_target_id_patch_admin:
|
volume_volume_target_id_patch_member:
|
||||||
path: '/v1/volume/targets/{volume_target_ident}'
|
path: '/v1/volume/targets/{volume_target_ident}'
|
||||||
method: patch
|
method: patch
|
||||||
body: *volume_target_patch
|
body: *volume_target_patch
|
||||||
headers: *member_headers
|
headers: *member_headers
|
||||||
assert_status: 403
|
assert_status: 404
|
||||||
deprecated: true
|
deprecated: true
|
||||||
|
|
||||||
volume_volume_target_id_patch_observer:
|
volume_volume_target_id_patch_observer:
|
||||||
@ -1520,7 +1520,7 @@ volume_volume_target_id_delete_member:
|
|||||||
path: '/v1/volume/targets/{volume_target_ident}'
|
path: '/v1/volume/targets/{volume_target_ident}'
|
||||||
method: delete
|
method: delete
|
||||||
headers: *member_headers
|
headers: *member_headers
|
||||||
assert_status: 403
|
assert_status: 404
|
||||||
deprecated: true
|
deprecated: true
|
||||||
|
|
||||||
volume_volume_target_id_delete_observer:
|
volume_volume_target_id_delete_observer:
|
||||||
@ -1564,7 +1564,7 @@ nodes_volume_connectors_get_member:
|
|||||||
path: '/v1/nodes/{node_ident}/volume/connectors'
|
path: '/v1/nodes/{node_ident}/volume/connectors'
|
||||||
method: get
|
method: get
|
||||||
headers: *member_headers
|
headers: *member_headers
|
||||||
assert_status: 403
|
assert_status: 404
|
||||||
deprecated: true
|
deprecated: true
|
||||||
|
|
||||||
nodes_volume_connectors_get_observer:
|
nodes_volume_connectors_get_observer:
|
||||||
@ -1585,7 +1585,7 @@ nodes_volume_targets_get_member:
|
|||||||
path: '/v1/nodes/{node_ident}/volume/targets'
|
path: '/v1/nodes/{node_ident}/volume/targets'
|
||||||
method: get
|
method: get
|
||||||
headers: *member_headers
|
headers: *member_headers
|
||||||
assert_status: 403
|
assert_status: 404
|
||||||
deprecated: true
|
deprecated: true
|
||||||
|
|
||||||
nodes_volume_targets_get_observer:
|
nodes_volume_targets_get_observer:
|
||||||
|
@ -1884,7 +1884,6 @@ owner_reader_can_list_volume_connectors:
|
|||||||
assert_status: 200
|
assert_status: 200
|
||||||
assert_list_length:
|
assert_list_length:
|
||||||
connectors: 2
|
connectors: 2
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
lessee_reader_can_list_volume_connectors:
|
lessee_reader_can_list_volume_connectors:
|
||||||
path: '/v1/volume/connectors'
|
path: '/v1/volume/connectors'
|
||||||
@ -1893,27 +1892,24 @@ lessee_reader_can_list_volume_connectors:
|
|||||||
assert_status: 200
|
assert_status: 200
|
||||||
assert_list_length:
|
assert_list_length:
|
||||||
connectors: 1
|
connectors: 1
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
third_party_admin_cannot_get_connector_list:
|
third_party_admin_cannot_get_connector_list:
|
||||||
path: '/v1/volume/targets'
|
path: '/v1/volume/connectors'
|
||||||
method: get
|
method: get
|
||||||
headers: *third_party_admin_headers
|
headers: *third_party_admin_headers
|
||||||
assert_status: 200
|
assert_status: 200
|
||||||
assert_list_length:
|
assert_list_length:
|
||||||
connectors: 0
|
connectors: 0
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
owner_admin_can_post_volume_connector:
|
owner_admin_can_post_volume_connector:
|
||||||
path: '/v1/volume/connectors'
|
path: '/v1/volume/connectors'
|
||||||
method: post
|
method: post
|
||||||
headers: *owner_reader_headers
|
headers: *owner_admin_headers
|
||||||
assert_status: 400
|
assert_status: 201
|
||||||
body: &volume_connector_body
|
body: &volume_connector_body
|
||||||
node_uuid: 68a552fb-dcd2-43bf-9302-e4c93287be16
|
node_uuid: 1ab63b9e-66d7-4cd7-8618-dddd0f9f7881
|
||||||
type: ip
|
type: ip
|
||||||
connector_id: 192.168.1.100
|
connector_id: 192.168.1.100
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
lessee_admin_cannot_post_volume_connector:
|
lessee_admin_cannot_post_volume_connector:
|
||||||
path: '/v1/volume/connectors'
|
path: '/v1/volume/connectors'
|
||||||
@ -1921,7 +1917,6 @@ lessee_admin_cannot_post_volume_connector:
|
|||||||
headers: *lessee_admin_headers
|
headers: *lessee_admin_headers
|
||||||
assert_status: 403
|
assert_status: 403
|
||||||
body: *volume_connector_body
|
body: *volume_connector_body
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
third_party_admin_cannot_post_volume_connector:
|
third_party_admin_cannot_post_volume_connector:
|
||||||
path: '/v1/volume/connectors'
|
path: '/v1/volume/connectors'
|
||||||
@ -1929,28 +1924,24 @@ third_party_admin_cannot_post_volume_connector:
|
|||||||
headers: *third_party_admin_headers
|
headers: *third_party_admin_headers
|
||||||
assert_status: 403
|
assert_status: 403
|
||||||
body: *volume_connector_body
|
body: *volume_connector_body
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
owner_reader_can_get_volume_connector:
|
owner_reader_can_get_volume_connector:
|
||||||
path: '/v1/volume/connectors/{volume_connector_ident}'
|
path: '/v1/volume/connectors/{volume_connector_ident}'
|
||||||
method: get
|
method: get
|
||||||
headers: *owner_reader_headers
|
headers: *owner_reader_headers
|
||||||
assert_status: 200
|
assert_status: 200
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
lessee_reader_can_get_volume_connector:
|
lessee_reader_can_get_volume_connector:
|
||||||
path: '/v1/volume/connectors/{volume_connector_ident}'
|
path: '/v1/volume/connectors/{volume_connector_ident}'
|
||||||
method: get
|
method: get
|
||||||
headers: *lessee_reader_headers
|
headers: *lessee_reader_headers
|
||||||
assert_status: 200
|
assert_status: 200
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
third_party_admin_cannot_get_volume_connector:
|
third_party_admin_cannot_get_volume_connector:
|
||||||
path: '/v1/volume/connectors/{volume_connector_ident}'
|
path: '/v1/volume/connectors/{volume_connector_ident}'
|
||||||
method: get
|
method: get
|
||||||
headers: *third_party_admin_headers
|
headers: *third_party_admin_headers
|
||||||
assert_status: 404
|
assert_status: 404
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
lessee_member_cannot_patch_volume_connectors:
|
lessee_member_cannot_patch_volume_connectors:
|
||||||
path: '/v1/volume/connectors/{volume_connector_ident}'
|
path: '/v1/volume/connectors/{volume_connector_ident}'
|
||||||
@ -1961,7 +1952,6 @@ lessee_member_cannot_patch_volume_connectors:
|
|||||||
path: /extra
|
path: /extra
|
||||||
value: {'test': 'testing'}
|
value: {'test': 'testing'}
|
||||||
assert_status: 403
|
assert_status: 403
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
owner_admin_can_patch_volume_connectors:
|
owner_admin_can_patch_volume_connectors:
|
||||||
path: '/v1/volume/connectors/{volume_connector_ident}'
|
path: '/v1/volume/connectors/{volume_connector_ident}'
|
||||||
@ -1969,7 +1959,6 @@ owner_admin_can_patch_volume_connectors:
|
|||||||
headers: *owner_member_headers
|
headers: *owner_member_headers
|
||||||
body: *connector_patch_body
|
body: *connector_patch_body
|
||||||
assert_status: 503
|
assert_status: 503
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
lessee_admin_cannot_patch_volume_connectors:
|
lessee_admin_cannot_patch_volume_connectors:
|
||||||
path: '/v1/volume/connectors/{volume_connector_ident}'
|
path: '/v1/volume/connectors/{volume_connector_ident}'
|
||||||
@ -1977,7 +1966,6 @@ lessee_admin_cannot_patch_volume_connectors:
|
|||||||
headers: *owner_member_headers
|
headers: *owner_member_headers
|
||||||
body: *connector_patch_body
|
body: *connector_patch_body
|
||||||
assert_status: 503
|
assert_status: 503
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
owner_member_can_patch_volume_connectors:
|
owner_member_can_patch_volume_connectors:
|
||||||
path: '/v1/volume/connectors/{volume_connector_ident}'
|
path: '/v1/volume/connectors/{volume_connector_ident}'
|
||||||
@ -1985,7 +1973,6 @@ owner_member_can_patch_volume_connectors:
|
|||||||
headers: *owner_member_headers
|
headers: *owner_member_headers
|
||||||
body: *connector_patch_body
|
body: *connector_patch_body
|
||||||
assert_status: 503
|
assert_status: 503
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
lessee_member_cannot_patch_volume_connectors:
|
lessee_member_cannot_patch_volume_connectors:
|
||||||
path: '/v1/volume/connectors/{volume_connector_ident}'
|
path: '/v1/volume/connectors/{volume_connector_ident}'
|
||||||
@ -1993,7 +1980,6 @@ lessee_member_cannot_patch_volume_connectors:
|
|||||||
headers: *lessee_member_headers
|
headers: *lessee_member_headers
|
||||||
body: *connector_patch_body
|
body: *connector_patch_body
|
||||||
assert_status: 403
|
assert_status: 403
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
third_party_admin_cannot_patch_volume_connectors:
|
third_party_admin_cannot_patch_volume_connectors:
|
||||||
path: '/v1/volume/connectors/{volume_connector_ident}'
|
path: '/v1/volume/connectors/{volume_connector_ident}'
|
||||||
@ -2001,28 +1987,24 @@ third_party_admin_cannot_patch_volume_connectors:
|
|||||||
headers: *third_party_admin_headers
|
headers: *third_party_admin_headers
|
||||||
body: *connector_patch_body
|
body: *connector_patch_body
|
||||||
assert_status: 404
|
assert_status: 404
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
owner_admin_can_delete_volume_connectors:
|
owner_admin_can_delete_volume_connectors:
|
||||||
path: '/v1/volume/connectors/{volume_connector_ident}'
|
path: '/v1/volume/connectors/{volume_connector_ident}'
|
||||||
method: delete
|
method: delete
|
||||||
headers: *owner_reader_headers
|
headers: *owner_reader_headers
|
||||||
assert_status: 403
|
assert_status: 403
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
lessee_admin_cannot_delete_volume_connectors:
|
lessee_admin_cannot_delete_volume_connectors:
|
||||||
path: '/v1/volume/connectors/{volume_connector_ident}'
|
path: '/v1/volume/connectors/{volume_connector_ident}'
|
||||||
method: delete
|
method: delete
|
||||||
headers: *lessee_reader_headers
|
headers: *lessee_reader_headers
|
||||||
assert_status: 403
|
assert_status: 403
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
third_party_admin_cannot_delete_volume_connector:
|
third_party_admin_cannot_delete_volume_connector:
|
||||||
path: '/v1/volume/connectors/{volume_connector_ident}'
|
path: '/v1/volume/connectors/{volume_connector_ident}'
|
||||||
method: delete
|
method: delete
|
||||||
headers: *third_party_admin_headers
|
headers: *third_party_admin_headers
|
||||||
assert_status: 403
|
assert_status: 404
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
# Volume targets
|
# Volume targets
|
||||||
|
|
||||||
@ -2034,7 +2016,6 @@ owner_reader_can_get_targets:
|
|||||||
assert_status: 200
|
assert_status: 200
|
||||||
assert_list_length:
|
assert_list_length:
|
||||||
targets: 2
|
targets: 2
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
lesse_reader_can_get_targets:
|
lesse_reader_can_get_targets:
|
||||||
path: '/v1/volume/targets'
|
path: '/v1/volume/targets'
|
||||||
@ -2043,7 +2024,6 @@ lesse_reader_can_get_targets:
|
|||||||
assert_status: 200
|
assert_status: 200
|
||||||
assert_list_length:
|
assert_list_length:
|
||||||
targets: 1
|
targets: 1
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
third_party_admin_cannot_get_target_list:
|
third_party_admin_cannot_get_target_list:
|
||||||
path: '/v1/volume/targets'
|
path: '/v1/volume/targets'
|
||||||
@ -2052,56 +2032,58 @@ third_party_admin_cannot_get_target_list:
|
|||||||
assert_status: 200
|
assert_status: 200
|
||||||
assert_list_length:
|
assert_list_length:
|
||||||
targets: 0
|
targets: 0
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
owner_reader_can_get_volume_target:
|
owner_reader_can_get_volume_target:
|
||||||
path: '/v1/volume/targets/{volume_target_ident}'
|
path: '/v1/volume/targets/{volume_target_ident}'
|
||||||
method: get
|
method: get
|
||||||
headers: *owner_reader_headers
|
headers: *owner_reader_headers
|
||||||
assert_status: 200
|
assert_status: 200
|
||||||
skip_reason: policy not implemented
|
assert_dict_contains:
|
||||||
|
# This helps assert that the field has been redacted.
|
||||||
|
properties:
|
||||||
|
redacted_contents: '** Value redacted: Requires permission baremetal:volume:view_target_properties access. Permission denied. **'
|
||||||
|
|
||||||
|
|
||||||
lessee_reader_can_get_volume_target:
|
lessee_reader_can_get_volume_target:
|
||||||
path: '/v1/volume/targets/{volume_target_ident}'
|
path: '/v1/volume/targets/{volume_target_ident}'
|
||||||
method: get
|
method: get
|
||||||
headers: *lessee_reader_headers
|
headers: *lessee_reader_headers
|
||||||
assert_status: 200
|
assert_status: 200
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
third_party_admin_cannot_get_volume_target:
|
third_party_admin_cannot_get_volume_target:
|
||||||
path: '/v1/volume/targets/{volume_target_ident}'
|
path: '/v1/volume/targets/{volume_target_ident}'
|
||||||
method: get
|
method: get
|
||||||
headers: *third_party_admin_headers
|
headers: *third_party_admin_headers
|
||||||
assert_status: 404
|
assert_status: 404
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
owner_admin_create_volume_target:
|
owner_admin_create_volume_target:
|
||||||
path: '/v1/volume/targets'
|
path: '/v1/volume/targets'
|
||||||
method: post
|
method: post
|
||||||
headers: *owner_admin_headers
|
headers: *owner_admin_headers
|
||||||
assert_status: 400
|
assert_status: 201
|
||||||
body: &volume_target_body
|
body: &volume_target_body
|
||||||
node_uuid: 68a552fb-dcd2-43bf-9302-e4c93287be16
|
node_uuid: 1ab63b9e-66d7-4cd7-8618-dddd0f9f7881
|
||||||
volume_type: iscsi
|
volume_type: iscsi
|
||||||
boot_index: 0
|
boot_index: 2
|
||||||
volume_id: 'test-id'
|
volume_id: 'test-id'
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
lessee_admin_create_volume_target:
|
lessee_admin_create_volume_target:
|
||||||
path: '/v1/volume/targets'
|
path: '/v1/volume/targets'
|
||||||
method: post
|
method: post
|
||||||
headers: *owner_admin_headers
|
headers: *owner_admin_headers
|
||||||
assert_status: 400
|
assert_status: 201
|
||||||
body: *volume_target_body
|
body:
|
||||||
skip_reason: policy not implemented
|
node_uuid: 38d5abed-c585-4fce-a57e-a2ffc2a2ec6f
|
||||||
|
volume_type: iscsi
|
||||||
|
boot_index: 2
|
||||||
|
volume_id: 'test-id2'
|
||||||
|
|
||||||
third_party_admin_cannot_create_volume_target:
|
third_party_admin_cannot_create_volume_target:
|
||||||
path: '/v1/volume/targets'
|
path: '/v1/volume/targets'
|
||||||
method: post
|
method: post
|
||||||
headers: *owner_admin_headers
|
headers: *third_party_admin_headers
|
||||||
assert_status: 400
|
assert_status: 403
|
||||||
body: *volume_target_body
|
body: *volume_target_body
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
owner_member_can_patch_volume_target:
|
owner_member_can_patch_volume_target:
|
||||||
path: '/v1/volume/targets/{volume_target_ident}'
|
path: '/v1/volume/targets/{volume_target_ident}'
|
||||||
@ -2110,16 +2092,22 @@ owner_member_can_patch_volume_target:
|
|||||||
- op: replace
|
- op: replace
|
||||||
path: /extra
|
path: /extra
|
||||||
value: {'test': 'testing'}
|
value: {'test': 'testing'}
|
||||||
assert_status: 403
|
headers: *owner_member_headers
|
||||||
skip_reason: policy not implemented
|
assert_status: 503
|
||||||
|
|
||||||
lessee_member_can_patch_volume_target:
|
lessee_admin_can_patch_volume_target:
|
||||||
|
path: '/v1/volume/targets/{volume_target_ident}'
|
||||||
|
method: patch
|
||||||
|
body: *volume_target_patch
|
||||||
|
headers: *lessee_admin_headers
|
||||||
|
assert_status: 503
|
||||||
|
|
||||||
|
lessee_member_cannot_patch_volume_target:
|
||||||
path: '/v1/volume/targets/{volume_target_ident}'
|
path: '/v1/volume/targets/{volume_target_ident}'
|
||||||
method: patch
|
method: patch
|
||||||
body: *volume_target_patch
|
body: *volume_target_patch
|
||||||
headers: *lessee_member_headers
|
headers: *lessee_member_headers
|
||||||
assert_status: 503
|
assert_status: 403
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
third_party_admin_cannot_patch_volume_target:
|
third_party_admin_cannot_patch_volume_target:
|
||||||
path: '/v1/volume/targets/{volume_target_ident}'
|
path: '/v1/volume/targets/{volume_target_ident}'
|
||||||
@ -2127,86 +2115,74 @@ third_party_admin_cannot_patch_volume_target:
|
|||||||
body: *volume_target_patch
|
body: *volume_target_patch
|
||||||
headers: *third_party_admin_headers
|
headers: *third_party_admin_headers
|
||||||
assert_status: 404
|
assert_status: 404
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
owner_admin_can_delete_volume_target:
|
owner_admin_can_delete_volume_target:
|
||||||
path: '/v1/volume/targets/{volume_target_ident}'
|
path: '/v1/volume/targets/{volume_target_ident}'
|
||||||
method: delete
|
method: delete
|
||||||
headers: *owner_admin_headers
|
headers: *owner_admin_headers
|
||||||
assert_status: 403
|
assert_status: 503
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
lessee_admin_can_delete_volume_target:
|
lessee_admin_can_delete_volume_target:
|
||||||
path: '/v1/volume/targets/{volume_target_ident}'
|
path: '/v1/volume/targets/{volume_target_ident}'
|
||||||
method: delete
|
method: delete
|
||||||
headers: *lessee_admin_headers
|
headers: *lessee_admin_headers
|
||||||
assert_status: 201
|
assert_status: 503
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
owner_member_cannot_delete_volume_target:
|
owner_member_cannot_delete_volume_target:
|
||||||
path: '/v1/volume/targets/{volume_target_ident}'
|
path: '/v1/volume/targets/{volume_target_ident}'
|
||||||
method: delete
|
method: delete
|
||||||
headers: *owner_member_headers
|
headers: *owner_member_headers
|
||||||
assert_status: 403
|
assert_status: 403
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
lessee_member_cannot_delete_volume_target:
|
lessee_member_cannot_delete_volume_target:
|
||||||
path: '/v1/volume/targets/{volume_target_ident}'
|
path: '/v1/volume/targets/{volume_target_ident}'
|
||||||
method: delete
|
method: delete
|
||||||
headers: *lessee_member_headers
|
headers: *lessee_member_headers
|
||||||
assert_status: 403
|
assert_status: 403
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
third_party_admin_cannot_delete_volume_target:
|
third_party_admin_cannot_delete_volume_target:
|
||||||
path: '/v1/volume/targets/{volume_target_ident}'
|
path: '/v1/volume/targets/{volume_target_ident}'
|
||||||
method: delete
|
method: delete
|
||||||
headers: *third_party_admin_headers
|
headers: *third_party_admin_headers
|
||||||
assert_status: 403
|
assert_status: 404
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
# Get Volumes by Node - https://docs.openstack.org/api-ref/baremetal/#listing-volume-resources-by-node-nodes-volume
|
# Get Volumes by Node - https://docs.openstack.org/api-ref/baremetal/#listing-volume-resources-by-node-nodes-volume
|
||||||
|
|
||||||
owner_reader_can_get_volume_connectors:
|
owner_reader_can_get_volume_connectors:
|
||||||
path: '/v1/nodes/{node_ident}/volume/connectors'
|
path: '/v1/nodes/{owner_node_ident}/volume/connectors'
|
||||||
method: get
|
method: get
|
||||||
headers: *owner_reader_headers
|
headers: *owner_reader_headers
|
||||||
assert_status: 200
|
assert_status: 200
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
lessee_reader_can_get_node_volume_connectors:
|
lessee_reader_can_get_node_volume_connectors:
|
||||||
path: '/v1/nodes/{node_ident}/volume/connectors'
|
path: '/v1/nodes/{lessee_node_ident}/volume/connectors'
|
||||||
method: get
|
method: get
|
||||||
headers: *lessee_reader_headers
|
headers: *lessee_reader_headers
|
||||||
assert_status: 200
|
assert_status: 200
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
third_party_admin_cannot_get_node_volume_connectors:
|
third_party_admin_cannot_get_node_volume_connectors:
|
||||||
path: '/v1/nodes/{node_ident}/volume/connectors'
|
path: '/v1/nodes/{lessee_node_ident}/volume/connectors'
|
||||||
method: get
|
method: get
|
||||||
headers: *third_party_admin_headers
|
headers: *third_party_admin_headers
|
||||||
assert_status: 200
|
assert_status: 404
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
owner_reader_can_get_node_volume_targets:
|
owner_reader_can_get_node_volume_targets:
|
||||||
path: '/v1/nodes/{node_ident}/volume/targets'
|
path: '/v1/nodes/{owner_node_ident}/volume/targets'
|
||||||
method: get
|
method: get
|
||||||
headers: *owner_reader_headers
|
headers: *owner_reader_headers
|
||||||
assert_status: 200
|
assert_status: 200
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
lessee_reader_can_get_node_volume_targets:
|
lessee_reader_can_get_node_volume_targets:
|
||||||
path: '/v1/nodes/{node_ident}/volume/targets'
|
path: '/v1/nodes/{lessee_node_ident}/volume/targets'
|
||||||
method: get
|
method: get
|
||||||
headers: *lessee_reader_headers
|
headers: *lessee_reader_headers
|
||||||
assert_status: 200
|
assert_status: 200
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
third_part_admin_cannot_read_node_volume_targets:
|
third_part_admin_cannot_read_node_volume_targets:
|
||||||
path: '/v1/nodes/{node_ident}/volume/targets'
|
path: '/v1/nodes/{lessee_node_ident}/volume/targets'
|
||||||
method: get
|
method: get
|
||||||
headers: *owner_reader_headers
|
headers: *third_party_admin_headers
|
||||||
assert_status: 404
|
assert_status: 404
|
||||||
skip_reason: policy not implemented
|
|
||||||
|
|
||||||
# Drivers - https://docs.openstack.org/api-ref/baremetal/#drivers-drivers
|
# Drivers - https://docs.openstack.org/api-ref/baremetal/#drivers-drivers
|
||||||
|
|
||||||
|
@ -1160,9 +1160,9 @@ volume_connectors_post_admin:
|
|||||||
path: '/v1/volume/connectors'
|
path: '/v1/volume/connectors'
|
||||||
method: post
|
method: post
|
||||||
headers: *admin_headers
|
headers: *admin_headers
|
||||||
assert_status: 400
|
assert_status: 201
|
||||||
body: &volume_connector_body
|
body: &volume_connector_body
|
||||||
node_uuid: 68a552fb-dcd2-43bf-9302-e4c93287be16
|
node_uuid: 1be26c0b-03f2-4d2e-ae87-c02d7f33c123
|
||||||
type: ip
|
type: ip
|
||||||
connector_id: 192.168.1.100
|
connector_id: 192.168.1.100
|
||||||
|
|
||||||
@ -1172,7 +1172,7 @@ volume_connectors_post_member:
|
|||||||
path: '/v1/volume/connectors'
|
path: '/v1/volume/connectors'
|
||||||
method: post
|
method: post
|
||||||
headers: *scoped_member_headers
|
headers: *scoped_member_headers
|
||||||
assert_status: 400
|
assert_status: 201
|
||||||
body: *volume_connector_body
|
body: *volume_connector_body
|
||||||
|
|
||||||
volume_connectors_post_reader:
|
volume_connectors_post_reader:
|
||||||
@ -1269,19 +1269,23 @@ volume_targets_post_admin:
|
|||||||
path: '/v1/volume/targets'
|
path: '/v1/volume/targets'
|
||||||
method: post
|
method: post
|
||||||
headers: *admin_headers
|
headers: *admin_headers
|
||||||
assert_status: 400
|
assert_status: 201
|
||||||
body: &volume_target_body
|
body: &volume_target_body
|
||||||
node_uuid: 68a552fb-dcd2-43bf-9302-e4c93287be16
|
node_uuid: 1be26c0b-03f2-4d2e-ae87-c02d7f33c123
|
||||||
volume_type: iscsi
|
volume_type: iscsi
|
||||||
boot_index: 0
|
boot_index: 1
|
||||||
volume_id: 'test-id'
|
volume_id: 'test-id'
|
||||||
|
|
||||||
volume_targets_post_member:
|
volume_targets_post_member:
|
||||||
path: '/v1/volume/targets'
|
path: '/v1/volume/targets'
|
||||||
method: post
|
method: post
|
||||||
headers: *scoped_member_headers
|
headers: *scoped_member_headers
|
||||||
assert_status: 400
|
assert_status: 201
|
||||||
body: *volume_target_body
|
body:
|
||||||
|
node_uuid: 1be26c0b-03f2-4d2e-ae87-c02d7f33c123
|
||||||
|
volume_type: iscsi
|
||||||
|
boot_index: 2
|
||||||
|
volume_id: 'test-id2'
|
||||||
|
|
||||||
volume_targets_post_reader:
|
volume_targets_post_reader:
|
||||||
path: '/v1/volume/targets'
|
path: '/v1/volume/targets'
|
||||||
|
@ -84,7 +84,8 @@ class TestVolumeConnectorObject(db_base.DbTestCase,
|
|||||||
self.context, limit=4, sort_key='uuid', sort_dir='asc')
|
self.context, limit=4, sort_key='uuid', sort_dir='asc')
|
||||||
|
|
||||||
mock_get_list.assert_called_once_with(
|
mock_get_list.assert_called_once_with(
|
||||||
limit=4, marker=None, sort_key='uuid', sort_dir='asc')
|
limit=4, marker=None, sort_key='uuid', sort_dir='asc',
|
||||||
|
project=None)
|
||||||
self.assertThat(volume_connectors, HasLength(1))
|
self.assertThat(volume_connectors, HasLength(1))
|
||||||
self.assertIsInstance(volume_connectors[0],
|
self.assertIsInstance(volume_connectors[0],
|
||||||
objects.VolumeConnector)
|
objects.VolumeConnector)
|
||||||
@ -98,7 +99,8 @@ class TestVolumeConnectorObject(db_base.DbTestCase,
|
|||||||
self.context, limit=4, sort_key='uuid', sort_dir='asc')
|
self.context, limit=4, sort_key='uuid', sort_dir='asc')
|
||||||
|
|
||||||
mock_get_list.assert_called_once_with(
|
mock_get_list.assert_called_once_with(
|
||||||
limit=4, marker=None, sort_key='uuid', sort_dir='asc')
|
limit=4, marker=None, sort_key='uuid', sort_dir='asc',
|
||||||
|
project=None)
|
||||||
self.assertEqual([], volume_connectors)
|
self.assertEqual([], volume_connectors)
|
||||||
|
|
||||||
def test_list_by_node_id(self):
|
def test_list_by_node_id(self):
|
||||||
@ -111,7 +113,8 @@ class TestVolumeConnectorObject(db_base.DbTestCase,
|
|||||||
self.context, node_id, limit=10, sort_dir='desc')
|
self.context, node_id, limit=10, sort_dir='desc')
|
||||||
|
|
||||||
mock_get_list_by_node_id.assert_called_once_with(
|
mock_get_list_by_node_id.assert_called_once_with(
|
||||||
node_id, limit=10, marker=None, sort_key=None, sort_dir='desc')
|
node_id, limit=10, marker=None, sort_key=None, sort_dir='desc',
|
||||||
|
project=None)
|
||||||
self.assertThat(volume_connectors, HasLength(1))
|
self.assertThat(volume_connectors, HasLength(1))
|
||||||
self.assertIsInstance(volume_connectors[0],
|
self.assertIsInstance(volume_connectors[0],
|
||||||
objects.VolumeConnector)
|
objects.VolumeConnector)
|
||||||
|
@ -83,7 +83,8 @@ class TestVolumeTargetObject(db_base.DbTestCase, obj_utils.SchemasTestMixIn):
|
|||||||
self.context, limit=4, sort_key='uuid', sort_dir='asc')
|
self.context, limit=4, sort_key='uuid', sort_dir='asc')
|
||||||
|
|
||||||
mock_get_list.assert_called_once_with(
|
mock_get_list.assert_called_once_with(
|
||||||
limit=4, marker=None, sort_key='uuid', sort_dir='asc')
|
limit=4, marker=None, sort_key='uuid', sort_dir='asc',
|
||||||
|
project=None)
|
||||||
self.assertThat(volume_targets, HasLength(1))
|
self.assertThat(volume_targets, HasLength(1))
|
||||||
self.assertIsInstance(volume_targets[0],
|
self.assertIsInstance(volume_targets[0],
|
||||||
objects.VolumeTarget)
|
objects.VolumeTarget)
|
||||||
@ -97,7 +98,8 @@ class TestVolumeTargetObject(db_base.DbTestCase, obj_utils.SchemasTestMixIn):
|
|||||||
self.context, limit=4, sort_key='uuid', sort_dir='asc')
|
self.context, limit=4, sort_key='uuid', sort_dir='asc')
|
||||||
|
|
||||||
mock_get_list.assert_called_once_with(
|
mock_get_list.assert_called_once_with(
|
||||||
limit=4, marker=None, sort_key='uuid', sort_dir='asc')
|
limit=4, marker=None, sort_key='uuid', sort_dir='asc',
|
||||||
|
project=None)
|
||||||
self.assertEqual([], volume_targets)
|
self.assertEqual([], volume_targets)
|
||||||
|
|
||||||
def test_list_by_node_id(self):
|
def test_list_by_node_id(self):
|
||||||
@ -109,7 +111,8 @@ class TestVolumeTargetObject(db_base.DbTestCase, obj_utils.SchemasTestMixIn):
|
|||||||
self.context, node_id, limit=10, sort_dir='desc')
|
self.context, node_id, limit=10, sort_dir='desc')
|
||||||
|
|
||||||
mock_get_list_by_node_id.assert_called_once_with(
|
mock_get_list_by_node_id.assert_called_once_with(
|
||||||
node_id, limit=10, marker=None, sort_key=None, sort_dir='desc')
|
node_id, limit=10, marker=None, sort_key=None, sort_dir='desc',
|
||||||
|
project=None)
|
||||||
self.assertThat(volume_targets, HasLength(1))
|
self.assertThat(volume_targets, HasLength(1))
|
||||||
self.assertIsInstance(volume_targets[0], objects.VolumeTarget)
|
self.assertIsInstance(volume_targets[0], objects.VolumeTarget)
|
||||||
self.assertEqual(self.context, volume_targets[0]._context)
|
self.assertEqual(self.context, volume_targets[0]._context)
|
||||||
@ -124,7 +127,7 @@ class TestVolumeTargetObject(db_base.DbTestCase, obj_utils.SchemasTestMixIn):
|
|||||||
|
|
||||||
mock_get_list_by_volume_id.assert_called_once_with(
|
mock_get_list_by_volume_id.assert_called_once_with(
|
||||||
volume_id, limit=10, marker=None,
|
volume_id, limit=10, marker=None,
|
||||||
sort_key=None, sort_dir='desc')
|
sort_key=None, sort_dir='desc', project=None)
|
||||||
self.assertThat(volume_targets, HasLength(1))
|
self.assertThat(volume_targets, HasLength(1))
|
||||||
self.assertIsInstance(volume_targets[0], objects.VolumeTarget)
|
self.assertIsInstance(volume_targets[0], objects.VolumeTarget)
|
||||||
self.assertEqual(self.context, volume_targets[0]._context)
|
self.assertEqual(self.context, volume_targets[0]._context)
|
||||||
|
Loading…
Reference in New Issue
Block a user