Merge lp:~vishvananda/nova/quotas into lp:~hudson-openstack/nova/trunk

Proposed by Vish Ishaya
Status: Merged
Approved by: Jay Pipes
Approved revision: 375
Merged at revision: 289
Proposed branch: lp:~vishvananda/nova/quotas
Merge into: lp:~hudson-openstack/nova/trunk
Prerequisite: lp:~vishvananda/nova/orm_deux
Diff against target: 163 lines (+32/-15)
3 files modified
bin/nova-manage (+27/-10)
nova/endpoint/cloud.py (+0/-1)
nova/quota.py (+5/-4)
To merge this branch: bzr merge lp:~vishvananda/nova/quotas
Reviewer Review Type Date Requested Status
Jay Pipes (community) Approve
Jesse Andrews (community) Approve
Review via email: mp+35316@code.launchpad.net

Commit message

Implements quotas with overrides for instances, volumes, and floating ips.

Description of the change

Implements quotas with overrides for instances, volumes, and floating ips.

To post a comment you must log in.
Revision history for this message
Jesse Andrews (anotherjesse) wrote :

I'd like to see nova-manage commands to alter the project's quotas before we merge this

Revision history for this message
Vish Ishaya (vishvananda) wrote :

Working on nova-manage commands

Revision history for this message
Vish Ishaya (vishvananda) wrote :

Nova manage command is in:
nova-manage project quota [key] [value]

Revision history for this message
Jay Pipes (jaypipes) wrote :

Hi Vish!

Nice stuff!

Some small stuff I noticed...

87 + project_quota = quota._get_quota(None, project_id)

You might as well make quota._get_quota() into a public function since it's used outside of the quote.py file, so a rename to quota.get_quota() would be good (also a docstring on that function would be super-cool that indicates the function returns a set of default quota configuration options, overridden with any non-null value stored in the db for that project...)

379 + size = int(size)
380 + if quota.allowed_volumes(context, 1, size) < 1:
381 + logging.warn("Quota exceeeded for %s, tried to create %sG volume",
382 + context.project.id, size)
383 + raise QuotaError("Volume quota exceeded. You cannot "
384 + "create a volume of size %s" %
385 + size)

The above interface for determining whether a quote has been exceeded could be refactored slightly to reduce misuse. Instead of having the caller remember to cast size to an int, the quota.allowed_volumes() would be a better place to do that cast since it means one less assumption on the part of the interface.

Also, you may consider changing the interface to return a boolean instead of a number of allowed volumes. This may improve readability.

For example, you could have the following code instead:

if quota.volumes_exceeded(context, size):
    ... raise error, log, etc..

Hope that makes sense...I think that would make the interface a bit more intuitive. You could even keep the quota.allowed_volumes() function and make a small convenience wrapper around it to remove some of the awkwardness in the current interface.

Anyway, just some thoughts. Great work overall, Vish :)

-jay

Revision history for this message
Vish Ishaya (vishvananda) wrote :

I went ahead and made the first couple of changes. The idea of passing in requested volumes and returning allowed is to allow for the future possibility for requesting more than one volume (or address at a time). The current api allows for launching n instances, so it makes sense there to pass in num_requested and get a num_allowed back.

I agree that it is a bit awkward given the current api, but i think keeping the same logic that we use for instances makes sense for the future idea of creating multiple volume and multiple ips in a single call.

Revision history for this message
Jay Pipes (jaypipes) wrote :

OK, no worries, like I mentioned, that last comment was recently just a suggestion, nothing more. :)

I would approve, but I'm seeing merge conflicts showing up. Vish, could you remerge trunk one more time? I think the scheduler branch may have introduced a conflict?

Cheers!
jay

review: Needs Fixing
lp:~vishvananda/nova/quotas updated
375. By Vish Ishaya

merged trunk

Revision history for this message
Jesse Andrews (anotherjesse) wrote :

lgtm

review: Approve
Revision history for this message
Jay Pipes (jaypipes) wrote :

