From ab7dd9a2ce27cbcb56c67773762d6ec871774803 Mon Sep 17 00:00:00 2001
From: joe <joe@incal.net>
Date: Wed, 20 Feb 2013 09:39:26 +0100
Subject: [PATCH] Timestamp feature and tests.

With a small change to preceding versions, now we use a "number" to store the timestamp data, as we need a little more fine-grain
and there is no reason to lose information at this stage.
---
 configdb/client/cli.py             | 24 ++++++++++++++++++++++--
 configdb/client/connection.py      | 15 ++++++++++++++-
 configdb/db/db_api.py              |  2 +-
 configdb/db/schema.py              |  4 ++--
 configdb/server/wsgiapp.py         |  1 +
 configdb/tests/db_api_test_base.py | 15 ++++++++++++++-
 configdb/tests/test_schema.py      |  2 +-
 7 files changed, 55 insertions(+), 8 deletions(-)

diff --git a/configdb/client/cli.py b/configdb/client/cli.py
index 70cb46d..cb30dbc 100644
--- a/configdb/client/cli.py
+++ b/configdb/client/cli.py
@@ -163,6 +163,18 @@ class GetAction(Action):
         obj = conn.get(entity.name, args._name)
         self.view.pprint(obj)
 
+class TimestampAction(Action):
+    """Get the timestamp of last update on an entity."""
+
+    name = 'timestamp'
+
+    def __init__(self, entity, parser):
+        pass
+
+    def run(self, conn, entity, args):
+        print conn.get_timestamp(entity.name)
+        
+
 
 class FindAction(Action):
     """Find instances."""
@@ -211,13 +223,18 @@ class AuditAction(object):
 
     name = 'audit'
     descr = 'query audit logs'
-
+    view = JsonPlain
+    
     AUDIT_ATTRS = ('entity', 'object', 'user', 'op')
 
+    @classmethod
+    def set_view(cls, viewclass):
+        cls.view = viewclass
+    
     def __init__(self, parser):
         for attr in self.AUDIT_ATTRS:
             parser.add_argument('--' + attr)
-
+            
     def run(self, conn, entity, args):
         query = dict((x, getattr(args, x))
                      for x in self.AUDIT_ATTRS
@@ -262,6 +279,7 @@ class Parser(object):
         UpdateAction,
         GetAction,
         FindAction,
+        TimestampAction
         )
 
     toplevel_actions = (
@@ -302,6 +320,8 @@ class Parser(object):
             help='use with --help for additional help',
             dest='_entity_name')
         for entity in self.schema.get_entities():
+            if entity.name in self.schema.sys_schema_tables:
+                continue
             subparser = subparsers.add_parser(entity.name,
                                               help=entity.description)
             self._init_subparser(entity, subparser)
diff --git a/configdb/client/connection.py b/configdb/client/connection.py
index aa99d4b..281ca6b 100644
--- a/configdb/client/connection.py
+++ b/configdb/client/connection.py
@@ -204,7 +204,7 @@ class Connection(object):
             data = data.to_net()
         return [self._from_net(entity_name, x)
                 for x in self._call(entity_name, 'find', data=data)]
-
+    
     def get(self, entity_name, object_name):
         """Fetch a single object.
 
@@ -251,3 +251,16 @@ class Connection(object):
         """
         return self._request(['audit'], query)
 
+    def get_timestamp(self, entity_name):
+        """Fetch the timestamp of last update to a entity.
+
+        Args:
+          entity_name: string, entity name
+
+        Returns:
+          A String representing the unix epoch of last update
+
+        Raises:
+          None
+        """
+        return self._call(entity_name, 'timestamp')
diff --git a/configdb/db/db_api.py b/configdb/db/db_api.py
index 3347e64..3522c5b 100644
--- a/configdb/db/db_api.py
+++ b/configdb/db/db_api.py
@@ -118,7 +118,7 @@ class AdmDbApi(object):
             #Avoid updating timestamp for tables that are not part of the schema.
             return True
         
