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
1=== modified file 'bin/nova-manage'
2--- bin/nova-manage 2010-09-11 07:21:58 +0000
3+++ bin/nova-manage 2010-09-21 04:22:40 +0000
4@@ -50,7 +50,6 @@
5
6 """
7 CLI interface for nova management.
8- Connects to the running ADMIN api in the api daemon.
9 """
10
11 import os
12@@ -68,7 +67,9 @@
13 sys.path.insert(0, possible_topdir)
14
15 from nova import db
16+from nova import exception
17 from nova import flags
18+from nova import quota
19 from nova import utils
20 from nova.auth import manager
21 from nova.cloudpipe import pipelib
22@@ -186,6 +187,13 @@
23 class UserCommands(object):
24 """Class for managing users."""
25
26+ @staticmethod
27+ def _print_export(user):
28+ """Print export variables to use with API."""
29+ print 'export EC2_ACCESS_KEY=%s' % user.access
30+ print 'export EC2_SECRET_KEY=%s' % user.secret
31+
32+
33 def __init__(self):
34 self.manager = manager.AuthManager()
35
36@@ -193,13 +201,13 @@
37 """creates a new admin and prints exports
38 arguments: name [access] [secret]"""
39 user = self.manager.create_user(name, access, secret, True)
40- print_export(user)
41+ self._print_export(user)
42
43 def create(self, name, access=None, secret=None):
44 """creates a new user and prints exports
45 arguments: name [access] [secret]"""
46 user = self.manager.create_user(name, access, secret, False)
47- print_export(user)
48+ self._print_export(user)
49
50 def delete(self, name):
51 """deletes an existing user
52@@ -211,7 +219,7 @@
53 arguments: name"""
54 user = self.manager.get_user(name)
55 if user:
56- print_export(user)
57+ self._print_export(user)
58 else:
59 print "User %s doesn't exist" % name
60
61@@ -222,12 +230,6 @@
62 print user.name
63
64
65-def print_export(user):
66- """Print export variables to use with API."""
67- print 'export EC2_ACCESS_KEY=%s' % user.access
68- print 'export EC2_SECRET_KEY=%s' % user.secret
69-
70-
71 class ProjectCommands(object):
72 """Class for managing projects."""
73
74@@ -262,6 +264,19 @@
75 for project in self.manager.get_projects():
76 print project.name
77
78+ def quota(self, project_id, key=None, value=None):
79+ """Set or display quotas for project
80+ arguments: project_id [key] [value]"""
81+ if key:
82+ quo = {'project_id': project_id, key: value}
83+ try:
84+ db.quota_update(None, project_id, quo)
85+ except exception.NotFound:
86+ db.quota_create(None, quo)
87+ project_quota = quota.get_quota(None, project_id)
88+ for key, value in project_quota.iteritems():
89+ print '%s: %s' % (key, value)
90+
91 def remove(self, project, user):
92 """Removes user from project
93 arguments: project user"""
94@@ -274,6 +289,7 @@
95 with open(filename, 'w') as f:
96 f.write(zip_file)
97
98+
99 class FloatingIpCommands(object):
100 """Class for managing floating ip."""
101
102@@ -306,6 +322,7 @@
103 floating_ip['address'],
104 instance)
105
106+
107 CATEGORIES = [
108 ('user', UserCommands),
109 ('project', ProjectCommands),
110
111=== modified file 'nova/endpoint/cloud.py'
112--- nova/endpoint/cloud.py 2010-09-21 04:08:25 +0000
113+++ nova/endpoint/cloud.py 2010-09-21 04:22:40 +0000
114@@ -294,7 +294,6 @@
115 @rbac.allow('projectmanager', 'sysadmin')
116 def create_volume(self, context, size, **kwargs):
117 # check quota
118- size = int(size)
119 if quota.allowed_volumes(context, 1, size) < 1:
120 logging.warn("Quota exceeeded for %s, tried to create %sG volume",
121 context.project.id, size)
122
123=== modified file 'nova/quota.py'
124--- nova/quota.py 2010-09-10 04:42:18 +0000
125+++ nova/quota.py 2010-09-21 04:22:40 +0000
126@@ -37,7 +37,7 @@
127 flags.DEFINE_integer('quota_floating_ips', 10,
128 'number of floating ips allowed per project')
129
130-def _get_quota(context, project_id):
131+def get_quota(context, project_id):
132 rval = {'instances': FLAGS.quota_instances,
133 'cores': FLAGS.quota_cores,
134 'volumes': FLAGS.quota_volumes,
135@@ -57,7 +57,7 @@
136 project_id = context.project.id
137 used_instances, used_cores = db.instance_data_get_for_project(context,
138 project_id)
139- quota = _get_quota(context, project_id)
140+ quota = get_quota(context, project_id)
141 allowed_instances = quota['instances'] - used_instances
142 allowed_cores = quota['cores'] - used_cores
143 type_cores = instance_types.INSTANCE_TYPES[instance_type]['vcpus']
144@@ -72,9 +72,10 @@
145 project_id = context.project.id
146 used_volumes, used_gigabytes = db.volume_data_get_for_project(context,
147 project_id)
148- quota = _get_quota(context, project_id)
149+ quota = get_quota(context, project_id)
150 allowed_volumes = quota['volumes'] - used_volumes
151 allowed_gigabytes = quota['gigabytes'] - used_gigabytes
152+ size = int(size)
153 num_gigabytes = num_volumes * size
154 allowed_volumes = min(allowed_volumes,
155 int(allowed_gigabytes // size))
156@@ -85,7 +86,7 @@
157 """Check quota and return min(num_floating_ips, allowed_floating_ips)"""
158 project_id = context.project.id
159 used_floating_ips = db.floating_ip_count_by_project(context, project_id)
160- quota = _get_quota(context, project_id)
161+ quota = get_quota(context, project_id)
162 allowed_floating_ips = quota['floating_ips'] - used_floating_ips
163 return min(num_floating_ips, allowed_floating_ips)
164