yep, cool with me.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'bin/nova-manage'
--- bin/nova-manage 2010-09-11 07:21:58 +0000
+++ bin/nova-manage 2010-09-21 04:22:40 +0000
@@ -50,7 +50,6 @@
5050
51"""51"""
52 CLI interface for nova management.52 CLI interface for nova management.
53 Connects to the running ADMIN api in the api daemon.
54"""53"""
5554
56import os55import os
@@ -68,7 +67,9 @@
68 sys.path.insert(0, possible_topdir)67 sys.path.insert(0, possible_topdir)
6968
70from nova import db69from nova import db
70from nova import exception
71from nova import flags71from nova import flags
72from nova import quota
72from nova import utils73from nova import utils
73from nova.auth import manager74from nova.auth import manager
74from nova.cloudpipe import pipelib75from nova.cloudpipe import pipelib
@@ -186,6 +187,13 @@
186class UserCommands(object):187class UserCommands(object):
187 """Class for managing users."""188 """Class for managing users."""
188189
190 @staticmethod
191 def _print_export(user):
192 """Print export variables to use with API."""
193 print 'export EC2_ACCESS_KEY=%s' % user.access
194 print 'export EC2_SECRET_KEY=%s' % user.secret
195
196
189 def __init__(self):197 def __init__(self):
190 self.manager = manager.AuthManager()198 self.manager = manager.AuthManager()
191199
@@ -193,13 +201,13 @@
193 """creates a new admin and prints exports201 """creates a new admin and prints exports
194 arguments: name [access] [secret]"""202 arguments: name [access] [secret]"""
195 user = self.manager.create_user(name, access, secret, True)203 user = self.manager.create_user(name, access, secret, True)
196 print_export(user)204 self._print_export(user)
197205
198 def create(self, name, access=None, secret=None):206 def create(self, name, access=None, secret=None):
199 """creates a new user and prints exports207 """creates a new user and prints exports
200 arguments: name [access] [secret]"""208 arguments: name [access] [secret]"""
201 user = self.manager.create_user(name, access, secret, False)209 user = self.manager.create_user(name, access, secret, False)
202 print_export(user)210 self._print_export(user)
203211
204 def delete(self, name):212 def delete(self, name):
205 """deletes an existing user213 """deletes an existing user
@@ -211,7 +219,7 @@
211 arguments: name"""219 arguments: name"""
212 user = self.manager.get_user(name)220 user = self.manager.get_user(name)
213 if user:221 if user:
214 print_export(user)222 self._print_export(user)
215 else:223 else:
216 print "User %s doesn't exist" % name224 print "User %s doesn't exist" % name
217225
@@ -222,12 +230,6 @@
222 print user.name230 print user.name
223231
224232
225def print_export(user):
226 """Print export variables to use with API."""
227 print 'export EC2_ACCESS_KEY=%s' % user.access
228 print 'export EC2_SECRET_KEY=%s' % user.secret
229
230
231class ProjectCommands(object):233class ProjectCommands(object):
232 """Class for managing projects."""234 """Class for managing projects."""
233235
@@ -262,6 +264,19 @@
262 for project in self.manager.get_projects():264 for project in self.manager.get_projects():
263 print project.name265 print project.name
264266
267 def quota(self, project_id, key=None, value=None):
268 """Set or display quotas for project
269 arguments: project_id [key] [value]"""
270 if key:
271 quo = {'project_id': project_id, key: value}
272 try:
273 db.quota_update(None, project_id, quo)
274 except exception.NotFound:
275 db.quota_create(None, quo)
276 project_quota = quota.get_quota(None, project_id)
277 for key, value in project_quota.iteritems():
278 print '%s: %s' % (key, value)
279
265 def remove(self, project, user):280 def remove(self, project, user):
266 """Removes user from project281 """Removes user from project
267 arguments: project user"""282 arguments: project user"""
@@ -274,6 +289,7 @@
274 with open(filename, 'w') as f:289 with open(filename, 'w') as f:
275 f.write(zip_file)290 f.write(zip_file)
276291
292
277class FloatingIpCommands(object):293class FloatingIpCommands(object):
278 """Class for managing floating ip."""294 """Class for managing floating ip."""
279295
@@ -306,6 +322,7 @@
306 floating_ip['address'],322 floating_ip['address'],
307 instance)323 instance)
308324
325
309CATEGORIES = [326CATEGORIES = [
310 ('user', UserCommands),327 ('user', UserCommands),
311 ('project', ProjectCommands),328 ('project', ProjectCommands),
312329
=== modified file 'nova/endpoint/cloud.py'
--- nova/endpoint/cloud.py 2010-09-21 04:08:25 +0000
+++ nova/endpoint/cloud.py 2010-09-21 04:22:40 +0000
@@ -294,7 +294,6 @@
294 @rbac.allow('projectmanager', 'sysadmin')294 @rbac.allow('projectmanager', 'sysadmin')
295 def create_volume(self, context, size, **kwargs):295 def create_volume(self, context, size, **kwargs):
296 # check quota296 # check quota
297 size = int(size)
298 if quota.allowed_volumes(context, 1, size) < 1:297 if quota.allowed_volumes(context, 1, size) < 1:
299 logging.warn("Quota exceeeded for %s, tried to create %sG volume",298 logging.warn("Quota exceeeded for %s, tried to create %sG volume",
300 context.project.id, size)299 context.project.id, size)
301300
=== modified file 'nova/quota.py'
--- nova/quota.py 2010-09-10 04:42:18 +0000
+++ nova/quota.py 2010-09-21 04:22:40 +0000
@@ -37,7 +37,7 @@
37flags.DEFINE_integer('quota_floating_ips', 10,37flags.DEFINE_integer('quota_floating_ips', 10,
38 'number of floating ips allowed per project')38 'number of floating ips allowed per project')
3939
40def _get_quota(context, project_id):40def get_quota(context, project_id):
41 rval = {'instances': FLAGS.quota_instances,41 rval = {'instances': FLAGS.quota_instances,
42 'cores': FLAGS.quota_cores,42 'cores': FLAGS.quota_cores,
43 'volumes': FLAGS.quota_volumes,43 'volumes': FLAGS.quota_volumes,
@@ -57,7 +57,7 @@
57 project_id = context.project.id57 project_id = context.project.id
58 used_instances, used_cores = db.instance_data_get_for_project(context,58 used_instances, used_cores = db.instance_data_get_for_project(context,
59 project_id)59 project_id)
60 quota = _get_quota(context, project_id)60 quota = get_quota(context, project_id)
61 allowed_instances = quota['instances'] - used_instances61 allowed_instances = quota['instances'] - used_instances
62 allowed_cores = quota['cores'] - used_cores62 allowed_cores = quota['cores'] - used_cores
63 type_cores = instance_types.INSTANCE_TYPES[instance_type]['vcpus']63 type_cores = instance_types.INSTANCE_TYPES[instance_type]['vcpus']
@@ -72,9 +72,10 @@
72 project_id = context.project.id72 project_id = context.project.id
73 used_volumes, used_gigabytes = db.volume_data_get_for_project(context,73 used_volumes, used_gigabytes = db.volume_data_get_for_project(context,
74 project_id)74 project_id)
75 quota = _get_quota(context, project_id)75 quota = get_quota(context, project_id)
76 allowed_volumes = quota['volumes'] - used_volumes76 allowed_volumes = quota['volumes'] - used_volumes
77 allowed_gigabytes = quota['gigabytes'] - used_gigabytes77 allowed_gigabytes = quota['gigabytes'] - used_gigabytes
78 size = int(size)
78 num_gigabytes = num_volumes * size79 num_gigabytes = num_volumes * size
79 allowed_volumes = min(allowed_volumes,80 allowed_volumes = min(allowed_volumes,
80 int(allowed_gigabytes // size))81 int(allowed_gigabytes // size))
@@ -85,7 +86,7 @@
85 """Check quota and return min(num_floating_ips, allowed_floating_ips)"""86 """Check quota and return min(num_floating_ips, allowed_floating_ips)"""
86 project_id = context.project.id87 project_id = context.project.id
87 used_floating_ips = db.floating_ip_count_by_project(context, project_id)88 used_floating_ips = db.floating_ip_count_by_project(context, project_id)
88 quota = _get_quota(context, project_id)89 quota = get_quota(context, project_id)
89 allowed_floating_ips = quota['floating_ips'] - used_floating_ips90 allowed_floating_ips = quota['floating_ips'] - used_floating_ips
90 return min(num_floating_ips, allowed_floating_ips)91 return min(num_floating_ips, allowed_floating_ips)
9192