Skip to content

Commit bf5bca2

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Implement control plane resizing with driver."
2 parents 1c3d7d0 + ab02c97 commit bf5bca2

File tree

9 files changed

+104
-22
lines changed

9 files changed

+104
-22
lines changed

magnum/api/attr_validator.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,11 @@ def validate_os_resources(context, cluster_template, cluster=None):
168168
validate_keypair(cli, cluster['keypair'])
169169

170170

171-
def validate_master_count(cluster, cluster_template):
172-
if cluster['master_count'] > 1 and \
173-
not cluster['master_lb_enabled']:
171+
def validate_master_count(context, cluster):
172+
if (
173+
cluster['master_count'] > 1 and
174+
not cluster['master_lb_enabled']
175+
):
174176
raise exception.InvalidParameterValue(_(
175177
"master_count must be 1 when master_lb_enabled is False"))
176178

magnum/api/controllers/v1/cluster.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,7 @@ def _check_cluster_quota_limit(self, context):
475475
@validation.ct_not_found_to_bad_request()
476476
@validation.enforce_cluster_type_supported()
477477
@validation.enforce_cluster_volume_storage_size()
478+
@validation.enforce_cluster_master_size_supported()
478479
def post(self, cluster):
479480
if cluster.node_count == 0:
480481
raise exception.ZeroNodeCountNotSupported()
@@ -484,6 +485,7 @@ def post(self, cluster):
484485
@expose.expose(ClusterID, body=Cluster, status_code=202)
485486
@validation.enforce_cluster_type_supported()
486487
@validation.enforce_cluster_volume_storage_size()
488+
@validation.enforce_cluster_master_size_supported()
487489
def post(self, cluster): # noqa
488490
return self._post(cluster)
489491

@@ -545,8 +547,8 @@ def _post(self, cluster):
545547
attr_validator.validate_os_resources(context,
546548
cluster_template.as_dict(),
547549
cluster_dict)
548-
attr_validator.validate_master_count(cluster_dict,
549-
cluster_template.as_dict())
550+
attr_validator.validate_master_count(context,
551+
cluster_dict)
550552

551553
cluster_dict['project_id'] = context.project_id
552554
cluster_dict['user_id'] = context.user_id

magnum/api/controllers/v1/cluster_actions.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from magnum.api import utils as api_utils
2121
from magnum.common import exception
2222
from magnum.common import policy
23+
from magnum.drivers.common.driver import Driver
2324
from magnum import objects
2425

2526

@@ -114,22 +115,24 @@ def _resize(self, cluster_ident, cluster_resize_req):
114115
nodegroup = objects.NodeGroup.get(
115116
context, cluster.uuid, cluster_resize_req.nodegroup)
116117

117-
if nodegroup.role == 'master':
118-
# NOTE(ttsiouts): Restrict the resize to worker nodegroups
119-
raise exception.MasterNGResizeNotSupported()
120-
121118
# NOTE(ttsiouts): Make sure that the new node count is within
122119
# the configured boundaries of the selected nodegroup.
123-
if nodegroup.min_node_count > cluster_resize_req.node_count:
120+
if (nodegroup.role != "master" and
121+
nodegroup.min_node_count > cluster_resize_req.node_count):
124122
raise exception.NGResizeOutBounds(
125123
nodegroup=nodegroup.name, min_nc=nodegroup.min_node_count,
126124
max_nc=nodegroup.max_node_count)
127-
if (nodegroup.max_node_count and
125+
if (nodegroup.role != "master" and nodegroup.max_node_count and
128126
nodegroup.max_node_count < cluster_resize_req.node_count):
129127
raise exception.NGResizeOutBounds(
130128
nodegroup=nodegroup.name, min_nc=nodegroup.min_node_count,
131129
max_nc=nodegroup.max_node_count)
132130

131+
if nodegroup.role == "master":
132+
cluster_driver = Driver.get_driver_for_cluster(context, cluster)
133+
cluster_driver.validate_master_resize(
134+
cluster_resize_req.node_count)
135+
133136
pecan.request.rpcapi.cluster_resize_async(
134137
cluster,
135138
cluster_resize_req.node_count,

magnum/api/validation.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,19 @@ def wrapper(func, *args, **kwargs):
9090
return wrapper
9191

9292

93+
def enforce_cluster_master_size_supported():
94+
@decorator.decorator
95+
def wrapper(func, *args, **kwargs):
96+
cluster = args[1]
97+
cluster_driver = driver.Driver.get_driver_for_cluster(
98+
pecan.request.context, cluster)
99+
# Call into the driver to validate initial master size
100+
cluster_driver.validate_master_size(cluster.master_count)
101+
return func(*args, **kwargs)
102+
103+
return wrapper
104+
105+
93106
def enforce_cluster_volume_storage_size():
94107
@decorator.decorator
95108
def wrapper(func, *args, **kwargs):

magnum/common/exception.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,8 +434,14 @@ class NodeGroupNotFound(ResourceNotFound):
434434
message = _("Nodegroup %(nodegroup)s could not be found.")
435435

436436

437+
class MasterNGSizeInvalid(InvalidParameterValue):
438+
message = _("master nodegroup size of %(requested_size)s is invalid, "
439+
"size cannot be an even number.")
440+
441+
437442
class MasterNGResizeNotSupported(NotSupported):
438-
message = _("Resizing a master nodegroup is not supported.")
443+
message = _("Resizing the master nodegroup is not supported "
444+
"by this driver.")
439445

440446

441447
class ZeroNodeCountNotSupported(NotSupported):

magnum/conductor/utils.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -185,11 +185,9 @@ def _get_nodegroup_object(context, cluster, node_count, is_master=False):
185185
else:
186186
ng.flavor_id = cluster.flavor_id or cluster.cluster_template.flavor_id
187187
ng.role = "worker"
188-
if (cluster.labels != wtypes.Unset and cluster.labels is not None
189-
and 'min_node_count' in cluster.labels):
190-
ng.min_node_count = cluster.labels['min_node_count']
191-
else:
192-
ng.min_node_count = 0
188+
if (cluster.labels != wtypes.Unset and cluster.labels is not None
189+
and 'min_node_count' in cluster.labels):
190+
ng.min_node_count = cluster.labels['min_node_count']
193191
ng.name = "default-%s" % ng.role
194192
ng.is_default = True
195193
ng.status = fields.ClusterStatus.CREATE_IN_PROGRESS

magnum/drivers/common/driver.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
from magnum.common import exception
2424
from magnum.objects import cluster_template
2525

26-
2726
CONF = cfg.CONF
2827
LOG = logging.getLogger(__name__)
2928

@@ -219,6 +218,15 @@ def resize_cluster(self, context, cluster, resize_manager,
219218
raise NotImplementedError("Subclasses must implement "
220219
"'resize_cluster'.")
221220

221+
def validate_master_size(self, node_count):
222+
if node_count % 2 == 0 or node_count < 1:
223+
raise exception.MasterNGSizeInvalid(
224+
requested_size=node_count)
225+
226+
def validate_master_resize(self, node_count):
227+
# Base driver does not support resizing masters.
228+
raise exception.MasterNGResizeNotSupported()
229+
222230
@abc.abstractmethod
223231
def create_federation(self, context, federation):
224232
raise NotImplementedError("Subclasses must implement "

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

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -708,7 +708,7 @@ def test_create_cluster_with_non_existent_cluster_template_name(self):
708708

709709
def test_create_cluster_with_cluster_template_name(self):
710710
modelname = self.cluster_template.name
711-
bdict = apiutils.cluster_post_data(cluster_template_id=modelname)
711+
bdict = apiutils.cluster_post_data(name=modelname)
712712
response = self.post_json('/clusters', bdict, expect_errors=True)
713713
self.assertEqual('application/json', response.content_type)
714714
self.assertEqual(202, response.status_int)
@@ -767,6 +767,40 @@ def test_create_cluster_with_no_master_count(self):
767767
self.assertEqual('application/json', response.content_type)
768768
self.assertEqual(202, response.status_int)
769769

770+
def test_create_cluster_with_even_master_count_oldmicroversion(self):
771+
bdict = apiutils.cluster_post_data()
772+
bdict['master_count'] = 2
773+
response = self.post_json(
774+
'/clusters',
775+
bdict,
776+
expect_errors=True,
777+
headers={"Openstack-Api-Version": "container-infra 1.9"}
778+
)
779+
self.assertEqual('application/json', response.content_type)
780+
self.assertEqual(400, response.status_int)
781+
self.assertTrue(response.json['errors'])
782+
783+
def test_create_cluster_with_even_master_count(self):
784+
bdict = apiutils.cluster_post_data()
785+
bdict['master_count'] = 2
786+
response = self.post_json(
787+
'/clusters',
788+
bdict,
789+
expect_errors=True,
790+
headers={"Openstack-Api-Version": "container-infra 1.10"}
791+
)
792+
self.assertEqual('application/json', response.content_type)
793+
self.assertEqual(400, response.status_int)
794+
self.assertTrue(response.json['errors'])
795+
796+
def test_create_cluster_with_negative_master_count(self):
797+
bdict = apiutils.cluster_post_data()
798+
bdict['master_count'] = -1
799+
response = self.post_json('/clusters', bdict, expect_errors=True)
800+
self.assertEqual('application/json', response.content_type)
801+
self.assertEqual(400, response.status_int)
802+
self.assertTrue(response.json['errors'])
803+
770804
def test_create_cluster_with_invalid_name(self):
771805
invalid_names = ['x' * 243, '123456', '123456test_cluster',
772806
'-test_cluster', '.test_cluster', '_test_cluster', '']
@@ -874,7 +908,7 @@ def test_create_cluster_with_multi_images_same_name(self):
874908
self.assertTrue(self.mock_valid_os_res.called)
875909
self.assertEqual(409, response.status_int)
876910

877-
def test_create_cluster_with_on_os_distro_image(self):
911+
def test_create_cluster_with_no_os_distro_image(self):
878912
bdict = apiutils.cluster_post_data()
879913
self.mock_valid_os_res.side_effect = \
880914
exception.OSDistroFieldNotFound('img')

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,24 @@ def test_resize_with_nodegroup(self):
8080
self.assertEqual(self.cluster_obj.cluster_template_id,
8181
response['cluster_template_id'])
8282

83-
def test_resize_with_master_nodegroup(self):
84-
new_node_count = 6
83+
def test_resize_with_master_nodegroup_even_unsupported(self):
84+
new_node_count = 4
85+
nodegroup = self.cluster_obj.default_ng_master
86+
cluster_resize_req = {
87+
"node_count": new_node_count,
88+
"nodegroup": nodegroup.uuid
89+
}
90+
response = self.post_json('/clusters/%s/actions/resize' %
91+
self.cluster_obj.uuid,
92+
cluster_resize_req,
93+
headers={"Openstack-Api-Version":
94+
"container-infra 1.9",
95+
"X-Roles": "member"},
96+
expect_errors=True)
97+
self.assertEqual(400, response.status_code)
98+
99+
def test_resize_with_master_nodegroup_odd_unsupported(self):
100+
new_node_count = 3
85101
nodegroup = self.cluster_obj.default_ng_master
86102
cluster_resize_req = {
87103
"node_count": new_node_count,

0 commit comments

Comments
 (0)