-        data = {'name': entity_name, 'ts': int(time()) }
+        data = {'name': entity_name, 'ts': time() }
         ts = self.schema.get_entity('__timestamp')
         data = self._unpack(ts, data)
 
diff --git a/configdb/db/schema.py b/configdb/db/schema.py
index b293322..311d745 100644
--- a/configdb/db/schema.py
+++ b/configdb/db/schema.py
@@ -175,7 +175,7 @@ class Schema(object):
         self.default_acl.set_acl(DEFAULT_ACL)
 
     def _add_timestamp(self):
-        ts_schema = {'name': { 'type': 'string', 'size': 32}, 'ts': {'type': 'int', 'nullable': False } }
+        ts_schema = {'name': { 'type': 'string', 'size': 32}, 'ts': {'type': 'number', 'nullable': False } }
         self.entities['__timestamp'] = Entity('__timestamp', ts_schema)
 
     def _relation_check(self):
@@ -195,7 +195,7 @@ class Schema(object):
         return self.entities.get(name)
 
     def get_entities(self):
-        return self.entities.itervalues()
+        return self.entities.itervalues() 
 
     def acl_check_fields(self, entity, fields, auth_context, op, obj):
         """Authorize an operation on the fields of an instance."""
diff --git a/configdb/server/wsgiapp.py b/configdb/server/wsgiapp.py
index b98b079..0301015 100644
--- a/configdb/server/wsgiapp.py
+++ b/configdb/server/wsgiapp.py
@@ -158,6 +158,7 @@ def delete(class_name, object_name):
 
 @api_app.route('/timestamp/<class_name>')
 @authenticate
+@json_response
 def ts(class_name):
     try:
         res = g.api.get_timestamp(class_name, g.auth_ctx)
diff --git a/configdb/tests/db_api_test_base.py b/configdb/tests/db_api_test_base.py
index 72e1712..d06a4bf 100644
--- a/configdb/tests/db_api_test_base.py
+++ b/configdb/tests/db_api_test_base.py
@@ -204,7 +204,7 @@ class DbApiTestBase(object):
     #                       self.api.get('host', 'utz', self.ctx).name)
     #     self.assertRaises(exceptions.NotFound,
     #                       self.api.get, 'host', 'obz', self.ctx)
-
+        
     def test_update_modify_relation(self):
         self.assertTrue(
             self.api.update('host', 'obz', {'roles': ['role2']}, self.ctx))
@@ -347,3 +347,16 @@ class DbApiTestBase(object):
                           self.api.get_audit,
                           {'entity': 'private', 'op': 'create'},
                           auth_ctx)
+
+    def test_timestamp_is_updated(self):
+        result = self.api.update('host', 'obz', {'ip': '2.3.4.5'}, self.ctx)
+        self.assertTrue(result)
+        ts1 = self.api.get_timestamp('host', self.ctx).ts
+        self.assertTrue(ts1 != 0)
+        result = self.api.update('host', 'obz', {'ip': '3.3.3.5'}, self.ctx)
+        self.assertTrue(result)
+        ts2 = self.api.get_timestamp('host', self.ctx).ts
+        self.assertTrue(ts2 > ts1)
+
+    def test_timestamp_for_non_updated_entity(self):
+        self.assertRaises(ValueError, self.api.get_timestamp('role',self.ctx))
diff --git a/configdb/tests/test_schema.py b/configdb/tests/test_schema.py
index 780fd33..193ce21 100644
--- a/configdb/tests/test_schema.py
+++ b/configdb/tests/test_schema.py
@@ -8,7 +8,7 @@ class SchemaTest(TestBase):
 
     def test_empty_schema_ok(self):
         s = schema.Schema('{}')
-        self.assertEquals({}, s.entities)
+        self.assertEquals(s.sys_schema_tables, s.entities.keys())
 
     def test_entity_without_name(self):
         data = """
-- 
GitLab