Add Validation for Node Manage/Action API's

Partially-Implements blueprint validation
Change-Id: If10e37e0036c88a4bd0b183793e4344d8c257b67
This commit is contained in:
Anusha Ramineni 2017-05-02 16:43:04 +05:30
parent a42b5a688f
commit 542f3ced2f
5 changed files with 112 additions and 26 deletions

View File

@ -332,7 +332,7 @@ Response
Manage Node Manage Node
=========== ===========
.. rest_method:: POST /v1/nodes/managed .. rest_method:: POST /v1/nodes/manage
Manage a composed node already existing in the RSD rack by creating a Manage a composed node already existing in the RSD rack by creating a
Valence database entry for it, allowing Valence to perform all operations Valence database entry for it, allowing Valence to perform all operations

View File

@ -48,14 +48,16 @@ class Node(Resource):
class NodeAction(Resource): class NodeAction(Resource):
@validator.check_input('node_action_schema')
def post(self, node_uuid): def post(self, node_uuid):
return utils.make_response( return utils.make_response(
http_client.OK, http_client.NO_CONTENT,
nodes.Node.node_action(node_uuid, request.get_json())) nodes.Node.node_action(node_uuid, request.get_json()))
class NodeManage(Resource): class NodeManage(Resource):
@validator.check_input('node_manage_schema')
def post(self): def post(self):
return utils.make_response( return utils.make_response(
http_client.OK, nodes.Node.manage_node(request.get_json())) http_client.OK, nodes.Node.manage_node(request.get_json()))

View File

@ -191,19 +191,6 @@ class Node(object):
param request_body: parameter of node action param request_body: parameter of node action
return: message of this deletion return: message of this deletion
""" """
# Get node detail from db, and map node uuid to index # Get node detail from db, and map node uuid to index
index = db_api.Connection.get_composed_node_by_uuid(node_uuid).index index = db_api.Connection.get_composed_node_by_uuid(node_uuid).index
# TODO(lin.yang): should validate request body whether follow specifc
# format, like
# {
# "Reset": {
# "Type": "On"
# }
# }
# Should rework this part after basic validation framework for api
# input is done.
# https://review.openstack.org/#/c/422547/
return redfish.node_action(index, request_body) return redfish.node_action(index, request_body)

View File

