Browse Source

Merge "introspection data backend: implements db"

tags/8.2.0
Zuul 6 months ago
parent
commit
bf5c155c31

+ 8
- 0
ironic_inspector/db.py View File

@@ -134,6 +134,14 @@ class RuleAction(Base):
134 134
         return res
135 135
 
136 136
 
137
+class IntrospectionData(Base):
138
+    __tablename__ = 'introspection_data'
139
+    uuid = Column(String(36), ForeignKey('nodes.uuid'), primary_key=True)
140
+    processed = Column(Boolean, default=False)
141
+    data = Column(db_types.JsonEncodedDict(mysql_as_long=True),
142
+                  nullable=True)
143
+
144
+
137 145
 def init():
138 146
     """Initialize the database.
139 147
 

+ 42
- 0
ironic_inspector/migrations/versions/bf8dec16023c_add_introspection_data_table.py View File

@@ -0,0 +1,42 @@
1
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
2
+# not use this file except in compliance with the License. You may obtain
3
+# a copy of the License at
4
+#
5
+# http://www.apache.org/licenses/LICENSE-2.0
6
+#
7
+# Unless required by applicable law or agreed to in writing, software
8
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10
+# License for the specific language governing permissions and limitations
11
+# under the License.
12
+
13
+"""add_introspection_data_table
14
+
15
+Revision ID: bf8dec16023c
16
+Revises: 2970d2d44edc
17
+Create Date: 2018-07-19 18:51:38.124614
18
+
19
+"""
20
+
21
+from alembic import op
22
+from oslo_db.sqlalchemy import types as db_types
23
+import sqlalchemy as sa
24
+
25
+# revision identifiers, used by Alembic.
26
+revision = 'bf8dec16023c'
27
+down_revision = '2970d2d44edc'
28
+branch_labels = None
29
+depends_on = None
30
+
31
+
32
+def upgrade():
33
+    op.create_table(
34
+        'introspection_data',
35
+        sa.Column('uuid', sa.String(36), sa.ForeignKey('nodes.uuid'),
36
+                  primary_key=True),
37
+        sa.Column('processed', sa.Boolean, default=False),
38
+        sa.Column('data', db_types.JsonEncodedDict(mysql_as_long=True).impl,
39
+                  nullable=True),
40
+        mysql_ENGINE='InnoDB',
41
+        mysql_DEFAULT_CHARSET='UTF8'
42
+    )

+ 42
- 1
ironic_inspector/node_cache.py View File

@@ -755,7 +755,7 @@ def _delete_node(uuid, session=None):
755 755
     with db.ensure_transaction(session) as session:
756 756
         db.model_query(db.Attribute, session=session).filter_by(
757 757
             node_uuid=uuid).delete()
758
-        for model in (db.Option, db.Node):
758
+        for model in (db.Option, db.IntrospectionData, db.Node):
759 759
             db.model_query(model,
760 760
                            session=session).filter_by(uuid=uuid).delete()
761 761
 
@@ -979,3 +979,44 @@ def get_node_list(ironic=None, marker=None, limit=None):
979 979
                                    ('started_at', 'uuid'),
980 980
                                    marker=marker, sort_dir='desc')
981 981
     return [NodeInfo.from_row(row, ironic=ironic) for row in rows]
982
+
983
+
984
+def store_introspection_data(node_id, introspection_data, processed=True):
985
+    """Store introspection data for this node.
986
+
987
+    :param node_id: node UUID.
988
+    :param introspection_data: A dictionary of introspection data
989
+    :param processed: Specify the type of introspected data, set to False
990
+                      indicates the data is unprocessed.
991
+    """
992
+    with db.ensure_transaction() as session:
993
+        record = db.model_query(db.IntrospectionData,
994
+                                session=session).filter_by(
995
+            uuid=node_id, processed=processed).first()
996
+        if record is None:
997
+            row = db.IntrospectionData()
998
+            row.update({'uuid': node_id, 'processed': processed,
999
+                        'data': introspection_data})
1000
+            session.add(row)
1001
+        else:
1002
+            record.update({'data': introspection_data})
1003
+        session.flush()
1004
+
1005
+
1006
+def get_introspection_data(node_id, processed=True):
1007
+    """Get introspection data for this node.
1008
+
1009
+    :param node_id: node UUID.
1010
+    :param processed: Specify the type of introspected data, set to False
1011
+                      indicates retrieving the unprocessed data.
1012
+    :return: A dictionary representation of intropsected data
1013
+    """
1014
+    try:
1015
+        ref = db.model_query(db.IntrospectionData).filter_by(
1016
+            uuid=node_id, processed=processed).one()
1017
+        return ref['data']
1018
+    except orm_errors.NoResultFound:
1019
+        msg = _('Introspection data not found for node %(node)s, '
1020
+                'processed=%(processed)s') % {'node': node_id,
1021
+                                              'processed': processed}
1022
+        raise utils.IntrospectionDataNotFound(msg)

+ 13
- 0
ironic_inspector/test/unit/test_migrations.py View File

@@ -441,6 +441,19 @@ class MigrationCheckersMixin(object):
441 441
         n = nodes.select(nodes.c.uuid == 'abcd').execute().first()
442 442
         self.assertIsNone(n['manage_boot'])
443 443
 
444
+    def _check_bf8dec16023c(self, engine, data):
445
+        introspection_data = db_utils.get_table(engine, 'introspection_data')
446
+        col_names = [column.name for column in introspection_data.c]
447
+        self.assertIn('uuid', col_names)
448
+        self.assertIn('processed', col_names)
449
+        self.assertIn('data', col_names)
450
+        self.assertIsInstance(introspection_data.c.uuid.type,
451
+                              sqlalchemy.types.String)
452
+        self.assertIsInstance(introspection_data.c.processed.type,
453
+                              sqlalchemy.types.Boolean)
454
+        self.assertIsInstance(introspection_data.c.data.type,
455
+                              sqlalchemy.types.Text)
456
+
444 457
     def test_upgrade_and_version(self):
445 458
         with patch_with_engine(self.engine):
446 459
             self.migration_ext.upgrade('head')

+ 29
- 0
ironic_inspector/test/unit/test_node_cache.py View File

@@ -331,6 +331,8 @@ class TestNodeCacheCleanUp(test_base.NodeTest):
331 331
                              value=v, node_uuid=self.uuid).save(session)
332 332
             db.Option(uuid=self.uuid, name='foo', value='bar').save(
333 333
                 session)
334
+            db.IntrospectionData(uuid=self.uuid, processed=False,
335
+                                 data={'fake': 'data'}).save(session)
334 336
 
335 337
     def test_no_timeout(self):
336 338
         CONF.set_override('timeout', 0)
@@ -358,6 +360,7 @@ class TestNodeCacheCleanUp(test_base.NodeTest):
358 360
         self.assertEqual(len(self.macs),
359 361
                          db.model_query(db.Attribute).count())
360 362
         self.assertEqual(1, db.model_query(db.Option).count())
363
+        self.assertEqual(1, db.model_query(db.IntrospectionData).count())
361 364
         self.assertFalse(get_lock_mock.called)
362 365
 
363 366
     @mock.patch.object(node_cache, '_get_lock', autospec=True)
@@ -1256,3 +1259,29 @@ class TestStartIntrospection(test_base.NodeTest):
1256 1259
                               node_cache.start_introspection,
1257 1260
                               self.node_info.uuid)
1258 1261
         self.assertFalse(add_node_mock.called)
1262
+
1263
+
1264
+class TestIntrospectionDataDbStore(test_base.NodeTest):
1265
+    def setUp(self):
1266
+        super(TestIntrospectionDataDbStore, self).setUp()
1267
+        node_cache.add_node(self.node.uuid,
1268
+                            istate.States.processing,
1269
+                            bmc_address='1.2.3.4')
1270
+
1271
+    def _test_store_and_get(self, processed=False):
1272
+        node_cache.store_introspection_data(self.node.uuid,
1273
+                                            copy.deepcopy(self.data),
1274
+                                            processed=processed)
1275
+        stored_data = node_cache.get_introspection_data(self.node.uuid,
1276
+                                                        processed=processed)
1277
+        self.assertEqual(stored_data, self.data)
1278
+
1279
+    def test_store_and_get_unprocessed(self):
1280
+        self._test_store_and_get(processed=False)
1281
+
1282
+    def test_store_and_get_processed(self):
1283
+        self._test_store_and_get(processed=True)
1284
+
1285
+    def test_get_no_data_available(self):
1286
+        self.assertRaises(utils.IntrospectionDataNotFound,
1287
+                          node_cache.get_introspection_data, self.node.uuid)

+ 4
- 0
ironic_inspector/utils.py View File

@@ -140,6 +140,10 @@ class NodeStateInvalidEvent(Error):
140 140
     """Invalid event attempted."""
141 141
 
142 142
 
143
+class IntrospectionDataNotFound(NotFoundInCacheError):
144
+    """Introspection data not found."""
145
+
146
+
143 147
 def executor():
144 148
     """Return the current futures executor."""
145 149
     global _EXECUTOR

Loading…
Cancel
Save