Merge lp:~vishvananda/nova/move-keypairs into lp:~hudson-openstack/nova/trunk
- move-keypairs
- Merge into trunk
Proposed by
Vish Ishaya
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Jesse Andrews | ||||
Approved revision: | 380 | ||||
Merged at revision: | 288 | ||||
Proposed branch: | lp:~vishvananda/nova/move-keypairs | ||||
Merge into: | lp:~hudson-openstack/nova/trunk | ||||
Diff against target: |
700 lines (+200/-225) 11 files modified
nova/auth/ldapdriver.py (+0/-60) nova/auth/manager.py (+6/-101) nova/cloudpipe/pipelib.py (+2/-2) nova/crypto.py (+1/-1) nova/db/api.py (+28/-0) nova/db/sqlalchemy/api.py (+40/-0) nova/db/sqlalchemy/models.py (+36/-0) nova/endpoint/cloud.py (+38/-19) nova/tests/api_unittest.py (+4/-3) nova/tests/auth_unittest.py (+0/-31) nova/tests/cloud_unittest.py (+45/-8) |
||||
To merge this branch: | bzr merge lp:~vishvananda/nova/move-keypairs | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jesse Andrews (community) | Approve | ||
Jay Pipes (community) | Approve | ||
Review via email:
|
Commit message
Moves keypairs out of ldap and into the common datastore.
Description of the change
Moves keypairs out of ldap and into the common datastore.
To post a comment you must log in.
- 380. By Vish Ishaya
-
merged trunk
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'nova/auth/ldapdriver.py' | |||
2 | --- nova/auth/ldapdriver.py 2010-09-10 11:52:48 +0000 | |||
3 | +++ nova/auth/ldapdriver.py 2010-09-21 04:21:00 +0000 | |||
4 | @@ -99,13 +99,6 @@ | |||
5 | 99 | dn = FLAGS.ldap_user_subtree | 99 | dn = FLAGS.ldap_user_subtree |
6 | 100 | return self.__to_user(self.__find_object(dn, query)) | 100 | return self.__to_user(self.__find_object(dn, query)) |
7 | 101 | 101 | ||
8 | 102 | def get_key_pair(self, uid, key_name): | ||
9 | 103 | """Retrieve key pair by uid and key name""" | ||
10 | 104 | dn = 'cn=%s,%s' % (key_name, | ||
11 | 105 | self.__uid_to_dn(uid)) | ||
12 | 106 | attr = self.__find_object(dn, '(objectclass=novaKeyPair)') | ||
13 | 107 | return self.__to_key_pair(uid, attr) | ||
14 | 108 | |||
15 | 109 | def get_project(self, pid): | 102 | def get_project(self, pid): |
16 | 110 | """Retrieve project by id""" | 103 | """Retrieve project by id""" |
17 | 111 | dn = 'cn=%s,%s' % (pid, | 104 | dn = 'cn=%s,%s' % (pid, |
18 | @@ -119,12 +112,6 @@ | |||
19 | 119 | '(objectclass=novaUser)') | 112 | '(objectclass=novaUser)') |
20 | 120 | return [self.__to_user(attr) for attr in attrs] | 113 | return [self.__to_user(attr) for attr in attrs] |
21 | 121 | 114 | ||
22 | 122 | def get_key_pairs(self, uid): | ||
23 | 123 | """Retrieve list of key pairs""" | ||
24 | 124 | attrs = self.__find_objects(self.__uid_to_dn(uid), | ||
25 | 125 | '(objectclass=novaKeyPair)') | ||
26 | 126 | return [self.__to_key_pair(uid, attr) for attr in attrs] | ||
27 | 127 | |||
28 | 128 | def get_projects(self, uid=None): | 115 | def get_projects(self, uid=None): |
29 | 129 | """Retrieve list of projects""" | 116 | """Retrieve list of projects""" |
30 | 130 | pattern = '(objectclass=novaProject)' | 117 | pattern = '(objectclass=novaProject)' |
31 | @@ -154,21 +141,6 @@ | |||
32 | 154 | self.conn.add_s(self.__uid_to_dn(name), attr) | 141 | self.conn.add_s(self.__uid_to_dn(name), attr) |
33 | 155 | return self.__to_user(dict(attr)) | 142 | return self.__to_user(dict(attr)) |
34 | 156 | 143 | ||
35 | 157 | def create_key_pair(self, uid, key_name, public_key, fingerprint): | ||
36 | 158 | """Create a key pair""" | ||
37 | 159 | # TODO(vish): possibly refactor this to store keys in their own ou | ||
38 | 160 | # and put dn reference in the user object | ||
39 | 161 | attr = [ | ||
40 | 162 | ('objectclass', ['novaKeyPair']), | ||
41 | 163 | ('cn', [key_name]), | ||
42 | 164 | ('sshPublicKey', [public_key]), | ||
43 | 165 | ('keyFingerprint', [fingerprint]), | ||
44 | 166 | ] | ||
45 | 167 | self.conn.add_s('cn=%s,%s' % (key_name, | ||
46 | 168 | self.__uid_to_dn(uid)), | ||
47 | 169 | attr) | ||
48 | 170 | return self.__to_key_pair(uid, dict(attr)) | ||
49 | 171 | |||
50 | 172 | def create_project(self, name, manager_uid, | 144 | def create_project(self, name, manager_uid, |
51 | 173 | description=None, member_uids=None): | 145 | description=None, member_uids=None): |
52 | 174 | """Create a project""" | 146 | """Create a project""" |
53 | @@ -283,19 +255,10 @@ | |||
54 | 283 | """Delete a user""" | 255 | """Delete a user""" |
55 | 284 | if not self.__user_exists(uid): | 256 | if not self.__user_exists(uid): |
56 | 285 | raise exception.NotFound("User %s doesn't exist" % uid) | 257 | raise exception.NotFound("User %s doesn't exist" % uid) |
57 | 286 | self.__delete_key_pairs(uid) | ||
58 | 287 | self.__remove_from_all(uid) | 258 | self.__remove_from_all(uid) |
59 | 288 | self.conn.delete_s('uid=%s,%s' % (uid, | 259 | self.conn.delete_s('uid=%s,%s' % (uid, |
60 | 289 | FLAGS.ldap_user_subtree)) | 260 | FLAGS.ldap_user_subtree)) |
61 | 290 | 261 | ||
62 | 291 | def delete_key_pair(self, uid, key_name): | ||
63 | 292 | """Delete a key pair""" | ||
64 | 293 | if not self.__key_pair_exists(uid, key_name): | ||
65 | 294 | raise exception.NotFound("Key Pair %s doesn't exist for user %s" % | ||
66 | 295 | (key_name, uid)) | ||
67 | 296 | self.conn.delete_s('cn=%s,uid=%s,%s' % (key_name, uid, | ||
68 | 297 | FLAGS.ldap_user_subtree)) | ||
69 | 298 | |||
70 | 299 | def delete_project(self, project_id): | 262 | def delete_project(self, project_id): |
71 | 300 | """Delete a project""" | 263 | """Delete a project""" |
72 | 301 | project_dn = 'cn=%s,%s' % (project_id, FLAGS.ldap_project_subtree) | 264 | project_dn = 'cn=%s,%s' % (project_id, FLAGS.ldap_project_subtree) |
73 | @@ -306,10 +269,6 @@ | |||
74 | 306 | """Check if user exists""" | 269 | """Check if user exists""" |
75 | 307 | return self.get_user(uid) != None | 270 | return self.get_user(uid) != None |
76 | 308 | 271 | ||
77 | 309 | def __key_pair_exists(self, uid, key_name): | ||
78 | 310 | """Check if key pair exists""" | ||
79 | 311 | return self.get_key_pair(uid, key_name) != None | ||
80 | 312 | |||
81 | 313 | def __project_exists(self, project_id): | 272 | def __project_exists(self, project_id): |
82 | 314 | """Check if project exists""" | 273 | """Check if project exists""" |
83 | 315 | return self.get_project(project_id) != None | 274 | return self.get_project(project_id) != None |
84 | @@ -359,13 +318,6 @@ | |||
85 | 359 | """Check if group exists""" | 318 | """Check if group exists""" |
86 | 360 | return self.__find_object(dn, '(objectclass=groupOfNames)') != None | 319 | return self.__find_object(dn, '(objectclass=groupOfNames)') != None |
87 | 361 | 320 | ||
88 | 362 | def __delete_key_pairs(self, uid): | ||
89 | 363 | """Delete all key pairs for user""" | ||
90 | 364 | keys = self.get_key_pairs(uid) | ||
91 | 365 | if keys != None: | ||
92 | 366 | for key in keys: | ||
93 | 367 | self.delete_key_pair(uid, key['name']) | ||
94 | 368 | |||
95 | 369 | @staticmethod | 321 | @staticmethod |
96 | 370 | def __role_to_dn(role, project_id=None): | 322 | def __role_to_dn(role, project_id=None): |
97 | 371 | """Convert role to corresponding dn""" | 323 | """Convert role to corresponding dn""" |
98 | @@ -490,18 +442,6 @@ | |||
99 | 490 | 'secret': attr['secretKey'][0], | 442 | 'secret': attr['secretKey'][0], |
100 | 491 | 'admin': (attr['isAdmin'][0] == 'TRUE')} | 443 | 'admin': (attr['isAdmin'][0] == 'TRUE')} |
101 | 492 | 444 | ||
102 | 493 | @staticmethod | ||
103 | 494 | def __to_key_pair(owner, attr): | ||
104 | 495 | """Convert ldap attributes to KeyPair object""" | ||
105 | 496 | if attr == None: | ||
106 | 497 | return None | ||
107 | 498 | return { | ||
108 | 499 | 'id': attr['cn'][0], | ||
109 | 500 | 'name': attr['cn'][0], | ||
110 | 501 | 'owner_id': owner, | ||
111 | 502 | 'public_key': attr['sshPublicKey'][0], | ||
112 | 503 | 'fingerprint': attr['keyFingerprint'][0]} | ||
113 | 504 | |||
114 | 505 | def __to_project(self, attr): | 445 | def __to_project(self, attr): |
115 | 506 | """Convert ldap attributes to Project object""" | 446 | """Convert ldap attributes to Project object""" |
116 | 507 | if attr == None: | 447 | if attr == None: |
117 | 508 | 448 | ||
118 | === modified file 'nova/auth/manager.py' | |||
119 | --- nova/auth/manager.py 2010-09-21 03:53:46 +0000 | |||
120 | +++ nova/auth/manager.py 2010-09-21 04:21:00 +0000 | |||
121 | @@ -128,24 +128,6 @@ | |||
122 | 128 | def is_project_manager(self, project): | 128 | def is_project_manager(self, project): |
123 | 129 | return AuthManager().is_project_manager(self, project) | 129 | return AuthManager().is_project_manager(self, project) |
124 | 130 | 130 | ||
125 | 131 | def generate_key_pair(self, name): | ||
126 | 132 | return AuthManager().generate_key_pair(self.id, name) | ||
127 | 133 | |||
128 | 134 | def create_key_pair(self, name, public_key, fingerprint): | ||
129 | 135 | return AuthManager().create_key_pair(self.id, | ||
130 | 136 | name, | ||
131 | 137 | public_key, | ||
132 | 138 | fingerprint) | ||
133 | 139 | |||
134 | 140 | def get_key_pair(self, name): | ||
135 | 141 | return AuthManager().get_key_pair(self.id, name) | ||
136 | 142 | |||
137 | 143 | def delete_key_pair(self, name): | ||
138 | 144 | return AuthManager().delete_key_pair(self.id, name) | ||
139 | 145 | |||
140 | 146 | def get_key_pairs(self): | ||
141 | 147 | return AuthManager().get_key_pairs(self.id) | ||
142 | 148 | |||
143 | 149 | def __repr__(self): | 131 | def __repr__(self): |
144 | 150 | return "User('%s', '%s', '%s', '%s', %s)" % (self.id, | 132 | return "User('%s', '%s', '%s', '%s', %s)" % (self.id, |
145 | 151 | self.name, | 133 | self.name, |
146 | @@ -154,29 +136,6 @@ | |||
147 | 154 | self.admin) | 136 | self.admin) |
148 | 155 | 137 | ||
149 | 156 | 138 | ||
150 | 157 | class KeyPair(AuthBase): | ||
151 | 158 | """Represents an ssh key returned from the datastore | ||
152 | 159 | |||
153 | 160 | Even though this object is named KeyPair, only the public key and | ||
154 | 161 | fingerprint is stored. The user's private key is not saved. | ||
155 | 162 | """ | ||
156 | 163 | |||
157 | 164 | def __init__(self, id, name, owner_id, public_key, fingerprint): | ||
158 | 165 | AuthBase.__init__(self) | ||
159 | 166 | self.id = id | ||
160 | 167 | self.name = name | ||
161 | 168 | self.owner_id = owner_id | ||
162 | 169 | self.public_key = public_key | ||
163 | 170 | self.fingerprint = fingerprint | ||
164 | 171 | |||
165 | 172 | def __repr__(self): | ||
166 | 173 | return "KeyPair('%s', '%s', '%s', '%s', '%s')" % (self.id, | ||
167 | 174 | self.name, | ||
168 | 175 | self.owner_id, | ||
169 | 176 | self.public_key, | ||
170 | 177 | self.fingerprint) | ||
171 | 178 | |||
172 | 179 | |||
173 | 180 | class Project(AuthBase): | 139 | class Project(AuthBase): |
174 | 181 | """Represents a Project returned from the datastore""" | 140 | """Represents a Project returned from the datastore""" |
175 | 182 | 141 | ||
176 | @@ -663,67 +622,13 @@ | |||
177 | 663 | return User(**user_dict) | 622 | return User(**user_dict) |
178 | 664 | 623 | ||
179 | 665 | def delete_user(self, user): | 624 | def delete_user(self, user): |
201 | 666 | """Deletes a user""" | 625 | """Deletes a user |
202 | 667 | with self.driver() as drv: | 626 | |
203 | 668 | drv.delete_user(User.safe_id(user)) | 627 | Additionally deletes all users key_pairs""" |
183 | 669 | |||
184 | 670 | def generate_key_pair(self, user, key_name): | ||
185 | 671 | """Generates a key pair for a user | ||
186 | 672 | |||
187 | 673 | Generates a public and private key, stores the public key using the | ||
188 | 674 | key_name, and returns the private key and fingerprint. | ||
189 | 675 | |||
190 | 676 | @type user: User or uid | ||
191 | 677 | @param user: User for which to create key pair. | ||
192 | 678 | |||
193 | 679 | @type key_name: str | ||
194 | 680 | @param key_name: Name to use for the generated KeyPair. | ||
195 | 681 | |||
196 | 682 | @rtype: tuple (private_key, fingerprint) | ||
197 | 683 | @return: A tuple containing the private_key and fingerprint. | ||
198 | 684 | """ | ||
199 | 685 | # NOTE(vish): generating key pair is slow so check for legal | ||
200 | 686 | # creation before creating keypair | ||
204 | 687 | uid = User.safe_id(user) | 628 | uid = User.safe_id(user) |
244 | 688 | with self.driver() as drv: | 629 | db.key_pair_destroy_all_by_user(None, uid) |
245 | 689 | if not drv.get_user(uid): | 630 | with self.driver() as drv: |
246 | 690 | raise exception.NotFound("User %s doesn't exist" % user) | 631 | drv.delete_user(uid) |
208 | 691 | if drv.get_key_pair(uid, key_name): | ||
209 | 692 | raise exception.Duplicate("The keypair %s already exists" | ||
210 | 693 | % key_name) | ||
211 | 694 | private_key, public_key, fingerprint = crypto.generate_key_pair() | ||
212 | 695 | self.create_key_pair(uid, key_name, public_key, fingerprint) | ||
213 | 696 | return private_key, fingerprint | ||
214 | 697 | |||
215 | 698 | def create_key_pair(self, user, key_name, public_key, fingerprint): | ||
216 | 699 | """Creates a key pair for user""" | ||
217 | 700 | with self.driver() as drv: | ||
218 | 701 | kp_dict = drv.create_key_pair(User.safe_id(user), | ||
219 | 702 | key_name, | ||
220 | 703 | public_key, | ||
221 | 704 | fingerprint) | ||
222 | 705 | if kp_dict: | ||
223 | 706 | return KeyPair(**kp_dict) | ||
224 | 707 | |||
225 | 708 | def get_key_pair(self, user, key_name): | ||
226 | 709 | """Retrieves a key pair for user""" | ||
227 | 710 | with self.driver() as drv: | ||
228 | 711 | kp_dict = drv.get_key_pair(User.safe_id(user), key_name) | ||
229 | 712 | if kp_dict: | ||
230 | 713 | return KeyPair(**kp_dict) | ||
231 | 714 | |||
232 | 715 | def get_key_pairs(self, user): | ||
233 | 716 | """Retrieves all key pairs for user""" | ||
234 | 717 | with self.driver() as drv: | ||
235 | 718 | kp_list = drv.get_key_pairs(User.safe_id(user)) | ||
236 | 719 | if not kp_list: | ||
237 | 720 | return [] | ||
238 | 721 | return [KeyPair(**kp_dict) for kp_dict in kp_list] | ||
239 | 722 | |||
240 | 723 | def delete_key_pair(self, user, key_name): | ||
241 | 724 | """Deletes a key pair for user""" | ||
242 | 725 | with self.driver() as drv: | ||
243 | 726 | drv.delete_key_pair(User.safe_id(user), key_name) | ||
247 | 727 | 632 | ||
248 | 728 | def get_credentials(self, user, project=None): | 633 | def get_credentials(self, user, project=None): |
249 | 729 | """Get credential zip for user in project""" | 634 | """Get credential zip for user in project""" |
250 | 730 | 635 | ||
251 | === modified file 'nova/cloudpipe/pipelib.py' | |||
252 | --- nova/cloudpipe/pipelib.py 2010-08-16 12:16:21 +0000 | |||
253 | +++ nova/cloudpipe/pipelib.py 2010-09-21 04:21:00 +0000 | |||
254 | @@ -58,7 +58,7 @@ | |||
255 | 58 | z.write(FLAGS.boot_script_template,'autorun.sh') | 58 | z.write(FLAGS.boot_script_template,'autorun.sh') |
256 | 59 | z.close() | 59 | z.close() |
257 | 60 | 60 | ||
259 | 61 | key_name = self.setup_keypair(project.project_manager_id, project_id) | 61 | key_name = self.setup_key_pair(project.project_manager_id, project_id) |
260 | 62 | zippy = open(zippath, "r") | 62 | zippy = open(zippath, "r") |
261 | 63 | context = api.APIRequestContext(handler=None, user=project.project_manager, project=project) | 63 | context = api.APIRequestContext(handler=None, user=project.project_manager, project=project) |
262 | 64 | 64 | ||
263 | @@ -74,7 +74,7 @@ | |||
264 | 74 | security_groups=["vpn-secgroup"]) | 74 | security_groups=["vpn-secgroup"]) |
265 | 75 | zippy.close() | 75 | zippy.close() |
266 | 76 | 76 | ||
268 | 77 | def setup_keypair(self, user_id, project_id): | 77 | def setup_key_pair(self, user_id, project_id): |
269 | 78 | key_name = '%s%s' % (project_id, FLAGS.vpn_key_suffix) | 78 | key_name = '%s%s' % (project_id, FLAGS.vpn_key_suffix) |
270 | 79 | try: | 79 | try: |
271 | 80 | private_key, fingerprint = self.manager.generate_key_pair(user_id, key_name) | 80 | private_key, fingerprint = self.manager.generate_key_pair(user_id, key_name) |
272 | 81 | 81 | ||
273 | === modified file 'nova/crypto.py' | |||
274 | --- nova/crypto.py 2010-08-16 12:16:21 +0000 | |||
275 | +++ nova/crypto.py 2010-09-21 04:21:00 +0000 | |||
276 | @@ -18,7 +18,7 @@ | |||
277 | 18 | 18 | ||
278 | 19 | """ | 19 | """ |
279 | 20 | Wrappers around standard crypto, including root and intermediate CAs, | 20 | Wrappers around standard crypto, including root and intermediate CAs, |
281 | 21 | SSH keypairs and x509 certificates. | 21 | SSH key_pairs and x509 certificates. |
282 | 22 | """ | 22 | """ |
283 | 23 | 23 | ||
284 | 24 | import base64 | 24 | import base64 |
285 | 25 | 25 | ||
286 | === modified file 'nova/db/api.py' | |||
287 | --- nova/db/api.py 2010-09-12 01:45:35 +0000 | |||
288 | +++ nova/db/api.py 2010-09-21 04:21:00 +0000 | |||
289 | @@ -296,6 +296,34 @@ | |||
290 | 296 | return IMPL.instance_update(context, instance_id, values) | 296 | return IMPL.instance_update(context, instance_id, values) |
291 | 297 | 297 | ||
292 | 298 | 298 | ||
293 | 299 | ################### | ||
294 | 300 | |||
295 | 301 | |||
296 | 302 | def key_pair_create(context, values): | ||
297 | 303 | """Create a key_pair from the values dictionary.""" | ||
298 | 304 | return IMPL.key_pair_create(context, values) | ||
299 | 305 | |||
300 | 306 | |||
301 | 307 | def key_pair_destroy(context, user_id, name): | ||
302 | 308 | """Destroy the key_pair or raise if it does not exist.""" | ||
303 | 309 | return IMPL.key_pair_destroy(context, user_id, name) | ||
304 | 310 | |||
305 | 311 | |||
306 | 312 | def key_pair_destroy_all_by_user(context, user_id): | ||
307 | 313 | """Destroy all key_pairs by user.""" | ||
308 | 314 | return IMPL.key_pair_destroy_all_by_user(context, user_id) | ||
309 | 315 | |||
310 | 316 | |||
311 | 317 | def key_pair_get(context, user_id, name): | ||
312 | 318 | """Get a key_pair or raise if it does not exist.""" | ||
313 | 319 | return IMPL.key_pair_get(context, user_id, name) | ||
314 | 320 | |||
315 | 321 | |||
316 | 322 | def key_pair_get_all_by_user(context, user_id): | ||
317 | 323 | """Get all key_pairs by user.""" | ||
318 | 324 | return IMPL.key_pair_get_all_by_user(context, user_id) | ||
319 | 325 | |||
320 | 326 | |||
321 | 299 | #################### | 327 | #################### |
322 | 300 | 328 | ||
323 | 301 | 329 | ||
324 | 302 | 330 | ||
325 | === modified file 'nova/db/sqlalchemy/api.py' | |||
326 | --- nova/db/sqlalchemy/api.py 2010-09-12 02:40:38 +0000 | |||
327 | +++ nova/db/sqlalchemy/api.py 2010-09-21 04:21:00 +0000 | |||
328 | @@ -462,6 +462,46 @@ | |||
329 | 462 | ################### | 462 | ################### |
330 | 463 | 463 | ||
331 | 464 | 464 | ||
332 | 465 | def key_pair_create(_context, values): | ||
333 | 466 | key_pair_ref = models.KeyPair() | ||
334 | 467 | for (key, value) in values.iteritems(): | ||
335 | 468 | key_pair_ref[key] = value | ||
336 | 469 | key_pair_ref.save() | ||
337 | 470 | return key_pair_ref | ||
338 | 471 | |||
339 | 472 | |||
340 | 473 | def key_pair_destroy(_context, user_id, name): | ||
341 | 474 | session = get_session() | ||
342 | 475 | with session.begin(): | ||
343 | 476 | key_pair_ref = models.KeyPair.find_by_args(user_id, | ||
344 | 477 | name, | ||
345 | 478 | session=session) | ||
346 | 479 | key_pair_ref.delete(session=session) | ||
347 | 480 | |||
348 | 481 | |||
349 | 482 | def key_pair_destroy_all_by_user(_context, user_id): | ||
350 | 483 | session = get_session() | ||
351 | 484 | with session.begin(): | ||
352 | 485 | # TODO(vish): do we have to use sql here? | ||
353 | 486 | session.execute('update key_pairs set deleted=1 where user_id=:id', | ||
354 | 487 | {'id': user_id}) | ||
355 | 488 | |||
356 | 489 | |||
357 | 490 | def key_pair_get(_context, user_id, name): | ||
358 | 491 | return models.KeyPair.find_by_args(user_id, name) | ||
359 | 492 | |||
360 | 493 | |||
361 | 494 | def key_pair_get_all_by_user(_context, user_id): | ||
362 | 495 | session = get_session() | ||
363 | 496 | return session.query(models.KeyPair | ||
364 | 497 | ).filter_by(user_id=user_id | ||
365 | 498 | ).filter_by(deleted=False | ||
366 | 499 | ).all() | ||
367 | 500 | |||
368 | 501 | |||
369 | 502 | ################### | ||
370 | 503 | |||
371 | 504 | |||
372 | 465 | def network_count(_context): | 505 | def network_count(_context): |
373 | 466 | return models.Network.count() | 506 | return models.Network.count() |
374 | 467 | 507 | ||
375 | 468 | 508 | ||
376 | === modified file 'nova/db/sqlalchemy/models.py' | |||
377 | --- nova/db/sqlalchemy/models.py 2010-09-12 03:00:56 +0000 | |||
378 | +++ nova/db/sqlalchemy/models.py 2010-09-21 04:21:00 +0000 | |||
379 | @@ -323,6 +323,42 @@ | |||
380 | 323 | uselist=False)) | 323 | uselist=False)) |
381 | 324 | 324 | ||
382 | 325 | 325 | ||
383 | 326 | class KeyPair(BASE, NovaBase): | ||
384 | 327 | """Represents a public key pair for ssh""" | ||
385 | 328 | __tablename__ = 'key_pairs' | ||
386 | 329 | id = Column(Integer, primary_key=True) | ||
387 | 330 | name = Column(String(255)) | ||
388 | 331 | |||
389 | 332 | user_id = Column(String(255)) | ||
390 | 333 | |||
391 | 334 | fingerprint = Column(String(255)) | ||
392 | 335 | public_key = Column(Text) | ||
393 | 336 | |||
394 | 337 | @property | ||
395 | 338 | def str_id(self): | ||
396 | 339 | return '%s.%s' % (self.user_id, self.name) | ||
397 | 340 | |||
398 | 341 | @classmethod | ||
399 | 342 | def find_by_str(cls, str_id, session=None, deleted=False): | ||
400 | 343 | user_id, _sep, name = str_id.partition('.') | ||
401 | 344 | return cls.find_by_str(user_id, name, session, deleted) | ||
402 | 345 | |||
403 | 346 | @classmethod | ||
404 | 347 | def find_by_args(cls, user_id, name, session=None, deleted=False): | ||
405 | 348 | if not session: | ||
406 | 349 | session = get_session() | ||
407 | 350 | try: | ||
408 | 351 | return session.query(cls | ||
409 | 352 | ).filter_by(user_id=user_id | ||
410 | 353 | ).filter_by(name=name | ||
411 | 354 | ).filter_by(deleted=deleted | ||
412 | 355 | ).one() | ||
413 | 356 | except exc.NoResultFound: | ||
414 | 357 | new_exc = exception.NotFound("No model for user %s, name %s" % | ||
415 | 358 | (user_id, name)) | ||
416 | 359 | raise new_exc.__class__, new_exc, sys.exc_info()[2] | ||
417 | 360 | |||
418 | 361 | |||
419 | 326 | class Network(BASE, NovaBase): | 362 | class Network(BASE, NovaBase): |
420 | 327 | """Represents a network""" | 363 | """Represents a network""" |
421 | 328 | __tablename__ = 'networks' | 364 | __tablename__ = 'networks' |
422 | 329 | 365 | ||
423 | === modified file 'nova/endpoint/cloud.py' | |||
424 | --- nova/endpoint/cloud.py 2010-09-21 04:08:25 +0000 | |||
425 | +++ nova/endpoint/cloud.py 2010-09-21 04:21:00 +0000 | |||
426 | @@ -30,6 +30,7 @@ | |||
427 | 30 | 30 | ||
428 | 31 | from twisted.internet import defer | 31 | from twisted.internet import defer |
429 | 32 | 32 | ||
430 | 33 | from nova import crypto | ||
431 | 33 | from nova import db | 34 | from nova import db |
432 | 34 | from nova import exception | 35 | from nova import exception |
433 | 35 | from nova import flags | 36 | from nova import flags |
434 | @@ -37,7 +38,6 @@ | |||
435 | 37 | from nova import rpc | 38 | from nova import rpc |
436 | 38 | from nova import utils | 39 | from nova import utils |
437 | 39 | from nova.auth import rbac | 40 | from nova.auth import rbac |
438 | 40 | from nova.auth import manager | ||
439 | 41 | from nova.compute.instance_types import INSTANCE_TYPES | 41 | from nova.compute.instance_types import INSTANCE_TYPES |
440 | 42 | from nova.endpoint import images | 42 | from nova.endpoint import images |
441 | 43 | 43 | ||
442 | @@ -51,14 +51,30 @@ | |||
443 | 51 | pass | 51 | pass |
444 | 52 | 52 | ||
445 | 53 | 53 | ||
448 | 54 | def _gen_key(user_id, key_name): | 54 | def _gen_key(context, user_id, key_name): |
449 | 55 | """ Tuck this into AuthManager """ | 55 | """Generate a key |
450 | 56 | |||
451 | 57 | This is a module level method because it is slow and we need to defer | ||
452 | 58 | it into a process pool.""" | ||
453 | 56 | try: | 59 | try: |
456 | 57 | mgr = manager.AuthManager() | 60 | # NOTE(vish): generating key pair is slow so check for legal |
457 | 58 | private_key, fingerprint = mgr.generate_key_pair(user_id, key_name) | 61 | # creation before creating key_pair |
458 | 62 | try: | ||
459 | 63 | db.key_pair_get(context, user_id, key_name) | ||
460 | 64 | raise exception.Duplicate("The key_pair %s already exists" | ||
461 | 65 | % key_name) | ||
462 | 66 | except exception.NotFound: | ||
463 | 67 | pass | ||
464 | 68 | private_key, public_key, fingerprint = crypto.generate_key_pair() | ||
465 | 69 | key = {} | ||
466 | 70 | key['user_id'] = user_id | ||
467 | 71 | key['name'] = key_name | ||
468 | 72 | key['public_key'] = public_key | ||
469 | 73 | key['fingerprint'] = fingerprint | ||
470 | 74 | db.key_pair_create(context, key) | ||
471 | 75 | return {'private_key': private_key, 'fingerprint': fingerprint} | ||
472 | 59 | except Exception as ex: | 76 | except Exception as ex: |
473 | 60 | return {'exception': ex} | 77 | return {'exception': ex} |
474 | 61 | return {'private_key': private_key, 'fingerprint': fingerprint} | ||
475 | 62 | 78 | ||
476 | 63 | 79 | ||
477 | 64 | class CloudController(object): | 80 | class CloudController(object): |
478 | @@ -193,18 +209,18 @@ | |||
479 | 193 | 209 | ||
480 | 194 | @rbac.allow('all') | 210 | @rbac.allow('all') |
481 | 195 | def describe_key_pairs(self, context, key_name=None, **kwargs): | 211 | def describe_key_pairs(self, context, key_name=None, **kwargs): |
483 | 196 | key_pairs = context.user.get_key_pairs() | 212 | key_pairs = db.key_pair_get_all_by_user(context, context.user.id) |
484 | 197 | if not key_name is None: | 213 | if not key_name is None: |
486 | 198 | key_pairs = [x for x in key_pairs if x.name in key_name] | 214 | key_pairs = [x for x in key_pairs if x['name'] in key_name] |
487 | 199 | 215 | ||
488 | 200 | result = [] | 216 | result = [] |
489 | 201 | for key_pair in key_pairs: | 217 | for key_pair in key_pairs: |
490 | 202 | # filter out the vpn keys | 218 | # filter out the vpn keys |
491 | 203 | suffix = FLAGS.vpn_key_suffix | 219 | suffix = FLAGS.vpn_key_suffix |
493 | 204 | if context.user.is_admin() or not key_pair.name.endswith(suffix): | 220 | if context.user.is_admin() or not key_pair['name'].endswith(suffix): |
494 | 205 | result.append({ | 221 | result.append({ |
497 | 206 | 'keyName': key_pair.name, | 222 | 'keyName': key_pair['name'], |
498 | 207 | 'keyFingerprint': key_pair.fingerprint, | 223 | 'keyFingerprint': key_pair['fingerprint'], |
499 | 208 | }) | 224 | }) |
500 | 209 | 225 | ||
501 | 210 | return {'keypairsSet': result} | 226 | return {'keypairsSet': result} |
502 | @@ -220,14 +236,18 @@ | |||
503 | 220 | dcall.callback({'keyName': key_name, | 236 | dcall.callback({'keyName': key_name, |
504 | 221 | 'keyFingerprint': kwargs['fingerprint'], | 237 | 'keyFingerprint': kwargs['fingerprint'], |
505 | 222 | 'keyMaterial': kwargs['private_key']}) | 238 | 'keyMaterial': kwargs['private_key']}) |
507 | 223 | pool.apply_async(_gen_key, [context.user.id, key_name], | 239 | # TODO(vish): when context is no longer an object, pass it here |
508 | 240 | pool.apply_async(_gen_key, [None, context.user.id, key_name], | ||
509 | 224 | callback=_complete) | 241 | callback=_complete) |
510 | 225 | return dcall | 242 | return dcall |
511 | 226 | 243 | ||
512 | 227 | @rbac.allow('all') | 244 | @rbac.allow('all') |
513 | 228 | def delete_key_pair(self, context, key_name, **kwargs): | 245 | def delete_key_pair(self, context, key_name, **kwargs): |
516 | 229 | context.user.delete_key_pair(key_name) | 246 | try: |
517 | 230 | # aws returns true even if the key doens't exist | 247 | db.key_pair_destroy(context, context.user.id, key_name) |
518 | 248 | except exception.NotFound: | ||
519 | 249 | # aws returns true even if the key doesn't exist | ||
520 | 250 | pass | ||
521 | 231 | return True | 251 | return True |
522 | 232 | 252 | ||
523 | 233 | @rbac.allow('all') | 253 | @rbac.allow('all') |
524 | @@ -575,11 +595,10 @@ | |||
525 | 575 | launch_time = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()) | 595 | launch_time = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()) |
526 | 576 | key_data = None | 596 | key_data = None |
527 | 577 | if kwargs.has_key('key_name'): | 597 | if kwargs.has_key('key_name'): |
533 | 578 | key_pair = context.user.get_key_pair(kwargs['key_name']) | 598 | key_pair_ref = db.key_pair_get(context, |
534 | 579 | if not key_pair: | 599 | context.user.id, |
535 | 580 | raise exception.ApiError('Key Pair %s not found' % | 600 | kwargs['key_name']) |
536 | 581 | kwargs['key_name']) | 601 | key_data = key_pair_ref['public_key'] |
532 | 582 | key_data = key_pair.public_key | ||
537 | 583 | 602 | ||
538 | 584 | # TODO: Get the real security group of launch in here | 603 | # TODO: Get the real security group of launch in here |
539 | 585 | security_group = "default" | 604 | security_group = "default" |
540 | 586 | 605 | ||
541 | === modified file 'nova/tests/api_unittest.py' | |||
542 | --- nova/tests/api_unittest.py 2010-08-10 13:55:00 +0000 | |||
543 | +++ nova/tests/api_unittest.py 2010-09-21 04:21:00 +0000 | |||
544 | @@ -41,8 +41,8 @@ | |||
545 | 41 | # it's pretty damn circuitous so apologies if you have to fix | 41 | # it's pretty damn circuitous so apologies if you have to fix |
546 | 42 | # a bug in it | 42 | # a bug in it |
547 | 43 | # NOTE(jaypipes) The pylint disables here are for R0913 (too many args) which | 43 | # NOTE(jaypipes) The pylint disables here are for R0913 (too many args) which |
550 | 44 | # isn't controllable since boto's HTTPRequest needs that many | 44 | # isn't controllable since boto's HTTPRequest needs that many |
551 | 45 | # args, and for the version-differentiated import of tornado's | 45 | # args, and for the version-differentiated import of tornado's |
552 | 46 | # httputil. | 46 | # httputil. |
553 | 47 | # NOTE(jaypipes): The disable-msg=E1101 and E1103 below is because pylint is | 47 | # NOTE(jaypipes): The disable-msg=E1101 and E1103 below is because pylint is |
554 | 48 | # unable to introspect the deferred's return value properly | 48 | # unable to introspect the deferred's return value properly |
555 | @@ -224,7 +224,8 @@ | |||
556 | 224 | for x in range(random.randint(4, 8))) | 224 | for x in range(random.randint(4, 8))) |
557 | 225 | user = self.manager.create_user('fake', 'fake', 'fake') | 225 | user = self.manager.create_user('fake', 'fake', 'fake') |
558 | 226 | project = self.manager.create_project('fake', 'fake', 'fake') | 226 | project = self.manager.create_project('fake', 'fake', 'fake') |
560 | 227 | self.manager.generate_key_pair(user.id, keyname) | 227 | # NOTE(vish): create depends on pool, so call helper directly |
561 | 228 | cloud._gen_key(None, user.id, keyname) | ||
562 | 228 | 229 | ||
563 | 229 | rv = self.ec2.get_all_key_pairs() | 230 | rv = self.ec2.get_all_key_pairs() |
564 | 230 | results = [k for k in rv if k.name == keyname] | 231 | results = [k for k in rv if k.name == keyname] |
565 | 231 | 232 | ||
566 | === modified file 'nova/tests/auth_unittest.py' | |||
567 | --- nova/tests/auth_unittest.py 2010-09-21 03:53:46 +0000 | |||
568 | +++ nova/tests/auth_unittest.py 2010-09-21 04:21:00 +0000 | |||
569 | @@ -17,8 +17,6 @@ | |||
570 | 17 | # under the License. | 17 | # under the License. |
571 | 18 | 18 | ||
572 | 19 | import logging | 19 | import logging |
573 | 20 | from M2Crypto import BIO | ||
574 | 21 | from M2Crypto import RSA | ||
575 | 22 | from M2Crypto import X509 | 20 | from M2Crypto import X509 |
576 | 23 | import unittest | 21 | import unittest |
577 | 24 | 22 | ||
578 | @@ -65,35 +63,6 @@ | |||
579 | 65 | 'export S3_URL="http://127.0.0.1:3333/"\n' + | 63 | 'export S3_URL="http://127.0.0.1:3333/"\n' + |
580 | 66 | 'export EC2_USER_ID="test1"\n') | 64 | 'export EC2_USER_ID="test1"\n') |
581 | 67 | 65 | ||
582 | 68 | def test_006_test_key_storage(self): | ||
583 | 69 | user = self.manager.get_user('test1') | ||
584 | 70 | user.create_key_pair('public', 'key', 'fingerprint') | ||
585 | 71 | key = user.get_key_pair('public') | ||
586 | 72 | self.assertEqual('key', key.public_key) | ||
587 | 73 | self.assertEqual('fingerprint', key.fingerprint) | ||
588 | 74 | |||
589 | 75 | def test_007_test_key_generation(self): | ||
590 | 76 | user = self.manager.get_user('test1') | ||
591 | 77 | private_key, fingerprint = user.generate_key_pair('public2') | ||
592 | 78 | key = RSA.load_key_string(private_key, callback=lambda: None) | ||
593 | 79 | bio = BIO.MemoryBuffer() | ||
594 | 80 | public_key = user.get_key_pair('public2').public_key | ||
595 | 81 | key.save_pub_key_bio(bio) | ||
596 | 82 | converted = crypto.ssl_pub_to_ssh_pub(bio.read()) | ||
597 | 83 | # assert key fields are equal | ||
598 | 84 | self.assertEqual(public_key.split(" ")[1].strip(), | ||
599 | 85 | converted.split(" ")[1].strip()) | ||
600 | 86 | |||
601 | 87 | def test_008_can_list_key_pairs(self): | ||
602 | 88 | keys = self.manager.get_user('test1').get_key_pairs() | ||
603 | 89 | self.assertTrue(filter(lambda k: k.name == 'public', keys)) | ||
604 | 90 | self.assertTrue(filter(lambda k: k.name == 'public2', keys)) | ||
605 | 91 | |||
606 | 92 | def test_009_can_delete_key_pair(self): | ||
607 | 93 | self.manager.get_user('test1').delete_key_pair('public') | ||
608 | 94 | keys = self.manager.get_user('test1').get_key_pairs() | ||
609 | 95 | self.assertFalse(filter(lambda k: k.name == 'public', keys)) | ||
610 | 96 | |||
611 | 97 | def test_010_can_list_users(self): | 66 | def test_010_can_list_users(self): |
612 | 98 | users = self.manager.get_users() | 67 | users = self.manager.get_users() |
613 | 99 | logging.warn(users) | 68 | logging.warn(users) |
614 | 100 | 69 | ||
615 | === modified file 'nova/tests/cloud_unittest.py' | |||
616 | --- nova/tests/cloud_unittest.py 2010-09-11 07:21:58 +0000 | |||
617 | +++ nova/tests/cloud_unittest.py 2010-09-21 04:21:00 +0000 | |||
618 | @@ -17,13 +17,18 @@ | |||
619 | 17 | # under the License. | 17 | # under the License. |
620 | 18 | 18 | ||
621 | 19 | import logging | 19 | import logging |
622 | 20 | from M2Crypto import BIO | ||
623 | 21 | from M2Crypto import RSA | ||
624 | 20 | import StringIO | 22 | import StringIO |
625 | 21 | import time | 23 | import time |
626 | 24 | |||
627 | 22 | from tornado import ioloop | 25 | from tornado import ioloop |
628 | 23 | from twisted.internet import defer | 26 | from twisted.internet import defer |
629 | 24 | import unittest | 27 | import unittest |
630 | 25 | from xml.etree import ElementTree | 28 | from xml.etree import ElementTree |
631 | 26 | 29 | ||
632 | 30 | from nova import crypto | ||
633 | 31 | from nova import db | ||
634 | 27 | from nova import flags | 32 | from nova import flags |
635 | 28 | from nova import rpc | 33 | from nova import rpc |
636 | 29 | from nova import test | 34 | from nova import test |
637 | @@ -55,16 +60,21 @@ | |||
638 | 55 | proxy=self.compute) | 60 | proxy=self.compute) |
639 | 56 | self.injected.append(self.compute_consumer.attach_to_tornado(self.ioloop)) | 61 | self.injected.append(self.compute_consumer.attach_to_tornado(self.ioloop)) |
640 | 57 | 62 | ||
647 | 58 | try: | 63 | self.manager = manager.AuthManager() |
648 | 59 | manager.AuthManager().create_user('admin', 'admin', 'admin') | 64 | self.user = self.manager.create_user('admin', 'admin', 'admin', True) |
649 | 60 | except: pass | 65 | self.project = self.manager.create_project('proj', 'admin', 'proj') |
650 | 61 | admin = manager.AuthManager().get_user('admin') | 66 | self.context = api.APIRequestContext(handler=None, |
651 | 62 | project = manager.AuthManager().create_project('proj', 'admin', 'proj') | 67 | user=self.user, |
652 | 63 | self.context = api.APIRequestContext(handler=None,project=project,user=admin) | 68 | project=self.project) |
653 | 64 | 69 | ||
654 | 65 | def tearDown(self): | 70 | def tearDown(self): |
657 | 66 | manager.AuthManager().delete_project('proj') | 71 | self.manager.delete_project(self.project) |
658 | 67 | manager.AuthManager().delete_user('admin') | 72 | self.manager.delete_user(self.user) |
659 | 73 | super(CloudTestCase, self).setUp() | ||
660 | 74 | |||
661 | 75 | def _create_key(self, name): | ||
662 | 76 | # NOTE(vish): create depends on pool, so just call helper directly | ||
663 | 77 | return cloud._gen_key(self.context, self.context.user.id, name) | ||
664 | 68 | 78 | ||
665 | 69 | def test_console_output(self): | 79 | def test_console_output(self): |
666 | 70 | if FLAGS.connection_type == 'fake': | 80 | if FLAGS.connection_type == 'fake': |
667 | @@ -77,6 +87,33 @@ | |||
668 | 77 | self.assert_(output) | 87 | self.assert_(output) |
669 | 78 | rv = yield self.compute.terminate_instance(instance_id) | 88 | rv = yield self.compute.terminate_instance(instance_id) |
670 | 79 | 89 | ||
671 | 90 | |||
672 | 91 | def test_key_generation(self): | ||
673 | 92 | result = self._create_key('test') | ||
674 | 93 | private_key = result['private_key'] | ||
675 | 94 | key = RSA.load_key_string(private_key, callback=lambda: None) | ||
676 | 95 | bio = BIO.MemoryBuffer() | ||
677 | 96 | public_key = db.key_pair_get(self.context, | ||
678 | 97 | self.context.user.id, | ||
679 | 98 | 'test')['public_key'] | ||
680 | 99 | key.save_pub_key_bio(bio) | ||
681 | 100 | converted = crypto.ssl_pub_to_ssh_pub(bio.read()) | ||
682 | 101 | # assert key fields are equal | ||
683 | 102 | self.assertEqual(public_key.split(" ")[1].strip(), | ||
684 | 103 | converted.split(" ")[1].strip()) | ||
685 | 104 | |||
686 | 105 | def test_describe_key_pairs(self): | ||
687 | 106 | self._create_key('test1') | ||
688 | 107 | self._create_key('test2') | ||
689 | 108 | result = self.cloud.describe_key_pairs(self.context) | ||
690 | 109 | keys = result["keypairsSet"] | ||
691 | 110 | self.assertTrue(filter(lambda k: k['keyName'] == 'test1', keys)) | ||
692 | 111 | self.assertTrue(filter(lambda k: k['keyName'] == 'test2', keys)) | ||
693 | 112 | |||
694 | 113 | def test_delete_key_pair(self): | ||
695 | 114 | self._create_key('test') | ||
696 | 115 | self.cloud.delete_key_pair(self.context, 'test') | ||
697 | 116 | |||
698 | 80 | def test_run_instances(self): | 117 | def test_run_instances(self): |
699 | 81 | if FLAGS.connection_type == 'fake': | 118 | if FLAGS.connection_type == 'fake': |
700 | 82 | logging.debug("Can't test instances without a real virtual env.") | 119 | logging.debug("Can't test instances without a real virtual env.") |
Rock on. One less thing in LDAP makes Jay a happy boy.