summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api-ref/source/baremetal-api-v1-nodes.inc6
-rw-r--r--api-ref/source/parameters.yaml7
-rw-r--r--doc/source/contributor/releasing.rst34
-rw-r--r--ironic/api/controllers/v1/node.py30
-rw-r--r--ironic/conductor/manager.py6
-rw-r--r--ironic/conductor/steps.py6
-rw-r--r--ironic/db/sqlalchemy/api.py3
-rw-r--r--ironic/tests/unit/api/controllers/v1/test_node.py11
8 files changed, 68 insertions, 35 deletions
diff --git a/api-ref/source/baremetal-api-v1-nodes.inc b/api-ref/source/baremetal-api-v1-nodes.inc
index 4734c1b73..47bcceb58 100644
--- a/api-ref/source/baremetal-api-v1-nodes.inc
+++ b/api-ref/source/baremetal-api-v1-nodes.inc
@@ -286,7 +286,7 @@ provision state, and maintenance setting for each Node.
Introduced the ``lessee`` field.
.. versionadded:: 1.82
- Introduced the ``shard`` field.
+ Introduced the ``shard`` field. Introduced the ``sharded`` request parameter.
Normal response codes: 200
@@ -309,6 +309,7 @@ Request
- owner: owner
- lessee: lessee
- shard: req_shard
+ - sharded: req_sharded
- description_contains: r_description_contains
- fields: fields
- limit: limit
@@ -381,7 +382,7 @@ Nova instance, eg. with a request to ``v1/nodes/detail?instance_uuid={NOVA INSTA
Introduced the ``lessee`` field.
.. versionadded:: 1.82
- Introduced the ``shard`` field.
+ Introduced the ``shard`` field. Introduced the ``sharded`` request parameter.
Normal response codes: 200
@@ -404,6 +405,7 @@ Request
- owner: owner
- lessee: lessee
- shard: req_shard
+ - sharded: req_sharded
- description_contains: r_description_contains
- limit: limit
- marker: marker
diff --git a/api-ref/source/parameters.yaml b/api-ref/source/parameters.yaml
index 13eb45d7d..6a50c9452 100644
--- a/api-ref/source/parameters.yaml
+++ b/api-ref/source/parameters.yaml
@@ -1827,6 +1827,13 @@ req_shard:
in: body
required: false
type: array
+req_sharded:
+ description: |
+ When true, filter the list of returned Nodes, and only return the ones with
+ a non-null ``shard`` value. When false, the inverse filter is performed.
+ in: body
+ required: false
+ type: boolean
req_standalone_ports_supported:
description: |
Indicates whether ports that are members of this portgroup can be
diff --git a/doc/source/contributor/releasing.rst b/doc/source/contributor/releasing.rst
index 3c0732c5b..a83bdc2bd 100644
--- a/doc/source/contributor/releasing.rst
+++ b/doc/source/contributor/releasing.rst
@@ -5,7 +5,7 @@ Releasing Ironic Projects
Since the responsibility for releases will move between people, we document
that process here.
-A full list of projects that ironic manages is available in the `governance
+A full list of projects that Ironic manages is available in the `governance
site`_.
.. _`governance site`: https://governance.openstack.org/reference/projects/ironic.html
@@ -33,7 +33,7 @@ documented in the `Project Team Guide`_.
What do we have to release?
===========================
-The ironic project has a number of deliverables under its governance. The
+The Ironic project has a number of deliverables under its governance. The
ultimate source of truth for this is `projects.yaml
<https://opendev.org/openstack/governance/src/branch/master/reference/projects.yaml>`__
in the governance repository. These deliverables have varying release models,
@@ -41,7 +41,7 @@ and these are defined in the `deliverables YAML files
<https://opendev.org/openstack/releases/src/branch/master/deliverables>`__ in
the releases repository.
-In general, ironic deliverables follow the `cycle-with-intermediary
+In general, Ironic deliverables follow the `cycle-with-intermediary
<https://releases.openstack.org/reference/release_models.html#cycle-with-intermediary>`__
release model.
@@ -130,7 +130,7 @@ OpenStack release that happens semi-annually. These releases can be
found in a ``stable/NAME`` branch.
They are also evaluated for additional bugfix releases between scheduled
-stable releases at the two and four month milestore between stable releases
+stable releases at the two and four month milestone between stable releases
(roughly every 2 months). These releases can be found in a ``bugfix/X.Y``
branch. A bugfix release is only created if there are significant
beneficial changes and a known downstream operator or distributor will consume
@@ -143,12 +143,12 @@ version.
Currently releases and retirements from bugfix branches cannot be automated and
must be done by the release team manually.
-After the creation of a bugfix branch it is utmost important to update the
-upper-constraints link for the tests in the tox.ini file, plus override the
-branch for the requirements project to be sure to use the correct
+After the creation of a bugfix branch it is of the utmost importance to update
+the upper-constraints link for the tests in the tox.ini file, plus override
+the branch for the requirements project to be sure to use the correct
upper-constraints; for example see the following change:
-https://review.opendev.org/c/openstack/ironic-python-agent/+/841290
+https://review.opendev.org/c/openstack/Ironic-python-agent/+/841290
Things to do before releasing
=============================
@@ -159,7 +159,7 @@ Things to do before releasing
Combine release notes if necessary (for example, a release note for a
feature and another release note to add to that feature may be combined).
-* For ironic releases only, not ironic-inspector releases: if any new API
+* For Ironic releases only, not Ironic-inspector releases: if any new API
microversions have been added since the last release, update the REST API
version history (``doc/source/contributor/webapi-version-history.rst``) to
indicate that they were part of the new release.
@@ -200,7 +200,7 @@ following the next steps:
deliverable (i.e. subproject) grouped by release cycles.
* The ``_independent`` directory contains yaml files for deliverables that
- are not bound to (official) cycles (e.g. ironic-python-agent-builder).
+ are not bound to (official) cycles (e.g. Ironic-python-agent-builder).
* To check the changes we're about to release we can use the tox environment
``list-unreleased-changes``, with this syntax:
@@ -213,7 +213,7 @@ following the next steps:
not stable/ussuri or stable/train).
For example, assuming we're in the main directory of the releases repository,
- to check the changes in the ussuri series for ironic-python-agent
+ to check the changes in the ussuri series for Ironic-python-agent
type:
.. code-block:: bash
@@ -243,12 +243,12 @@ following the next steps:
The ``--intermediate-branch`` option is used to create an intermediate
bugfix branch following the
- `new release model for ironic projects <https://specs.openstack.org/openstack/ironic-specs/specs/not-implemented/new-release-model.html>`_.
+ `new release model for Ironic projects <https://specs.openstack.org/openstack/ironic-specs/specs/not-implemented/new-release-model.html>`_.
To propose the release, use the script to update the deliverable file, then
commit the change, and propose it for review.
- For example, to propose a minor release for ironic in the master branch
+ For example, to propose a minor release for Ironic in the master branch
(current development branch), considering that the code name of the future
stable release is wallaby, use:
@@ -260,7 +260,7 @@ following the next steps:
deliverable, the new version and the branch, if applicable.
A good commit message title should also include the same, for example
- "Release ironic 1.2.3 for ussuri"
+ "Release Ironic 1.2.3 for ussuri"
* As an optional step, we can use ``tox -e list-changes`` to double-check the
changes before submitting them for review.
@@ -310,7 +310,7 @@ This includes:
We need to submit patches for changes in the stable branch to:
-* update the ironic devstack plugin to point at the branched tarball for IPA.
+* update the Ironic devstack plugin to point at the branched tarball for IPA.
An example of this patch is
`here <https://review.opendev.org/685069/>`_.
* set appropriate defaults for ``TEMPEST_BAREMETAL_MIN_MICROVERSION`` and
@@ -324,7 +324,7 @@ We need to submit patches for changes on master to:
need to make these changes. Note that we need to wait until *after* the
switch in grenade is made to test the latest release (N) with master
(e.g. `for stable/queens <https://review.opendev.org/#/c/543615>`_).
- Doing these changes sooner -- after the ironic release and before the switch
+ Doing these changes sooner -- after the Ironic release and before the switch
when grenade is testing the prior release (N-1) with master, will cause
the tests to fail. (You may want to ask/remind infra/qa team, as to
when they will do this switch.)
@@ -335,7 +335,7 @@ We need to submit patches for changes on master to:
only support upgrades from the most recent named release to master.
* remove any DB migration scripts from ``ironic.cmd.dbsync.ONLINE_MIGRATIONS``
- and remove the corresponding code from ironic. (These migration scripts
+ and remove the corresponding code from Ironic. (These migration scripts
are used to migrate from an old release to this latest release; they
shouldn't be needed after that.)
diff --git a/ironic/api/controllers/v1/node.py b/ironic/api/controllers/v1/node.py
index 022b5f66a..d21b075c8 100644
--- a/ironic/api/controllers/v1/node.py
+++ b/ironic/api/controllers/v1/node.py
@@ -2072,7 +2072,8 @@ class NodesController(rest.RestController):
fields=None, fault=None, conductor_group=None,
detail=None, conductor=None, owner=None,
lessee=None, project=None,
- description_contains=None, shard=None):
+ description_contains=None, shard=None,
+ sharded=None):
if self.from_chassis and not chassis_uuid:
raise exception.MissingParameterValue(
_("Chassis id not specified."))
@@ -2113,7 +2114,8 @@ class NodesController(rest.RestController):
'project': project,
'description_contains': description_contains,
'retired': retired,
- 'instance_uuid': instance_uuid
+ 'instance_uuid': instance_uuid,
+ 'sharded': sharded
}
filters = {}
for key, value in possible_filters.items():
@@ -2258,14 +2260,14 @@ class NodesController(rest.RestController):
detail=args.boolean, conductor=args.string,
owner=args.string, description_contains=args.string,
lessee=args.string, project=args.string,
- shard=args.string_list)
+ shard=args.string_list, sharded=args.boolean)
def get_all(self, chassis_uuid=None, instance_uuid=None, associated=None,
maintenance=None, retired=None, provision_state=None,
marker=None, limit=None, sort_key='id', sort_dir='asc',
driver=None, fields=None, resource_class=None, fault=None,
conductor_group=None, detail=None, conductor=None,
owner=None, description_contains=None, lessee=None,
- project=None, shard=None):
+ project=None, shard=None, sharded=None):
"""Retrieve a list of nodes.
:param chassis_uuid: Optional UUID of a chassis, to get only nodes for
@@ -2311,6 +2313,9 @@ class NodesController(rest.RestController):
:param description_contains: Optional string value to get only nodes
with description field contains matching
value.
+ :param sharded: Optional boolean whether to return a list of
+ nodes with or without a shard set. May be combined
+ with other parameters.
"""
project = api_utils.check_list_policy('node', project)
@@ -2326,6 +2331,8 @@ class NodesController(rest.RestController):
api_utils.check_allow_filter_by_owner(owner)
api_utils.check_allow_filter_by_lessee(lessee)
api_utils.check_allow_filter_by_shard(shard)
+ # Sharded is guarded by the same API version as shard
+ api_utils.check_allow_filter_by_shard(sharded)
fields = api_utils.get_request_return_fields(fields, detail,
_DEFAULT_RETURN_FIELDS)
@@ -2342,8 +2349,8 @@ class NodesController(rest.RestController):
detail=detail,
conductor=conductor,
owner=owner, lessee=lessee,
- shard=shard, project=project,
- **extra_args)
+ shard=shard, sharded=sharded,
+ project=project, **extra_args)
@METRICS.timer('NodesController.detail')
@method.expose()
@@ -2356,14 +2363,14 @@ class NodesController(rest.RestController):
conductor_group=args.string, conductor=args.string,
owner=args.string, description_contains=args.string,
lessee=args.string, project=args.string,
- shard=args.string_list)
+ shard=args.string_list, sharded=args.boolean)
def detail(self, chassis_uuid=None, instance_uuid=None, associated=None,
maintenance=None, retired=None, provision_state=None,
marker=None, limit=None, sort_key='id', sort_dir='asc',
driver=None, resource_class=None, fault=None,
conductor_group=None, conductor=None, owner=None,
description_contains=None, lessee=None, project=None,
- shard=None):
+ shard=None, sharded=None):
"""Retrieve a list of nodes with detail.
:param chassis_uuid: Optional UUID of a chassis, to get only nodes for
@@ -2404,6 +2411,9 @@ class NodesController(rest.RestController):
:param description_contains: Optional string value to get only nodes
with description field contains matching
value.
+ :param sharded: Optional boolean whether to return a list of
+ nodes with or without a shard set. May be combined
+ with other parameters.
"""
project = api_utils.check_list_policy('node', project)
@@ -2422,6 +2432,8 @@ class NodesController(rest.RestController):
api_utils.check_allow_filter_by_conductor(conductor)
api_utils.check_allow_filter_by_shard(shard)
+ # Sharded is guarded by the same API version as shard
+ api_utils.check_allow_filter_by_shard(sharded)
extra_args = {'description_contains': description_contains}
return self._get_nodes_collection(chassis_uuid, instance_uuid,
@@ -2436,7 +2448,7 @@ class NodesController(rest.RestController):
conductor=conductor,
owner=owner, lessee=lessee,
project=project, shard=shard,
- **extra_args)
+ sharded=sharded, **extra_args)
@METRICS.timer('NodesController.validate')
@method.expose()
diff --git a/ironic/conductor/manager.py b/ironic/conductor/manager.py
index a5817cf2e..c658ae8a6 100644
--- a/ironic/conductor/manager.py
+++ b/ironic/conductor/manager.py
@@ -2034,9 +2034,9 @@ class ConductorManager(base_manager.BaseConductorManager):
'node %(node)s: %(e)s',
{'node': node.uuid, 'e': e})
else:
- LOG.error('Swift object cannot be orphaned during '
- 'destruction of node %(node)s: %(e)s',
- {'node': node.uuid, 'e': e})
+ LOG.error('Swift object cannot be orphaned without '
+ 'maintenance mode during destruction of node '
+ '%(node)s: %(e)s', {'node': node.uuid, 'e': e})
raise
except Exception as err:
LOG.error('Failed to delete Swift entries related '
diff --git a/ironic/conductor/steps.py b/ironic/conductor/steps.py
index 252b094a9..5a0fdd7b4 100644
--- a/ironic/conductor/steps.py
+++ b/ironic/conductor/steps.py
@@ -194,9 +194,9 @@ def _get_cleaning_steps(task, enabled=False, sort=True):
sort_step_key=sort_key,
prio_overrides=csp_override)
- LOG.debug("cleaning_steps after applying "
- "clean_step_priority_override for node %(node)s: %(step)s",
- task.node.uuid, cleaning_steps)
+ LOG.debug('cleaning_steps after applying '
+ 'clean_step_priority_override for node %(node)s: %(steps)s',
+ {'node': task.node.uuid, 'steps': cleaning_steps})
else:
cleaning_steps = _get_steps(task, CLEANING_INTERFACE_PRIORITY,
'get_clean_steps', enabled=enabled,
diff --git a/ironic/db/sqlalchemy/api.py b/ironic/db/sqlalchemy/api.py
index c33cecff0..9202aab09 100644
--- a/ironic/db/sqlalchemy/api.py
+++ b/ironic/db/sqlalchemy/api.py
@@ -401,7 +401,8 @@ class Connection(api.Connection):
for field in ('uuid', 'provision_state', 'shard')}
_NODE_NON_NULL_FILTERS = {'associated': 'instance_uuid',
'reserved': 'reservation',
- 'with_power_state': 'power_state'}
+ 'with_power_state': 'power_state',
+ 'sharded': 'shard'}
_NODE_FILTERS = ({'chassis_uuid', 'reserved_by_any_of',
'provisioned_before', 'inspection_started_before',
'description_contains', 'project'}
diff --git a/ironic/tests/unit/api/controllers/v1/test_node.py b/ironic/tests/unit/api/controllers/v1/test_node.py
index 19ef1235c..d56652b1e 100644
--- a/ironic/tests/unit/api/controllers/v1/test_node.py
+++ b/ironic/tests/unit/api/controllers/v1/test_node.py
@@ -8040,6 +8040,17 @@ class TestNodeShardGets(test_api_base.BaseApiTest):
expect_errors=True, headers=headers)
self.assertEqual(http_client.NOT_ACCEPTABLE, result.status_code)
+ def test_filtering_by_sharded(self):
+ obj_utils.create_test_node(self.context, uuid=uuid.uuid4())
+ obj_utils.create_test_node(self.context, uuid=uuid.uuid4())
+ # We now have one node in shard foo (setUp) and two unsharded.
+ result_true = self.get_json(
+ '/nodes?sharded=true', headers=self.headers)
+ result_false = self.get_json(
+ '/nodes?sharded=false', headers=self.headers)
+ self.assertEqual(1, len(result_true['nodes']))
+ self.assertEqual(2, len(result_false['nodes']))
+
@mock.patch.object(rpcapi.ConductorAPI, 'create_node',
lambda _api, _ctx, node, _topic: _create_node_locally(node))