@ -3,6 +3,7 @@ import json
import mock import mock
from oslotest import base from oslotest import base
from six.moves import http_client
from valence.api import app as flask_app from valence.api import app as flask_app
from valence.common import constants from valence.common import constants
@ -32,7 +33,7 @@ class TestFlavorApi(TestApiValidation):
response = self.app.post('/v1/flavors', response = self.app.post('/v1/flavors',
content_type='application/json', content_type='application/json',
data=json.dumps(flavor)) data=json.dumps(flavor))
self.assertEqual(200, response.status_code) self.assertEqual(http_client.OK, response.status_code)
mock_create.assert_called_once_with(flavor) mock_create.assert_called_once_with(flavor)
def test_flavor_create_incorrect_param(self): def test_flavor_create_incorrect_param(self):
@ -44,7 +45,7 @@ class TestFlavorApi(TestApiValidation):
content_type='application/json', content_type='application/json',
data=json.dumps(self.flavor)) data=json.dumps(self.flavor))
response = json.loads(response.data.decode()) response = json.loads(response.data.decode())
self.assertEqual(400, response['status']) self.assertEqual(http_client.BAD_REQUEST, response['status'])
self.assertEqual('ValidationError', response['code']) self.assertEqual('ValidationError', response['code'])
# Test invalid key # Test invalid key
@ -53,7 +54,7 @@ class TestFlavorApi(TestApiValidation):
content_type='application/json', content_type='application/json',
data=json.dumps(self.flavor)) data=json.dumps(self.flavor))
response = json.loads(response.data.decode()) response = json.loads(response.data.decode())
self.assertEqual(400, response['status']) self.assertEqual(http_client.BAD_REQUEST, response['status'])
self.assertEqual('ValidationError', response['code']) self.assertEqual('ValidationError', response['code'])
@ -84,7 +85,7 @@ class TestPodmanagerApi(TestApiValidation):
response = self.app.post('/v1/pod_managers', response = self.app.post('/v1/pod_managers',
content_type='application/json', content_type='application/json',
data=json.dumps(values)) data=json.dumps(values))
self.assertEqual(200, response.status_code) self.assertEqual(http_client.OK, response.status_code)
def test_check_creation_incomplete_parameters(self): def test_check_creation_incomplete_parameters(self):
incomplete_values = { incomplete_values = {
@ -95,7 +96,7 @@ class TestPodmanagerApi(TestApiValidation):
content_type='application/json', content_type='application/json',
data=json.dumps(incomplete_values)) data=json.dumps(incomplete_values))
response = json.loads(response.data.decode()) response = json.loads(response.data.decode())
self.assertEqual(400, response['status']) self.assertEqual(http_client.BAD_REQUEST, response['status'])
self.assertEqual('ValidationError', response['code']) self.assertEqual('ValidationError', response['code'])
def test_check_creation_invalid_authentication(self): def test_check_creation_invalid_authentication(self):
@ -111,7 +112,7 @@ class TestPodmanagerApi(TestApiValidation):
content_type='application/json', content_type='application/json',
data=json.dumps(invalid_auth_values)) data=json.dumps(invalid_auth_values))
response = json.loads(response.data.decode()) response = json.loads(response.data.decode())
self.assertEqual(400, response['status']) self.assertEqual(http_client.BAD_REQUEST, response['status'])
self.assertEqual('ValidationError', response['code']) self.assertEqual('ValidationError', response['code'])
@ -136,7 +137,7 @@ class TestNodeApi(TestApiValidation):
resp = self.app.post('/v1/nodes', resp = self.app.post('/v1/nodes',
content_type='application/json', content_type='application/json',
data=json.dumps(req)) data=json.dumps(req))
self.assertEqual(200, resp.status_code) self.assertEqual(http_client.OK, resp.status_code)
@mock.patch('valence.controller.nodes.Node.compose_node') @mock.patch('valence.controller.nodes.Node.compose_node')
def test_compose_request_using_flavor(self, mock_compose): def test_compose_request_using_flavor(self, mock_compose):
@ -148,7 +149,7 @@ class TestNodeApi(TestApiValidation):
resp = self.app.post('/v1/nodes', resp = self.app.post('/v1/nodes',
content_type='application/json', content_type='application/json',
data=json.dumps(req)) data=json.dumps(req))
self.assertEqual(200, resp.status_code) self.assertEqual(http_client.OK, resp.status_code)
def test_compose_request_invalid_params(self): def test_compose_request_invalid_params(self):
req = { req = {
@ -158,5 +159,54 @@ class TestNodeApi(TestApiValidation):
content_type='application/json', content_type='application/json',
data=json.dumps(req)) data=json.dumps(req))
response = json.loads(resp.data.decode()) response = json.loads(resp.data.decode())
self.assertEqual(400, response['status']) self.assertEqual(http_client.BAD_REQUEST, response['status'])
self.assertEqual('ValidationError', response['code'])
@mock.patch('valence.controller.nodes.Node.manage_node')
def test_node_manage_request(self, mock_manage):
req = {"node_index": "fake-index"}
mock_manage.return_value = {"uuid": "ea8e2a25-2901-438d-8157-de7ffd",
"links": "fake-links",
"name": "fake-node",
"index": "fake-index"}
resp = self.app.post('/v1/nodes/manage',
content_type='application/json',
data=json.dumps(req))
mock_manage.assert_called_once_with(req)
self.assertEqual(http_client.OK, resp.status_code)
def test_node_manage_request_invalid(self):
req = {"node_id": "fake-index"}
resp = self.app.post('/v1/nodes/manage',
content_type='application/json',
data=json.dumps(req))
response = json.loads(resp.data.decode())
self.assertEqual(http_client.BAD_REQUEST, response['status'])
self.assertEqual('ValidationError', response['code'])
@mock.patch('valence.controller.nodes.Node.node_action')
def test_node_action_request(self, mock_action):
req = {
"Reset": {
"Type": "On"
}
}
mock_action.return_value = None
resp = self.app.post('/v1/nodes/fake-node/action',
content_type='application/json',
data=json.dumps(req))
mock_action.assert_called_once_with('fake-node', req)
self.assertEqual(http_client.NO_CONTENT, resp.status_code)
def test_node_action_request_invalid(self):
req = {
"Boot": {
"Type": "On"
}
}
resp = self.app.post('/v1/nodes/fake-node/action',
content_type='application/json',
data=json.dumps(req))
response = json.loads(resp.data.decode())
self.assertEqual(http_client.BAD_REQUEST, response['status'])
self.assertEqual('ValidationError', response['code']) self.assertEqual('ValidationError', response['code'])

View File

@ -68,7 +68,6 @@ podmanager_schema = {
'additionalProperties': False, 'additionalProperties': False,
} }
jsonschema.Draft4Validator.check_schema(podmanager_schema) jsonschema.Draft4Validator.check_schema(podmanager_schema)
compose_node_with_flavor = { compose_node_with_flavor = {
@ -91,6 +90,54 @@ compose_node_schema = {
jsonschema.Draft4Validator.check_schema(compose_node_schema) jsonschema.Draft4Validator.check_schema(compose_node_schema)
node_manage_schema = {
'type': 'object',
'properties': {
'node_index': {'type': 'string'},
},
'required': ['node_index'],
'additionalProperties': False,
}
jsonschema.Draft4Validator.check_schema(node_manage_schema)
node_action_schema = {
'type': 'object',
'properties': {
'Boot': {
'type': 'object',
'properties': {
'Enabled': {
'enum': ['Once', 'Continuous']
},
'Target': {
'enum': ['Pxe', 'Hdd', 'None']
},
},
'required': ['Enabled', 'Target'],
'additionalProperties': False,
},
'Reset': {
'type': 'object',
'properties': {
'Type': {
'enum': ['On', 'ForceOn', 'ForceOff', 'GracefulRestart']
},
},
'required': ['Type'],
'additionalProperties': False,
},
},
'oneOf': [
{'required': ['Boot']},
{'required': ['Reset']}],
'additionalProperties': False,
}
jsonschema.Draft4Validator.check_schema(node_action_schema)
SCHEMAS = {'flavor_schema': flavor_schema, SCHEMAS = {'flavor_schema': flavor_schema,
'podmanager_schema': podmanager_schema, 'podmanager_schema': podmanager_schema,
'compose_node_schema': compose_node_schema, } 'compose_node_schema': compose_node_schema,
'node_manage_schema': node_manage_schema,
'node_action_schema': node_action_schema, }