Add port/portgroup list conductor groups filter
Allow filtering on conductor groups when listing ports and portgroups. Story: 2010373 Task: 46611 Change-Id: Id5b4f9eb29c2f598bc29fbf0b4b7c896ece3756d
This commit is contained in:

committed by
Afonne-CID

parent
1bc466e30a
commit
65f1396d19
@@ -293,6 +293,23 @@ r_conductor_group:
|
|||||||
in: query
|
in: query
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
|
r_conductor_groups:
|
||||||
|
description: |
|
||||||
|
Filter the list of returned ports or portgroups, and only return those with
|
||||||
|
the specified ``conductor_group`` or an empty set if none found. List of
|
||||||
|
case-insensitive strings up to 255 characters, containing ``a-z``, ``0-9``,
|
||||||
|
``_``, ``-``, and ``.``. This cannot be used if ``node``, ``node_uuid``,
|
||||||
|
``portgroup`` or ``address`` is specified.
|
||||||
|
|
||||||
|
For example, the following request returns only the ports for nodes
|
||||||
|
in conductor groups ``bear`` and ``metal``:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
GET /v1/ports?conductor_groups=bear,metal
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
type: array
|
||||||
r_description_contains:
|
r_description_contains:
|
||||||
description: |
|
description: |
|
||||||
Filter the list of returned nodes, and only return those containing
|
Filter the list of returned nodes, and only return those containing
|
||||||
|
@@ -2,6 +2,12 @@
|
|||||||
REST API Version History
|
REST API Version History
|
||||||
========================
|
========================
|
||||||
|
|
||||||
|
1.99 (Flamingo)
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Add ability to filter on node conductor group when listing ports and
|
||||||
|
portgroups.
|
||||||
|
|
||||||
1.98 (Flamingo)
|
1.98 (Flamingo)
|
||||||
|
|
||||||
Add support for patching object attributes with keys containing ~ or /.
|
Add support for patching object attributes with keys containing ~ or /.
|
||||||
|
@@ -233,7 +233,8 @@ class PortsController(rest.RestController):
|
|||||||
def _get_ports_collection(self, node_ident, address, portgroup_ident,
|
def _get_ports_collection(self, node_ident, address, portgroup_ident,
|
||||||
shard, marker, limit, sort_key, sort_dir,
|
shard, marker, limit, sort_key, sort_dir,
|
||||||
resource_url=None, fields=None, detail=None,
|
resource_url=None, fields=None, detail=None,
|
||||||
project=None, description_contains=None):
|
project=None, description_contains=None,
|
||||||
|
conductor_groups=None):
|
||||||
"""Retrieve a collection of ports.
|
"""Retrieve a collection of ports.
|
||||||
|
|
||||||
:param node_ident: UUID or name of a node, to get only ports for that
|
:param node_ident: UUID or name of a node, to get only ports for that
|
||||||
@@ -259,6 +260,7 @@ class PortsController(rest.RestController):
|
|||||||
:param description_contains: Optional string value to get only ports
|
:param description_contains: Optional string value to get only ports
|
||||||
with description field contains matching
|
with description field contains matching
|
||||||
value.
|
value.
|
||||||
|
:param conductor_groups: conductor groups, to filter the request by.
|
||||||
:returns: a list of ports.
|
:returns: a list of ports.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -290,6 +292,11 @@ class PortsController(rest.RestController):
|
|||||||
if description_contains:
|
if description_contains:
|
||||||
filters['description_contains'] = description_contains
|
filters['description_contains'] = description_contains
|
||||||
|
|
||||||
|
if conductor_groups and (node_ident or portgroup_ident or address):
|
||||||
|
raise exception.Invalid(
|
||||||
|
_("Filtering by conductor_groups cannot be combined with "
|
||||||
|
"node_ident, portgroup_ident, or node address filters."))
|
||||||
|
|
||||||
if portgroup_ident:
|
if portgroup_ident:
|
||||||
# FIXME: Since all we need is the portgroup ID, we can
|
# FIXME: Since all we need is the portgroup ID, we can
|
||||||
# make this more efficient by only querying
|
# make this more efficient by only querying
|
||||||
@@ -327,6 +334,7 @@ class PortsController(rest.RestController):
|
|||||||
ports = objects.Port.list(api.request.context, limit,
|
ports = objects.Port.list(api.request.context, limit,
|
||||||
marker_obj, sort_key=sort_key,
|
marker_obj, sort_key=sort_key,
|
||||||
sort_dir=sort_dir, project=project,
|
sort_dir=sort_dir, project=project,
|
||||||
|
conductor_groups=conductor_groups,
|
||||||
filters=filters)
|
filters=filters)
|
||||||
parameters = {}
|
parameters = {}
|
||||||
|
|
||||||
@@ -405,11 +413,12 @@ class PortsController(rest.RestController):
|
|||||||
limit=args.integer, sort_key=args.string,
|
limit=args.integer, sort_key=args.string,
|
||||||
sort_dir=args.string, fields=args.string_list,
|
sort_dir=args.string, fields=args.string_list,
|
||||||
portgroup=args.uuid_or_name, detail=args.boolean,
|
portgroup=args.uuid_or_name, detail=args.boolean,
|
||||||
shard=args.string_list, description_contains=args.string)
|
shard=args.string_list, description_contains=args.string,
|
||||||
|
conductor_groups=args.string_list)
|
||||||
def get_all(self, node=None, node_uuid=None, address=None, marker=None,
|
def get_all(self, node=None, node_uuid=None, address=None, marker=None,
|
||||||
limit=None, sort_key='id', sort_dir='asc', fields=None,
|
limit=None, sort_key='id', sort_dir='asc', fields=None,
|
||||||
portgroup=None, detail=None, shard=None,
|
portgroup=None, detail=None, shard=None,
|
||||||
description_contains=None):
|
description_contains=None, conductor_groups=None):
|
||||||
"""Retrieve a list of ports.
|
"""Retrieve a list of ports.
|
||||||
|
|
||||||
Note that the 'node_uuid' interface is deprecated in favour
|
Note that the 'node_uuid' interface is deprecated in favour
|
||||||
@@ -437,6 +446,7 @@ class PortsController(rest.RestController):
|
|||||||
:param description_contains: Optional string value to get only ports
|
:param description_contains: Optional string value to get only ports
|
||||||
with description field contains matching
|
with description field contains matching
|
||||||
value.
|
value.
|
||||||
|
:param conductor_groups: conductor groups, to filter the request by.
|
||||||
:raises: NotAcceptable, HTTPNotFound
|
:raises: NotAcceptable, HTTPNotFound
|
||||||
"""
|
"""
|
||||||
project = api_utils.check_port_list_policy(
|
project = api_utils.check_port_list_policy(
|
||||||
@@ -476,7 +486,9 @@ class PortsController(rest.RestController):
|
|||||||
sort_key, sort_dir,
|
sort_key, sort_dir,
|
||||||
resource_url='ports',
|
resource_url='ports',
|
||||||
fields=fields, detail=detail,
|
fields=fields, detail=detail,
|
||||||
project=project, **extra_args)
|
project=project,
|
||||||
|
conductor_groups=conductor_groups,
|
||||||
|
**extra_args)
|
||||||
|
|
||||||
@METRICS.timer('PortsController.detail')
|
@METRICS.timer('PortsController.detail')
|
||||||
@method.expose()
|
@method.expose()
|
||||||
@@ -484,10 +496,11 @@ class PortsController(rest.RestController):
|
|||||||
address=args.mac_address, marker=args.uuid,
|
address=args.mac_address, marker=args.uuid,
|
||||||
limit=args.integer, sort_key=args.string,
|
limit=args.integer, sort_key=args.string,
|
||||||
sort_dir=args.string, portgroup=args.uuid_or_name,
|
sort_dir=args.string, portgroup=args.uuid_or_name,
|
||||||
shard=args.string_list, description_contains=args.string)
|
shard=args.string_list, description_contains=args.string,
|
||||||
|
conductor_groups=args.string_list)
|
||||||
def detail(self, node=None, node_uuid=None, address=None, marker=None,
|
def detail(self, node=None, node_uuid=None, address=None, marker=None,
|
||||||
limit=None, sort_key='id', sort_dir='asc', portgroup=None,
|
limit=None, sort_key='id', sort_dir='asc', portgroup=None,
|
||||||
shard=None, description_contains=None):
|
shard=None, description_contains=None, conductor_groups=None):
|
||||||
"""Retrieve a list of ports with detail.
|
"""Retrieve a list of ports with detail.
|
||||||
|
|
||||||
Note that the 'node_uuid' interface is deprecated in favour
|
Note that the 'node_uuid' interface is deprecated in favour
|
||||||
@@ -513,6 +526,7 @@ class PortsController(rest.RestController):
|
|||||||
:param description_contains: Optional string value to get only ports
|
:param description_contains: Optional string value to get only ports
|
||||||
with description field contains matching
|
with description field contains matching
|
||||||
value.
|
value.
|
||||||
|
:param conductor_groups: conductor groups, to filter the request by.
|
||||||
:raises: NotAcceptable, HTTPNotFound
|
:raises: NotAcceptable, HTTPNotFound
|
||||||
"""
|
"""
|
||||||
project = api_utils.check_port_list_policy(
|
project = api_utils.check_port_list_policy(
|
||||||
@@ -543,7 +557,9 @@ class PortsController(rest.RestController):
|
|||||||
portgroup, shard, marker, limit,
|
portgroup, shard, marker, limit,
|
||||||
sort_key, sort_dir,
|
sort_key, sort_dir,
|
||||||
resource_url='ports/detail',
|
resource_url='ports/detail',
|
||||||
project=project, **extra_args)
|
project=project,
|
||||||
|
conductor_groups=conductor_groups,
|
||||||
|
**extra_args)
|
||||||
|
|
||||||
@METRICS.timer('PortsController.get_one')
|
@METRICS.timer('PortsController.get_one')
|
||||||
@method.expose()
|
@method.expose()
|
||||||
|
@@ -166,7 +166,8 @@ class PortgroupsController(pecan.rest.RestController):
|
|||||||
def _get_portgroups_collection(self, node_ident, address,
|
def _get_portgroups_collection(self, node_ident, address,
|
||||||
marker, limit, sort_key, sort_dir,
|
marker, limit, sort_key, sort_dir,
|
||||||
resource_url=None, fields=None,
|
resource_url=None, fields=None,
|
||||||
detail=None, project=None):
|
detail=None, project=None,
|
||||||
|
conductor_groups=None):
|
||||||
"""Return portgroups collection.
|
"""Return portgroups collection.
|
||||||
|
|
||||||
:param node_ident: UUID or name of a node.
|
:param node_ident: UUID or name of a node.
|
||||||
@@ -179,6 +180,7 @@ class PortgroupsController(pecan.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 project: Optional, project ID to filter the request by.
|
:param project: Optional, project ID to filter the request by.
|
||||||
|
:param conductor_groups: conductor groups, to filter the request by.
|
||||||
"""
|
"""
|
||||||
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)
|
||||||
@@ -195,6 +197,11 @@ class PortgroupsController(pecan.rest.RestController):
|
|||||||
|
|
||||||
node_ident = self.parent_node_ident or node_ident
|
node_ident = self.parent_node_ident or node_ident
|
||||||
|
|
||||||
|
if conductor_groups and (node_ident or address):
|
||||||
|
raise exception.Invalid(
|
||||||
|
_("Filtering by conductor_groups and node_ident/address "
|
||||||
|
"simultaneously is not supported."))
|
||||||
|
|
||||||
if node_ident:
|
if node_ident:
|
||||||
# FIXME: Since all we need is the node ID, we can
|
# FIXME: Since all we need is the node ID, we can
|
||||||
# make this more efficient by only querying
|
# make this more efficient by only querying
|
||||||
@@ -209,10 +216,10 @@ class PortgroupsController(pecan.rest.RestController):
|
|||||||
portgroups = self._get_portgroups_by_address(address,
|
portgroups = self._get_portgroups_by_address(address,
|
||||||
project=project)
|
project=project)
|
||||||
else:
|
else:
|
||||||
portgroups = objects.Portgroup.list(api.request.context, limit,
|
portgroups = objects.Portgroup.list(
|
||||||
marker_obj, sort_key=sort_key,
|
api.request.context, limit,
|
||||||
sort_dir=sort_dir,
|
marker_obj, sort_key=sort_key, sort_dir=sort_dir,
|
||||||
project=project)
|
project=project, conductor_groups=conductor_groups)
|
||||||
parameters = {}
|
parameters = {}
|
||||||
if detail is not None:
|
if detail is not None:
|
||||||
parameters['detail'] = detail
|
parameters['detail'] = detail
|
||||||
@@ -246,10 +253,10 @@ class PortgroupsController(pecan.rest.RestController):
|
|||||||
@args.validate(node=args.uuid_or_name, address=args.mac_address,
|
@args.validate(node=args.uuid_or_name, address=args.mac_address,
|
||||||
marker=args.uuid, limit=args.integer, sort_key=args.string,
|
marker=args.uuid, limit=args.integer, sort_key=args.string,
|
||||||
sort_dir=args.string, fields=args.string_list,
|
sort_dir=args.string, fields=args.string_list,
|
||||||
detail=args.boolean)
|
detail=args.boolean, conductor_groups=args.string_list)
|
||||||
def get_all(self, node=None, address=None, marker=None,
|
def get_all(self, node=None, address=None, marker=None,
|
||||||
limit=None, sort_key='id', sort_dir='asc', fields=None,
|
limit=None, sort_key='id', sort_dir='asc', fields=None,
|
||||||
detail=None):
|
detail=None, conductor_groups=None):
|
||||||
"""Retrieve a list of portgroups.
|
"""Retrieve a list of portgroups.
|
||||||
|
|
||||||
:param node: UUID or name of a node, to get only portgroups for that
|
:param node: UUID or name of a node, to get only portgroups for that
|
||||||
@@ -265,6 +272,7 @@ class PortgroupsController(pecan.rest.RestController):
|
|||||||
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
|
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
|
||||||
: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 conductor_groups: conductor groups, to filter the request by.
|
||||||
"""
|
"""
|
||||||
if not api_utils.allow_portgroups():
|
if not api_utils.allow_portgroups():
|
||||||
raise exception.NotFound()
|
raise exception.NotFound()
|
||||||
@@ -284,21 +292,22 @@ class PortgroupsController(pecan.rest.RestController):
|
|||||||
fields = api_utils.get_request_return_fields(fields, detail,
|
fields = api_utils.get_request_return_fields(fields, detail,
|
||||||
_DEFAULT_RETURN_FIELDS)
|
_DEFAULT_RETURN_FIELDS)
|
||||||
|
|
||||||
return self._get_portgroups_collection(node, address,
|
return self._get_portgroups_collection(
|
||||||
marker, limit,
|
node, address, marker, limit, sort_key, sort_dir,
|
||||||
sort_key, sort_dir,
|
fields=fields,
|
||||||
fields=fields,
|
resource_url='portgroups',
|
||||||
resource_url='portgroups',
|
detail=detail,
|
||||||
detail=detail,
|
project=project,
|
||||||
project=project)
|
conductor_groups=conductor_groups)
|
||||||
|
|
||||||
@METRICS.timer('PortgroupsController.detail')
|
@METRICS.timer('PortgroupsController.detail')
|
||||||
@method.expose()
|
@method.expose()
|
||||||
@args.validate(node=args.uuid_or_name, address=args.mac_address,
|
@args.validate(node=args.uuid_or_name, address=args.mac_address,
|
||||||
marker=args.uuid, limit=args.integer, sort_key=args.string,
|
marker=args.uuid, limit=args.integer, sort_key=args.string,
|
||||||
sort_dir=args.string)
|
sort_dir=args.string, conductor_groups=args.string_list)
|
||||||
def detail(self, node=None, address=None, marker=None,
|
def detail(self, node=None, address=None, marker=None,
|
||||||
limit=None, sort_key='id', sort_dir='asc'):
|
limit=None, sort_key='id', sort_dir='asc',
|
||||||
|
conductor_groups=None):
|
||||||
"""Retrieve a list of portgroups with detail.
|
"""Retrieve a list of portgroups with detail.
|
||||||
|
|
||||||
:param node: UUID or name of a node, to get only portgroups for that
|
:param node: UUID or name of a node, to get only portgroups for that
|
||||||
@@ -312,6 +321,7 @@ class PortgroupsController(pecan.rest.RestController):
|
|||||||
max_limit resources will be returned.
|
max_limit resources will be returned.
|
||||||
:param sort_key: column to sort results by. Default: id.
|
:param sort_key: column to sort results by. Default: id.
|
||||||
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
|
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
|
||||||
|
:param conductor_groups: conductor groups, to filter the request by.
|
||||||
"""
|
"""
|
||||||
if not api_utils.allow_portgroups():
|
if not api_utils.allow_portgroups():
|
||||||
raise exception.NotFound()
|
raise exception.NotFound()
|
||||||
@@ -334,7 +344,8 @@ class PortgroupsController(pecan.rest.RestController):
|
|||||||
|
|
||||||
return self._get_portgroups_collection(
|
return self._get_portgroups_collection(
|
||||||
node, address, marker, limit, sort_key, sort_dir,
|
node, address, marker, limit, sort_key, sort_dir,
|
||||||
resource_url='portgroups/detail', project=project)
|
resource_url='portgroups/detail', project=project,
|
||||||
|
conductor_groups=conductor_groups)
|
||||||
|
|
||||||
@METRICS.timer('PortgroupsController.get_one')
|
@METRICS.timer('PortgroupsController.get_one')
|
||||||
@method.expose()
|
@method.expose()
|
||||||
|
@@ -136,6 +136,7 @@ BASE_VERSION = 1
|
|||||||
# v1.96: Migrate inspection rules from Inspector
|
# v1.96: Migrate inspection rules from Inspector
|
||||||
# v1.97: Add description field to port.
|
# v1.97: Add description field to port.
|
||||||
# v1.98: Add support for object attributes with keys containing ~ or /.
|
# v1.98: Add support for object attributes with keys containing ~ or /.
|
||||||
|
# v1.99: Add conductor group filtering to port and portgroup list
|
||||||
|
|
||||||
MINOR_0_JUNO = 0
|
MINOR_0_JUNO = 0
|
||||||
MINOR_1_INITIAL_VERSION = 1
|
MINOR_1_INITIAL_VERSION = 1
|
||||||
@@ -236,6 +237,7 @@ MINOR_95_DISABLE_POWER_OFF = 95
|
|||||||
MINOR_96_INSPECTION_RULES = 96
|
MINOR_96_INSPECTION_RULES = 96
|
||||||
MINOR_97_PORT_DESCRIPTION = 97
|
MINOR_97_PORT_DESCRIPTION = 97
|
||||||
MINOR_98_SUPPORT_SPECIAL_CHAR_IN_ATTRIBUTES = 98
|
MINOR_98_SUPPORT_SPECIAL_CHAR_IN_ATTRIBUTES = 98
|
||||||
|
MINOR_99_PORT_PORTGROUP_CONDUCTOR_GROUP_FILTER = 99
|
||||||
|
|
||||||
# When adding another version, update:
|
# When adding another version, update:
|
||||||
# - MINOR_MAX_VERSION
|
# - MINOR_MAX_VERSION
|
||||||
@@ -243,7 +245,8 @@ MINOR_98_SUPPORT_SPECIAL_CHAR_IN_ATTRIBUTES = 98
|
|||||||
# explanation of what changed in the new version
|
# explanation of what changed in the new version
|
||||||
# - common/release_mappings.py, RELEASE_MAPPING['master']['api']
|
# - common/release_mappings.py, RELEASE_MAPPING['master']['api']
|
||||||
|
|
||||||
MINOR_MAX_VERSION = MINOR_98_SUPPORT_SPECIAL_CHAR_IN_ATTRIBUTES
|
|
||||||
|
MINOR_MAX_VERSION = MINOR_99_PORT_PORTGROUP_CONDUCTOR_GROUP_FILTER
|
||||||
|
|
||||||
# String representations of the minor and maximum versions
|
# String representations of the minor and maximum versions
|
||||||
_MIN_VERSION_STRING = '{}.{}'.format(BASE_VERSION, MINOR_1_INITIAL_VERSION)
|
_MIN_VERSION_STRING = '{}.{}'.format(BASE_VERSION, MINOR_1_INITIAL_VERSION)
|
||||||
|
@@ -545,7 +545,7 @@ RELEASE_MAPPING = {
|
|||||||
'Deployment': ['1.0'],
|
'Deployment': ['1.0'],
|
||||||
'DeployTemplate': ['1.1'],
|
'DeployTemplate': ['1.1'],
|
||||||
'Port': ['1.11'],
|
'Port': ['1.11'],
|
||||||
'Portgroup': ['1.4'],
|
'Portgroup': ['1.5'],
|
||||||
'Trait': ['1.0'],
|
'Trait': ['1.0'],
|
||||||
'TraitList': ['1.0'],
|
'TraitList': ['1.0'],
|
||||||
'VolumeConnector': ['1.0'],
|
'VolumeConnector': ['1.0'],
|
||||||
@@ -848,7 +848,7 @@ RELEASE_MAPPING = {
|
|||||||
# make it below. To release, we will preserve a version matching
|
# make it below. To release, we will preserve a version matching
|
||||||
# the release as a separate block of text, like above.
|
# the release as a separate block of text, like above.
|
||||||
'master': {
|
'master': {
|
||||||
'api': '1.98',
|
'api': '1.99',
|
||||||
'rpc': '1.61',
|
'rpc': '1.61',
|
||||||
'objects': {
|
'objects': {
|
||||||
'Allocation': ['1.1'],
|
'Allocation': ['1.1'],
|
||||||
|
@@ -285,7 +285,8 @@ class Connection(object, metaclass=abc.ABCMeta):
|
|||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def get_port_list(self, limit=None, marker=None,
|
def get_port_list(self, limit=None, marker=None,
|
||||||
sort_key=None, sort_dir=None, filters=None):
|
sort_key=None, sort_dir=None,
|
||||||
|
conductor_groups=None, filters=None):
|
||||||
"""Return a list of ports.
|
"""Return a list of ports.
|
||||||
|
|
||||||
:param limit: Maximum number of ports to return.
|
:param limit: Maximum number of ports to return.
|
||||||
@@ -294,6 +295,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 conductor_groups: A list of conductor groups to filter by,
|
||||||
|
defaults to None
|
||||||
:param filters: Filters to apply, defaults to None
|
:param filters: Filters to apply, defaults to None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -400,7 +403,8 @@ class Connection(object, metaclass=abc.ABCMeta):
|
|||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def get_portgroup_list(self, limit=None, marker=None,
|
def get_portgroup_list(self, limit=None, marker=None,
|
||||||
sort_key=None, sort_dir=None,
|
sort_key=None, sort_dir=None,
|
||||||
project=None):
|
project=None, conductor_groups=None,
|
||||||
|
filters=None):
|
||||||
"""Return a list of portgroups.
|
"""Return a list of portgroups.
|
||||||
|
|
||||||
:param limit: Maximum number of portgroups to return.
|
:param limit: Maximum number of portgroups to return.
|
||||||
@@ -410,6 +414,9 @@ class Connection(object, metaclass=abc.ABCMeta):
|
|||||||
: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: A node owner or lessee to filter by.
|
:param project: A node owner or lessee to filter by.
|
||||||
|
:param conductor_groups: A list of conductor groups to filter by,
|
||||||
|
defaults to None
|
||||||
|
:param filters: Filters to apply, defaults to None
|
||||||
:returns: A list of portgroups.
|
:returns: A list of portgroups.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@@ -337,6 +337,24 @@ def add_port_filter_by_portgroup(query, value):
|
|||||||
return query.filter(models.Portgroup.uuid == value)
|
return query.filter(models.Portgroup.uuid == value)
|
||||||
|
|
||||||
|
|
||||||
|
def add_port_filter_by_node_conductor_groups(query, conductor_groups):
|
||||||
|
if conductor_groups:
|
||||||
|
query = query.join(models.Node,
|
||||||
|
models.Port.node_id == models.Node.id)
|
||||||
|
query = query.filter(
|
||||||
|
models.Node.conductor_group.in_(conductor_groups))
|
||||||
|
return query
|
||||||
|
|
||||||
|
|
||||||
|
def add_portgroup_filter_by_node_conductor_groups(query, conductor_groups):
|
||||||
|
if conductor_groups:
|
||||||
|
query = query.join(models.Node,
|
||||||
|
models.Portgroup.node_id == models.Node.id)
|
||||||
|
query = query.filter(
|
||||||
|
models.Node.conductor_group.in_(conductor_groups))
|
||||||
|
return query
|
||||||
|
|
||||||
|
|
||||||
def add_node_filter_by_chassis(query, value):
|
def add_node_filter_by_chassis(query, value):
|
||||||
if strutils.is_int_like(value):
|
if strutils.is_int_like(value):
|
||||||
return query.filter_by(chassis_id=value)
|
return query.filter_by(chassis_id=value)
|
||||||
@@ -1080,13 +1098,16 @@ class Connection(api.Connection):
|
|||||||
|
|
||||||
def get_port_list(self, limit=None, marker=None,
|
def get_port_list(self, limit=None, marker=None,
|
||||||
sort_key=None, sort_dir=None, owner=None,
|
sort_key=None, sort_dir=None, owner=None,
|
||||||
project=None, filters=None):
|
project=None, conductor_groups=None, filters=None):
|
||||||
query = sa.select(models.Port)
|
query = sa.select(models.Port)
|
||||||
|
|
||||||
if owner:
|
if owner:
|
||||||
query = add_port_filter_by_node_owner(query, owner)
|
query = add_port_filter_by_node_owner(query, owner)
|
||||||
elif project:
|
elif project:
|
||||||
query = add_port_filter_by_node_project(query, project)
|
query = add_port_filter_by_node_project(query, project)
|
||||||
query = add_port_filter_description_contains(query, filters)
|
query = add_port_filter_description_contains(query, filters)
|
||||||
|
query = add_port_filter_by_node_conductor_groups(query,
|
||||||
|
conductor_groups)
|
||||||
return _paginate_query(models.Port, limit, marker,
|
return _paginate_query(models.Port, limit, marker,
|
||||||
sort_key, sort_dir, query)
|
sort_key, sort_dir, query)
|
||||||
|
|
||||||
@@ -1223,8 +1244,11 @@ class Connection(api.Connection):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
def get_portgroup_list(self, limit=None, marker=None,
|
def get_portgroup_list(self, limit=None, marker=None,
|
||||||
sort_key=None, sort_dir=None, project=None):
|
sort_key=None, sort_dir=None, project=None,
|
||||||
|
conductor_groups=None, filters=None):
|
||||||
query = sa.select(models.Portgroup)
|
query = sa.select(models.Portgroup)
|
||||||
|
query = add_portgroup_filter_by_node_conductor_groups(
|
||||||
|
query, conductor_groups)
|
||||||
if project:
|
if project:
|
||||||
query = add_portgroup_filter_by_node_project(query, project)
|
query = add_portgroup_filter_by_node_project(query, project)
|
||||||
return _paginate_query(models.Portgroup, limit, marker,
|
return _paginate_query(models.Portgroup, limit, marker,
|
||||||
|
@@ -266,7 +266,8 @@ class Port(base.IronicObject, object_base.VersionedObjectDictCompat):
|
|||||||
# @object_base.remotable_classmethod
|
# @object_base.remotable_classmethod
|
||||||
@classmethod
|
@classmethod
|
||||||
def list(cls, context, limit=None, marker=None, sort_key=None,
|
def list(cls, context, limit=None, marker=None, sort_key=None,
|
||||||
sort_dir=None, owner=None, project=None, filters=None):
|
sort_dir=None, owner=None, project=None, conductor_groups=None,
|
||||||
|
filters=None):
|
||||||
"""Return a list of Port objects.
|
"""Return a list of Port objects.
|
||||||
|
|
||||||
:param context: Security context.
|
:param context: Security context.
|
||||||
@@ -276,6 +277,8 @@ class Port(base.IronicObject, object_base.VersionedObjectDictCompat):
|
|||||||
:param sort_dir: direction to sort. "asc" or "desc".
|
:param sort_dir: direction to sort. "asc" or "desc".
|
||||||
:param owner: DEPRECATED a node owner to match against
|
:param owner: DEPRECATED a node owner to match against
|
||||||
:param project: a node owner or lessee to match against
|
:param project: a node owner or lessee to match against
|
||||||
|
:param conductor_groups: A list of conductor groups to filter by,
|
||||||
|
defaults to None
|
||||||
:param filters: Filters to apply, defaults to None
|
:param filters: Filters to apply, defaults to None
|
||||||
:returns: a list of :class:`Port` object.
|
:returns: a list of :class:`Port` object.
|
||||||
:raises: InvalidParameterValue
|
:raises: InvalidParameterValue
|
||||||
@@ -288,7 +291,8 @@ class Port(base.IronicObject, object_base.VersionedObjectDictCompat):
|
|||||||
sort_key=sort_key,
|
sort_key=sort_key,
|
||||||
sort_dir=sort_dir,
|
sort_dir=sort_dir,
|
||||||
project=project,
|
project=project,
|
||||||
filters=filters)
|
filters=filters,
|
||||||
|
conductor_groups=conductor_groups)
|
||||||
return cls._from_db_object_list(context, db_ports)
|
return cls._from_db_object_list(context, db_ports)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@@ -195,7 +195,8 @@ class Portgroup(base.IronicObject, object_base.VersionedObjectDictCompat):
|
|||||||
# @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, project=None):
|
sort_key=None, sort_dir=None, project=None,
|
||||||
|
conductor_groups=None, filters=None):
|
||||||
"""Return a list of Portgroup objects.
|
"""Return a list of Portgroup objects.
|
||||||
|
|
||||||
:param cls: the :class:`Portgroup`
|
:param cls: the :class:`Portgroup`
|
||||||
@@ -205,15 +206,21 @@ class Portgroup(base.IronicObject, object_base.VersionedObjectDictCompat):
|
|||||||
: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: a node owner or lessee to match against.
|
:param project: a node owner or lessee to match against.
|
||||||
|
:param conductor_groups: A list of conductor groups to filter by,
|
||||||
|
defaults to None
|
||||||
|
:param filters: Filters to apply, defaults to None
|
||||||
:returns: A list of :class:`Portgroup` object.
|
:returns: A list of :class:`Portgroup` object.
|
||||||
:raises: InvalidParameterValue
|
:raises: InvalidParameterValue
|
||||||
|
|
||||||
"""
|
"""
|
||||||
db_portgroups = cls.dbapi.get_portgroup_list(limit=limit,
|
db_portgroups = cls.dbapi.get_portgroup_list(
|
||||||
marker=marker,
|
limit=limit,
|
||||||
sort_key=sort_key,
|
marker=marker,
|
||||||
sort_dir=sort_dir,
|
sort_key=sort_key,
|
||||||
project=project)
|
sort_dir=sort_dir,
|
||||||
|
project=project,
|
||||||
|
filters=filters,
|
||||||
|
conductor_groups=conductor_groups)
|
||||||
return cls._from_db_object_list(context, db_portgroups)
|
return cls._from_db_object_list(context, db_portgroups)
|
||||||
|
|
||||||
# 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
|
||||||
|
@@ -197,7 +197,22 @@ class TestPortsController__GetPortsCollection(base.TestCase):
|
|||||||
resource_url='ports')
|
resource_url='ports')
|
||||||
mock_list.assert_called_once_with('fake-context', 1000, None,
|
mock_list.assert_called_once_with('fake-context', 1000, None,
|
||||||
project=None, sort_dir='asc',
|
project=None, sort_dir='asc',
|
||||||
sort_key=None, filters={})
|
sort_key=None, conductor_groups=None,
|
||||||
|
filters={})
|
||||||
|
|
||||||
|
def test__get_ports_collection_conductor_groups(self, mock_request,
|
||||||
|
mock_list):
|
||||||
|
mock_request.context = 'fake-context'
|
||||||
|
mock_list.return_value = []
|
||||||
|
self.controller._get_ports_collection(None, None, None, None, None,
|
||||||
|
None, None, 'asc',
|
||||||
|
resource_url='ports',
|
||||||
|
conductor_groups=['foo'])
|
||||||
|
mock_list.assert_called_once_with('fake-context', 1000, None,
|
||||||
|
project=None, sort_dir='asc',
|
||||||
|
sort_key=None,
|
||||||
|
conductor_groups=['foo'],
|
||||||
|
filters={})
|
||||||
|
|
||||||
|
|
||||||
@mock.patch.object(objects.Port, 'get_by_address', autospec=True)
|
@mock.patch.object(objects.Port, 'get_by_address', autospec=True)
|
||||||
@@ -1171,6 +1186,44 @@ class TestListPortsByShard(test_api_base.BaseApiTest):
|
|||||||
self.assertNotEqual(res['ports'][0]['address'], bad_shard_address)
|
self.assertNotEqual(res['ports'][0]['address'], bad_shard_address)
|
||||||
self.assertNotEqual(res['ports'][1]['address'], bad_shard_address)
|
self.assertNotEqual(res['ports'][1]['address'], bad_shard_address)
|
||||||
|
|
||||||
|
def test_get_all_by_conductor_groups(self):
|
||||||
|
# Get /v1/ports with filter on conductor group
|
||||||
|
node_a = obj_utils.create_test_node(self.context,
|
||||||
|
uuid=uuidutils.generate_uuid(),
|
||||||
|
conductor_group='group_a')
|
||||||
|
node_b = obj_utils.create_test_node(self.context,
|
||||||
|
uuid=uuidutils.generate_uuid(),
|
||||||
|
conductor_group='group_b')
|
||||||
|
uuids_group_a = []
|
||||||
|
uuids_group_b = []
|
||||||
|
for i in range(0, 2):
|
||||||
|
port = obj_utils.create_test_port(self.context,
|
||||||
|
node_id=node_a.id,
|
||||||
|
uuid=uuidutils.generate_uuid(),
|
||||||
|
address='52:54:00:cf:2d:3%s' % i)
|
||||||
|
uuids_group_a.append(port.uuid)
|
||||||
|
for i in range(3, 6):
|
||||||
|
port = obj_utils.create_test_port(self.context,
|
||||||
|
node_id=node_b.id,
|
||||||
|
uuid=uuidutils.generate_uuid(),
|
||||||
|
address='52:54:00:cf:2d:3%s' % i)
|
||||||
|
uuids_group_b.append(port.uuid)
|
||||||
|
data = self.get_json(
|
||||||
|
'/ports?conductor_groups=%s' % 'group_a,group_b',
|
||||||
|
headers={api_base.Version.string: str(api_v1.max_version())})
|
||||||
|
self.assertEqual(5, len(data['ports']))
|
||||||
|
self.assertJsonEqual(uuids_group_a + uuids_group_b,
|
||||||
|
[x['uuid'] for x in data['ports']])
|
||||||
|
data = self.get_json(
|
||||||
|
'/ports?conductor_groups=%s' % 'group_b',
|
||||||
|
headers={api_base.Version.string: str(api_v1.max_version())})
|
||||||
|
self.assertEqual(3, len(data['ports']))
|
||||||
|
self.assertJsonEqual(uuids_group_b, [x['uuid'] for x in data['ports']])
|
||||||
|
data = self.get_json(
|
||||||
|
'/ports?conductor_groups=%s' % 'no_such_group',
|
||||||
|
headers={api_base.Version.string: str(api_v1.max_version())})
|
||||||
|
self.assertEqual([], data['ports'])
|
||||||
|
|
||||||
|
|
||||||
@mock.patch.object(rpcapi.ConductorAPI, 'update_port', autospec=True,
|
@mock.patch.object(rpcapi.ConductorAPI, 'update_port', autospec=True,
|
||||||
side_effect=_rpcapi_update_port)
|
side_effect=_rpcapi_update_port)
|
||||||
|
@@ -596,6 +596,49 @@ class TestListPortgroups(test_api_base.BaseApiTest):
|
|||||||
self.assertEqual(portgroup.uuid, data['portgroups'][0]['uuid'])
|
self.assertEqual(portgroup.uuid, data['portgroups'][0]['uuid'])
|
||||||
self.assertEqual(self.node.uuid, data['portgroups'][0]['node_uuid'])
|
self.assertEqual(self.node.uuid, data['portgroups'][0]['node_uuid'])
|
||||||
|
|
||||||
|
def test_get_all_by_conductor_groups(self):
|
||||||
|
# Get /v1/portgroups with filter on conductor group
|
||||||
|
node_a = obj_utils.create_test_node(self.context,
|
||||||
|
uuid=uuidutils.generate_uuid(),
|
||||||
|
conductor_group='group_a')
|
||||||
|
node_b = obj_utils.create_test_node(self.context,
|
||||||
|
uuid=uuidutils.generate_uuid(),
|
||||||
|
conductor_group='group_b')
|
||||||
|
uuids_group_a = []
|
||||||
|
uuids_group_b = []
|
||||||
|
for i in range(0, 2):
|
||||||
|
portgroup = obj_utils.create_test_portgroup(
|
||||||
|
self.context,
|
||||||
|
node_id=node_a.id,
|
||||||
|
uuid=uuidutils.generate_uuid(),
|
||||||
|
name='foo-%s' % i,
|
||||||
|
address='52:54:00:cf:2d:3%s' % i)
|
||||||
|
uuids_group_a.append(portgroup.uuid)
|
||||||
|
for i in range(3, 6):
|
||||||
|
portgroup = obj_utils.create_test_portgroup(
|
||||||
|
self.context,
|
||||||
|
node_id=node_b.id,
|
||||||
|
uuid=uuidutils.generate_uuid(),
|
||||||
|
name='foo-%s' % i,
|
||||||
|
address='52:54:00:cf:2d:3%s' % i)
|
||||||
|
uuids_group_b.append(portgroup.uuid)
|
||||||
|
data = self.get_json(
|
||||||
|
'/portgroups?conductor_groups=%s' % 'group_a,group_b',
|
||||||
|
headers={api_base.Version.string: str(api_v1.max_version())})
|
||||||
|
self.assertEqual(5, len(data['portgroups']))
|
||||||
|
self.assertJsonEqual(uuids_group_a + uuids_group_b,
|
||||||
|
[x['uuid'] for x in data['portgroups']])
|
||||||
|
data = self.get_json(
|
||||||
|
'/portgroups?conductor_groups=%s' % 'group_b',
|
||||||
|
headers={api_base.Version.string: str(api_v1.max_version())})
|
||||||
|
self.assertEqual(3, len(data['portgroups']))
|
||||||
|
self.assertJsonEqual(uuids_group_b,
|
||||||
|
[x['uuid'] for x in data['portgroups']])
|
||||||
|
data = self.get_json(
|
||||||
|
'/portgroups?conductor_groups=%s' % 'no_such_group',
|
||||||
|
headers={api_base.Version.string: str(api_v1.max_version())})
|
||||||
|
self.assertEqual([], data['portgroups'])
|
||||||
|
|
||||||
|
|
||||||
@mock.patch.object(rpcapi.ConductorAPI, 'update_portgroup', autospec=True)
|
@mock.patch.object(rpcapi.ConductorAPI, 'update_portgroup', autospec=True)
|
||||||
class TestPatch(test_api_base.BaseApiTest):
|
class TestPatch(test_api_base.BaseApiTest):
|
||||||
|
@@ -111,6 +111,41 @@ class DbportgroupTestCase(base.DbTestCase):
|
|||||||
def test_get_portgroups_by_node_id_that_does_not_exist(self):
|
def test_get_portgroups_by_node_id_that_does_not_exist(self):
|
||||||
self.assertEqual([], self.dbapi.get_portgroups_by_node_id(99))
|
self.assertEqual([], self.dbapi.get_portgroups_by_node_id(99))
|
||||||
|
|
||||||
|
def test_get_portgoups_by_conductor_groups(self):
|
||||||
|
group_a_node = db_utils.create_test_node(
|
||||||
|
uuid=uuidutils.generate_uuid(),
|
||||||
|
conductor_group='group_a')
|
||||||
|
group_b_node = db_utils.create_test_node(
|
||||||
|
uuid=uuidutils.generate_uuid(),
|
||||||
|
conductor_group='group_b')
|
||||||
|
|
||||||
|
group_a_uuids = []
|
||||||
|
group_b_uuids = []
|
||||||
|
for i in range(1, 3):
|
||||||
|
port = db_utils.create_test_portgroup(
|
||||||
|
uuid=uuidutils.generate_uuid(), node_id=group_a_node.id,
|
||||||
|
address='52:54:00:cf:2d:4%s' % i, name='group_a_node_pg%s' % i)
|
||||||
|
group_a_uuids.append(str(port.uuid))
|
||||||
|
for i in range(7, 9):
|
||||||
|
port = db_utils.create_test_portgroup(
|
||||||
|
uuid=uuidutils.generate_uuid(), node_id=group_b_node.id,
|
||||||
|
address='52:54:00:cf:2d:4%s' % i, name='group_b_node_pg%s' % i)
|
||||||
|
group_b_uuids.append(str(port.uuid))
|
||||||
|
for i in range(4, 6):
|
||||||
|
port = db_utils.create_test_portgroup(
|
||||||
|
uuid=uuidutils.generate_uuid(), node_id=self.node.id,
|
||||||
|
address='52:54:00:cf:2d:4%s' % i, name='SetUp_node_pg%s' % i)
|
||||||
|
res = self.dbapi.get_portgroup_list(conductor_groups=['group_a'])
|
||||||
|
res_uuids = [r.uuid for r in res]
|
||||||
|
self.assertJsonEqual(group_a_uuids, res_uuids)
|
||||||
|
res = self.dbapi.get_portgroup_list(conductor_groups=['group_b'])
|
||||||
|
res_uuids = [r.uuid for r in res]
|
||||||
|
self.assertJsonEqual(group_b_uuids, res_uuids)
|
||||||
|
res = self.dbapi.get_portgroup_list(
|
||||||
|
conductor_groups=['group_a', 'group_b'])
|
||||||
|
res_uuids = [r.uuid for r in res]
|
||||||
|
self.assertJsonEqual(group_a_uuids + group_b_uuids, res_uuids)
|
||||||
|
|
||||||
def test_destroy_portgroup(self):
|
def test_destroy_portgroup(self):
|
||||||
self.dbapi.destroy_portgroup(self.portgroup.id)
|
self.dbapi.destroy_portgroup(self.portgroup.id)
|
||||||
self.assertRaises(exception.PortgroupNotFound,
|
self.assertRaises(exception.PortgroupNotFound,
|
||||||
|
@@ -165,6 +165,39 @@ class DbPortTestCase(base.DbTestCase):
|
|||||||
res_uuids = [r.uuid for r in res]
|
res_uuids = [r.uuid for r in res]
|
||||||
self.assertCountEqual(uuids, res_uuids)
|
self.assertCountEqual(uuids, res_uuids)
|
||||||
|
|
||||||
|
def test_get_port_list_filter_by_conductor_groups(self):
|
||||||
|
group_a_node = db_utils.create_test_node(
|
||||||
|
uuid=uuidutils.generate_uuid(), conductor_group='group_a')
|
||||||
|
group_b_node = db_utils.create_test_node(
|
||||||
|
uuid=uuidutils.generate_uuid(), conductor_group='group_b')
|
||||||
|
|
||||||
|
group_a_uuids = []
|
||||||
|
group_b_uuids = []
|
||||||
|
for i in range(1, 3):
|
||||||
|
port = db_utils.create_test_port(uuid=uuidutils.generate_uuid(),
|
||||||
|
node_id=group_a_node.id,
|
||||||
|
address='52:54:00:cf:2d:4%s' % i)
|
||||||
|
group_a_uuids.append(str(port.uuid))
|
||||||
|
for i in range(7, 9):
|
||||||
|
port = db_utils.create_test_port(uuid=uuidutils.generate_uuid(),
|
||||||
|
node_id=group_b_node.id,
|
||||||
|
address='52:54:00:cf:2d:4%s' % i)
|
||||||
|
group_b_uuids.append(str(port.uuid))
|
||||||
|
for i in range(4, 6):
|
||||||
|
port = db_utils.create_test_port(uuid=uuidutils.generate_uuid(),
|
||||||
|
node_id=self.node.id,
|
||||||
|
address='52:54:00:cf:2d:4%s' % i)
|
||||||
|
res = self.dbapi.get_port_list(conductor_groups=['group_a'])
|
||||||
|
res_uuids = [r.uuid for r in res]
|
||||||
|
self.assertJsonEqual(group_a_uuids, res_uuids)
|
||||||
|
res = self.dbapi.get_port_list(conductor_groups=['group_b'])
|
||||||
|
res_uuids = [r.uuid for r in res]
|
||||||
|
self.assertJsonEqual(group_b_uuids, res_uuids)
|
||||||
|
res = self.dbapi.get_port_list(
|
||||||
|
conductor_groups=['group_a', 'group_b'])
|
||||||
|
res_uuids = [r.uuid for r in res]
|
||||||
|
self.assertJsonEqual(group_a_uuids + group_b_uuids, res_uuids)
|
||||||
|
|
||||||
def test_get_ports_by_node_id(self):
|
def test_get_ports_by_node_id(self):
|
||||||
res = self.dbapi.get_ports_by_node_id(self.node.id)
|
res = self.dbapi.get_ports_by_node_id(self.node.id)
|
||||||
self.assertEqual(self.port.address, res[0].address)
|
self.assertEqual(self.port.address, res[0].address)
|
||||||
|
@@ -167,7 +167,7 @@ class TestPortObject(db_base.DbTestCase, obj_utils.SchemasTestMixIn):
|
|||||||
self.assertEqual(self.context, ports[0]._context)
|
self.assertEqual(self.context, ports[0]._context)
|
||||||
mock_get_list.assert_called_once_with(
|
mock_get_list.assert_called_once_with(
|
||||||
limit=None, marker=None, project=None, sort_dir=None,
|
limit=None, marker=None, project=None, sort_dir=None,
|
||||||
sort_key=None, filters=None)
|
sort_key=None, conductor_groups=None, filters=None)
|
||||||
|
|
||||||
def test_list_deprecated_owner(self):
|
def test_list_deprecated_owner(self):
|
||||||
with mock.patch.object(self.dbapi, 'get_port_list',
|
with mock.patch.object(self.dbapi, 'get_port_list',
|
||||||
@@ -180,7 +180,7 @@ class TestPortObject(db_base.DbTestCase, obj_utils.SchemasTestMixIn):
|
|||||||
self.assertEqual(self.context, ports[0]._context)
|
self.assertEqual(self.context, ports[0]._context)
|
||||||
mock_get_list.assert_called_once_with(
|
mock_get_list.assert_called_once_with(
|
||||||
limit=None, marker=None, project='12345', sort_dir=None,
|
limit=None, marker=None, project='12345', sort_dir=None,
|
||||||
sort_key=None, filters=None)
|
sort_key=None, conductor_groups=None, filters=None)
|
||||||
|
|
||||||
@mock.patch.object(obj_base.IronicObject, 'supports_version',
|
@mock.patch.object(obj_base.IronicObject, 'supports_version',
|
||||||
spec_set=types.FunctionType)
|
spec_set=types.FunctionType)
|
||||||
|
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
It is now possible to filter by conductor groups when listing ports and
|
||||||
|
portgroups. For example, the following request returns only the ports for
|
||||||
|
nodes in conductor groups ``bear`` and ``metal``::
|
||||||
|
|
||||||
|
GET /v1/ports?conductor_groups=bear,metal
|
||||||
|
|
Reference in New Issue
Block a user