1
Origin: Pre-disclosure from upstream
2
Description: Place limit on number of security groups a user may create
3
Index: nova-2012.1/nova/api/ec2/cloud.py
4
===================================================================
5
--- nova-2012.1.orig/nova/api/ec2/cloud.py 2012-05-03 15:24:56.000000000 -0500
6
+++ nova-2012.1/nova/api/ec2/cloud.py 2012-05-03 15:39:18.000000000 -0500
8
from nova import log as logging
9
from nova import network
10
from nova.rpc import common as rpc_common
11
+from nova import quota
12
from nova import utils
13
from nova import volume
16
raise exception.EC2APIError(err % values_for_rule)
17
postvalues.append(values_for_rule)
19
+ allowed = quota.allowed_security_group_rules(context,
20
+ security_group['id'],
23
+ msg = _("Quota exceeded, too many security group rules.")
24
+ raise exception.EC2APIError(msg)
27
for values_for_rule in postvalues:
28
security_group_rule = db.security_group_rule_create(
30
msg = _('group %s already exists')
31
raise exception.EC2APIError(msg % group_name)
33
+ if quota.allowed_security_groups(context, 1) < 1:
34
+ msg = _("Quota exceeded, too many security groups.")
35
+ raise exception.EC2APIError(msg)
37
group = {'user_id': context.user_id,
38
'project_id': context.project_id,
40
Index: nova-2012.1/nova/api/openstack/compute/contrib/quotas.py
41
===================================================================
42
--- nova-2012.1.orig/nova/api/openstack/compute/contrib/quotas.py 2012-04-04 13:29:56.000000000 -0500
43
+++ nova-2012.1/nova/api/openstack/compute/contrib/quotas.py 2012-05-03 15:39:18.000000000 -0500
46
quota_resources = ['metadata_items', 'injected_file_content_bytes',
47
'volumes', 'gigabytes', 'ram', 'floating_ips', 'instances',
48
- 'injected_files', 'cores']
49
+ 'injected_files', 'cores', 'security_groups', 'security_group_rules']
52
class QuotaTemplate(xmlutil.TemplateBuilder):
53
Index: nova-2012.1/nova/api/openstack/compute/contrib/security_groups.py
54
===================================================================
55
--- nova-2012.1.orig/nova/api/openstack/compute/contrib/security_groups.py 2012-04-04 13:29:54.000000000 -0500
56
+++ nova-2012.1/nova/api/openstack/compute/contrib/security_groups.py 2012-05-03 15:39:18.000000000 -0500
58
from nova import exception
59
from nova import flags
60
from nova import log as logging
61
+from nova import quota
62
from nova import utils
66
group_name = group_name.strip()
67
group_description = group_description.strip()
69
+ if quota.allowed_security_groups(context, 1) < 1:
70
+ msg = _("Quota exceeded, too many security groups.")
71
+ raise exc.HTTPBadRequest(explanation=msg)
73
LOG.audit(_("Create Security Group %s"), group_name, context=context)
74
self.compute_api.ensure_default_security_group(context)
75
if db.security_group_exists(context, context.project_id, group_name):
77
msg = _('This rule already exists in group %s') % parent_group_id
78
raise exc.HTTPBadRequest(explanation=msg)
80
+ allowed = quota.allowed_security_group_rules(context,
84
+ msg = _("Quota exceeded, too many security group rules.")
85
+ raise exc.HTTPBadRequest(explanation=msg)
87
security_group_rule = db.security_group_rule_create(context, values)
88
self.sgh.trigger_security_group_rule_create_refresh(
89
context, [security_group_rule['id']])
90
Index: nova-2012.1/nova/db/api.py
91
===================================================================
92
--- nova-2012.1.orig/nova/db/api.py 2012-04-04 13:29:56.000000000 -0500
93
+++ nova-2012.1/nova/db/api.py 2012-05-03 15:39:18.000000000 -0500
94
@@ -1118,6 +1118,11 @@
95
return IMPL.security_group_destroy(context, security_group_id)
98
+def security_group_count_by_project(context, project_id):
99
+ """Count number of security groups in a project."""
100
+ return IMPL.security_group_count_by_project(context, project_id)
106
@@ -1149,6 +1154,11 @@
107
return IMPL.security_group_rule_get(context, security_group_rule_id)
110
+def security_group_rule_count_by_group(context, security_group_id):
111
+ """Count rules in a given security group."""
112
+ return IMPL.security_group_rule_count_by_group(context, security_group_id)
118
Index: nova-2012.1/nova/db/sqlalchemy/api.py
119
===================================================================
120
--- nova-2012.1.orig/nova/db/sqlalchemy/api.py 2012-04-04 13:29:56.000000000 -0500
121
+++ nova-2012.1/nova/db/sqlalchemy/api.py 2012-05-03 15:39:18.000000000 -0500
122
@@ -2813,6 +2813,13 @@
123
'updated_at': literal_column('updated_at')})
127
+def security_group_count_by_project(context, project_id):
128
+ authorize_project_context(context, project_id)
129
+ return model_query(context, models.SecurityGroup, read_deleted="no").\
130
+ filter_by(project_id=project_id).\
136
@@ -2871,6 +2878,14 @@
137
security_group_rule.delete(session=session)
141
+def security_group_rule_count_by_group(context, security_group_id):
142
+ return model_query(context, models.SecurityGroupIngressRule,
143
+ read_deleted="no").\
144
+ filter_by(parent_group_id=security_group_id).\
151
@@ -3018,6 +3033,7 @@
152
user_ref.save(session=session)
159
Index: nova-2012.1/nova/quota.py
160
===================================================================
161
--- nova-2012.1.orig/nova/quota.py 2012-04-04 13:29:56.000000000 -0500
162
+++ nova-2012.1/nova/quota.py 2012-05-03 15:39:18.000000000 -0500
164
cfg.IntOpt('quota_max_injected_file_path_bytes',
166
help='number of bytes allowed per injected file path'),
167
+ cfg.IntOpt('quota_security_groups',
169
+ help='number of security groups per project'),
170
+ cfg.IntOpt('quota_security_group_rules',
172
+ help='number of security rules per security group'),
177
'injected_files': FLAGS.quota_max_injected_files,
178
'injected_file_content_bytes':
179
FLAGS.quota_max_injected_file_content_bytes,
180
+ 'security_groups': FLAGS.quota_security_groups,
181
+ 'security_group_rules': FLAGS.quota_security_group_rules,
183
# -1 in the quota flags means unlimited
184
for key in defaults.keys():
186
return min(requested_floating_ips, allowed_floating_ips)
189
+def allowed_security_groups(context, requested_security_groups):
190
+ """Check quota and return min(requested, allowed) security groups."""
191
+ project_id = context.project_id
192
+ context = context.elevated()
193
+ used_sec_groups = db.security_group_count_by_project(context, project_id)
194
+ quota = get_project_quotas(context, project_id)
195
+ allowed_sec_groups = _get_request_allotment(requested_security_groups,
197
+ quota['security_groups'])
198
+ return min(requested_security_groups, allowed_sec_groups)
201
+def allowed_security_group_rules(context, security_group_id,
203
+ """Check quota and return min(requested, allowed) sec group rules."""
204
+ project_id = context.project_id
205
+ context = context.elevated()
206
+ used_rules = db.security_group_rule_count_by_group(context,
208
+ quota = get_project_quotas(context, project_id)
209
+ allowed_rules = _get_request_allotment(requested_rules,
211
+ quota['security_group_rules'])
212
+ return min(requested_rules, allowed_rules)
215
def _calculate_simple_quota(context, resource, requested):
216
"""Check quota for resource; return min(requested, allowed)."""
217
quota = get_project_quotas(context, context.project_id)
218
Index: nova-2012.1/nova/tests/api/ec2/test_cloud.py
219
===================================================================
220
--- nova-2012.1.orig/nova/tests/api/ec2/test_cloud.py 2012-04-04 13:29:56.000000000 -0500
221
+++ nova-2012.1/nova/tests/api/ec2/test_cloud.py 2012-05-03 15:39:18.000000000 -0500
223
delete = self.cloud.delete_security_group
224
self.assertTrue(delete(self.context, 'testgrp'))
226
+ def test_security_group_quota_limit(self):
227
+ self.flags(quota_security_groups=10)
228
+ for i in range(1, 10):
229
+ name = 'test name %i' % i
230
+ descript = 'test description %i' % i
231
+ create = self.cloud.create_security_group
232
+ result = create(self.context, name, descript)
234
+ # 11'th group should fail
235
+ self.assertRaises(exception.EC2APIError,
236
+ create, self.context, 'foo', 'bar')
238
def test_delete_security_group_by_id(self):
239
sec = db.security_group_create(self.context,
240
{'project_id': self.context.project_id,
242
self.assertRaises(exception.EC2APIError, authz, self.context,
243
group_name=sec['name'], **kwargs)
245
+ def test_security_group_ingress_quota_limit(self):
246
+ self.flags(quota_security_group_rules=20)
247
+ kwargs = {'project_id': self.context.project_id, 'name': 'test'}
248
+ sec_group = db.security_group_create(self.context, kwargs)
249
+ authz = self.cloud.authorize_security_group_ingress
250
+ for i in range(100, 120):
251
+ kwargs = {'to_port': i, 'from_port': i, 'ip_protocol': 'tcp'}
252
+ authz(self.context, group_id=sec_group['id'], **kwargs)
254
+ kwargs = {'to_port': 121, 'from_port': 121, 'ip_protocol': 'tcp'}
255
+ self.assertRaises(exception.EC2APIError, authz, self.context,
256
+ group_id=sec_group['id'], **kwargs)
258
def _test_authorize_security_group_no_ports_with_source_group(self, proto):
259
kwargs = {'project_id': self.context.project_id, 'name': 'test'}
260
sec = db.security_group_create(self.context, kwargs)
261
Index: nova-2012.1/nova/tests/api/openstack/compute/contrib/test_quotas.py
262
===================================================================
263
--- nova-2012.1.orig/nova/tests/api/openstack/compute/contrib/test_quotas.py 2012-04-04 13:29:56.000000000 -0500
264
+++ nova-2012.1/nova/tests/api/openstack/compute/contrib/test_quotas.py 2012-05-03 15:39:18.000000000 -0500
266
return {'quota_set': {'id': id, 'metadata_items': 128, 'volumes': 10,
267
'gigabytes': 1000, 'ram': 51200, 'floating_ips': 10,
268
'instances': 10, 'injected_files': 5, 'cores': 20,
269
- 'injected_file_content_bytes': 10240}}
270
+ 'injected_file_content_bytes': 10240,
271
+ 'security_groups': 10, 'security_group_rules': 20}}
274
def quota_set_list():
276
'metadata_items': 128,
279
- 'injected_file_content_bytes': 10240}
280
+ 'injected_file_content_bytes': 10240,
281
+ 'security_groups': 10,
282
+ 'security_group_rules': 20,
285
quota_set = quotas.QuotaSetsController()._format_quota_set('1234',
288
self.assertEqual(qs['metadata_items'], 128)
289
self.assertEqual(qs['injected_files'], 5)
290
self.assertEqual(qs['injected_file_content_bytes'], 10240)
291
+ self.assertEqual(qs['security_groups'], 10)
292
+ self.assertEqual(qs['security_group_rules'], 20)
294
def test_quotas_defaults(self):
295
uri = '/v2/fake_tenant/os-quota-sets/fake_tenant/defaults'
298
'metadata_items': 128,
300
- 'injected_file_content_bytes': 10240}}
301
+ 'injected_file_content_bytes': 10240,
302
+ 'security_groups': 10,
303
+ 'security_group_rules': 20,
306
self.assertEqual(res_dict, expected)
309
'ram': 51200, 'volumes': 10,
310
'gigabytes': 1000, 'floating_ips': 10,
311
'metadata_items': 128, 'injected_files': 5,
312
- 'injected_file_content_bytes': 10240}}
313
+ 'injected_file_content_bytes': 10240,
314
+ 'security_groups': 10,
315
+ 'security_group_rules': 20}}
317
req = fakes.HTTPRequest.blank('/v2/fake4/os-quota-sets/update_me',
318
use_admin_context=True)
320
'ram': 51200, 'volumes': 10,
321
'gigabytes': 1000, 'floating_ips': 10,
322
'metadata_items': 128, 'injected_files': 5,
323
- 'injected_file_content_bytes': 10240}}
324
+ 'injected_file_content_bytes': 10240,
325
+ 'security_groups': 10,
326
+ 'security_group_rules': 20}}
328
req = fakes.HTTPRequest.blank('/v2/fake4/os-quota-sets/update_me')
329
self.assertRaises(webob.exc.HTTPForbidden, self.controller.update,
334
+ security_groups=10,
335
+ security_group_rules=20,
337
text = self.serializer.serialize(exemplar)
343
+ security_groups='10',
344
+ security_group_rules='20',
346
intext = ("<?xml version='1.0' encoding='UTF-8'?>\n"
349
'<floating_ips>60</floating_ips>'
350
'<instances>70</instances>'
351
'<injected_files>80</injected_files>'
352
+ '<security_groups>10</security_groups>'
353
+ '<security_group_rules>20</security_group_rules>'
357
Index: nova-2012.1/nova/tests/api/openstack/compute/contrib/test_security_groups.py
358
===================================================================
359
--- nova-2012.1.orig/nova/tests/api/openstack/compute/contrib/test_security_groups.py 2012-04-04 13:29:54.000000000 -0500
360
+++ nova-2012.1/nova/tests/api/openstack/compute/contrib/test_security_groups.py 2012-05-03 15:39:18.000000000 -0500
362
from nova.api.openstack import wsgi
364
from nova import exception
365
+from nova import flags
366
from nova import test
367
from nova.tests.api.openstack import fakes
370
FAKE_UUID = 'a47ae74e-ab08-447f-8eee-ffd43fc46c16'
375
class AttrDict(dict):
376
def __getattr__(self, k):
378
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
379
req, {'security_group': sg})
381
+ def test_create_security_group_quota_limit(self):
382
+ req = fakes.HTTPRequest.blank('/v2/fake/os-security-groups')
383
+ for num in range(1, FLAGS.quota_security_groups):
384
+ name = 'test%s' % num
385
+ sg = security_group_template(name=name)
386
+ res_dict = self.controller.create(req, {'security_group': sg})
387
+ self.assertEqual(res_dict['security_group']['name'], name)
389
+ sg = security_group_template()
390
+ self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
391
+ req, {'security_group': sg})
393
def test_get_security_group_list(self):
395
for i, name in enumerate(['default', 'test']):
397
self.assertRaises(webob.exc.HTTPNotFound, self.controller.delete,
398
req, '22222222222222')
400
+ def test_create_rule_quota_limit(self):
401
+ req = fakes.HTTPRequest.blank('/v2/fake/os-security-group-rules')
402
+ for num in range(100, 100 + FLAGS.quota_security_group_rules):
404
+ 'ip_protocol': 'tcp', 'from_port': num,
405
+ 'to_port': num, 'parent_group_id': '2', 'group_id': '1'
407
+ self.controller.create(req, {'security_group_rule': rule})
410
+ 'ip_protocol': 'tcp', 'from_port': '121', 'to_port': '121',
411
+ 'parent_group_id': '2', 'group_id': '1'
413
+ self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
414
+ req, {'security_group_rule': rule})
417
class TestSecurityGroupRulesXMLDeserializer(unittest.TestCase):
419
Index: nova-2012.1/nova/tests/test_quota.py
420
===================================================================
421
--- nova-2012.1.orig/nova/tests/test_quota.py 2012-04-04 13:29:56.000000000 -0500
422
+++ nova-2012.1/nova/tests/test_quota.py 2012-05-03 15:39:18.000000000 -0500
424
floating_ips = quota.allowed_floating_ips(self.context, 101)
425
self.assertEqual(floating_ips, 101)
427
+ def test_unlimited_security_groups(self):
428
+ self.flags(quota_security_groups=10)
429
+ security_groups = quota.allowed_security_groups(self.context, 100)
430
+ self.assertEqual(security_groups, 10)
431
+ db.quota_create(self.context, self.project_id, 'security_groups', None)
432
+ security_groups = quota.allowed_security_groups(self.context, 100)
433
+ self.assertEqual(security_groups, 100)
434
+ security_groups = quota.allowed_security_groups(self.context, 101)
435
+ self.assertEqual(security_groups, 101)
437
+ def test_unlimited_security_group_rules(self):
439
+ def fake_security_group_rule_count_by_group(context, sec_group_id):
442
+ self.stubs.Set(db, 'security_group_rule_count_by_group',
443
+ fake_security_group_rule_count_by_group)
445
+ self.flags(quota_security_group_rules=20)
446
+ rules = quota.allowed_security_group_rules(self.context, 1234, 100)
447
+ self.assertEqual(rules, 20)
448
+ db.quota_create(self.context, self.project_id, 'security_group_rules',
450
+ rules = quota.allowed_security_group_rules(self.context, 1234, 100)
451
+ self.assertEqual(rules, 100)
452
+ rules = quota.allowed_security_group_rules(self.context, 1234, 101)
453
+ self.assertEqual(rules, 101)
455
def test_unlimited_metadata_items(self):
456
self.flags(quota_metadata_items=10)
457
items = quota.allowed_metadata_items(self.context, 100)