Skip to content

Commit 285f91b

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Optimize cluster list api"
2 parents 7b1dfaa + 0792885 commit 285f91b

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)