Skip to content

Commit 0792885

Browse files
author
Stavros Moiras
committed
Optimize cluster list api
Up till now, cluster api controller cluster_template_id was a property field loading the id from the DB every time. With this change the field becomes of text type and mandatory, so wsme fwk guarantees that the field is provided when needed. Cluster objects will not load the cluster template on creation. Instead cluster templates will be loaded when they are actually needed. story: 2006693 task: 36989 Co-Authored-By: Stavros Moiras <[email protected]> Change-Id: I2313c6a8b647e521cfa476f9cec65ab286fa5a23
1 parent bc6ec3a commit 0792885

File tree

9 files changed

+63
-63
lines changed

9 files changed

+63
-63
lines changed

magnum/api/controllers/v1/cluster.py

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -68,25 +68,6 @@ class Cluster(base.APIBase):
6868
between the internal object model and the API representation of a Cluster.
6969
"""
7070

71-
_cluster_template_id = None
72-
73-
def _get_cluster_template_id(self):
74-
return self._cluster_template_id
75-
76-
def _set_cluster_template_id(self, value):
77-
if value and self._cluster_template_id != value:
78-
try:
79-
cluster_template = api_utils.get_resource('ClusterTemplate',
80-
value)
81-
self._cluster_template_id = cluster_template.uuid
82-
except exception.ClusterTemplateNotFound as e:
83-
# Change error code because 404 (NotFound) is inappropriate
84-
# response for a POST request to create a Cluster
85-
e.code = 400 # BadRequest
86-
raise
87-
elif value == wtypes.Unset:
88-
self._cluster_template_id = wtypes.Unset
89-
9071
uuid = types.uuid
9172
"""Unique UUID for this cluster"""
9273

@@ -95,10 +76,7 @@ def _set_cluster_template_id(self, value):
9576
"""Name of this cluster, max length is limited to 242 because of heat
9677
stack requires max length limit to 255, and Magnum amend a uuid length"""
9778

98-
cluster_template_id = wsme.wsproperty(wtypes.text,
99-
_get_cluster_template_id,
100-
_set_cluster_template_id,
101-
mandatory=True)
79+
cluster_template_id = wsme.wsattr(wtypes.text, mandatory=True)
10280
"""The cluster_template UUID"""
10381

10482
keypair = wsme.wsattr(wtypes.StringType(min_length=1, max_length=255),
@@ -493,6 +471,7 @@ def _check_cluster_quota_limit(self, context):
493471

494472
@base.Controller.api_version("1.1", "1.9")
495473
@expose.expose(ClusterID, body=Cluster, status_code=202)
474+
@validation.ct_not_found_to_bad_request()
496475
@validation.enforce_cluster_type_supported()
497476
@validation.enforce_cluster_volume_storage_size()
498477
def post(self, cluster):
@@ -519,8 +498,10 @@ def _post(self, cluster):
519498
self._check_cluster_quota_limit(context)
520499

521500
temp_id = cluster.cluster_template_id
522-
cluster_template = objects.ClusterTemplate.get_by_uuid(context,
523-
temp_id)
501+
cluster_template = objects.ClusterTemplate.get(context, temp_id)
502+
# We are not sure if we got a uuid or name here. So just set
503+
# explicitly the uuid of the cluster template in the cluster.
504+
cluster.cluster_template_id = cluster_template.uuid
524505
# If keypair not present, use cluster_template value
525506
if cluster.keypair is None:
526507
cluster.keypair = cluster_template.keypair_id

magnum/api/validation.py

100644100755
Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,25 @@
3434
federation_update_allowed_properties = set(['member_ids', 'properties'])
3535

3636

37+
def ct_not_found_to_bad_request():
38+
@decorator.decorator
39+
def wrapper(func, *args, **kwargs):
40+
try:
41+
return func(*args, **kwargs)
42+
except exception.ClusterTemplateNotFound as e:
43+
# Change error code because 404 (NotFound) is inappropriate
44+
# response for a POST request to create a Cluster
45+
e.code = 400 # BadRequest
46+
raise
47+
48+
return wrapper
49+
50+
3751
def enforce_cluster_type_supported():
3852
@decorator.decorator
3953
def wrapper(func, *args, **kwargs):
4054
cluster = args[1]
41-
cluster_template = objects.ClusterTemplate.get_by_uuid(
55+
cluster_template = objects.ClusterTemplate.get(
4256
pecan.request.context, cluster.cluster_template_id)
4357
cluster_type = (cluster_template.server_type,
4458
cluster_template.cluster_distro,
@@ -77,7 +91,7 @@ def enforce_cluster_volume_storage_size():
7791
@decorator.decorator
7892
def wrapper(func, *args, **kwargs):
7993
cluster = args[1]
80-
cluster_template = objects.ClusterTemplate.get_by_uuid(
94+
cluster_template = objects.ClusterTemplate.get(
8195
pecan.request.context, cluster.cluster_template_id)
8296
_enforce_volume_storage_size(
8397
cluster_template.as_dict(), cluster.as_dict())

magnum/common/exception.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,3 +480,8 @@ class InvalidClusterTemplateForUpgrade(Conflict):
480480

481481
class ClusterAPIAddressUnavailable(Conflict):
482482
message = _("Cluster API address is not available yet")
483+
484+
485+
class ObjectError(MagnumException):
486+
message = _("Failed to perform action %{action}s on %{obj_name}s with "
487+
"uuid %{obj_id}s: %{reason}s")

magnum/objects/cluster.py

100644100755
Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
from magnum.objects.nodegroup import NodeGroup
2323

2424

25+
LAZY_LOADED_ATTRS = ['cluster_template']
26+
27+
2528
@base.MagnumObjectRegistry.register
2629
class Cluster(base.MagnumPersistentObject, base.MagnumObject,
2730
base.MagnumObjectDictCompat):
@@ -100,16 +103,11 @@ class Cluster(base.MagnumPersistentObject, base.MagnumObject,
100103
def _from_db_object(cluster, db_cluster):
101104
"""Converts a database entity to a formal object."""
102105
for field in cluster.fields:
106+
# cluster_template will be loaded lazily when it is needed
107+
# by obj_load_attr.
103108
if field != 'cluster_template':
104109
cluster[field] = db_cluster[field]
105110

106-
# Note(eliqiao): The following line needs to be placed outside the
107-
# loop because there is a dependency from cluster_template to
108-
# cluster_template_id. The cluster_template_id must be populated
109-
# first in the loop before it can be used to find the cluster_template.
110-
cluster['cluster_template'] = ClusterTemplate.get_by_uuid(
111-
cluster._context, cluster.cluster_template_id)
112-
113111
cluster.obj_reset_changes()
114112
return cluster
115113

@@ -331,6 +329,17 @@ def refresh(self, context=None):
331329
if self.obj_attr_is_set(field) and self[field] != current[field]:
332330
self[field] = current[field]
333331

332+
def obj_load_attr(self, attrname):
333+
if attrname not in LAZY_LOADED_ATTRS:
334+
raise exception.ObjectError(
335+
action='obj_load_attr', obj_name=self.name, obj_id=self.uuid,
336+
reason='unable to lazy-load %s' % attrname)
337+
338+
self['cluster_template'] = ClusterTemplate.get_by_uuid(
339+
self._context, self.cluster_template_id)
340+
341+
self.obj_reset_changes(['cluster_template'])
342+
334343
def as_dict(self):
335344
dict_ = super(Cluster, self).as_dict()
336345
# Update the dict with the attributes coming form

magnum/objects/cluster_template.py

100644100755
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
from oslo_utils import uuidutils
1515
from oslo_versionedobjects import fields
1616

17-
from magnum.common import exception
1817
from magnum.db import api as dbapi
1918
from magnum.objects import base
2019
from magnum.objects import fields as m_fields
@@ -112,7 +111,7 @@ def get(cls, context, cluster_template_id):
112111
elif uuidutils.is_uuid_like(cluster_template_id):
113112
return cls.get_by_uuid(context, cluster_template_id)
114113
else:
115-
raise exception.InvalidIdentity(identity=cluster_template_id)
114+
return cls.get_by_name(context, cluster_template_id)
116115

117116
@base.remotable_classmethod
118117
def get_by_id(cls, context, cluster_template_id):

magnum/tests/unit/api/controllers/v1/test_cluster.py

100644100755
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,9 @@ def test_get_all_with_pagination_marker(self):
187187

188188
@mock.patch("magnum.common.policy.enforce")
189189
@mock.patch("magnum.common.context.make_context")
190-
def test_get_all_with_all_projects(self, mock_context, mock_policy):
190+
@mock.patch("magnum.objects.Cluster.obj_load_attr")
191+
@mock.patch("magnum.objects.Cluster.cluster_template")
192+
def test_get_all_with_all_projects(self, mock_context, mock_policy, mock_load, mock_template):
191193
for id_ in range(4):
192194
temp_uuid = uuidutils.generate_uuid()
193195
obj_utils.create_test_cluster(self.context, id=id_,

magnum/tests/unit/api/test_validation.py

100644100755
Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
class TestValidation(base.BaseTestCase):
3131

3232
def _test_enforce_cluster_type_supported(
33-
self, mock_cluster_template_get_by_uuid, mock_cluster_get_by_uuid,
33+
self, mock_cluster_template_get, mock_cluster_get_by_uuid,
3434
mock_pecan_request, cluster_type, assert_raised=False):
3535

3636
@v.enforce_cluster_type_supported()
@@ -41,7 +41,7 @@ def test(self, cluster):
4141
cluster_template = obj_utils.get_test_cluster_template(
4242
mock_pecan_request.context, uuid='cluster_template_id',
4343
coe=coe, cluster_distro=cluster_distro, server_type=server_type)
44-
mock_cluster_template_get_by_uuid.return_value = cluster_template
44+
mock_cluster_template_get.return_value = cluster_template
4545

4646
cluster = mock.MagicMock()
4747
cluster.cluster_template_id = 'cluster_template_id'
@@ -56,26 +56,26 @@ def test(self, cluster):
5656

5757
@mock.patch('pecan.request')
5858
@mock.patch('magnum.objects.Cluster.get_by_uuid')
59-
@mock.patch('magnum.objects.ClusterTemplate.get_by_uuid')
59+
@mock.patch('magnum.objects.ClusterTemplate.get')
6060
def test_enforce_cluster_type_supported(
61-
self, mock_cluster_template_get_by_uuid, mock_cluster_get_by_uuid,
61+
self, mock_cluster_template_get, mock_cluster_get_by_uuid,
6262
mock_pecan_request):
6363

6464
cluster_type = ('vm', 'fedora-atomic', 'kubernetes')
6565
self._test_enforce_cluster_type_supported(
66-
mock_cluster_template_get_by_uuid, mock_cluster_get_by_uuid,
66+
mock_cluster_template_get, mock_cluster_get_by_uuid,
6767
mock_pecan_request, cluster_type)
6868

6969
@mock.patch('pecan.request')
7070
@mock.patch('magnum.objects.Cluster.get_by_uuid')
71-
@mock.patch('magnum.objects.ClusterTemplate.get_by_uuid')
71+
@mock.patch('magnum.objects.ClusterTemplate.get')
7272
def test_enforce_cluster_type_not_supported(
73-
self, mock_cluster_template_get_by_uuid, mock_cluster_get_by_uuid,
73+
self, mock_cluster_template_get, mock_cluster_get_by_uuid,
7474
mock_pecan_request):
7575

7676
cluster_type = ('vm', 'foo', 'kubernetes')
7777
exc = self._test_enforce_cluster_type_supported(
78-
mock_cluster_template_get_by_uuid, mock_cluster_get_by_uuid,
78+
mock_cluster_template_get, mock_cluster_get_by_uuid,
7979
mock_pecan_request, cluster_type, assert_raised=True)
8080
self.assertEqual('Cluster type (vm, foo, kubernetes) not supported.',
8181
exc.message)

magnum/tests/unit/objects/test_cluster.py

100644100755
Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,12 @@ def test_list_all(self, mock_cluster_template_get):
117117
self.assertThat(clusters, HasLength(1))
118118
self.assertIsInstance(clusters[0], objects.Cluster)
119119
self.assertEqual(self.context, clusters[0]._context)
120+
mock_cluster_template_get.assert_not_called()
120121

121-
@mock.patch('magnum.objects.ClusterTemplate.get_by_uuid')
122-
def test_list_with_filters(self, mock_cluster_template_get):
122+
def test_list_with_filters(self):
123123
with mock.patch.object(self.dbapi, 'get_cluster_list',
124124
autospec=True) as mock_get_list:
125125
mock_get_list.return_value = [self.fake_cluster]
126-
mock_cluster_template_get.return_value = self.fake_cluster_template
127126
filters = {'name': 'cluster1'}
128127
clusters = objects.Cluster.list(self.context, filters=filters)
129128

@@ -136,24 +135,20 @@ def test_list_with_filters(self, mock_cluster_template_get):
136135
self.assertIsInstance(clusters[0], objects.Cluster)
137136
self.assertEqual(self.context, clusters[0]._context)
138137

139-
@mock.patch('magnum.objects.ClusterTemplate.get_by_uuid')
140-
def test_create(self, mock_cluster_template_get):
138+
def test_create(self):
141139
with mock.patch.object(self.dbapi, 'create_cluster',
142140
autospec=True) as mock_create_cluster:
143-
mock_cluster_template_get.return_value = self.fake_cluster_template
144141
mock_create_cluster.return_value = self.fake_cluster
145142
cluster = objects.Cluster(self.context, **self.fake_cluster)
146143
cluster.create()
147144
mock_create_cluster.assert_called_once_with(self.fake_cluster)
148145
self.assertEqual(self.context, cluster._context)
149146

150-
@mock.patch('magnum.objects.ClusterTemplate.get_by_uuid')
151-
def test_destroy(self, mock_cluster_template_get):
147+
def test_destroy(self):
152148
uuid = self.fake_cluster['uuid']
153149
with mock.patch.object(self.dbapi, 'get_cluster_by_uuid',
154150
autospec=True) as mock_get_cluster:
155151
mock_get_cluster.return_value = self.fake_cluster
156-
mock_cluster_template_get.return_value = self.fake_cluster_template
157152
with mock.patch.object(self.dbapi, 'destroy_cluster',
158153
autospec=True) as mock_destroy_cluster:
159154
cluster = objects.Cluster.get_by_uuid(self.context, uuid)
@@ -162,12 +157,10 @@ def test_destroy(self, mock_cluster_template_get):
162157
mock_destroy_cluster.assert_called_once_with(uuid)
163158
self.assertEqual(self.context, cluster._context)
164159

165-
@mock.patch('magnum.objects.ClusterTemplate.get_by_uuid')
166-
def test_save(self, mock_cluster_template_get):
160+
def test_save(self):
167161
uuid = self.fake_cluster['uuid']
168162
with mock.patch.object(self.dbapi, 'get_cluster_by_uuid',
169163
autospec=True) as mock_get_cluster:
170-
mock_cluster_template_get.return_value = self.fake_cluster_template
171164
mock_get_cluster.return_value = self.fake_cluster
172165
with mock.patch.object(self.dbapi, 'update_cluster',
173166
autospec=True) as mock_update_cluster:
@@ -177,12 +170,10 @@ def test_save(self, mock_cluster_template_get):
177170

178171
mock_get_cluster.assert_called_once_with(self.context, uuid)
179172
mock_update_cluster.assert_called_once_with(
180-
uuid, {'status': 'DELETE_IN_PROGRESS',
181-
'cluster_template': self.fake_cluster_template})
173+
uuid, {'status': 'DELETE_IN_PROGRESS'})
182174
self.assertEqual(self.context, cluster._context)
183175

184-
@mock.patch('magnum.objects.ClusterTemplate.get_by_uuid')
185-
def test_refresh(self, mock_cluster_template_get):
176+
def test_refresh(self):
186177
uuid = self.fake_cluster['uuid']
187178
new_uuid = uuidutils.generate_uuid()
188179
returns = [dict(self.fake_cluster, uuid=uuid),
@@ -192,7 +183,6 @@ def test_refresh(self, mock_cluster_template_get):
192183
with mock.patch.object(self.dbapi, 'get_cluster_by_uuid',
193184
side_effect=returns,
194185
autospec=True) as mock_get_cluster:
195-
mock_cluster_template_get.return_value = self.fake_cluster_template
196186
cluster = objects.Cluster.get_by_uuid(self.context, uuid)
197187
self.assertEqual(uuid, cluster.uuid)
198188
cluster.refresh()

magnum/tests/unit/objects/test_cluster_template.py

100644100755
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def test_get_by_uuid(self):
5252
self.assertEqual(self.context, cluster_template._context)
5353

5454
def test_get_bad_id_and_uuid(self):
55-
self.assertRaises(exception.InvalidIdentity,
55+
self.assertRaises(exception.ClusterTemplateNotFound,
5656
objects.ClusterTemplate.get, self.context,
5757
'not-a-uuid')
5858

0 commit comments

Comments
 (0)