Add Node.maintenance_reason

When a node is maintenanced by Ironic or an operator, an operator
should know the reason for that. Add a string column to allow
Ironic or operators to enter a reason that the node is in
maintenance mode.

Implements: blueprint maintenance-reason

Change-Id: I7c77c92501e53f81704e9b9e5b52e0e2a410cc91
This commit is contained in:
Jim Rollenhagen 2014-10-15 06:16:22 -07:00
parent 5a2e9b3559
commit 827db7fe72
7 changed files with 56 additions and 3 deletions

View File

@ -49,7 +49,7 @@ class NodePatchType(types.JsonPatchType):
return defaults + ['/console_enabled', '/last_error',
'/power_state', '/provision_state', '/reservation',
'/target_power_state', '/target_provision_state',
'/provision_updated_at']
'/provision_updated_at', '/maintenance_reason']
@staticmethod
def mandatory_attrs():
@ -396,6 +396,9 @@ class Node(base.APIBase):
maintenance = types.boolean
"Indicates whether the node is in maintenance mode."
maintenance_reason = wsme.wsattr(wtypes.text, readonly=True)
"Indicates reason for putting a node in maintenance mode."
target_provision_state = wsme.wsattr(wtypes.text, readonly=True)
"The user modified desired provision state of the node."
@ -500,7 +503,8 @@ class Node(base.APIBase):
properties={'memory_mb': '1024', 'local_gb': '10',
'cpus': '1'}, updated_at=time, created_at=time,
provision_updated_at=time, instance_info={},
maintenance=False, console_enabled=False)
maintenance=False, maintenance_reason=None,
console_enabled=False)
# NOTE(matty_dubs): The chassis_uuid getter() is based on the
# _chassis_uuid variable:
sample._chassis_uuid = 'edcad704-b2da-41d5-96d9-afd580ecfa12'

View File

@ -0,0 +1,36 @@
# 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 Node.maintenance_reason
Revision ID: 242cc6a923b3
Revises: 487deb87cc9d
Create Date: 2014-10-15 23:00:43.164061
"""
# revision identifiers, used by Alembic.
revision = '242cc6a923b3'
down_revision = '487deb87cc9d'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.add_column('nodes', sa.Column('maintenance_reason',
sa.Text(),
nullable=True))
def downgrade():
op.drop_column('nodes', 'maintenance_reason')

View File

@ -180,6 +180,7 @@ class Node(Base):
nullable=True)
maintenance = Column(Boolean, default=False)
maintenance_reason = Column(Text, nullable=True)
console_enabled = Column(Boolean, default=False)
extra = Column(JSONEncodedDict)

View File

@ -30,7 +30,8 @@ class Node(base.IronicObject):
# Version 1.5: Add list()
# Version 1.6: Add reserve() and release()
# Version 1.7: Add conductor_affinity
VERSION = '1.7'
# Version 1.8: Add maintenance_reason
VERSION = '1.8'
dbapi = db_api.get_instance()
@ -65,6 +66,7 @@ class Node(base.IronicObject):
'target_provision_state': obj_utils.str_or_none,
'maintenance': bool,
'maintenance_reason': obj_utils.str_or_none,
'console_enabled': bool,
# Any error from the most recent (last) asynchronous transaction

View File

@ -97,6 +97,7 @@ class TestListNodes(base.FunctionalTest):
self.assertNotIn('target_power_state', data['nodes'][0])
self.assertNotIn('target_provision_state', data['nodes'][0])
self.assertNotIn('provision_updated_at', data['nodes'][0])
self.assertNotIn('maintenance_reason', data['nodes'][0])
# never expose the chassis_id
self.assertNotIn('chassis_id', data['nodes'][0])
@ -110,6 +111,7 @@ class TestListNodes(base.FunctionalTest):
self.assertIn('properties', data)
self.assertIn('chassis_uuid', data)
self.assertIn('reservation', data)
self.assertIn('maintenance_reason', data)
# never expose the chassis_id
self.assertNotIn('chassis_id', data)

View File

@ -321,6 +321,13 @@ class MigrationCheckersMixin(object):
(sqlalchemy.exc.IntegrityError, db_exc.DBDuplicateEntry),
nodes.insert().execute, data)
def _check_242cc6a923b3(self, engine, data):
nodes = db_utils.get_table(engine, 'nodes')
col_names = [column.name for column in nodes.c]
self.assertIn('maintenance_reason', col_names)
self.assertIsInstance(nodes.c.maintenance_reason.type,
sqlalchemy.types.String)
def test_upgrade_and_version(self):
with patch_with_engine(self.engine):
self.migration_api.upgrade('head')

View File

@ -169,6 +169,7 @@ def get_test_node(**kw):
'properties': kw.get('properties', properties),
'reservation': kw.get('reservation'),
'maintenance': kw.get('maintenance', False),
'maintenance_reason': kw.get('maintenance_reason'),
'console_enabled': kw.get('console_enabled', False),
'extra': kw.get('extra', {}),
'updated_at': kw.get('created_at'),