Add timestamp fields for neutron ext resources

Propose a new extension named "timestamp_ext" to add timestamp to
neutron ext resources like router/floatingip/security_group/security_group_rule.

APIImpact
DocImpact: Neutron ext resources now contain 'timestamp' fields like
           'created_at' and 'updated_at'
Implements: blueprint add-neutron-extension-resource-timestamp

Change-Id: I78b00516e31ce83376d37f57299b2229b6fb8fcf
diff --git a/neutron/tests/tempest/api/test_timestamp.py b/neutron/tests/tempest/api/test_timestamp.py
index 6c36cf6..290e162 100644
--- a/neutron/tests/tempest/api/test_timestamp.py
+++ b/neutron/tests/tempest/api/test_timestamp.py
@@ -16,6 +16,11 @@
 from tempest import test
 
 from neutron.tests.tempest.api import base
+from neutron.tests.tempest.api import base_routers
+from neutron.tests.tempest.api import base_security_groups
+from neutron.tests.tempest import config
+
+CONF = config.CONF
 
 
 class TestTimeStamp(base.BaseAdminNetworkTest):
@@ -174,3 +179,157 @@
         # verify the timestamp from creation and showed is same
         self.assertEqual(sp['created_at'], show_sp['created_at'])
         self.assertEqual(sp['updated_at'], show_sp['updated_at'])
