Merge "Remove baylock"

This commit is contained in:
Jenkins 2015-12-21 06:52:40 +00:00 committed by Gerrit Code Review
commit 2862a60203
13 changed files with 29 additions and 594 deletions

View File

@ -83,7 +83,6 @@ magnum/tests/unit/objects/test_objects.py::
object_data = {
'Bay': '1.0-35edde13ad178e9419e7ea8b6d580bcd',
'BayLock': '1.0-7d1eb08cf2070523bd210369c7a2e076',
'BayModel': '1.0-06863f04ab4b98307e3d1b736d3137bf',
'Container': '1.1-22b40e8eed0414561ca921906b189820',
'MyObj': '1.0-b43567e512438205e32f4e95ca616697',

View File

@ -1,131 +0,0 @@
#
# 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.
import contextlib
from oslo_config import cfg
from oslo_log import log as logging
import oslo_messaging as messaging
from oslo_utils import excutils
from magnum.common import exception
from magnum.conductor.api import ListenerAPI
from magnum.i18n import _LI
from magnum.i18n import _LW
from magnum import objects
cfg.CONF.import_opt('topic', 'magnum.conductor.config',
group='conductor')
cfg.CONF.import_opt('conductor_life_check_timeout', 'magnum.conductor.config',
group='conductor')
LOG = logging.getLogger(__name__)
class BayLock(object):
def __init__(self, context, bay, conductor_id):
self.context = context
self.bay = bay
self.conductor_id = conductor_id
@staticmethod
def conductor_alive(context, conductor_id):
topic = cfg.CONF.conductor.topic
timeout = cfg.CONF.conductor.conductor_life_check_timeout
listener_api = ListenerAPI(context=context, topic=topic,
server=conductor_id, timeout=timeout)
try:
return listener_api.ping_conductor()
except messaging.MessagingTimeout:
return False
def acquire(self, retry=True):
"""Acquire a lock on the bay.
:param retry: When True, retry if lock was released while stealing.
"""
lock_conductor_id = objects.BayLock.create(self.bay.uuid,
self.conductor_id)
if lock_conductor_id is None:
LOG.debug("Conductor %(conductor)s acquired lock on bay "
"%(bay)s" % {'conductor': self.conductor_id,
'bay': self.bay.uuid})
return
if (lock_conductor_id == self.conductor_id or
self.conductor_alive(self.context, lock_conductor_id)):
LOG.debug("Lock on bay %(bay)s is owned by conductor "
"%(conductor)s" % {'bay': self.bay.uuid,
'conductor': lock_conductor_id})
raise exception.OperationInProgress(bay_name=self.bay.name)
else:
LOG.info(_LI("Stale lock detected on bay %(bay)s. Conductor "
"%(conductor)s will attempt to steal the lock"),
{'bay': self.bay.uuid, 'conductor': self.conductor_id})
result = objects.BayLock.steal(self.bay.uuid,
lock_conductor_id,
self.conductor_id)
if result is None:
LOG.info(_LI("Conductor %(conductor)s successfully stole the "
"lock on bay %(bay)s"),
{'conductor': self.conductor_id,
'bay': self.bay.uuid})
return
elif result is True:
if retry:
LOG.info(_LI("The lock on bay %(bay)s was released while "
"conductor %(conductor)s was stealing it. "
"Trying again"),
{'bay': self.bay.uuid,
'conductor': self.conductor_id})
return self.acquire(retry=False)
else:
new_lock_conductor_id = result
LOG.info(_LI("Failed to steal lock on bay %(bay)s. "
"Conductor %(conductor)s stole the lock first"),
{'bay': self.bay.uuid,
'conductor': new_lock_conductor_id})
raise exception.OperationInProgress(bay_name=self.bay.name)
def release(self, bay_uuid):
"""Release a bay lock."""
# Only the conductor that owns the lock will be releasing it.
result = objects.BayLock.release(bay_uuid, self.conductor_id)
if result is True:
LOG.warn(_LW("Lock was already released on bay %s!"), bay_uuid)
else:
LOG.debug("Conductor %(conductor)s released lock on bay "
"%(bay)s" % {'conductor': self.conductor_id,
'bay': bay_uuid})
@contextlib.contextmanager
def thread_lock(self, bay_uuid):
"""Acquire a lock and release it only if there is an exception.
The release method still needs to be scheduled to be run at the
end of the thread using the Thread.link method.
"""
try:
self.acquire()
yield
except exception.OperationInProgress:
raise
except: # noqa
with excutils.save_and_reraise_exception():
self.release(bay_uuid)

View File

@ -121,41 +121,6 @@ class Connection(object):
:raises: BayNotFound
"""
@abc.abstractmethod
def create_bay_lock(self, bay_uuid, conductor_id):
"""Create a new baylock.
This method will fail if the bay has already been locked.
:param bay_uuid: The uuid of a bay.
:param conductor_id: The id of a conductor.
:returns: None if success.
Otherwise, the id of the conductor that locks the bay.
"""
@abc.abstractmethod
def steal_bay_lock(self, bay_uuid, old_conductor_id, new_conductor_id):
"""Steal lock of a bay.
Lock the bay with new_conductor_id if the bay is currently locked by
old_conductor_id.
:param bay_uuid: The uuid of a bay.
:param old_conductor_id: The id of the old conductor.
:param new_conductor_id: The id of the new conductor.
:returns: None if success. True if the bay is not locked.
Otherwise, the id of the conductor that locks the bay.
"""
@abc.abstractmethod
def release_bay_lock(self, bay_uuid, conductor_id):
"""Release lock of a bay.
:param bay_uuid: The uuid of a bay.
:param conductor_id: The id of a conductor.
:returns: None if success. True otherwise.
"""
@abc.abstractmethod
def get_baymodel_list(self, context, filters=None,
limit=None, marker=None, sort_key=None,

View File

@ -0,0 +1,29 @@
# 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.
"""remove baylock
Revision ID: 57fbdf2327a2
Revises: adc3b7679ae
Create Date: 2015-12-17 09:27:18.429773
"""
# revision identifiers, used by Alembic.
revision = '57fbdf2327a2'
down_revision = 'adc3b7679ae'
from alembic import op
def upgrade():
op.drop_table('baylock')

View File

@ -252,38 +252,6 @@ class Connection(api.Connection):
ref.update(values)
return ref
def create_bay_lock(self, bay_uuid, conductor_id):
session = get_session()
with session.begin():
query = model_query(models.BayLock, session=session)
lock = query.filter_by(bay_uuid=bay_uuid).first()
if lock is not None:
return lock.conductor_id
session.add(models.BayLock(bay_uuid=bay_uuid,
conductor_id=conductor_id))
def steal_bay_lock(self, bay_uuid, old_conductor_id, new_conductor_id):
session = get_session()
with session.begin():
query = model_query(models.BayLock, session=session)
lock = query.filter_by(bay_uuid=bay_uuid).first()
if lock is None:
return True
elif lock.conductor_id != old_conductor_id:
return lock.conductor_id
else:
lock.update({'conductor_id': new_conductor_id})
def release_bay_lock(self, bay_uuid, conductor_id):
session = get_session()
with session.begin():
query = model_query(models.BayLock, session=session)
query = query.filter_by(bay_uuid=bay_uuid,
conductor_id=conductor_id)
count = query.delete()
if count == 0:
return True
def _add_baymodels_filters(self, query, filters):
if filters is None:
filters = {}

View File

@ -132,19 +132,6 @@ class Bay(Base):
magnum_cert_ref = Column(String(512))
class BayLock(Base):
"""Represents a baylock."""
__tablename__ = 'baylock'
__table_args__ = (
schema.UniqueConstraint('bay_uuid', name='uniq_baylock0bay_uuid'),
table_args()
)
id = Column(Integer, primary_key=True)
bay_uuid = Column(String(36))
conductor_id = Column(String(64))
class BayModel(Base):
"""Represents a bay model."""

View File

@ -13,7 +13,6 @@
# under the License.
from magnum.objects import bay
from magnum.objects import baylock
from magnum.objects import baymodel
from magnum.objects import certificate
from magnum.objects import container
@ -27,7 +26,6 @@ from magnum.objects import x509keypair
Container = container.Container
Bay = bay.Bay
BayLock = baylock.BayLock
BayModel = baymodel.BayModel
MagnumService = magnum_service.MagnumService
Node = node.Node
@ -37,7 +35,6 @@ Service = service.Service
X509KeyPair = x509keypair.X509KeyPair
Certificate = certificate.Certificate
__all__ = (Bay,
BayLock,
BayModel,
Container,
MagnumService,

View File

@ -1,44 +0,0 @@
# 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 oslo_versionedobjects import fields
from magnum.db import api as dbapi
from magnum.objects import base
@base.MagnumObjectRegistry.register
class BayLock(base.MagnumPersistentObject, base.MagnumObject,
base.MagnumObjectDictCompat):
# Version 1.0: Initial version
VERSION = '1.0'
dbapi = dbapi.get_instance()
fields = {
'id': fields.IntegerField(),
'bay_uuid': fields.StringField(nullable=True),
'conductor_id': fields.StringField(nullable=True),
}
@base.remotable_classmethod
def create(cls, bay_uuid, conductor_id):
return cls.dbapi.create_bay_lock(bay_uuid, conductor_id)
@base.remotable_classmethod
def steal(cls, bay_uuid, old_conductor_id, new_conductor_id):
return cls.dbapi.steal_bay_lock(bay_uuid, old_conductor_id,
new_conductor_id)
@base.remotable_classmethod
def release(cls, bay_uuid, conductor_id):
return cls.dbapi.release_bay_lock(bay_uuid, conductor_id)

View File

@ -1,185 +0,0 @@
#
# 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.
import mock
import oslo_messaging as messaging
from magnum.common import exception
from magnum.common import short_id
from magnum.conductor import bay_lock
from magnum.tests import base
from magnum.tests.unit.objects import utils as obj_utils
from mock import patch
class BayLockTest(base.TestCase):
def setUp(self):
super(BayLockTest, self).setUp()
self.conductor_id = short_id.generate_id()
self.bay = obj_utils.get_test_bay(self.context)
class TestThreadLockException(Exception):
pass
@patch('magnum.objects.BayLock.create', return_value=None)
def test_successful_acquire_new_lock(self, mock_object_create):
baylock = bay_lock.BayLock(self.context, self.bay, self.conductor_id)
baylock.acquire()
mock_object_create.assert_called_once_with(self.bay.uuid,
self.conductor_id)
@patch('magnum.objects.BayLock.create')
def test_failed_acquire_current_conductor_lock(self, mock_object_create):
mock_object_create.return_value = self.conductor_id
baylock = bay_lock.BayLock(self.context, self.bay, self.conductor_id)
self.assertRaises(exception.OperationInProgress, baylock.acquire)
mock_object_create.assert_called_once_with(self.bay.uuid,
self.conductor_id)
@patch('magnum.objects.BayLock.steal', return_value=None)
@patch('magnum.objects.BayLock.create', return_value='fake-conductor-id')
def test_successful_acquire_dead_conductor_lock(self, mock_object_create,
mock_object_steal):
baylock = bay_lock.BayLock(self.context, self.bay, self.conductor_id)
with mock.patch.object(baylock, 'conductor_alive',
return_value=False):
baylock.acquire()
mock_object_create.assert_called_once_with(self.bay.uuid,
self.conductor_id)
mock_object_steal.assert_called_once_with(
self.bay.uuid,
'fake-conductor-id', self.conductor_id)
@patch('magnum.objects.BayLock.create', return_value='fake-conductor-id')
def test_failed_acquire_alive_conductor_lock(self, mock_object_create):
baylock = bay_lock.BayLock(self.context, self.bay, self.conductor_id)
with mock.patch.object(baylock, 'conductor_alive',
return_value=True):
self.assertRaises(exception.OperationInProgress, baylock.acquire)
mock_object_create.assert_called_once_with(self.bay.uuid,
self.conductor_id)
@patch('magnum.objects.BayLock.steal', return_value='fake-conductor-id2')
@patch('magnum.objects.BayLock.create', return_value='fake-conductor-id')
def test_failed_acquire_dead_conductor_lock(self, mock_object_create,
mock_object_steal):
baylock = bay_lock.BayLock(self.context, self.bay, self.conductor_id)
with mock.patch.object(baylock, 'conductor_alive',
return_value=False):
self.assertRaises(exception.OperationInProgress, baylock.acquire)
mock_object_create.assert_called_once_with(self.bay.uuid,
self.conductor_id)
mock_object_steal.assert_called_once_with(
self.bay.uuid,
'fake-conductor-id', self.conductor_id)
@patch('magnum.objects.BayLock.steal', side_effect=[True, None])
@patch('magnum.objects.BayLock.create', return_value='fake-conductor-id')
def test_successful_acquire_with_retry(self, mock_object_create,
mock_object_steal):
baylock = bay_lock.BayLock(self.context, self.bay, self.conductor_id)
with mock.patch.object(baylock, 'conductor_alive',
return_value=False):
baylock.acquire()
mock_object_create.assert_has_calls(
[mock.call(self.bay.uuid, self.conductor_id)] * 2)
mock_object_steal.assert_has_calls(
[mock.call(self.bay.uuid, 'fake-conductor-id',
self.conductor_id)] * 2)
@patch('magnum.objects.BayLock.steal', return_value=True)
@patch('magnum.objects.BayLock.create', return_value='fake-conductor-id')
def test_failed_acquire_one_retry_only(self, mock_object_create,
mock_object_steal):
baylock = bay_lock.BayLock(self.context, self.bay, self.conductor_id)
with mock.patch.object(baylock, 'conductor_alive',
return_value=False):
self.assertRaises(exception.OperationInProgress, baylock.acquire)
mock_object_create.assert_has_calls(
[mock.call(self.bay.uuid, self.conductor_id)] * 2)
mock_object_steal.assert_has_calls(
[mock.call(self.bay.uuid, 'fake-conductor-id',
self.conductor_id)] * 2)
@patch('magnum.objects.BayLock.release', return_value=None)
@patch('magnum.objects.BayLock.create', return_value=None)
def test_thread_lock_acquire_success_with_exception(self,
mock_object_create,
mock_object_release):
baylock = bay_lock.BayLock(self.context, self.bay, self.conductor_id)
def check_thread_lock():
with baylock.thread_lock(self.bay.uuid):
self.assertEqual(1, mock_object_create.call_count)
raise self.TestThreadLockException
self.assertRaises(self.TestThreadLockException, check_thread_lock)
self.assertEqual(1, mock_object_release.call_count)
@patch('magnum.objects.BayLock.release', return_value=None)
@patch('magnum.objects.BayLock.create')
def test_thread_lock_acquire_fail_with_exception(self, mock_object_create,
mock_object_release):
mock_object_create.return_value = self.conductor_id
baylock = bay_lock.BayLock(self.context, self.bay, self.conductor_id)
def check_thread_lock():
with baylock.thread_lock(self.bay.uuid):
self.assertEqual(1, mock_object_create.call_count)
raise exception.OperationInProgress
self.assertRaises(exception.OperationInProgress, check_thread_lock)
assert not mock_object_release.called
@patch('magnum.objects.BayLock.release', return_value=None)
@patch('magnum.objects.BayLock.create', return_value=None)
def test_thread_lock_acquire_success_no_exception(self, mock_object_create,
mock_object_release):
baylock = bay_lock.BayLock(self.context, self.bay, self.conductor_id)
with baylock.thread_lock(self.bay.uuid):
self.assertEqual(1, mock_object_create.call_count)
assert not mock_object_release.called
@patch('magnum.conductor.api.ListenerAPI.__new__')
def test_conductor_alive_ok(self, mock_listener_api_new):
mock_listener_api = mock.MagicMock()
mock_listener_api.ping_conductor.return_value = True
mock_listener_api_new.return_value = mock_listener_api
baylock = bay_lock.BayLock(self.context, self.bay, self.conductor_id)
ret = baylock.conductor_alive(self.context, self.conductor_id)
self.assertIs(True, ret)
self.assertEqual(1, mock_listener_api_new.call_count)
@patch('magnum.conductor.api.ListenerAPI.__new__')
def test_conductor_alive_timeout(self, mock_listener_api_new):
mock_listener_api = mock.MagicMock()
mock_listener_api.ping_conductor.side_effect = (
messaging.MessagingTimeout('too slow'))
mock_listener_api_new.return_value = mock_listener_api
baylock = bay_lock.BayLock(self.context, self.bay, self.conductor_id)
ret = baylock.conductor_alive(self.context, self.conductor_id)
self.assertIs(False, ret)
self.assertEqual(1, mock_listener_api_new.call_count)

View File

@ -1,88 +0,0 @@
# 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.
"""Tests for manipulating BayLocks via the DB API"""
import uuid
from magnum.tests.unit.db import base
from magnum.tests.unit.db import utils as utils
class DbBayLockTestCase(base.DbTestCase):
def setUp(self):
super(DbBayLockTestCase, self).setUp()
self.bay = utils.create_test_bay()
def test_create_bay_lock_success(self):
ret = self.dbapi.create_bay_lock(self.bay.uuid, str(uuid.uuid4()))
self.assertIsNone(ret)
def test_create_bay_lock_fail_double_same(self):
conductor_id = str(uuid.uuid4())
self.dbapi.create_bay_lock(self.bay.uuid, conductor_id)
ret = self.dbapi.create_bay_lock(self.bay.uuid, conductor_id)
self.assertEqual(conductor_id, ret)
def test_create_bay_lock_fail_double_different(self):
conductor_id = str(uuid.uuid4())
self.dbapi.create_bay_lock(self.bay.uuid, conductor_id)
ret = self.dbapi.create_bay_lock(self.bay.uuid, str(uuid.uuid4()))
self.assertEqual(conductor_id, ret)
def test_steal_bay_lock_success(self):
conductor_id = str(uuid.uuid4())
self.dbapi.create_bay_lock(self.bay.uuid, conductor_id)
ret = self.dbapi.steal_bay_lock(self.bay.uuid, conductor_id,
str(uuid.uuid4()))
self.assertIsNone(ret)
def test_steal_bay_lock_fail_gone(self):
conductor_id = str(uuid.uuid4())
self.dbapi.create_bay_lock(self.bay.uuid, conductor_id)
self.dbapi.release_bay_lock(self.bay.uuid, conductor_id)
ret = self.dbapi.steal_bay_lock(self.bay.uuid, conductor_id,
str(uuid.uuid4()))
self.assertTrue(ret)
def test_steal_bay_lock_fail_stolen(self):
conductor_id = str(uuid.uuid4())
self.dbapi.create_bay_lock(self.bay.uuid, conductor_id)
# Simulate stolen lock
conductor_id2 = str(uuid.uuid4())
self.dbapi.release_bay_lock(self.bay.uuid, conductor_id)
self.dbapi.create_bay_lock(self.bay.uuid, conductor_id2)
ret = self.dbapi.steal_bay_lock(self.bay.uuid, str(uuid.uuid4()),
conductor_id2)
self.assertEqual(conductor_id2, ret)
def test_release_bay_lock_success(self):
conductor_id = str(uuid.uuid4())
self.dbapi.create_bay_lock(self.bay.uuid, conductor_id)
ret = self.dbapi.release_bay_lock(self.bay.uuid, conductor_id)
self.assertIsNone(ret)
def test_release_bay_lock_fail_double(self):
conductor_id = str(uuid.uuid4())
self.dbapi.create_bay_lock(self.bay.uuid, conductor_id)
self.dbapi.release_bay_lock(self.bay.uuid, conductor_id)
ret = self.dbapi.release_bay_lock(self.bay.uuid, conductor_id)
self.assertTrue(ret)
def test_release_bay_lock_fail_wrong_conductor_id(self):
conductor_id = str(uuid.uuid4())
self.dbapi.create_bay_lock(self.bay.uuid, conductor_id)
ret = self.dbapi.release_bay_lock(self.bay.uuid, str(uuid.uuid4()))
self.assertTrue(ret)

View File

@ -278,15 +278,6 @@ def create_test_rc(**kw):
return dbapi.create_rc(replication_controller)
def get_test_baylock(**kw):
return {
'id': kw.get('id', 42),
'bay_uuid': kw.get('bay_uuid', '5d12f6fd-a196-4bf0-ae4c-1f639a523a52'),
'conductor_id': kw.get('conductor_id',
'72625085-c507-4410-9b28-cd7cf1fbf1ad'),
}
def get_test_x509keypair(**kw):
return {
'id': kw.get('id', 42),

View File

@ -1,52 +0,0 @@
# 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.
import mock
import uuid
from magnum import objects
from magnum.tests.unit.db import base
from magnum.tests.unit.db import utils
class TestBayLockObject(base.DbTestCase):
def setUp(self):
super(TestBayLockObject, self).setUp()
baylock_dict = utils.get_test_baylock()
self.bay_uuid = baylock_dict['bay_uuid']
self.conductor_id = baylock_dict['conductor_id']
def test_create(self):
with mock.patch.object(self.dbapi, 'create_bay_lock',
autospec=True) as mock_create_baylock:
objects.BayLock.create(self.bay_uuid, self.conductor_id)
mock_create_baylock.assert_called_once_with(self.bay_uuid,
self.conductor_id)
def test_steal(self):
with mock.patch.object(self.dbapi, 'steal_bay_lock',
autospec=True) as mock_steal_baylock:
old_conductor_id = self.conductor_id
new_conductor_id = str(uuid.uuid4())
objects.BayLock.steal(self.bay_uuid, old_conductor_id,
new_conductor_id)
mock_steal_baylock.assert_called_once_with(
self.bay_uuid,
old_conductor_id, new_conductor_id)
def test_release(self):
with mock.patch.object(self.dbapi, 'release_bay_lock',
autospec=True) as mock_release_baylock:
objects.BayLock.release(self.bay_uuid, self.conductor_id)
mock_release_baylock.assert_called_once_with(self.bay_uuid,
self.conductor_id)

View File

@ -424,7 +424,6 @@ class _TestObject(object):
# http://docs.openstack.org/developer/magnum/objects.html
object_data = {
'Bay': '1.2-0749bac339a2cc24dc03f45a4359013d',
'BayLock': '1.0-7d1eb08cf2070523bd210369c7a2e076',
'BayModel': '1.8-a4bb0976be245f06edbd1db087a18071',
'Certificate': '1.0-2aff667971b85c1edf8d15684fd7d5e2',
'Container': '1.3-e2d9d2e8a8844d421148cd9fde6c6bd6',