
1. What is the problem In the current controller implementation in nova_apigw and cinder_apigw, pecan.abort is used to raise an exception when error occurs. The problem of using pecan.abort is that the response body doesn't have the same format with the error response body in nova api and cinder api. Thus python client may not correctly extract the error message, also, tempest test may fail. 2. What is the solution to the problem Replace pecan.abort with correct response body. 3. What the features need to be implemented to the Tricircle to realize the solution In this patch, we remove pecan.abort calls in controllers of nova and cinder resources and directly return the error response body with correct format. Controllers for the Tricircle api still keep pecan.abort calls since we don't have special requirement on the format of error response body. Change-Id: I0e6fe9ddfce3f001fee0be2160d24c9c628d0a88
160 lines
5.9 KiB
Python
160 lines
5.9 KiB
Python
# Copyright (c) 2015 Huawei Tech. Co., Ltd.
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
from mock import patch
|
|
import pecan
|
|
import unittest
|
|
|
|
from oslo_utils import uuidutils
|
|
|
|
from tricircle.common import client
|
|
from tricircle.common import constants
|
|
from tricircle.common import context
|
|
from tricircle.common import exceptions
|
|
from tricircle.db import api
|
|
from tricircle.db import core
|
|
from tricircle.db import models
|
|
from tricircle.nova_apigw.controllers import action
|
|
|
|
|
|
class FakeResponse(object):
|
|
def __new__(cls, code=500):
|
|
cls.status = code
|
|
cls.status_code = code
|
|
return super(FakeResponse, cls).__new__(cls)
|
|
|
|
|
|
class ActionTest(unittest.TestCase):
|
|
def setUp(self):
|
|
core.initialize()
|
|
core.ModelBase.metadata.create_all(core.get_engine())
|
|
self.context = context.get_admin_context()
|
|
self.project_id = 'test_project'
|
|
self.controller = action.ActionController(self.project_id, '')
|
|
|
|
def _prepare_pod(self, bottom_pod_num=1):
|
|
t_pod = {'pod_id': 't_pod_uuid', 'pod_name': 't_region',
|
|
'az_name': ''}
|
|
api.create_pod(self.context, t_pod)
|
|
b_pods = []
|
|
if bottom_pod_num == 1:
|
|
b_pod = {'pod_id': 'b_pod_uuid', 'pod_name': 'b_region',
|
|
'az_name': 'b_az'}
|
|
api.create_pod(self.context, b_pod)
|
|
b_pods.append(b_pod)
|
|
else:
|
|
for i in xrange(1, bottom_pod_num + 1):
|
|
b_pod = {'pod_id': 'b_pod_%d_uuid' % i,
|
|
'pod_name': 'b_region_%d' % i,
|
|
'az_name': 'b_az_%d' % i}
|
|
api.create_pod(self.context, b_pod)
|
|
b_pods.append(b_pod)
|
|
return t_pod, b_pods
|
|
|
|
def _prepare_server(self, pod):
|
|
t_server_id = uuidutils.generate_uuid()
|
|
b_server_id = t_server_id
|
|
with self.context.session.begin():
|
|
core.create_resource(
|
|
self.context, models.ResourceRouting,
|
|
{'top_id': t_server_id, 'bottom_id': b_server_id,
|
|
'pod_id': pod['pod_id'], 'project_id': self.project_id,
|
|
'resource_type': constants.RT_SERVER})
|
|
return t_server_id
|
|
|
|
def _validate_error_code(self, res, code):
|
|
self.assertEqual(code, res[res.keys()[0]]['code'])
|
|
|
|
@patch.object(pecan, 'response', new=FakeResponse)
|
|
@patch.object(context, 'extract_context_from_environ')
|
|
def test_action_not_supported(self, mock_context):
|
|
mock_context.return_value = self.context
|
|
|
|
body = {'unsupported_action': ''}
|
|
res = self.controller.post(**body)
|
|
self._validate_error_code(res, 400)
|
|
|
|
@patch.object(pecan, 'response', new=FakeResponse)
|
|
@patch.object(context, 'extract_context_from_environ')
|
|
def test_action_server_not_found(self, mock_context):
|
|
mock_context.return_value = self.context
|
|
|
|
body = {'os-start': ''}
|
|
res = self.controller.post(**body)
|
|
self._validate_error_code(res, 404)
|
|
|
|
@patch.object(pecan, 'response', new=FakeResponse)
|
|
@patch.object(client.Client, 'action_resources')
|
|
@patch.object(context, 'extract_context_from_environ')
|
|
def test_action_exception(self, mock_context, mock_action):
|
|
mock_context.return_value = self.context
|
|
|
|
t_pod, b_pods = self._prepare_pod()
|
|
t_server_id = self._prepare_server(b_pods[0])
|
|
self.controller.server_id = t_server_id
|
|
|
|
mock_action.side_effect = exceptions.HTTPForbiddenError(
|
|
msg='Server operation forbidden')
|
|
body = {'os-start': ''}
|
|
res = self.controller.post(**body)
|
|
self._validate_error_code(res, 403)
|
|
|
|
mock_action.side_effect = exceptions.ServiceUnavailable
|
|
body = {'os-start': ''}
|
|
res = self.controller.post(**body)
|
|
self._validate_error_code(res, 500)
|
|
|
|
mock_action.side_effect = Exception
|
|
body = {'os-start': ''}
|
|
res = self.controller.post(**body)
|
|
self._validate_error_code(res, 500)
|
|
|
|
@patch.object(pecan, 'response', new=FakeResponse)
|
|
@patch.object(client.Client, 'action_resources')
|
|
@patch.object(context, 'extract_context_from_environ')
|
|
def test_start_action(self, mock_context, mock_action):
|
|
mock_context.return_value = self.context
|
|
mock_action.return_value = (FakeResponse(202), None)
|
|
|
|
t_pod, b_pods = self._prepare_pod()
|
|
t_server_id = self._prepare_server(b_pods[0])
|
|
self.controller.server_id = t_server_id
|
|
|
|
body = {'os-start': ''}
|
|
res = self.controller.post(**body)
|
|
mock_action.assert_called_once_with(
|
|
'server', self.context, 'start', t_server_id)
|
|
self.assertEqual(202, res.status)
|
|
|
|
@patch.object(pecan, 'response', new=FakeResponse)
|
|
@patch.object(client.Client, 'action_resources')
|
|
@patch.object(context, 'extract_context_from_environ')
|
|
def test_stop_action(self, mock_context, mock_action):
|
|
mock_context.return_value = self.context
|
|
mock_action.return_value = (FakeResponse(202), None)
|
|
|
|
t_pod, b_pods = self._prepare_pod()
|
|
t_server_id = self._prepare_server(b_pods[0])
|
|
self.controller.server_id = t_server_id
|
|
|
|
body = {'os-stop': ''}
|
|
res = self.controller.post(**body)
|
|
mock_action.assert_called_once_with(
|
|
'server', self.context, 'stop', t_server_id)
|
|
self.assertEqual(202, res.status)
|
|
|
|
def tearDown(self):
|
|
core.ModelBase.metadata.drop_all(core.get_engine())
|