mirror of
https://opendev.org/openstack/ironic.git
synced 2026-01-16 23:01:47 +00:00
Merge "Add node.instance_name"
This commit is contained in:
commit
1a031b03a7
24 changed files with 276 additions and 14 deletions
|
|
@ -116,12 +116,15 @@ supplied when the Node is created, or the resource may be updated later.
|
|||
.. versionadded:: 1.82
|
||||
Introduced the ``shard`` field.
|
||||
|
||||
.. versionadded: 1.83
|
||||
.. versionadded:: 1.83
|
||||
Introduced the ``parent_node`` field.
|
||||
|
||||
.. versionadded: 1.95
|
||||
.. versionadded:: 1.95
|
||||
Introduced the ``disable_power_off`` field.
|
||||
|
||||
.. versionadded:: 1.104
|
||||
Introduced the ``instance_name`` field.
|
||||
|
||||
Normal response codes: 201
|
||||
|
||||
Error codes: 400,403,406
|
||||
|
|
@ -160,6 +163,7 @@ Request
|
|||
- chassis_uuid: req_chassis_uuid
|
||||
- instance_info: req_instance_info
|
||||
- instance_uuid: req_instance_uuid
|
||||
- instance_name: req_instance_name
|
||||
- maintenance: req_maintenance
|
||||
- maintenance_reason: maintenance_reason
|
||||
- network_data: network_data
|
||||
|
|
@ -203,6 +207,7 @@ microversion 1.95.
|
|||
- properties: n_properties
|
||||
- instance_info: instance_info
|
||||
- instance_uuid: instance_uuid
|
||||
- instance_name: instance_name
|
||||
- chassis_uuid: chassis_uuid
|
||||
- extra: extra
|
||||
- console_enabled: console_enabled
|
||||
|
|
@ -314,6 +319,9 @@ provision state, and maintenance setting for each Node.
|
|||
nodes to be enumerated, which are normally hidden as child nodes are not
|
||||
normally intended for direct consumption by end users.
|
||||
|
||||
.. versionadded:: 1.104
|
||||
Introduced the ``instance_name`` query parameter and response field.
|
||||
|
||||
Normal response codes: 200
|
||||
|
||||
Error codes: 400,403,406
|
||||
|
|
@ -324,6 +332,7 @@ Request
|
|||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- instance_uuid: r_instance_uuid
|
||||
- instance_name: r_instance_name
|
||||
- maintenance: r_maintenance
|
||||
- associated: r_associated
|
||||
- provision_state: r_provision_state
|
||||
|
|
@ -412,6 +421,10 @@ Nova instance, eg. with a request to ``v1/nodes/detail?instance_uuid={NOVA INSTA
|
|||
.. versionadded:: 1.82
|
||||
Introduced the ``shard`` field. Introduced the ``sharded`` request parameter.
|
||||
|
||||
.. versionadded:: 1.104
|
||||
Introduced the ``instance_name`` field.
|
||||
|
||||
|
||||
Normal response codes: 200
|
||||
|
||||
Error codes: 400,403,406
|
||||
|
|
@ -422,6 +435,7 @@ Request
|
|||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- instance_uuid: r_instance_uuid
|
||||
- instance_name: r_instance_name
|
||||
- maintenance: r_maintenance
|
||||
- fault: r_fault
|
||||
- associated: r_associated
|
||||
|
|
@ -462,6 +476,7 @@ Response
|
|||
- properties: n_properties
|
||||
- instance_info: instance_info
|
||||
- instance_uuid: instance_uuid
|
||||
- instance_name: instance_name
|
||||
- chassis_uuid: chassis_uuid
|
||||
- extra: extra
|
||||
- console_enabled: console_enabled
|
||||
|
|
@ -571,6 +586,9 @@ only the specified set.
|
|||
.. versionadded:: 1.95
|
||||
Introduced the ``disable_power_off`` field.
|
||||
|
||||
.. versionadded:: 1.104
|
||||
Introduced the ``instance_name`` field.
|
||||
|
||||
Normal response codes: 200
|
||||
|
||||
Error codes: 400,403,404,406
|
||||
|
|
@ -605,6 +623,7 @@ Response
|
|||
- properties: n_properties
|
||||
- instance_info: instance_info
|
||||
- instance_uuid: instance_uuid
|
||||
- instance_name: instance_name
|
||||
- chassis_uuid: chassis_uuid
|
||||
- extra: extra
|
||||
- console_enabled: console_enabled
|
||||
|
|
@ -668,6 +687,9 @@ managed through sub-resources.
|
|||
.. versionadded:: 1.82
|
||||
Introduced the ability to set/unset a node's shard.
|
||||
|
||||
.. versionadded:: 1.104
|
||||
Introduced the ability to set/unset node's instance_name.
|
||||
|
||||
Normal response codes: 200
|
||||
|
||||
Error codes: 400,403,404,406,409
|
||||
|
|
@ -715,6 +737,7 @@ Response
|
|||
- properties: n_properties
|
||||
- instance_info: instance_info
|
||||
- instance_uuid: instance_uuid
|
||||
- instance_name: instance_name
|
||||
- chassis_uuid: chassis_uuid
|
||||
- extra: extra
|
||||
- console_enabled: console_enabled
|
||||
|
|
|
|||
|
|
@ -332,6 +332,13 @@ r_fault:
|
|||
in: query
|
||||
required: false
|
||||
type: string
|
||||
r_instance_name:
|
||||
description: |
|
||||
Filter the list of returned nodes, and only return the node with this
|
||||
specific instance name, or an empty set if not found.
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
r_instance_uuid:
|
||||
description: |
|
||||
Filter the list of returned nodes, and only return the node with this
|
||||
|
|
@ -1288,6 +1295,14 @@ instance_info:
|
|||
in: body
|
||||
required: true
|
||||
type: JSON
|
||||
instance_name:
|
||||
description: |
|
||||
A human-readable name for the instance deployed on this node. This is
|
||||
automatically synchronized with the ``display_name`` from the node's
|
||||
``instance_info`` for backward compatibility with Nova.
|
||||
in: body
|
||||
required: false
|
||||
type: string
|
||||
instance_uuid:
|
||||
description: |
|
||||
UUID of the Nova instance associated with this Node.
|
||||
|
|
@ -1897,6 +1912,14 @@ req_instance_info:
|
|||
in: body
|
||||
required: false
|
||||
type: JSON
|
||||
req_instance_name:
|
||||
description: |
|
||||
A human-readable name for the instance deployed on this node. This is
|
||||
automatically synchronized with the ``display_name`` from the node's
|
||||
``instance_info`` for backward compatibility with Nova.
|
||||
in: body
|
||||
required: false
|
||||
type: string
|
||||
req_instance_uuid:
|
||||
description: |
|
||||
UUID of the Nova instance associated with this Node.
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"inspection_started_at": null,
|
||||
"instance_info": {},
|
||||
"instance_uuid": null,
|
||||
"instance_name": null,
|
||||
"last_error": null,
|
||||
"lessee": null,
|
||||
"links": [
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
"inspection_started_at": null,
|
||||
"instance_info": {},
|
||||
"instance_uuid": null,
|
||||
"instance_name": null,
|
||||
"last_error": null,
|
||||
"lessee": null,
|
||||
"links": [
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
"inspection_started_at": null,
|
||||
"instance_info": {},
|
||||
"instance_uuid": null,
|
||||
"instance_name": null,
|
||||
"last_error": null,
|
||||
"lessee": null,
|
||||
"links": [
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
"inspection_started_at": null,
|
||||
"instance_info": {},
|
||||
"instance_uuid": "5344a3e2-978a-444e-990a-cbf47c62ef88",
|
||||
"instance_name": "my-test-instance",
|
||||
"last_error": null,
|
||||
"lessee": null,
|
||||
"links": [
|
||||
|
|
@ -133,6 +134,7 @@
|
|||
"inspection_started_at": null,
|
||||
"instance_info": {},
|
||||
"instance_uuid": null,
|
||||
"instance_name": null,
|
||||
"last_error": null,
|
||||
"lessee": null,
|
||||
"links": [
|
||||
|
|
|
|||
|
|
@ -2,6 +2,14 @@
|
|||
REST API Version History
|
||||
========================
|
||||
|
||||
1.104 (Gazpacho)
|
||||
------------------------
|
||||
|
||||
Add a new ``instance_name`` field to the node resource. This field provides
|
||||
a human-readable name for the instance deployed on a node and is automatically
|
||||
synchronized with ``instance_info.display_name`` for backward compatibility
|
||||
with Nova.
|
||||
|
||||
1.103 (Gazpacho)
|
||||
-----------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -184,6 +184,8 @@ def node_schema():
|
|||
'firmware_interface': {'type': ['string', 'null']},
|
||||
'inspect_interface': {'type': ['string', 'null']},
|
||||
'instance_info': {'type': ['object', 'null']},
|
||||
'instance_name': {'type': ['string', 'null'], 'minLength': 1,
|
||||
'maxLength': 255},
|
||||
'instance_uuid': {'type': ['string', 'null']},
|
||||
'lessee': {'type': ['string', 'null']},
|
||||
'management_interface': {'type': ['string', 'null']},
|
||||
|
|
@ -278,6 +280,7 @@ PATCH_ALLOWED_FIELDS = [
|
|||
'extra',
|
||||
'inspect_interface',
|
||||
'instance_info',
|
||||
'instance_name',
|
||||
'instance_uuid',
|
||||
'lessee',
|
||||
'maintenance',
|
||||
|
|
@ -1584,6 +1587,7 @@ def _get_fields_for_node_query(fields=None):
|
|||
'inspection_started_at',
|
||||
'inspect_interface',
|
||||
'instance_info',
|
||||
'instance_name',
|
||||
'instance_uuid',
|
||||
'last_error',
|
||||
'lessee',
|
||||
|
|
@ -2445,7 +2449,7 @@ class NodesController(rest.RestController):
|
|||
lessee=None, project=None,
|
||||
description_contains=None, shard=None,
|
||||
sharded=None, include_children=None,
|
||||
parent_node=None):
|
||||
parent_node=None, instance_name=None):
|
||||
if self.from_chassis and not chassis_uuid:
|
||||
raise exception.MissingParameterValue(
|
||||
_("Chassis id not specified."))
|
||||
|
|
@ -2486,6 +2490,7 @@ class NodesController(rest.RestController):
|
|||
'project': project,
|
||||
'description_contains': description_contains,
|
||||
'retired': retired,
|
||||
'instance_name': instance_name,
|
||||
'instance_uuid': instance_uuid,
|
||||
'sharded': sharded,
|
||||
'include_children': include_children,
|
||||
|
|
@ -2635,7 +2640,8 @@ class NodesController(rest.RestController):
|
|||
owner=args.string, description_contains=args.string,
|
||||
lessee=args.string, project=args.string,
|
||||
shard=args.string_list, sharded=args.boolean,
|
||||
include_children=args.boolean, parent_node=args.string)
|
||||
include_children=args.boolean, parent_node=args.string,
|
||||
instance_name=args.string)
|
||||
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',
|
||||
|
|
@ -2643,7 +2649,7 @@ class NodesController(rest.RestController):
|
|||
conductor_group=None, detail=None, conductor=None,
|
||||
owner=None, description_contains=None, lessee=None,
|
||||
project=None, shard=None, sharded=None, include_children=None,
|
||||
parent_node=None):
|
||||
parent_node=None, instance_name=None):
|
||||
"""Retrieve a list of nodes.
|
||||
|
||||
:param chassis_uuid: Optional UUID of a chassis, to get only nodes for
|
||||
|
|
@ -2692,6 +2698,8 @@ class NodesController(rest.RestController):
|
|||
:param sharded: Optional boolean whether to return a list of
|
||||
nodes with or without a shard set. May be combined
|
||||
with other parameters.
|
||||
:param instance_name: Optional string value to get nodes with a
|
||||
matching instance_name
|
||||
"""
|
||||
project = api_utils.check_list_policy('node', project)
|
||||
|
||||
|
|
@ -2709,6 +2717,7 @@ class NodesController(rest.RestController):
|
|||
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)
|
||||
api_utils.check_allow_filter_by_instance_name(instance_name)
|
||||
api_utils.check_allow_child_node_params(
|
||||
include_children=include_children,
|
||||
parent_node=parent_node)
|
||||
|
|
@ -2732,6 +2741,7 @@ class NodesController(rest.RestController):
|
|||
project=project,
|
||||
include_children=include_children,
|
||||
parent_node=parent_node,
|
||||
instance_name=instance_name,
|
||||
**extra_args)
|
||||
|
||||
@METRICS.timer('NodesController.detail')
|
||||
|
|
@ -2745,7 +2755,8 @@ 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, sharded=args.boolean)
|
||||
shard=args.string_list, sharded=args.boolean,
|
||||
instance_name=args.string)
|
||||
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',
|
||||
|
|
@ -2753,7 +2764,7 @@ class NodesController(rest.RestController):
|
|||
conductor_group=None, conductor=None, owner=None,
|
||||
description_contains=None, lessee=None, project=None,
|
||||
shard=None, sharded=None, include_children=None,
|
||||
parent_node=None):
|
||||
parent_node=None, instance_name=None):
|
||||
"""Retrieve a list of nodes with detail.
|
||||
|
||||
:param chassis_uuid: Optional UUID of a chassis, to get only nodes for
|
||||
|
|
@ -2797,6 +2808,8 @@ class NodesController(rest.RestController):
|
|||
:param sharded: Optional boolean whether to return a list of
|
||||
nodes with or without a shard set. May be combined
|
||||
with other parameters.
|
||||
:param instance_name: Optional string that sets an instance_name to
|
||||
search for
|
||||
"""
|
||||
project = api_utils.check_list_policy('node', project)
|
||||
|
||||
|
|
@ -2817,6 +2830,7 @@ class NodesController(rest.RestController):
|
|||
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)
|
||||
api_utils.check_allow_filter_by_instance_name(instance_name)
|
||||
|
||||
extra_args = {'description_contains': description_contains}
|
||||
return self._get_nodes_collection(chassis_uuid, instance_uuid,
|
||||
|
|
@ -2834,6 +2848,7 @@ class NodesController(rest.RestController):
|
|||
sharded=sharded,
|
||||
include_children=include_children,
|
||||
parent_node=parent_node,
|
||||
instance_name=instance_name,
|
||||
**extra_args)
|
||||
|
||||
@METRICS.timer('NodesController.validate')
|
||||
|
|
@ -3063,6 +3078,7 @@ class NodesController(rest.RestController):
|
|||
('/properties', 'baremetal:node:update:properties'),
|
||||
('/chassis_uuid', 'baremetal:node:update:chassis_uuid'),
|
||||
('/instance_uuid', 'baremetal:node:update:instance_uuid'),
|
||||
('/instance_name', 'baremetal:node:update:instance_info'),
|
||||
('/lessee', 'baremetal:node:update:lessee'),
|
||||
('/owner', 'baremetal:node:update:owner'),
|
||||
('/driver', 'baremetal:node:update:driver_interfaces'),
|
||||
|
|
@ -3214,6 +3230,15 @@ class NodesController(rest.RestController):
|
|||
node_dict, node_patch_schema(), node_patch_validator)
|
||||
|
||||
self._update_changed_fields(node_dict, rpc_node)
|
||||
|
||||
# For forward compatibility, sync display_name from instance_info
|
||||
# to instance_name if instance_name is not already set
|
||||
changed_fields = rpc_node.obj_what_changed()
|
||||
if ('instance_info' in changed_fields
|
||||
and not rpc_node.instance_name
|
||||
and rpc_node.instance_info.get('display_name')):
|
||||
rpc_node.instance_name = rpc_node.instance_info['display_name']
|
||||
|
||||
# NOTE(tenbrae): we calculate the rpc topic here in case node.driver
|
||||
# has changed, so that update is sent to the
|
||||
# new conductor, not the old one which may fail to
|
||||
|
|
|
|||
|
|
@ -897,6 +897,7 @@ VERSIONED_FIELDS = {
|
|||
'firmware_interface': versions.MINOR_86_FIRMWARE_INTERFACE,
|
||||
'service_step': versions.MINOR_87_SERVICE,
|
||||
'disable_power_off': versions.MINOR_95_DISABLE_POWER_OFF,
|
||||
'instance_name': versions.MINOR_104_NODE_INSTANCE_NAME,
|
||||
}
|
||||
|
||||
for field in V31_FIELDS:
|
||||
|
|
@ -2283,3 +2284,17 @@ def allow_port_category():
|
|||
Version 1.101 of the API added category field to the port object.
|
||||
"""
|
||||
return api.request.version.minor >= versions.MINOR_101_PORT_CATEGORY
|
||||
|
||||
|
||||
def allow_node_instance_name():
|
||||
"""Check if instance_name is allowed for nodes.
|
||||
|
||||
Version 1.104 of the API added instance_name field to the node object.
|
||||
"""
|
||||
return api.request.version.minor >= versions.MINOR_104_NODE_INSTANCE_NAME
|
||||
|
||||
|
||||
def check_allow_filter_by_instance_name(instance_name):
|
||||
if instance_name is not None and not allow_node_instance_name():
|
||||
raise exception.NotAcceptable(
|
||||
_("instance_name is not acceptable in this API version"))
|
||||
|
|
|
|||
|
|
@ -140,6 +140,9 @@ BASE_VERSION = 1
|
|||
# v1.100: Add vendor field to port.
|
||||
# v1.101: Add category field to port.
|
||||
# v1.102: Add physical_network field to portgroup.
|
||||
# v1.103: Add category field to portgroup
|
||||
# v1.104: Add instance_name to node
|
||||
|
||||
|
||||
MINOR_0_JUNO = 0
|
||||
MINOR_1_INITIAL_VERSION = 1
|
||||
|
|
@ -245,15 +248,18 @@ MINOR_100_PORT_VENDOR = 100
|
|||
MINOR_101_PORT_CATEGORY = 101
|
||||
MINOR_102_PORTGROUP_PHYSICAL_NETWORK = 102
|
||||
MINOR_103_PORTGROUP_CATEGORY = 103
|
||||
MINOR_104_NODE_INSTANCE_NAME = 104
|
||||
|
||||
|
||||
# When adding another version, update:
|
||||
# - MINOR_MAX_VERSION
|
||||
# - doc/source/contributor/webapi-version-history.rst with a detailed
|
||||
# explanation of what changed in the new version
|
||||
# - common/release_mappings.py, RELEASE_MAPPING['master']['api']
|
||||
# - Add a comment describing the change above the list of consts
|
||||
|
||||
|
||||
MINOR_MAX_VERSION = MINOR_103_PORTGROUP_CATEGORY
|
||||
MINOR_MAX_VERSION = MINOR_104_NODE_INSTANCE_NAME
|
||||
|
||||
# String representations of the minor and maximum versions
|
||||
_MIN_VERSION_STRING = '{}.{}'.format(BASE_VERSION, MINOR_1_INITIAL_VERSION)
|
||||
|
|
|
|||
|
|
@ -920,12 +920,12 @@ RELEASE_MAPPING = {
|
|||
# make it below. To release, we will preserve a version matching
|
||||
# the release as a separate block of text, like above.
|
||||
'master': {
|
||||
'api': '1.103',
|
||||
'api': '1.104',
|
||||
'rpc': '1.62',
|
||||
'objects': {
|
||||
'Allocation': ['1.3', '1.2', '1.1'],
|
||||
'BIOSSetting': ['1.2', '1.1'],
|
||||
'Node': ['1.42', '1.41'],
|
||||
'Node': ['1.43', '1.42', '1.41'],
|
||||
'NodeHistory': ['1.1', '1.0'],
|
||||
'NodeInventory': ['1.1', '1.0'],
|
||||
'Conductor': ['1.6', '1.5', '1.4'],
|
||||
|
|
|
|||
|
|
@ -1157,6 +1157,7 @@ class ConductorManager(base_manager.BaseConductorManager):
|
|||
# But we do need to clear the instance-related fields.
|
||||
node.instance_info = {}
|
||||
node.instance_uuid = None
|
||||
node.instance_name = None
|
||||
utils.wipe_deploy_internal_info(task)
|
||||
node.del_driver_internal_info('instance')
|
||||
node.del_driver_internal_info('root_uuid_or_disk_id')
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ class Connection(object, metaclass=abc.ABCMeta):
|
|||
nodes with inspection_started_at field before this
|
||||
interval in seconds
|
||||
:instance_uuid: uuid of instance
|
||||
:instance_name: name of instance
|
||||
:lessee: node's lessee (e.g. project ID)
|
||||
:maintenance: True | False
|
||||
:owner: node's owner (e.g. project ID)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""add instance_name field to nodes
|
||||
|
||||
Revision ID: 15e9d00367b0
|
||||
Revises: 1c14278d6e33
|
||||
Create Date: 2025-06-17 12:59:58.807596
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '15e9d00367b0'
|
||||
down_revision = '763b2f62d215'
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column('nodes', sa.Column('instance_name', sa.String(length=255),
|
||||
nullable=True))
|
||||
|
|
@ -480,7 +480,7 @@ class Connection(api.Connection):
|
|||
_NODE_QUERY_FIELDS = {'console_enabled', 'maintenance', 'retired',
|
||||
'driver', 'resource_class', 'provision_state',
|
||||
'uuid', 'id', 'fault', 'conductor_group',
|
||||
'owner', 'lessee', 'instance_uuid'}
|
||||
'owner', 'lessee', 'instance_uuid', 'instance_name'}
|
||||
_NODE_IN_QUERY_FIELDS = {'%s_in' % field: field
|
||||
for field in ('uuid', 'provision_state', 'shard')}
|
||||
_NODE_NON_NULL_FILTERS = {'associated': 'instance_uuid',
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@ class NodeBase(Base):
|
|||
# filter on it more efficiently, even though it is
|
||||
# user-settable, and would otherwise be in node.properties.
|
||||
instance_uuid = Column(String(36), nullable=True)
|
||||
instance_name = Column(String(255), nullable=True)
|
||||
name = Column(String(255), nullable=True)
|
||||
chassis_id = Column(Integer, ForeignKey('chassis.id'), nullable=True)
|
||||
power_state = Column(String(15), nullable=True)
|
||||
|
|
|
|||
|
|
@ -219,6 +219,7 @@ class Deployment(base.IronicObject, object_base.VersionedObjectDictCompat):
|
|||
assert node.uuid == self.node_uuid
|
||||
node.instance_uuid = None
|
||||
node.instance_info = {}
|
||||
node.instance_name = None
|
||||
node.save()
|
||||
self._update_from_node_object(node)
|
||||
self.obj_reset_changes()
|
||||
|
|
|
|||
|
|
@ -84,7 +84,8 @@ class Node(base.IronicObject, object_base.VersionedObjectDictCompat):
|
|||
# Version 1.40: Add service_step field
|
||||
# Version 1.41: Add disable_power_off field
|
||||
# Version 1.42: Moves multiple methods to be remotable methods.
|
||||
VERSION = '1.42'
|
||||
# Version 1.43: Add instance_name field
|
||||
VERSION = '1.43'
|
||||
|
||||
dbapi = db_api.get_instance()
|
||||
|
||||
|
|
@ -185,6 +186,7 @@ class Node(base.IronicObject, object_base.VersionedObjectDictCompat):
|
|||
'shard': object_fields.StringField(nullable=True),
|
||||
'parent_node': object_fields.StringField(nullable=True),
|
||||
'disable_power_off': objects.fields.BooleanField(nullable=True),
|
||||
'instance_name': object_fields.StringField(nullable=True),
|
||||
}
|
||||
|
||||
def as_dict(self, secure=False, mask_configdrive=True):
|
||||
|
|
@ -634,6 +636,9 @@ class Node(base.IronicObject, object_base.VersionedObjectDictCompat):
|
|||
Version 1.39: firmware_interface field was added. Its default value is
|
||||
None. For versions prior to this, it should be set to None (or
|
||||
removed).
|
||||
Version 1.43: instance_name field was added. Its default value is
|
||||
None. For versions prior to this, it should be set to None (or
|
||||
removed).
|
||||
|
||||
:param target_version: the desired version of the object
|
||||
:param remove_unavailable_fields: True to remove fields that are
|
||||
|
|
@ -650,7 +655,7 @@ class Node(base.IronicObject, object_base.VersionedObjectDictCompat):
|
|||
('owner', 30), ('allocation_id', 31), ('description', 32),
|
||||
('retired_reason', 33), ('lessee', 34), ('boot_mode', 36),
|
||||
('secure_boot', 36), ('shard', 37),
|
||||
('firmware_interface', 39)]
|
||||
('firmware_interface', 39), ('instance_name', 43)]
|
||||
|
||||
for name, minor in fields:
|
||||
self._adjust_field_to_version(name, None, target_version,
|
||||
|
|
|
|||
|
|
@ -176,6 +176,25 @@ class TestListNodes(test_api_base.BaseApiTest):
|
|||
mock.call().__bool__(),
|
||||
])
|
||||
|
||||
def test_instance_name_field_with_api_version(self):
|
||||
instance_name = 'test-instance-name'
|
||||
obj_utils.create_test_node(self.context,
|
||||
chassis_id=self.chassis.id,
|
||||
instance_name=instance_name)
|
||||
# Test with API version 1.104 - instance_name should be visible
|
||||
data = self.get_json(
|
||||
'/nodes?fields=uuid,instance_name',
|
||||
headers={api_base.Version.string: '1.104'})
|
||||
self.assertIn('instance_name', data['nodes'][0])
|
||||
self.assertEqual(instance_name, data['nodes'][0]['instance_name'])
|
||||
|
||||
# Test with older API version - instance_name should not be visible
|
||||
data = self.get_json(
|
||||
'/nodes?fields=uuid,instance_name',
|
||||
headers={api_base.Version.string: '1.99'},
|
||||
expect_errors=True)
|
||||
self.assertEqual(http_client.NOT_ACCEPTABLE, data.status_int)
|
||||
|
||||
def test_get_one(self):
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
chassis_id=self.chassis.id)
|
||||
|
|
@ -4488,6 +4507,82 @@ class TestPatch(test_api_base.BaseApiTest):
|
|||
'baremetal:node:update'],
|
||||
node.uuid, with_suffix=True)
|
||||
|
||||
@mock.patch.object(api_utils, 'check_multiple_node_policies_and_retrieve',
|
||||
autospec=True)
|
||||
def test_patch_display_name_sets_instance_name_when_not_provided(
|
||||
self, mock_cmnpar):
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
uuid=uuidutils.generate_uuid())
|
||||
mock_cmnpar.return_value = node
|
||||
self.mock_update_node.return_value = node
|
||||
headers = {api_base.Version.string: '1.104'}
|
||||
display_name = 'my-display-name'
|
||||
response = self.patch_json('/nodes/%s' % node.uuid,
|
||||
[{'path': '/instance_info/display_name',
|
||||
'value': display_name,
|
||||
'op': 'add'}],
|
||||
headers=headers)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(http_client.OK, response.status_code)
|
||||
# Verify that update_node was called with instance_name set
|
||||
# to the same value as display_name
|
||||
self.mock_update_node.assert_called_once()
|
||||
updated_node = self.mock_update_node.call_args.args[2]
|
||||
self.assertEqual(display_name, updated_node.instance_name)
|
||||
|
||||
@mock.patch.object(api_utils, 'check_multiple_node_policies_and_retrieve',
|
||||
autospec=True)
|
||||
def test_patch_display_name_does_not_override_instance_name(
|
||||
self, mock_cmnpar):
|
||||
existing_instance_name = 'existing-instance-name'
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
uuid=uuidutils.generate_uuid(),
|
||||
instance_name=existing_instance_name)
|
||||
mock_cmnpar.return_value = node
|
||||
self.mock_update_node.return_value = node
|
||||
headers = {api_base.Version.string: '1.104'}
|
||||
display_name = 'different-display-name'
|
||||
response = self.patch_json('/nodes/%s' % node.uuid,
|
||||
[{'path': '/instance_info/display_name',
|
||||
'value': display_name,
|
||||
'op': 'add'},
|
||||
{'path': '/instance_name',
|
||||
'value': existing_instance_name,
|
||||
'op': 'replace'}],
|
||||
headers=headers)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(http_client.OK, response.status_code)
|
||||
# Verify that update_node was called with instance_name unchanged
|
||||
self.mock_update_node.assert_called_once()
|
||||
updated_node = self.mock_update_node.call_args.args[2]
|
||||
self.assertEqual(existing_instance_name, updated_node.instance_name)
|
||||
|
||||
@mock.patch.object(api_utils, 'check_multiple_node_policies_and_retrieve',
|
||||
autospec=True)
|
||||
def test_patch_display_name_preserves_existing_instance_name(
|
||||
self, mock_cmnpar):
|
||||
"""display_name doesn't overwrite existing instance_name."""
|
||||
existing_instance_name = 'existing-instance-name'
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
uuid=uuidutils.generate_uuid(),
|
||||
instance_name=existing_instance_name)
|
||||
mock_cmnpar.return_value = node
|
||||
self.mock_update_node.return_value = node
|
||||
headers = {api_base.Version.string: '1.104'}
|
||||
display_name = 'new-display-name'
|
||||
# Only patch display_name, don't touch instance_name
|
||||
response = self.patch_json('/nodes/%s' % node.uuid,
|
||||
[{'path': '/instance_info/display_name',
|
||||
'value': display_name,
|
||||
'op': 'add'}],
|
||||
headers=headers)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(http_client.OK, response.status_code)
|
||||
# Verify that instance_name remains unchanged
|
||||
self.mock_update_node.assert_called_once()
|
||||
updated_node = self.mock_update_node.call_args.args[2]
|
||||
self.assertEqual(existing_instance_name, updated_node.instance_name)
|
||||
|
||||
|
||||
def _create_node_locally(node):
|
||||
driver_factory.check_and_update_node_interfaces(node)
|
||||
|
|
|
|||
|
|
@ -2508,6 +2508,7 @@ class DoNodeTearDownTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
|
|||
self.assertIsNone(node.instance_uuid)
|
||||
self.assertIsNone(node.allocation_id)
|
||||
self.assertIsNone(node.lessee)
|
||||
self.assertIsNone(node.instance_name)
|
||||
self.assertEqual({}, node.instance_info)
|
||||
self.assertNotIn('instance', node.driver_internal_info)
|
||||
self.assertIsNone(node.driver_internal_info['deploy_steps'])
|
||||
|
|
|
|||
|
|
@ -125,6 +125,11 @@ class DbNodeTestCase(base.DbTestCase):
|
|||
self.dbapi.get_node_by_name,
|
||||
'spam-eggs-bacon-spam')
|
||||
|
||||
def test_create_node_with_instance_name(self):
|
||||
instance_name = 'test-instance-name'
|
||||
node = utils.create_test_node(instance_name=instance_name)
|
||||
self.assertEqual(instance_name, node.instance_name)
|
||||
|
||||
def test_get_nodeinfo_list_defaults(self):
|
||||
node_id_list = []
|
||||
for i in range(1, 6):
|
||||
|
|
|
|||
|
|
@ -197,6 +197,7 @@ def get_test_node(**kw):
|
|||
'provision_updated_at': kw.get('provision_updated_at'),
|
||||
'last_error': kw.get('last_error'),
|
||||
'instance_uuid': kw.get('instance_uuid'),
|
||||
'instance_name': kw.get('instance_name'),
|
||||
'instance_info': kw.get('instance_info', fake_instance_info),
|
||||
'driver': kw.get('driver', 'fake-hardware'),
|
||||
'driver_info': kw.get('driver_info', fake_driver_info),
|
||||
|
|
|
|||
|
|
@ -675,7 +675,7 @@ class TestObject(_LocalTest, _TestObject):
|
|||
# version bump. It is an MD5 hash of the object fields and remotable methods.
|
||||
# The fingerprint values should only be changed if there is a version bump.
|
||||
expected_object_fingerprints = {
|
||||
'Node': '1.42-a1d3e6011e3cdb27aafa9353b7c0b6d4',
|
||||
'Node': '1.43-cb09f9a8f82f8fb9d55691acf46ef861',
|
||||
'MyObj': '1.5-9459d30d6954bffc7a9afd347a807ca6',
|
||||
'Chassis': '1.4-fe427272d8bad232a8d46e996a5ca42a',
|
||||
'Port': '1.15-013610c0fe2e370b14f4304e0d8aeb3a',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
Adds a new ``instance_name`` field to Ironic nodes. This field can be used
|
||||
to store the display name of the Nova instance that is associated with the
|
||||
node, matching the constraints and format of Nova's ``display_name`` field.
|
||||
The field supports strings up to 255 characters with minimum length of 1
|
||||
character when not null.
|
||||
|
||||
The ``instance_name`` field is automatically cleared when instance data
|
||||
is cleared during node teardown operations. For forward compatibility,
|
||||
when Nova or other API clients update ``instance_info`` with a
|
||||
``display_name`` value, that value is automatically copied to the
|
||||
``instance_name`` field if ``instance_name`` is not explicitly being set
|
||||
in the same request.
|
||||
Loading…
Add table
Reference in a new issue