Support for async bay operations
Current implementation of magnum bay operations are synchronous and as a result API requests are blocked until response from HEAT service is received. With this change bay-create, bay-update and bay-delete calls will be asynchronous. Please note that with this change bay-create/bay-update api calls will return bay uuid instead of bay object and also microversion 1.2 is added for new behavior. Change-Id: I4ca1f9f386b6417726154c466e7a9104b6e6e5e1 Closes-Bug: #1588425
This commit is contained in:
parent
fc9c1a8fc0
commit
bf30b9b4cb
@ -13,6 +13,8 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
import pecan
|
import pecan
|
||||||
@ -54,6 +56,13 @@ class BayPatchType(types.JsonPatchType):
|
|||||||
return types.JsonPatchType.internal_attrs() + internal_attrs
|
return types.JsonPatchType.internal_attrs() + internal_attrs
|
||||||
|
|
||||||
|
|
||||||
|
class BayID(wtypes.Base):
|
||||||
|
uuid = types.uuid
|
||||||
|
|
||||||
|
def __init__(self, uuid):
|
||||||
|
self.uuid = uuid
|
||||||
|
|
||||||
|
|
||||||
class Bay(base.APIBase):
|
class Bay(base.APIBase):
|
||||||
"""API representation of a bay.
|
"""API representation of a bay.
|
||||||
|
|
||||||
@ -319,12 +328,33 @@ class BaysController(base.Controller):
|
|||||||
|
|
||||||
return bay
|
return bay
|
||||||
|
|
||||||
|
@base.Controller.api_version("1.1", "1.1")
|
||||||
@expose.expose(Bay, body=Bay, status_code=201)
|
@expose.expose(Bay, body=Bay, status_code=201)
|
||||||
def post(self, bay):
|
def post(self, bay):
|
||||||
"""Create a new bay.
|
"""Create a new bay.
|
||||||
|
|
||||||
:param bay: a bay within the request body.
|
:param bay: a bay within the request body.
|
||||||
"""
|
"""
|
||||||
|
new_bay = self._post(bay)
|
||||||
|
res_bay = pecan.request.rpcapi.bay_create(new_bay,
|
||||||
|
bay.bay_create_timeout)
|
||||||
|
|
||||||
|
# Set the HTTP Location Header
|
||||||
|
pecan.response.location = link.build_url('bays', res_bay.uuid)
|
||||||
|
return Bay.convert_with_links(res_bay)
|
||||||
|
|
||||||
|
@base.Controller.api_version("1.2") # noqa
|
||||||
|
@expose.expose(BayID, body=Bay, status_code=202)
|
||||||
|
def post(self, bay):
|
||||||
|
"""Create a new bay.
|
||||||
|
|
||||||
|
:param bay: a bay within the request body.
|
||||||
|
"""
|
||||||
|
new_bay = self._post(bay)
|
||||||
|
pecan.request.rpcapi.bay_create_async(new_bay, bay.bay_create_timeout)
|
||||||
|
return BayID(new_bay.uuid)
|
||||||
|
|
||||||
|
def _post(self, bay):
|
||||||
context = pecan.request.context
|
context = pecan.request.context
|
||||||
policy.enforce(context, 'bay:create',
|
policy.enforce(context, 'bay:create',
|
||||||
action='bay:create')
|
action='bay:create')
|
||||||
@ -340,13 +370,10 @@ class BaysController(base.Controller):
|
|||||||
bay_dict['name'] = name
|
bay_dict['name'] = name
|
||||||
|
|
||||||
new_bay = objects.Bay(context, **bay_dict)
|
new_bay = objects.Bay(context, **bay_dict)
|
||||||
res_bay = pecan.request.rpcapi.bay_create(new_bay,
|
new_bay.uuid = uuid.uuid4()
|
||||||
bay.bay_create_timeout)
|
return new_bay
|
||||||
|
|
||||||
# Set the HTTP Location Header
|
|
||||||
pecan.response.location = link.build_url('bays', res_bay.uuid)
|
|
||||||
return Bay.convert_with_links(res_bay)
|
|
||||||
|
|
||||||
|
@base.Controller.api_version("1.1", "1.1")
|
||||||
@wsme.validate(types.uuid, [BayPatchType])
|
@wsme.validate(types.uuid, [BayPatchType])
|
||||||
@expose.expose(Bay, types.uuid_or_name, body=[BayPatchType])
|
@expose.expose(Bay, types.uuid_or_name, body=[BayPatchType])
|
||||||
def patch(self, bay_ident, patch):
|
def patch(self, bay_ident, patch):
|
||||||
@ -355,6 +382,25 @@ class BaysController(base.Controller):
|
|||||||
:param bay_ident: UUID or logical name of a bay.
|
:param bay_ident: UUID or logical name of a bay.
|
||||||
:param patch: a json PATCH document to apply to this bay.
|
:param patch: a json PATCH document to apply to this bay.
|
||||||
"""
|
"""
|
||||||
|
bay = self._patch(bay_ident, patch)
|
||||||
|
res_bay = pecan.request.rpcapi.bay_update(bay)
|
||||||
|
return Bay.convert_with_links(res_bay)
|
||||||
|
|
||||||
|
@base.Controller.api_version("1.2") # noqa
|
||||||
|
@wsme.validate(types.uuid, [BayPatchType])
|
||||||
|
@expose.expose(BayID, types.uuid_or_name, body=[BayPatchType],
|
||||||
|
status_code=202)
|
||||||
|
def patch(self, bay_ident, patch):
|
||||||
|
"""Update an existing bay.
|
||||||
|
|
||||||
|
:param bay_ident: UUID or logical name of a bay.
|
||||||
|
:param patch: a json PATCH document to apply to this bay.
|
||||||
|
"""
|
||||||
|
bay = self._patch(bay_ident, patch)
|
||||||
|
pecan.request.rpcapi.bay_update_async(bay)
|
||||||
|
return BayID(bay.uuid)
|
||||||
|
|
||||||
|
def _patch(self, bay_ident, patch):
|
||||||
context = pecan.request.context
|
context = pecan.request.context
|
||||||
bay = api_utils.get_resource('Bay', bay_ident)
|
bay = api_utils.get_resource('Bay', bay_ident)
|
||||||
policy.enforce(context, 'bay:update', bay,
|
policy.enforce(context, 'bay:update', bay,
|
||||||
@ -380,19 +426,33 @@ class BaysController(base.Controller):
|
|||||||
delta = bay.obj_what_changed()
|
delta = bay.obj_what_changed()
|
||||||
|
|
||||||
validate_bay_properties(delta)
|
validate_bay_properties(delta)
|
||||||
|
return bay
|
||||||
|
|
||||||
res_bay = pecan.request.rpcapi.bay_update(bay)
|
@base.Controller.api_version("1.1", "1.1")
|
||||||
return Bay.convert_with_links(res_bay)
|
|
||||||
|
|
||||||
@expose.expose(None, types.uuid_or_name, status_code=204)
|
@expose.expose(None, types.uuid_or_name, status_code=204)
|
||||||
def delete(self, bay_ident):
|
def delete(self, bay_ident):
|
||||||
"""Delete a bay.
|
"""Delete a bay.
|
||||||
|
|
||||||
:param bay_ident: UUID of a bay or logical name of the bay.
|
:param bay_ident: UUID of a bay or logical name of the bay.
|
||||||
"""
|
"""
|
||||||
|
bay = self._delete(bay_ident)
|
||||||
|
|
||||||
|
pecan.request.rpcapi.bay_delete(bay.uuid)
|
||||||
|
|
||||||
|
@base.Controller.api_version("1.2") # noqa
|
||||||
|
@expose.expose(None, types.uuid_or_name, status_code=204)
|
||||||
|
def delete(self, bay_ident):
|
||||||
|
"""Delete a bay.
|
||||||
|
|
||||||
|
:param bay_ident: UUID of a bay or logical name of the bay.
|
||||||
|
"""
|
||||||
|
bay = self._delete(bay_ident)
|
||||||
|
|
||||||
|
pecan.request.rpcapi.bay_delete_async(bay.uuid)
|
||||||
|
|
||||||
|
def _delete(self, bay_ident):
|
||||||
context = pecan.request.context
|
context = pecan.request.context
|
||||||
bay = api_utils.get_resource('Bay', bay_ident)
|
bay = api_utils.get_resource('Bay', bay_ident)
|
||||||
policy.enforce(context, 'bay:delete', bay,
|
policy.enforce(context, 'bay:delete', bay,
|
||||||
action='bay:delete')
|
action='bay:delete')
|
||||||
|
return bay
|
||||||
pecan.request.rpcapi.bay_delete(bay.uuid)
|
|
||||||
|
@ -28,7 +28,9 @@ from magnum.i18n import _
|
|||||||
# Add details of new api versions here:
|
# Add details of new api versions here:
|
||||||
|
|
||||||
BASE_VER = '1.1'
|
BASE_VER = '1.1'
|
||||||
CURRENT_MAX_VER = '1.1'
|
CURRENT_MAX_VER = '1.2'
|
||||||
|
# 1.2 Async bay operations support
|
||||||
|
# 1.1 Initial version
|
||||||
|
|
||||||
|
|
||||||
class Version(object):
|
class Version(object):
|
||||||
|
@ -34,12 +34,22 @@ class API(rpc_service.API):
|
|||||||
return self._call('bay_create', bay=bay,
|
return self._call('bay_create', bay=bay,
|
||||||
bay_create_timeout=bay_create_timeout)
|
bay_create_timeout=bay_create_timeout)
|
||||||
|
|
||||||
|
def bay_create_async(self, bay, bay_create_timeout):
|
||||||
|
self._cast('bay_create', bay=bay,
|
||||||
|
bay_create_timeout=bay_create_timeout)
|
||||||
|
|
||||||
def bay_delete(self, uuid):
|
def bay_delete(self, uuid):
|
||||||
return self._call('bay_delete', uuid=uuid)
|
return self._call('bay_delete', uuid=uuid)
|
||||||
|
|
||||||
|
def bay_delete_async(self, uuid):
|
||||||
|
self._cast('bay_delete', uuid=uuid)
|
||||||
|
|
||||||
def bay_update(self, bay):
|
def bay_update(self, bay):
|
||||||
return self._call('bay_update', bay=bay)
|
return self._call('bay_update', bay=bay)
|
||||||
|
|
||||||
|
def bay_update_async(self, bay):
|
||||||
|
self._cast('bay_update', bay=bay)
|
||||||
|
|
||||||
# CA operations
|
# CA operations
|
||||||
|
|
||||||
def sign_certificate(self, bay, certificate):
|
def sign_certificate(self, bay, certificate):
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import uuid
|
|
||||||
|
|
||||||
from heatclient.common import template_utils
|
from heatclient.common import template_utils
|
||||||
from heatclient import exc
|
from heatclient import exc
|
||||||
@ -145,7 +144,6 @@ class Handler(object):
|
|||||||
|
|
||||||
osc = clients.OpenStackClients(context)
|
osc = clients.OpenStackClients(context)
|
||||||
|
|
||||||
bay.uuid = uuid.uuid4()
|
|
||||||
try:
|
try:
|
||||||
# Create trustee/trust and set them to bay
|
# Create trustee/trust and set them to bay
|
||||||
trust_manager.create_trustee_and_trust(osc, bay)
|
trust_manager.create_trustee_and_trust(osc, bay)
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
import fixtures
|
import fixtures
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
from oslo_utils import uuidutils
|
||||||
from tempest.lib.common.utils import data_utils
|
from tempest.lib.common.utils import data_utils
|
||||||
from tempest.lib import exceptions
|
from tempest.lib import exceptions
|
||||||
import testtools
|
import testtools
|
||||||
@ -90,16 +91,21 @@ class BayTest(base.BaseTempestTest):
|
|||||||
resp, model = self.baymodel_client.delete_baymodel(baymodel_id)
|
resp, model = self.baymodel_client.delete_baymodel(baymodel_id)
|
||||||
return resp, model
|
return resp, model
|
||||||
|
|
||||||
def _create_bay(self, bay_model):
|
def _create_bay(self, bay_model, is_async=False):
|
||||||
self.LOG.debug('We will create bay for %s' % bay_model)
|
self.LOG.debug('We will create bay for %s' % bay_model)
|
||||||
resp, model = self.bay_client.post_bay(bay_model)
|
headers = {'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json'}
|
||||||
|
if is_async:
|
||||||
|
headers["OpenStack-API-Version"] = "container-infra 1.2"
|
||||||
|
resp, model = self.bay_client.post_bay(bay_model, headers=headers)
|
||||||
self.LOG.debug('Response: %s' % resp)
|
self.LOG.debug('Response: %s' % resp)
|
||||||
|
if is_async:
|
||||||
|
self.assertEqual(202, resp.status)
|
||||||
|
else:
|
||||||
self.assertEqual(201, resp.status)
|
self.assertEqual(201, resp.status)
|
||||||
self.assertIsNotNone(model.uuid)
|
self.assertIsNotNone(model.uuid)
|
||||||
|
self.assertTrue(uuidutils.is_uuid_like(model.uuid))
|
||||||
self.bays.append(model.uuid)
|
self.bays.append(model.uuid)
|
||||||
self.assertEqual(BayStatus.CREATE_IN_PROGRESS, model.status)
|
|
||||||
self.assertIsNone(model.status_reason)
|
|
||||||
self.assertEqual(model.baymodel_id, self.baymodel.uuid)
|
|
||||||
self.bay_uuid = model.uuid
|
self.bay_uuid = model.uuid
|
||||||
if config.Config.copy_logs:
|
if config.Config.copy_logs:
|
||||||
self.addOnException(self.copy_logs_handler(
|
self.addOnException(self.copy_logs_handler(
|
||||||
@ -134,6 +140,8 @@ class BayTest(base.BaseTempestTest):
|
|||||||
|
|
||||||
# test bay create
|
# test bay create
|
||||||
_, temp_model = self._create_bay(gen_model)
|
_, temp_model = self._create_bay(gen_model)
|
||||||
|
self.assertEqual(BayStatus.CREATE_IN_PROGRESS, temp_model.status)
|
||||||
|
self.assertIsNone(temp_model.status_reason)
|
||||||
|
|
||||||
# test bay list
|
# test bay list
|
||||||
resp, model = self.bay_client.list_bays()
|
resp, model = self.bay_client.list_bays()
|
||||||
@ -153,6 +161,26 @@ class BayTest(base.BaseTempestTest):
|
|||||||
self._delete_bay(temp_model.uuid)
|
self._delete_bay(temp_model.uuid)
|
||||||
self.bays.remove(temp_model.uuid)
|
self.bays.remove(temp_model.uuid)
|
||||||
|
|
||||||
|
@testtools.testcase.attr('positive')
|
||||||
|
def test_create_delete_bays_async(self):
|
||||||
|
gen_model = datagen.valid_bay_data(
|
||||||
|
baymodel_id=self.baymodel.uuid, node_count=1)
|
||||||
|
|
||||||
|
# test bay create
|
||||||
|
_, temp_model = self._create_bay(gen_model, is_async=True)
|
||||||
|
self.assertNotIn('status', temp_model)
|
||||||
|
|
||||||
|
# test bay list
|
||||||
|
resp, model = self.bay_client.list_bays()
|
||||||
|
self.assertEqual(200, resp.status)
|
||||||
|
self.assertGreater(len(model.bays), 0)
|
||||||
|
self.assertIn(
|
||||||
|
temp_model.uuid, list([x['uuid'] for x in model.bays]))
|
||||||
|
|
||||||
|
# test bay delete
|
||||||
|
self._delete_bay(temp_model.uuid)
|
||||||
|
self.bays.remove(temp_model.uuid)
|
||||||
|
|
||||||
@testtools.testcase.attr('negative')
|
@testtools.testcase.attr('negative')
|
||||||
def test_create_bay_for_nonexisting_baymodel(self):
|
def test_create_bay_for_nonexisting_baymodel(self):
|
||||||
gen_model = datagen.valid_bay_data(baymodel_id='this-does-not-exist')
|
gen_model = datagen.valid_bay_data(baymodel_id='this-does-not-exist')
|
||||||
|
@ -40,7 +40,7 @@ class TestRootController(api_base.FunctionalTest):
|
|||||||
[{u'href': u'http://localhost/v1/',
|
[{u'href': u'http://localhost/v1/',
|
||||||
u'rel': u'self'}],
|
u'rel': u'self'}],
|
||||||
u'status': u'CURRENT',
|
u'status': u'CURRENT',
|
||||||
u'max_version': u'1.1',
|
u'max_version': u'1.2',
|
||||||
u'min_version': u'1.1'}]}
|
u'min_version': u'1.1'}]}
|
||||||
|
|
||||||
self.v1_expected = {
|
self.v1_expected = {
|
||||||
|
@ -16,7 +16,6 @@ import mock
|
|||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
from six.moves.urllib import parse as urlparse
|
|
||||||
|
|
||||||
from magnum.api import attr_validator
|
from magnum.api import attr_validator
|
||||||
from magnum.api.controllers.v1 import bay as api_bay
|
from magnum.api.controllers.v1 import bay as api_bay
|
||||||
@ -428,10 +427,7 @@ class TestPost(api_base.FunctionalTest):
|
|||||||
self.assertEqual(201, response.status_int)
|
self.assertEqual(201, response.status_int)
|
||||||
# Check location header
|
# Check location header
|
||||||
self.assertIsNotNone(response.location)
|
self.assertIsNotNone(response.location)
|
||||||
expected_location = '/v1/bays/%s' % bdict['uuid']
|
self.assertTrue(uuidutils.is_uuid_like(response.json['uuid']))
|
||||||
self.assertEqual(expected_location,
|
|
||||||
urlparse.urlparse(response.location).path)
|
|
||||||
self.assertEqual(bdict['uuid'], response.json['uuid'])
|
|
||||||
self.assertNotIn('updated_at', response.json.keys)
|
self.assertNotIn('updated_at', response.json.keys)
|
||||||
return_created_at = timeutils.parse_isotime(
|
return_created_at = timeutils.parse_isotime(
|
||||||
response.json['created_at']).replace(tzinfo=None)
|
response.json['created_at']).replace(tzinfo=None)
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import six
|
import six
|
||||||
import uuid
|
|
||||||
|
|
||||||
from heatclient import exc
|
from heatclient import exc
|
||||||
import mock
|
import mock
|
||||||
@ -172,14 +171,11 @@ class TestHandler(db_base.DbTestCase):
|
|||||||
@patch('magnum.conductor.handlers.bay_conductor.trust_manager')
|
@patch('magnum.conductor.handlers.bay_conductor.trust_manager')
|
||||||
@patch('magnum.conductor.handlers.bay_conductor.cert_manager')
|
@patch('magnum.conductor.handlers.bay_conductor.cert_manager')
|
||||||
@patch('magnum.conductor.handlers.bay_conductor._create_stack')
|
@patch('magnum.conductor.handlers.bay_conductor._create_stack')
|
||||||
@patch('magnum.conductor.handlers.bay_conductor.uuid')
|
|
||||||
@patch('magnum.common.clients.OpenStackClients')
|
@patch('magnum.common.clients.OpenStackClients')
|
||||||
def test_create(self, mock_openstack_client_class, mock_uuid,
|
def test_create(self, mock_openstack_client_class,
|
||||||
mock_create_stack, mock_cert_manager, mock_trust_manager,
|
mock_create_stack, mock_cert_manager, mock_trust_manager,
|
||||||
mock_heat_poller_class):
|
mock_heat_poller_class):
|
||||||
timeout = 15
|
timeout = 15
|
||||||
test_uuid = uuid.uuid4()
|
|
||||||
mock_uuid.uuid4.return_value = test_uuid
|
|
||||||
mock_poller = mock.MagicMock()
|
mock_poller = mock.MagicMock()
|
||||||
mock_poller.poll_and_check.return_value = loopingcall.LoopingCallDone()
|
mock_poller.poll_and_check.return_value = loopingcall.LoopingCallDone()
|
||||||
mock_heat_poller_class.return_value = mock_poller
|
mock_heat_poller_class.return_value = mock_poller
|
||||||
@ -187,7 +183,6 @@ class TestHandler(db_base.DbTestCase):
|
|||||||
mock_openstack_client_class.return_value = osc
|
mock_openstack_client_class.return_value = osc
|
||||||
|
|
||||||
def create_stack_side_effect(context, osc, bay, timeout):
|
def create_stack_side_effect(context, osc, bay, timeout):
|
||||||
self.assertEqual(str(test_uuid), bay.uuid)
|
|
||||||
return {'stack': {'id': 'stack-id'}}
|
return {'stack': {'id': 'stack-id'}}
|
||||||
|
|
||||||
mock_create_stack.side_effect = create_stack_side_effect
|
mock_create_stack.side_effect = create_stack_side_effect
|
||||||
@ -334,16 +329,12 @@ class TestHandler(db_base.DbTestCase):
|
|||||||
@patch('magnum.conductor.handlers.bay_conductor.trust_manager')
|
@patch('magnum.conductor.handlers.bay_conductor.trust_manager')
|
||||||
@patch('magnum.conductor.handlers.bay_conductor.cert_manager')
|
@patch('magnum.conductor.handlers.bay_conductor.cert_manager')
|
||||||
@patch('magnum.conductor.handlers.bay_conductor._create_stack')
|
@patch('magnum.conductor.handlers.bay_conductor._create_stack')
|
||||||
@patch('magnum.conductor.handlers.bay_conductor.uuid')
|
|
||||||
@patch('magnum.common.clients.OpenStackClients')
|
@patch('magnum.common.clients.OpenStackClients')
|
||||||
def test_create_with_invalid_unicode_name(self,
|
def test_create_with_invalid_unicode_name(self,
|
||||||
mock_openstack_client_class,
|
mock_openstack_client_class,
|
||||||
mock_uuid,
|
|
||||||
mock_create_stack,
|
mock_create_stack,
|
||||||
mock_cert_manager,
|
mock_cert_manager,
|
||||||
mock_trust_manager):
|
mock_trust_manager):
|
||||||
test_uuid = uuid.uuid4()
|
|
||||||
mock_uuid.uuid4.return_value = test_uuid
|
|
||||||
error_message = six.u("""Invalid stack name 测试集群-zoyh253geukk
|
error_message = six.u("""Invalid stack name 测试集群-zoyh253geukk
|
||||||
must contain only alphanumeric or "_-."
|
must contain only alphanumeric or "_-."
|
||||||
characters, must start with alpha""")
|
characters, must start with alpha""")
|
||||||
@ -376,11 +367,9 @@ class TestHandler(db_base.DbTestCase):
|
|||||||
@patch('magnum.conductor.handlers.bay_conductor.trust_manager')
|
@patch('magnum.conductor.handlers.bay_conductor.trust_manager')
|
||||||
@patch('magnum.conductor.handlers.bay_conductor.cert_manager')
|
@patch('magnum.conductor.handlers.bay_conductor.cert_manager')
|
||||||
@patch('magnum.conductor.handlers.bay_conductor.short_id')
|
@patch('magnum.conductor.handlers.bay_conductor.short_id')
|
||||||
@patch('magnum.conductor.handlers.bay_conductor.uuid')
|
|
||||||
@patch('magnum.common.clients.OpenStackClients')
|
@patch('magnum.common.clients.OpenStackClients')
|
||||||
def test_create_with_environment(self,
|
def test_create_with_environment(self,
|
||||||
mock_openstack_client_class,
|
mock_openstack_client_class,
|
||||||
mock_uuid,
|
|
||||||
mock_short_id,
|
mock_short_id,
|
||||||
mock_cert_manager,
|
mock_cert_manager,
|
||||||
mock_trust_manager,
|
mock_trust_manager,
|
||||||
@ -390,8 +379,6 @@ class TestHandler(db_base.DbTestCase):
|
|||||||
mock_heat_poller_class):
|
mock_heat_poller_class):
|
||||||
timeout = 15
|
timeout = 15
|
||||||
self.bay.baymodel_id = self.baymodel.uuid
|
self.bay.baymodel_id = self.baymodel.uuid
|
||||||
test_uuid = uuid.uuid4()
|
|
||||||
mock_uuid.uuid4.return_value = test_uuid
|
|
||||||
bay_name = self.bay.name
|
bay_name = self.bay.name
|
||||||
mock_short_id.generate_id.return_value = 'short_id'
|
mock_short_id.generate_id.return_value = 'short_id'
|
||||||
mock_poller = mock.MagicMock()
|
mock_poller = mock.MagicMock()
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Current implementation of magnum bay operations are
|
||||||
|
synchronous and as a result API requests are blocked
|
||||||
|
until response from HEAT service is received. This release
|
||||||
|
adds support for asynchronous bay operations (bay-create,
|
||||||
|
bay-update, and bay-delete). Please note that with this
|
||||||
|
change, bay-create, bay-update API calls will return bay uuid
|
||||||
|
instead of bay object and also return HTTP status code 202
|
||||||
|
instead of 201. Microversion 1.2 is added for new behavior.
|
||||||
|
|
||||||
|
upgrade:
|
||||||
|
- Magnum bay operations API default behavior changed from
|
||||||
|
synchronous to asynchronous. User can specify
|
||||||
|
OpenStack-API-Version 1.1 in request header for synchronous
|
||||||
|
bay operations.
|
Loading…
Reference in New Issue
Block a user