diff options
-rw-r--r-- | api-ref/source/baremetal-api-v1-nodes.inc | 6 | ||||
-rw-r--r-- | api-ref/source/parameters.yaml | 7 | ||||
-rw-r--r-- | doc/source/contributor/releasing.rst | 34 | ||||
-rw-r--r-- | ironic/api/controllers/v1/node.py | 30 | ||||
-rw-r--r-- | ironic/conductor/manager.py | 6 | ||||
-rw-r--r-- | ironic/conductor/steps.py | 6 | ||||
-rw-r--r-- | ironic/db/sqlalchemy/api.py | 3 | ||||
-rw-r--r-- | ironic/tests/unit/api/controllers/v1/test_node.py | 11 |
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)) |