Add exec_instances to data model
Zun is going to implement a proxy for interactive execute. The first step is to introduce data model for tracking each exec instances of a container. An exec instance in Zun is an one-to-one mapping for exec instance in Docker. We will use it to track a docker's exec instance as well as a generated token for granting access of this exec instance. In the future, the websocket proxy will retrieve the token from incoming request and match it to the one stored in DB. The request will be rejected if the token doesn't match. This patch introduces the data model of exec_instance in DB api and objects layer. It basically contains three fields: container_id, exec_id, url, token. The container_id is the id of the container record in Zun. The exec_id is the ID of the docker's exec instance. The url is the docker daemon endpoint for the exec instance The token is as described above. Partial-Bug: #1735076 Change-Id: Ib38a46c0e3f3aae58e1f562536b858bc4cd23bf8
This commit is contained in:
parent
4db4adf6e5
commit
48075909a9
|
@ -421,6 +421,11 @@ class ContainerAlreadyExists(ResourceExists):
|
|||
message = _("A container with %(field)s %(value)s already exists.")
|
||||
|
||||
|
||||
class ExecInstanceAlreadyExists(ResourceExists):
|
||||
message = _("An exec instance with exec_id %(exec_id)s already exists"
|
||||
"in container.")
|
||||
|
||||
|
||||
class ComputeNodeAlreadyExists(ResourceExists):
|
||||
message = _("A compute node with %(field)s %(value)s already exists.")
|
||||
|
||||
|
|
|
@ -1030,3 +1030,37 @@ def update_network(context, uuid, values):
|
|||
"""
|
||||
return _get_dbdriver_instance().update_network(
|
||||
context, uuid, values)
|
||||
|
||||
|
||||
@profiler.trace("db")
|
||||
def create_exec_instance(context, values):
|
||||
"""Create a new exec instance.
|
||||
|
||||
:param context: The security context
|
||||
:param values: A dict containing several items used to identify
|
||||
and track the exec instance, and several dicts which are
|
||||
passed into the Drivers when managing this exec instance.
|
||||
:returns: An exec instance.
|
||||
"""
|
||||
return _get_dbdriver_instance().create_exec_instance(context, values)
|
||||
|
||||
|
||||
@profiler.trace("db")
|
||||
def list_exec_instances(context, filters=None, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
"""List matching exec instances.
|
||||
|
||||
Return a list exec instances that match the specified filters.
|
||||
|
||||
:param context: The security context
|
||||
:param filters: Filters to apply. Defaults to None.
|
||||
:param limit: Maximum number of containers to return.
|
||||
:param marker: the last item of the previous page; we return the next
|
||||
result set.
|
||||
:param sort_key: Attribute by which results should be sorted.
|
||||
:param sort_dir: Direction in which results should be sorted.
|
||||
(asc, desc)
|
||||
:returns: A list of exec instances.
|
||||
"""
|
||||
return _get_dbdriver_instance().list_exec_instances(
|
||||
context, filters, limit, marker, sort_key, sort_dir)
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
# 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.
|
||||
|
||||
"""create exec_instance table
|
||||
|
||||
Revision ID: 26896d5f9053
|
||||
Revises: 012a730926e8
|
||||
Create Date: 2018-06-03 17:24:33.192354
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '26896d5f9053'
|
||||
down_revision = '012a730926e8'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table(
|
||||
'exec_instance',
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('id', sa.Integer(), primary_key=True, nullable=False),
|
||||
sa.Column('container_id', sa.Integer(), nullable=False),
|
||||
sa.Column('exec_id', sa.String(255), nullable=False),
|
||||
sa.Column('token', sa.String(255), nullable=True),
|
||||
sa.Column('url', sa.String(255), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.ForeignKeyConstraint(['container_id'], ['container.id'],
|
||||
ondelete='CASCADE'),
|
||||
sa.UniqueConstraint('container_id', 'exec_id',
|
||||
name='uniq_exec_instance0container_id_exec_id'),
|
||||
)
|
|
@ -1234,3 +1234,25 @@ class Connection(object):
|
|||
return query.one()
|
||||
except NoResultFound:
|
||||
raise exception.NetworkNotFound(network=network_uuid)
|
||||
|
||||
def list_exec_instances(self, context, filters=None, limit=None,
|
||||
marker=None, sort_key=None, sort_dir=None):
|
||||
query = model_query(models.ExecInstance)
|
||||
query = self._add_exec_instances_filters(query, filters)
|
||||
return _paginate_query(models.ExecInstance, limit, marker,
|
||||
sort_key, sort_dir, query)
|
||||
|
||||
def _add_exec_instances_filters(self, query, filters):
|
||||
filter_names = ['container_id', 'exec_id', 'token']
|
||||
return self._add_filters(query, filters=filters,
|
||||
filter_names=filter_names)
|
||||
|
||||
def create_exec_instance(self, context, values):
|
||||
exec_inst = models.ExecInstance()
|
||||
exec_inst.update(values)
|
||||
try:
|
||||
exec_inst.save()
|
||||
except db_exc.DBDuplicateEntry:
|
||||
raise exception.ExecInstanceAlreadyExists(
|
||||
exec_id=values['exec_id'])
|
||||
return exec_inst
|
||||
|
|
|
@ -197,6 +197,30 @@ class VolumeMapping(Base):
|
|||
auto_remove = Column(Boolean, default=False)
|
||||
|
||||
|
||||
class ExecInstance(Base):
|
||||
"""Represents an exec instance."""
|
||||
|
||||
__tablename__ = 'exec_instance'
|
||||
__table_args__ = (
|
||||
schema.UniqueConstraint(
|
||||
'container_id', 'exec_id',
|
||||
name='uniq_exec_instance0container_id_exec_id'),
|
||||
table_args()
|
||||
)
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
container_id = Column(Integer,
|
||||
ForeignKey('container.id', ondelete="CASCADE"),
|
||||
nullable=False)
|
||||
exec_id = Column(String(255), nullable=False)
|
||||
token = Column(String(255), nullable=True)
|
||||
url = Column(String(255), nullable=True)
|
||||
container = orm.relationship(
|
||||
Container,
|
||||
backref=orm.backref('exec_instances'),
|
||||
foreign_keys=container_id,
|
||||
primaryjoin='and_(ExecInstance.container_id==Container.id)')
|
||||
|
||||
|
||||
class Image(Base):
|
||||
"""Represents an image. """
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ from zun.objects import compute_node
|
|||
from zun.objects import container
|
||||
from zun.objects import container_action
|
||||
from zun.objects import container_pci_requests
|
||||
from zun.objects import exec_instance
|
||||
from zun.objects import image
|
||||
from zun.objects import network
|
||||
from zun.objects import numa
|
||||
|
@ -47,6 +48,7 @@ ContainerPCIRequest = container_pci_requests.ContainerPCIRequest
|
|||
ContainerPCIRequests = container_pci_requests.ContainerPCIRequests
|
||||
ContainerAction = container_action.ContainerAction
|
||||
ContainerActionEvent = container_action.ContainerActionEvent
|
||||
ExecInstance = exec_instance.ExecInstance
|
||||
|
||||
__all__ = (
|
||||
'Container',
|
||||
|
@ -68,4 +70,5 @@ __all__ = (
|
|||
'ContainerPCIRequests',
|
||||
'ContainerAction',
|
||||
'ContainerActionEvent',
|
||||
'ExecInstance',
|
||||
)
|
||||
|
|
|
@ -16,6 +16,7 @@ from oslo_versionedobjects import fields
|
|||
from zun.common import exception
|
||||
from zun.db import api as dbapi
|
||||
from zun.objects import base
|
||||
from zun.objects import exec_instance as exec_inst
|
||||
from zun.objects import fields as z_fields
|
||||
from zun.objects import pci_device
|
||||
|
||||
|
@ -23,7 +24,7 @@ from zun.objects import pci_device
|
|||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
CONTAINER_OPTIONAL_ATTRS = ["pci_devices"]
|
||||
CONTAINER_OPTIONAL_ATTRS = ["pci_devices", "exec_instances"]
|
||||
|
||||
|
||||
@base.ZunObjectRegistry.register
|
||||
|
@ -60,7 +61,8 @@ class Container(base.ZunPersistentObject, base.ZunObject):
|
|||
# Version 1.29: Add 'Restarting' to ContainerStatus
|
||||
# Version 1.30: Add capsule_id attribute
|
||||
# Version 1.31: Add 'started_at' attribute
|
||||
VERSION = '1.31'
|
||||
# Version 1.32: Add 'exec_instances' attribute
|
||||
VERSION = '1.32'
|
||||
|
||||
fields = {
|
||||
'id': fields.IntegerField(),
|
||||
|
@ -100,13 +102,15 @@ class Container(base.ZunPersistentObject, base.ZunObject):
|
|||
'auto_heal': fields.BooleanField(nullable=True),
|
||||
'capsule_id': fields.IntegerField(nullable=True),
|
||||
'started_at': fields.DateTimeField(tzinfo_aware=False, nullable=True),
|
||||
'exec_instances': fields.ListOfObjectsField('ExecInstance',
|
||||
nullable=True),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _from_db_object(container, db_container):
|
||||
"""Converts a database entity to a formal object."""
|
||||
for field in container.fields:
|
||||
if field in ['pci_devices']:
|
||||
if field in ['pci_devices', 'exec_instances']:
|
||||
continue
|
||||
setattr(container, field, db_container[field])
|
||||
|
||||
|
@ -293,8 +297,15 @@ class Container(base.ZunPersistentObject, base.ZunObject):
|
|||
if attrname == 'pci_devices':
|
||||
self._load_pci_devices()
|
||||
|
||||
if attrname == 'exec_instances':
|
||||
self._load_exec_instances()
|
||||
|
||||
self.obj_reset_changes([attrname])
|
||||
|
||||
def _load_pci_devices(self):
|
||||
self.pci_devices = pci_device.PciDevice.list_by_container_uuid(
|
||||
self._context, self.uuid)
|
||||
|
||||
def _load_exec_instances(self):
|
||||
self.exec_instances = exec_inst.ExecInstance.list_by_container_id(
|
||||
self._context, self.id)
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
# 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_log import log as logging
|
||||
from oslo_versionedobjects import fields
|
||||
|
||||
from zun.db import api as dbapi
|
||||
from zun.objects import base
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@base.ZunObjectRegistry.register
|
||||
class ExecInstance(base.ZunPersistentObject, base.ZunObject):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'id': fields.IntegerField(),
|
||||
'container_id': fields.IntegerField(nullable=False),
|
||||
'exec_id': fields.StringField(nullable=False),
|
||||
'token': fields.StringField(nullable=True),
|
||||
'url': fields.StringField(nullable=True),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _from_db_object(exec_inst, db_exec_inst):
|
||||
"""Converts a database entity to a formal object."""
|
||||
for field in exec_inst.fields:
|
||||
setattr(exec_inst, field, db_exec_inst[field])
|
||||
|
||||
exec_inst.obj_reset_changes()
|
||||
return exec_inst
|
||||
|
||||
@staticmethod
|
||||
def _from_db_object_list(db_objects, cls, context):
|
||||
"""Converts a list of database entities to a list of formal objects."""
|
||||
return [ExecInstance._from_db_object(cls(context), obj)
|
||||
for obj in db_objects]
|
||||
|
||||
@base.remotable_classmethod
|
||||
def list_by_container_id(cls, context, container_id):
|
||||
db_objects = dbapi.list_exec_instances(
|
||||
context, filters={'container_id': container_id})
|
||||
return ExecInstance._from_db_object_list(db_objects, cls, context)
|
||||
|
||||
@base.remotable
|
||||
def create(self, context):
|
||||
values = self.obj_get_changes()
|
||||
db_exec_inst = dbapi.create_exec_instance(context, values)
|
||||
self._from_db_object(self, db_exec_inst)
|
|
@ -0,0 +1,90 @@
|
|||
# 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_utils import uuidutils
|
||||
import six
|
||||
|
||||
from zun.common import exception
|
||||
import zun.conf
|
||||
from zun.db import api as dbapi
|
||||
from zun.tests.unit.db import base
|
||||
from zun.tests.unit.db import utils
|
||||
|
||||
CONF = zun.conf.CONF
|
||||
|
||||
|
||||
class DbExecInstanceTestCase(base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(DbExecInstanceTestCase, self).setUp()
|
||||
|
||||
def test_create_exec_instance(self):
|
||||
utils.create_test_exec_instance(context=self.context)
|
||||
|
||||
def test_create_exec_instance_already_exists(self):
|
||||
utils.create_test_exec_instance(context=self.context,
|
||||
container_id=1, exec_id='test-id')
|
||||
with self.assertRaisesRegex(
|
||||
exception.ExecInstanceAlreadyExists,
|
||||
'An exec instance with exec_id test-id .*'):
|
||||
utils.create_test_exec_instance(
|
||||
context=self.context, container_id=1, exec_id='test-id')
|
||||
|
||||
def test_list_exec_instances(self):
|
||||
exec_ids = []
|
||||
for i in range(1, 6):
|
||||
exec_inst = utils.create_test_exec_instance(
|
||||
id=i,
|
||||
context=self.context,
|
||||
container_id=1,
|
||||
exec_id=uuidutils.generate_uuid())
|
||||
exec_ids.append(six.text_type(exec_inst['exec_id']))
|
||||
res = dbapi.list_exec_instances(self.context)
|
||||
res_exec_ids = [r.exec_id for r in res]
|
||||
self.assertEqual(sorted(exec_ids), sorted(res_exec_ids))
|
||||
|
||||
def test_list_exec_instances_sorted(self):
|
||||
exec_ids = []
|
||||
for i in range(5):
|
||||
exec_inst = utils.create_test_exec_instance(
|
||||
id=i,
|
||||
context=self.context,
|
||||
container_id=1,
|
||||
exec_id=uuidutils.generate_uuid())
|
||||
exec_ids.append(six.text_type(exec_inst['exec_id']))
|
||||
res = dbapi.list_exec_instances(self.context, sort_key='exec_id')
|
||||
res_exec_ids = [r.exec_id for r in res]
|
||||
self.assertEqual(sorted(exec_ids), res_exec_ids)
|
||||
|
||||
def test_list_exec_instances_with_filters(self):
|
||||
exec_inst1 = utils.create_test_exec_instance(
|
||||
id=1,
|
||||
context=self.context,
|
||||
container_id=1,
|
||||
exec_id='exec-one')
|
||||
exec_inst2 = utils.create_test_exec_instance(
|
||||
id=2,
|
||||
context=self.context,
|
||||
container_id=2,
|
||||
exec_id='exec-two')
|
||||
|
||||
res = dbapi.list_exec_instances(
|
||||
self.context, filters={'container_id': 1})
|
||||
self.assertEqual([exec_inst1.id], [r.id for r in res])
|
||||
|
||||
res = dbapi.list_exec_instances(
|
||||
self.context, filters={'container_id': 2})
|
||||
self.assertEqual([exec_inst2.id], [r.id for r in res])
|
||||
|
||||
res = dbapi.list_exec_instances(
|
||||
self.context, filters={'container_id': 777})
|
||||
self.assertEqual([], [r.id for r in res])
|
|
@ -581,6 +581,33 @@ def get_test_network(**kwargs):
|
|||
}
|
||||
|
||||
|
||||
def get_test_exec_instance(**kwargs):
|
||||
return {
|
||||
'id': kwargs.get('id', 43),
|
||||
'container_id': kwargs.get('container_id', 42),
|
||||
'exec_id': kwargs.get('exec_id', 'fake-exec-id'),
|
||||
'token': kwargs.get('token', 'fake-exec-token'),
|
||||
'url': kwargs.get('url', 'fake-url'),
|
||||
'created_at': kwargs.get('created_at'),
|
||||
'updated_at': kwargs.get('updated_at'),
|
||||
}
|
||||
|
||||
|
||||
def create_test_exec_instance(**kwargs):
|
||||
"""Create test exec instance entry in DB and return ExecInstance DB object.
|
||||
|
||||
Function to be used to create test ExecInstance objects in the database.
|
||||
:param kwargs: kwargs with overriding values for default attributes.
|
||||
:returns: Test ExecInstance DB object.
|
||||
"""
|
||||
exec_inst = get_test_exec_instance(**kwargs)
|
||||
# Let DB generate ID if it isn't specified explicitly
|
||||
if 'id' not in kwargs:
|
||||
del exec_inst['id']
|
||||
dbapi = _get_dbapi()
|
||||
return dbapi.create_exec_instance(kwargs['context'], exec_inst)
|
||||
|
||||
|
||||
def create_test_network(**kwargs):
|
||||
network = get_test_network(**kwargs)
|
||||
# Let DB generate ID if it isn't specified explicitly
|
||||
|
|
|
@ -159,7 +159,7 @@ class TestContainerObject(base.DbTestCase):
|
|||
self.assertEqual(self.context, container._context)
|
||||
|
||||
@mock.patch('zun.objects.PciDevice.list_by_container_uuid')
|
||||
def test_obj_load_attr(self, mock_list):
|
||||
def test_obj_load_attr_pci_devices(self, mock_list):
|
||||
uuid = self.fake_container['uuid']
|
||||
dev_dict = {'dev_id': 'fake_dev_id',
|
||||
'container_uuid': 'fake_container_uuid'}
|
||||
|
@ -171,3 +171,19 @@ class TestContainerObject(base.DbTestCase):
|
|||
container = objects.Container.get_by_uuid(self.context, uuid)
|
||||
container.obj_load_attr('pci_devices')
|
||||
self.assertEqual(pci_devices, container.pci_devices)
|
||||
|
||||
@mock.patch('zun.objects.ExecInstance.list_by_container_id')
|
||||
def test_obj_load_attr_exec_instances(self, mock_list):
|
||||
uuid = self.fake_container['uuid']
|
||||
exec_dict = {'exec_id': 'fake_exec_id',
|
||||
'container_id': self.fake_container['id']}
|
||||
exec_inst = objects.ExecInstance(self.context, **exec_dict)
|
||||
exec_inst.create(self.context)
|
||||
exec_insts = [exec_inst]
|
||||
mock_list.return_value = exec_insts
|
||||
with mock.patch.object(self.dbapi, 'get_container_by_uuid',
|
||||
autospec=True) as mock_get_container:
|
||||
mock_get_container.return_value = self.fake_container
|
||||
container = objects.Container.get_by_uuid(self.context, uuid)
|
||||
container.obj_load_attr('exec_instances')
|
||||
self.assertEqual(exec_insts, container.exec_instances)
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
# Copyright 2015 OpenStack Foundation
|
||||
# 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.
|
||||
|
||||
import mock
|
||||
|
||||
from testtools.matchers import HasLength
|
||||
|
||||
from zun import objects
|
||||
from zun.tests.unit.db import base
|
||||
from zun.tests.unit.db import utils
|
||||
|
||||
|
||||
class TestExecInstanceObject(base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestExecInstanceObject, self).setUp()
|
||||
self.fake_exec_inst = utils.get_test_exec_instance()
|
||||
|
||||
def test_list_by_container_id(self):
|
||||
with mock.patch.object(self.dbapi, 'list_exec_instances',
|
||||
autospec=True) as mock_get_list:
|
||||
mock_get_list.return_value = [self.fake_exec_inst]
|
||||
exec_insts = objects.ExecInstance.list_by_container_id(
|
||||
self.context, 111)
|
||||
mock_get_list.assert_called_once_with(
|
||||
self.context, {'container_id': 111}, None, None, None, None)
|
||||
self.assertThat(exec_insts, HasLength(1))
|
||||
self.assertIsInstance(exec_insts[0], objects.ExecInstance)
|
||||
self.assertEqual(self.context, exec_insts[0]._context)
|
||||
|
||||
def test_create(self):
|
||||
with mock.patch.object(self.dbapi, 'create_exec_instance',
|
||||
autospec=True) as mock_create_exec_instance:
|
||||
mock_create_exec_instance.return_value = self.fake_exec_inst
|
||||
exec_inst = objects.ExecInstance(
|
||||
self.context, **self.fake_exec_inst)
|
||||
exec_inst.create(self.context)
|
||||
mock_create_exec_instance.assert_called_once_with(
|
||||
self.context, self.fake_exec_inst)
|
||||
self.assertEqual(self.context, exec_inst._context)
|
|
@ -344,7 +344,7 @@ class TestObject(test_base.TestCase, _TestObject):
|
|||
# For more information on object version testing, read
|
||||
# https://docs.openstack.org/zun/latest/
|
||||
object_data = {
|
||||
'Container': '1.31-4e1f27e1326bc42c7fa7ca0681bbe883',
|
||||
'Container': '1.32-9e9a594ca58e978fb7b580292692a1a7',
|
||||
'VolumeMapping': '1.1-50df6202f7846a136a91444c38eba841',
|
||||
'Image': '1.1-330e6205c80b99b59717e1cfc6a79935',
|
||||
'MyObj': '1.0-34c4b1aadefd177b13f9a2f894cc23cd',
|
||||
|
@ -365,6 +365,7 @@ object_data = {
|
|||
'ContainerAction': '1.1-b0c721f9e10c6c0d1e41e512c49eb877',
|
||||
'ContainerActionEvent': '1.0-2974d0a6f5d4821fd4e223a88c10181a',
|
||||
'Network': '1.0-235ba13359282107f27c251af9aaffcd',
|
||||
'ExecInstance': '1.0-59464e7b96db847c0abb1e96d3cec30a',
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue