Skip to content

Commit ea302ba

Browse files
Zuulopenstack-gerrit
Zuul
authored andcommitted
Merge "add validation for boot volume size"
2 parents fe0b330 + d8d2016 commit ea302ba

File tree

10 files changed

+200
-0
lines changed

10 files changed

+200
-0
lines changed

magnum/api/attr_validator.py

+20
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,26 @@ def validate_master_count(context, cluster):
177177
"master_count must be 1 when master_lb_enabled is False"))
178178

179179

180+
def validate_flavor_root_volume_size(cli, flavor, boot_volume_size):
181+
"""Validate flavor root volume size."""
182+
183+
if boot_volume_size > 0:
184+
return
185+
186+
flavor_obj = None
187+
flavor_list = cli.nova().flavors.list()
188+
for f in flavor_list:
189+
if f.name == flavor or f.id == flavor:
190+
flavor_obj = f
191+
break
192+
193+
if flavor_obj is None:
194+
raise exception.FlavorNotFound(flavor=flavor)
195+
196+
if flavor_obj.disk == 0 and boot_volume_size == 0:
197+
raise exception.FlavorZeroRootVolumeNotSupported()
198+
199+
180200
def validate_federation_hostcluster(cluster_uuid):
181201
"""Validate Federation `hostcluster_id` parameter.
182202

magnum/api/controllers/v1/cluster.py

+19
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from magnum.api import expose
3434
from magnum.api import utils as api_utils
3535
from magnum.api import validation
36+
from magnum.common import clients
3637
from magnum.common import exception
3738
from magnum.common import name_generator
3839
from magnum.common import policy
@@ -542,6 +543,15 @@ def _post(self, cluster):
542543
not getattr(cluster, attr)):
543544
setattr(cluster, attr, getattr(cluster_template, attr))
544545

546+
# check root volume size
547+
boot_volume_size = cluster.labels.get(
548+
'boot_volume_size', CONF.cinder.default_boot_volume_size)
549+
cli = clients.OpenStackClients(context)
550+
attr_validator.validate_flavor_root_volume_size(
551+
cli, cluster.flavor_id, boot_volume_size)
552+
attr_validator.validate_flavor_root_volume_size(
553+
cli, cluster.master_flavor_id, boot_volume_size)
554+
545555
cluster_dict = cluster.as_dict()
546556

547557
attr_validator.validate_os_resources(context,
@@ -660,6 +670,15 @@ def _patch(self, cluster_ident, patch):
660670
if getattr(cluster, field) != getattr(new_cluster, field):
661671
delta.add(field)
662672

673+
# check root volume size
674+
boot_volume_size = new_cluster.labels.get(
675+
'boot_volume_size', CONF.cinder.default_boot_volume_size)
676+
cli = clients.OpenStackClients(context)
677+
attr_validator.validate_flavor_root_volume_size(
678+
cli, new_cluster.flavor_id, boot_volume_size)
679+
attr_validator.validate_flavor_root_volume_size(
680+
cli, new_cluster.master_flavor_id, boot_volume_size)
681+
663682
validation.validate_cluster_properties(delta)
664683

665684
# NOTE(brtknr): cluster.node_count is the size of the whole cluster

magnum/api/controllers/v1/cluster_template.py

+21
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@
3434
from magnum import objects
3535
from magnum.objects import fields
3636

37+
from oslo_config import cfg
38+
39+
CONF = cfg.CONF
40+
3741
LOG = logging.getLogger(__name__)
3842

3943

@@ -425,6 +429,14 @@ def post(self, cluster_template):
425429
do_raise=False):
426430
raise exception.ClusterTemplatePublishDenied()
427431

432+
# check root volume size
433+
boot_volume_size = cluster_template.labels.get(
434+
'boot_volume_size', CONF.cinder.default_boot_volume_size)
435+
attr_validator.validate_flavor_root_volume_size(
436+
cli, cluster_template.flavor_id, boot_volume_size)
437+
attr_validator.validate_flavor_root_volume_size(
438+
cli, cluster_template.master_flavor_id, boot_volume_size)
439+
428440
if (cluster_template.docker_storage_driver in ('devicemapper',
429441
'overlay')):
430442
warnings.warn(self._devicemapper_overlay_deprecation_note,
@@ -498,6 +510,15 @@ def patch(self, cluster_template_ident, patch): # noqa
498510
do_raise=False):
499511
raise exception.ClusterTemplatePublishDenied()
500512

513+
# check root volume size
514+
boot_volume_size = cluster_template.labels.get(
515+
'boot_volume_size', CONF.cinder.default_boot_volume_size)
516+
cli = clients.OpenStackClients(context)
517+
attr_validator.validate_flavor_root_volume_size(
518+
cli, new_cluster_template.flavor_id, boot_volume_size)
519+
attr_validator.validate_flavor_root_volume_size(
520+
cli, new_cluster_template.master_flavor_id, boot_volume_size)
521+
501522
# Update only the fields that have changed
502523
for field in objects.ClusterTemplate.fields:
503524
try:

magnum/api/controllers/v1/nodegroup.py

+21
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,22 @@
1818
import wsme
1919
from wsme import types as wtypes
2020

21+
from magnum.api import attr_validator
2122
from magnum.api.controllers import base
2223
from magnum.api.controllers import link
2324
from magnum.api.controllers.v1 import collection
2425
from magnum.api.controllers.v1 import types
2526
from magnum.api import expose
2627
from magnum.api import utils as api_utils
28+
from magnum.common import clients
2729
from magnum.common import exception
2830
from magnum.common import policy
31+
import magnum.conf
2932
from magnum import objects
3033
from magnum.objects import fields
3134

35+
CONF = magnum.conf.CONF
36+
3237

3338
def _validate_node_count(ng):
3439
if ng.max_node_count:
@@ -350,6 +355,13 @@ def post(self, cluster_id, nodegroup):
350355
labels.update(nodegroup.labels)
351356
nodegroup.labels = labels
352357

358+
# check root volume size
359+
boot_volume_size = cluster.labels.get(
360+
'boot_volume_size', CONF.cinder.default_boot_volume_size)
361+
cli = clients.OpenStackClients(context)
362+
attr_validator.validate_flavor_root_volume_size(
363+
cli, nodegroup.flavor_id, boot_volume_size)
364+
353365
nodegroup_dict = nodegroup.as_dict()
354366
nodegroup_dict['cluster_id'] = cluster.uuid
355367
nodegroup_dict['project_id'] = context.project_id
@@ -414,6 +426,15 @@ def _patch(self, cluster_uuid, nodegroup_id, patch):
414426
patch_val = None
415427
if nodegroup[field] != patch_val:
416428
nodegroup[field] = patch_val
429+
430+
# check root volume size
431+
cluster = api_utils.get_resource('Cluster', cluster_uuid)
432+
boot_volume_size = cluster.labels.get(
433+
'boot_volume_size', CONF.cinder.default_boot_volume_size)
434+
cli = clients.OpenStackClients(context)
435+
attr_validator.validate_flavor_root_volume_size(
436+
cli, nodegroup.flavor_id, boot_volume_size)
437+
417438
_validate_node_count(nodegroup)
418439

419440
return nodegroup

magnum/common/exception.py

+5
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,11 @@ class ZeroNodeCountNotSupported(NotSupported):
449449
"provided microversion.")
450450

451451

452+
class FlavorZeroRootVolumeNotSupported(NotSupported):
453+
message = _("Flavor with zero root volume size is not supported "
454+
"when boot_volume_size is zero.")
455+
456+
452457
class ClusterUpgradeNotSupported(NotSupported):
453458
message = _("Cluster upgrade is not supported in the "
454459
"provided microversion.")

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

+17
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,10 @@ def setUp(self):
288288
self.mock_cluster_update = p.start()
289289
self.mock_cluster_update.side_effect = self._sim_rpc_cluster_update
290290
self.addCleanup(p.stop)
291+
p = mock.patch.object(
292+
attr_validator, 'validate_flavor_root_volume_size')
293+
self.mock_valid_flavor_disk = p.start()
294+
self.addCleanup(p.stop)
291295

292296
def _sim_rpc_cluster_update(self, cluster, node_count, health_status,
293297
health_status_reason, rollback=False):
@@ -612,6 +616,10 @@ def setUp(self):
612616
p = mock.patch.object(attr_validator, 'validate_os_resources')
613617
self.mock_valid_os_res = p.start()
614618
self.addCleanup(p.stop)
619+
p = mock.patch.object(
620+
attr_validator, 'validate_flavor_root_volume_size')
621+
self.mock_valid_flavor_disk = p.start()
622+
self.addCleanup(p.stop)
615623

616624
def _simulate_cluster_create(self, cluster, master_count, node_count,
617625
create_timeout):
@@ -873,6 +881,15 @@ def test_create_cluster_with_invalid_flavor(self):
873881
self.assertTrue(self.mock_valid_os_res.called)
874882
self.assertEqual(400, response.status_int)
875883

884+
def test_create_cluster_with_invalid_flavor_disk_size(self):
885+
bdict = apiutils.cluster_post_data()
886+
self.mock_valid_flavor_disk.side_effect = \
887+
exception.FlavorZeroRootVolumeNotSupported()
888+
response = self.post_json('/clusters', bdict, expect_errors=True)
889+
self.assertEqual('application/json', response.content_type)
890+
self.assertTrue(self.mock_valid_flavor_disk.called)
891+
self.assertEqual(400, response.status_int)
892+
876893
def test_create_cluster_with_invalid_ext_network(self):
877894
bdict = apiutils.cluster_post_data()
878895
self.mock_valid_os_res.side_effect = \

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

+36
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,10 @@ def setUp(self):
269269
labels={'key1': 'val1', 'key2': 'val2'},
270270
hidden=False
271271
)
272+
p = mock.patch.object(
273+
attr_validator, 'validate_flavor_root_volume_size')
274+
self.mock_valid_flavor_disk = p.start()
275+
self.addCleanup(p.stop)
272276

273277
def test_update_not_found(self):
274278
uuid = uuidutils.generate_uuid()
@@ -482,6 +486,19 @@ def test_replace_cluster_template_with_no_exist_flavor_id(self):
482486
self.assertEqual(400, response.status_code)
483487
self.assertTrue(response.json['errors'])
484488

489+
def test_replace_cluster_template_with_invalid_flavor(self):
490+
self.mock_valid_flavor_disk.side_effect = \
491+
exception.FlavorZeroRootVolumeNotSupported()
492+
response = self.patch_json('/clustertemplates/%s' %
493+
self.cluster_template.uuid,
494+
[{'path': '/flavor_id', 'value': 'aaa',
495+
'op': 'replace'}],
496+
expect_errors=True)
497+
self.assertEqual('application/json', response.content_type)
498+
self.assertEqual(400, response.status_code)
499+
self.assertTrue(self.mock_valid_flavor_disk.called)
500+
self.assertTrue(response.json['errors'])
501+
485502
def test_replace_cluster_template_with_no_exist_keypair_id(self):
486503
self.mock_valid_os_res.side_effect = exception.KeyPairNotFound("aaa")
487504
response = self.patch_json('/clustertemplates/%s' %
@@ -632,6 +649,10 @@ def setUp(self):
632649
p = mock.patch.object(attr_validator, 'validate_os_resources')
633650
self.mock_valid_os_res = p.start()
634651
self.addCleanup(p.stop)
652+
p = mock.patch.object(
653+
attr_validator, 'validate_flavor_root_volume_size')
654+
self.mock_valid_flavor_disk = p.start()
655+
self.addCleanup(p.stop)
635656

636657
@mock.patch('magnum.api.attr_validator.validate_image')
637658
@mock.patch('oslo_utils.timeutils.utcnow')
@@ -1110,6 +1131,21 @@ def test_create_cluster_template_with_no_exist_flavor(self,
11101131
expect_errors=True)
11111132
self.assertEqual(400, response.status_int)
11121133

1134+
@mock.patch('magnum.api.attr_validator.validate_image')
1135+
def test_create_cluster_template_with_invalid_flavor(
1136+
self,
1137+
mock_image_data
1138+
):
1139+
self.mock_valid_flavor_disk.side_effect = \
1140+
exception.FlavorZeroRootVolumeNotSupported()
1141+
mock_image_data.return_value = {'name': 'mock_name',
1142+
'os_distro': 'fedora-coreos'}
1143+
bdict = apiutils.cluster_template_post_data()
1144+
response = self.post_json('/clustertemplates', bdict,
1145+
expect_errors=True)
1146+
self.assertTrue(self.mock_valid_flavor_disk.called)
1147+
self.assertEqual(400, response.status_int)
1148+
11131149
@mock.patch('magnum.api.attr_validator.validate_image')
11141150
def test_create_cluster_template_with_external_network(self,
11151151
mock_image_data):

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

+9
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from oslo_utils import timeutils
2020
from oslo_utils import uuidutils
2121

22+
from magnum.api import attr_validator
2223
from magnum.api.controllers.v1 import nodegroup as api_nodegroup
2324
from magnum.conductor import api as rpcapi
2425
import magnum.conf
@@ -265,6 +266,10 @@ def setUp(self):
265266
self.mock_ng_create.side_effect = self._simulate_nodegroup_create
266267
self.addCleanup(p.stop)
267268
self.url = "/clusters/%s/nodegroups" % self.cluster.uuid
269+
p = mock.patch.object(
270+
attr_validator, 'validate_flavor_root_volume_size')
271+
self.mock_valid_flavor_disk = p.start()
272+
self.addCleanup(p.stop)
268273

269274
def _simulate_nodegroup_create(self, cluster, nodegroup):
270275
nodegroup.create()
@@ -571,6 +576,10 @@ def setUp(self):
571576
self.mock_ng_update.side_effect = self._simulate_nodegroup_update
572577
self.addCleanup(p.stop)
573578
self.url = "/clusters/%s/nodegroups/" % self.cluster.uuid
579+
p = mock.patch.object(
580+
attr_validator, 'validate_flavor_root_volume_size')
581+
self.mock_valid_flavor_disk = p.start()
582+
self.addCleanup(p.stop)
574583

575584
def _simulate_nodegroup_update(self, cluster, nodegroup):
576585
nodegroup.save()

magnum/tests/unit/api/test_attr_validator.py

+47
Original file line numberDiff line numberDiff line change
@@ -367,3 +367,50 @@ def test_validate_os_resources_with_cluster(self, mock_os_cli):
367367
attr_validator.validate_os_resources(mock_context,
368368
mock_cluster_template,
369369
mock_cluster)
370+
371+
def test_validate_flavor_root_volume_size_with_valid_boot_volume_size(
372+
self
373+
):
374+
boot_volume_size = 100
375+
mock_flavor = mock.MagicMock()
376+
mock_flavor.name = 'test_flavor'
377+
mock_flavor.id = 'test_flavor_id'
378+
mock_flavor.disk = 0
379+
mock_flavors = [mock_flavor]
380+
mock_nova = mock.MagicMock()
381+
mock_nova.flavors.list.return_value = mock_flavors
382+
mock_os_cli = mock.MagicMock()
383+
mock_os_cli.nova.return_value = mock_nova
384+
attr_validator.validate_flavor_root_volume_size(
385+
mock_os_cli, 'test_flavor', boot_volume_size)
386+
self.assertFalse(mock_nova.flavors.list.called)
387+
388+
def test_validate_flavor_root_volume_size_with_valid_flavor(self):
389+
boot_volume_size = 0
390+
mock_flavor = mock.MagicMock()
391+
mock_flavor.name = 'test_flavor'
392+
mock_flavor.id = 'test_flavor_id'
393+
mock_flavor.disk = 100
394+
mock_flavors = [mock_flavor]
395+
mock_nova = mock.MagicMock()
396+
mock_nova.flavors.list.return_value = mock_flavors
397+
mock_os_cli = mock.MagicMock()
398+
mock_os_cli.nova.return_value = mock_nova
399+
attr_validator.validate_flavor_root_volume_size(
400+
mock_os_cli, 'test_flavor', boot_volume_size)
401+
self.assertTrue(mock_nova.flavors.list.called)
402+
403+
def test_validate_flavor_root_volume_size_with_invalid_resources(self):
404+
boot_volume_size = 0
405+
mock_flavor = mock.MagicMock()
406+
mock_flavor.name = 'test_flavor'
407+
mock_flavor.id = 'test_flavor_id'
408+
mock_flavor.disk = 0
409+
mock_flavors = [mock_flavor]
410+
mock_nova = mock.MagicMock()
411+
mock_nova.flavors.list.return_value = mock_flavors
412+
mock_os_cli = mock.MagicMock()
413+
mock_os_cli.nova.return_value = mock_nova
414+
self.assertRaises(exception.FlavorZeroRootVolumeNotSupported,
415+
attr_validator.validate_flavor_root_volume_size,
416+
mock_os_cli, 'test_flavor', boot_volume_size)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
features:
3+
- |
4+
Add a validation for the case when boot_volume_size
5+
label and flavor's disk are both zero.

0 commit comments

Comments
 (0)