+
+
+class TestTimeStampWithL3(base_routers.BaseRouterTest):
+    @classmethod
+    def skip_checks(cls):
+        super(TestTimeStampWithL3, cls).skip_checks()
+
+        if not test.is_extension_enabled('timestamp_ext', 'network'):
+            raise cls.skipException("timestamp_ext extension not enabled")
+
+    @classmethod
+    def resource_setup(cls):
+        super(TestTimeStampWithL3, cls).resource_setup()
+        cls.ext_net_id = CONF.network.public_network_id
+
+    @test.idempotent_id('433ba770-b310-4da9-5d42-733217a1c7b1')
+    def test_create_router_with_timestamp(self):
+        router = self.create_router(router_name='test')
+        # Verifies body contains timestamp fields
+        self.assertIsNotNone(router['created_at'])
+        self.assertIsNotNone(router['updated_at'])
+
+    @test.idempotent_id('4a65417a-c11c-4b4d-a351-af01abcf57c6')
+    def test_update_router_with_timestamp(self):
+        router = self.create_router(router_name='test')
+        origin_updated_at = router['updated_at']
+        update_body = {'name': router['name'] + 'new'}
+        body = self.client.update_router(router['id'], **update_body)
+        updated_router = body['router']
+        new_updated_at = updated_router['updated_at']
+        self.assertEqual(router['created_at'], updated_router['created_at'])
+        # Verify that origin_updated_at is not same with new_updated_at
+        self.assertIsNot(origin_updated_at, new_updated_at)
+
+    @test.idempotent_id('1ab50ac2-7cbd-4a17-b23e-a9e36cfa4ec2')
+    def test_show_router_attribute_with_timestamp(self):
+        router = self.create_router(router_name='test')
+        body = self.client.show_router(router['id'])
+        show_router = body['router']
+        # verify the timestamp from creation and showed is same
+        self.assertEqual(router['created_at'],
+                         show_router['created_at'])
+        self.assertEqual(router['updated_at'],
+                         show_router['updated_at'])
+
+    @test.idempotent_id('8ae55186-464f-4b87-1c9f-eb2765ee81ac')
+    def test_create_floatingip_with_timestamp(self):
+        fip = self.create_floatingip(self.ext_net_id)
+        # Verifies body contains timestamp fields
+        self.assertIsNotNone(fip['created_at'])
+        self.assertIsNotNone(fip['updated_at'])
+
+    @test.idempotent_id('a3ac215a-61ac-13f9-9d3c-57c51f11afa1')
+    def test_update_floatingip_with_timestamp(self):
+        fip = self.create_floatingip(self.ext_net_id)
+        origin_updated_at = fip['updated_at']
+        update_body = {'description': 'new'}
+        body = self.client.update_floatingip(fip['id'], **update_body)
+        updated_fip = body['floatingip']
+        new_updated_at = updated_fip['updated_at']
+        self.assertEqual(fip['created_at'], updated_fip['created_at'])
+        # Verify that origin_updated_at is not same with new_updated_at
+        self.assertIsNot(origin_updated_at, new_updated_at)
+
+    @test.idempotent_id('32a6a086-e1ef-413b-b13a-0cfe13ef051e')
+    def test_show_floatingip_attribute_with_timestamp(self):
+        fip = self.create_floatingip(self.ext_net_id)
+        body = self.client.show_floatingip(fip['id'])
+        show_fip = body['floatingip']
+        # verify the timestamp from creation and showed is same
+        self.assertEqual(fip['created_at'],
+                         show_fip['created_at'])
+        self.assertEqual(fip['updated_at'],
+                         show_fip['updated_at'])
+
+
+class TestTimeStampWithSecurityGroup(base_security_groups.BaseSecGroupTest):
+    @classmethod
+    def skip_checks(cls):
+        super(TestTimeStampWithSecurityGroup, cls).skip_checks()
+
+        if not test.is_extension_enabled('timestamp_ext', 'network'):
+            raise cls.skipException("timestamp_ext extension not enabled")
+
+    @classmethod
+    def resource_setup(cls):
+        super(TestTimeStampWithSecurityGroup, cls).resource_setup()
+        cls.ext_net_id = CONF.network.public_network_id
+
+    @test.idempotent_id('a3150a7b-d31a-423a-abf3-45e71c97cbac')
+    def test_create_sg_with_timestamp(self):
+        sg, _ = self._create_security_group()
+        # Verifies body contains timestamp fields
+        self.assertIsNotNone(sg['security_group']['created_at'])
+        self.assertIsNotNone(sg['security_group']['updated_at'])
+
+    @test.idempotent_id('432ae0d3-32b4-413e-a9b3-091ac76da31b')
+    def test_update_sg_with_timestamp(self):
+        sgc, _ = self._create_security_group()
+        sg = sgc['security_group']
+        origin_updated_at = sg['updated_at']
+        update_body = {'name': sg['name'] + 'new'}
+        body = self.client.update_security_group(sg['id'], **update_body)
+        updated_sg = body['security_group']
+        new_updated_at = updated_sg['updated_at']
+        self.assertEqual(sg['created_at'], updated_sg['created_at'])
+        # Verify that origin_updated_at is not same with new_updated_at
+        self.assertIsNot(origin_updated_at, new_updated_at)
+
+    @test.idempotent_id('521e6723-43d6-12a6-8c3d-f5042ad9fc32')
+    def test_show_sg_attribute_with_timestamp(self):
+        sg, _ = self._create_security_group()
+        body = self.client.show_security_group(sg['security_group']['id'])
+        show_sg = body['security_group']
+        # verify the timestamp from creation and showed is same
+        self.assertEqual(sg['security_group']['created_at'],
+                         show_sg['created_at'])
+        self.assertEqual(sg['security_group']['updated_at'],
+                         show_sg['updated_at'])
+
+    def _prepare_sgrule_test(self):
+        sg, _ = self._create_security_group()
+        sg_id = sg['security_group']['id']
+        direction = 'ingress'
+        protocol = 'tcp'
+        port_range_min = 77
+        port_range_max = 77
+        rule_create_body = self.client.create_security_group_rule(
+            security_group_id=sg_id,
+            direction=direction,
+            ethertype=self.ethertype,
+            protocol=protocol,
+            port_range_min=port_range_min,
+            port_range_max=port_range_max,
+            remote_group_id=None,
+            remote_ip_prefix=None
+        )
+        return rule_create_body['security_group_rule']
+
+    @test.idempotent_id('83e8bd32-43e0-a3f0-1af3-12a5733c653e')
+    def test_create_sgrule_with_timestamp(self):
+        sgrule = self._prepare_sgrule_test()
+        # Verifies body contains timestamp fields
+        self.assertIsNotNone(sgrule['created_at'])
+        self.assertIsNotNone(sgrule['updated_at'])
+
+    @test.idempotent_id('143da0e6-ba17-43ad-b3d7-03aa759c3cb4')
+    def test_show_sgrule_attribute_with_timestamp(self):
+        sgrule = self._prepare_sgrule_test()
+        body = self.client.show_security_group_rule(sgrule['id'])
+        show_sgrule = body['security_group_rule']
+        # verify the timestamp from creation and showed is same
+        self.assertEqual(sgrule['created_at'], show_sgrule['created_at'])
+        self.assertEqual(sgrule['updated_at'], show_sgrule['updated_at'])