Verify get_instance_action attributes of Nova API

This patch adds the JSON schema for Nova V2 & V3 get_instance_actions
API response and validate the response with added JSON schema
to block the backward incompatibility change in the future.

The response body of get_instance_actions V2 API is below:
{
    "instanceAction": {
        "action": "reboot",
        "instance_uuid": "b48316c5-71e8-45e4-9884-6c78055b9b13",
        "request_id": "req-3293a3f1-b44c-4609-b8d2-d81b105636b8",
        "user_id": "789",
        "project_id": "147",
        "start_time": "2012-12-05 00:00:00.000000",
        "message": "",
        "events": [
            {
                "event": "schedule",
                "start_time": "2012-12-05 01:00:02.000000",
                "finish_time": "2012-12-05 01:02:00.000000",
                "result": "Success",
                "traceback": ""
            },
            {
                "event": "compute_create",
                "start_time": "2012-12-05 01:03:00.000000",
                "finish_time": "2012-12-05 01:04:00.000000",
                "result": "Success",
                "traceback": ""
            }
        ]
    }
}

The response body of get_instance_actions V3 API is below:
{
    "server_action": {
        "action": "reboot",
        "server_uuid": "b48316c5-71e8-45e4-9884-6c78055b9b13",
        "request_id": "req-3293a3f1-b44c-4609-b8d2-d81b105636b8",
        "user_id": "789",
        "project_id": "147",
        "start_time": "2012-12-05T00:00:00.000000",
        "message": "",
        "events": [
            {
                "event": "schedule",
                "start_time": "2012-12-05T01:00:02.000000",
                "finish_time": "2012-12-05T01:02:00.000000",
                "result": "Success",
                "traceback": ""
            },
            {
                "event": "compute_create",
                "start_time": "2012-12-05T01:03:00.000000",
                "finish_time": "2012-12-05T01:04:00.000000",
                "result": "Success",
                "traceback": ""
            }
        ]
    }
}

Partially implements blueprint nova-api-attribute-test

Change-Id: I7b0d64b1db19359e5ff81b7cbbebef0b11946a1b
diff --git a/tempest/api_schema/compute/servers.py b/tempest/api_schema/compute/servers.py
index 6b27f3d..a16e425 100644
--- a/tempest/api_schema/compute/servers.py
+++ b/tempest/api_schema/compute/servers.py
@@ -182,6 +182,29 @@
                  'start_time', 'message']
 }
 
+instance_action_events = {
+    'type': 'array',
+    'items': {
+        'type': 'object',
+        'properties': {
+            'event': {'type': 'string'},
+            'start_time': {'type': 'string'},
+            'finish_time': {'type': 'string'},
+            'result': {'type': 'string'},
+            'traceback': {'type': ['string', 'null']}
+        },
+        'required': ['event', 'start_time', 'finish_time', 'result',
+                     'traceback']
+    }
+}
+
+common_get_instance_action = copy.deepcopy(common_instance_actions)
+
+common_get_instance_action['properties'].update({
+    'events': instance_action_events})
+# 'events' does not come in response body always so it is not
+# defined as 'required'
+
 base_list_servers_detail = {
     'status_code': [200],
     'response_body': {
diff --git a/tempest/api_schema/compute/v2/servers.py b/tempest/api_schema/compute/v2/servers.py
index cd46f9b..95c5760 100644
--- a/tempest/api_schema/compute/v2/servers.py
+++ b/tempest/api_schema/compute/v2/servers.py
@@ -241,6 +241,22 @@
     }
 }
 
+get_instance_actions_object = copy.deepcopy(servers.common_get_instance_action)
+get_instance_actions_object[
+    'properties'].update({'instance_uuid': {'type': 'string'}})
+get_instance_actions_object['required'].extend(['instance_uuid'])
+
+get_instance_action = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'instanceAction': get_instance_actions_object
+        },
+        'required': ['instanceAction']
+    }
+}
+
 list_servers_detail = copy.deepcopy(servers.base_list_servers_detail)
 list_servers_detail['response_body']['properties']['servers']['items'][
     'properties'].update({
diff --git a/tempest/api_schema/compute/v3/servers.py b/tempest/api_schema/compute/v3/servers.py
index b6cb13b..dc800cd 100644
--- a/tempest/api_schema/compute/v3/servers.py
+++ b/tempest/api_schema/compute/v3/servers.py
@@ -152,6 +152,22 @@
     }
 }
 
+get_server_actions_object = copy.deepcopy(servers.common_get_instance_action)
+get_server_actions_object[
+    'properties'].update({'server_uuid': {'type': 'string'}})
+get_server_actions_object['required'].extend(['server_uuid'])
+
+get_server_action = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'server_action': get_server_actions_object
+        },
+        'required': ['server_action']
+    }
+}
+
 list_servers_detail = copy.deepcopy(servers.base_list_servers_detail)
 list_servers_detail['response_body']['properties']['servers']['items'][
     'properties'].update({
diff --git a/tempest/services/compute/json/servers_client.py b/tempest/services/compute/json/servers_client.py
index fea0d28..80bb711 100644
--- a/tempest/services/compute/json/servers_client.py
+++ b/tempest/services/compute/json/servers_client.py
@@ -474,6 +474,7 @@
         resp, body = self.get("servers/%s/os-instance-actions/%s" %
                               (str(server_id), str(request_id)))
         body = json.loads(body)
+        self.validate_response(schema.get_instance_action, resp, body)
         return resp, body['instanceAction']
 
     def force_delete_server(self, server_id, **kwargs):
diff --git a/tempest/services/compute/v3/json/servers_client.py b/tempest/services/compute/v3/json/servers_client.py
index 932cc2c..a5b31d3 100644
--- a/tempest/services/compute/v3/json/servers_client.py
+++ b/tempest/services/compute/v3/json/servers_client.py
@@ -471,6 +471,7 @@
         resp, body = self.get("servers/%s/os-server-actions/%s" %
                               (str(server_id), str(request_id)))
         body = json.loads(body)
+        self.validate_response(schema.get_server_action, resp, body)
         return resp, body['server_action']
 
     def force_delete_server(self, server_id, **kwargs):