summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xbin/nova-manage37
-rw-r--r--nova/api/ec2/cloud.py78
-rw-r--r--nova/auth/fakeldap.py5
-rw-r--r--nova/auth/ldapdriver.py78
-rw-r--r--nova/auth/manager.py123
-rw-r--r--nova/cloudpipe/pipelib.py4
-rw-r--r--nova/crypto.py2
-rw-r--r--nova/db/api.py28
-rw-r--r--nova/db/sqlalchemy/api.py40
-rw-r--r--nova/db/sqlalchemy/models.py36
-rw-r--r--nova/flags.py3
-rw-r--r--nova/quota.py9
-rw-r--r--nova/tests/api_unittest.py3
-rw-r--r--nova/tests/auth_unittest.py37
-rw-r--r--nova/tests/cloud_unittest.py53
15 files changed, 294 insertions, 242 deletions
diff --git a/bin/nova-manage b/bin/nova-manage
index 325245ac4..824e00ac5 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -50,7 +50,6 @@
"""
CLI interface for nova management.
- Connects to the running ADMIN api in the api daemon.
"""
import os
@@ -68,7 +67,9 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
sys.path.insert(0, possible_topdir)
from nova import db
+from nova import exception
from nova import flags
+from nova import quota
from nova import utils
from nova.auth import manager
from nova.cloudpipe import pipelib
@@ -186,6 +187,13 @@ class RoleCommands(object):
class UserCommands(object):
"""Class for managing users."""
+ @staticmethod
+ def _print_export(user):
+ """Print export variables to use with API."""
+ print 'export EC2_ACCESS_KEY=%s' % user.access
+ print 'export EC2_SECRET_KEY=%s' % user.secret
+
+
def __init__(self):
self.manager = manager.AuthManager()
@@ -193,13 +201,13 @@ class UserCommands(object):
"""creates a new admin and prints exports
arguments: name [access] [secret]"""
user = self.manager.create_user(name, access, secret, True)
- print_export(user)
+ self._print_export(user)
def create(self, name, access=None, secret=None):
"""creates a new user and prints exports
arguments: name [access] [secret]"""
user = self.manager.create_user(name, access, secret, False)
- print_export(user)
+ self._print_export(user)
def delete(self, name):
"""deletes an existing user
@@ -211,7 +219,7 @@ class UserCommands(object):
arguments: name"""
user = self.manager.get_user(name)
if user:
- print_export(user)
+ self._print_export(user)
else:
print "User %s doesn't exist" % name
@@ -222,12 +230,6 @@ class UserCommands(object):
print user.name
-def print_export(user):
- """Print export variables to use with API."""
- print 'export EC2_ACCESS_KEY=%s' % user.access
- print 'export EC2_SECRET_KEY=%s' % user.secret
-
-
class ProjectCommands(object):
"""Class for managing projects."""
@@ -262,6 +264,19 @@ class ProjectCommands(object):
for project in self.manager.get_projects():
print project.name
+ def quota(self, project_id, key=None, value=None):
+ """Set or display quotas for project
+ arguments: project_id [key] [value]"""
+ if key:
+ quo = {'project_id': project_id, key: value}
+ try:
+ db.quota_update(None, project_id, quo)
+ except exception.NotFound:
+ db.quota_create(None, quo)
+ project_quota = quota.get_quota(None, project_id)
+ for key, value in project_quota.iteritems():
+ print '%s: %s' % (key, value)
+
def remove(self, project, user):
"""Removes user from project
arguments: project user"""
@@ -274,6 +289,7 @@ class ProjectCommands(object):
with open(filename, 'w') as f:
f.write(zip_file)
+
class FloatingIpCommands(object):
"""Class for managing floating ip."""
@@ -306,6 +322,7 @@ class FloatingIpCommands(object):
floating_ip['address'],
instance)
+
CATEGORIES = [
('user', UserCommands),
('project', ProjectCommands),
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 25daa5988..367511e3b 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -28,13 +28,13 @@ import logging
import os
import time
+from nova import crypto
from nova import db
from nova import exception
from nova import flags
from nova import quota
from nova import rpc
from nova import utils
-from nova.auth import manager
from nova.compute.instance_types import INSTANCE_TYPES
from nova.api.ec2 import images
@@ -48,10 +48,26 @@ class QuotaError(exception.ApiError):
pass
-def _gen_key(user_id, key_name):
- """ Tuck this into AuthManager """
- mgr = manager.AuthManager()
- private_key, fingerprint = mgr.generate_key_pair(user_id, key_name)
+def _gen_key(context, user_id, key_name):
+ """Generate a key
+
+ This is a module level method because it is slow and we need to defer
+ it into a process pool."""
+ # NOTE(vish): generating key pair is slow so check for legal
+ # creation before creating key_pair
+ try:
+ db.key_pair_get(context, user_id, key_name)
+ raise exception.Duplicate("The key_pair %s already exists"
+ % key_name)
+ except exception.NotFound:
+ pass
+ private_key, public_key, fingerprint = crypto.generate_key_pair()
+ key = {}
+ key['user_id'] = user_id
+ key['name'] = key_name
+ key['public_key'] = public_key
+ key['fingerprint'] = fingerprint
+ db.key_pair_create(context, key)
return {'private_key': private_key, 'fingerprint': fingerprint}
@@ -89,10 +105,11 @@ class CloudController(object):
if instance['fixed_ip']:
line = '%s slots=%d' % (instance['fixed_ip']['str_id'],
INSTANCE_TYPES[instance['instance_type']]['vcpus'])
- if instance['key_name'] in result:
- result[instance['key_name']].append(line)
+ key = str(instance['key_name'])
+ if key in result:
+ result[key].append(line)
else:
- result[instance['key_name']] = [line]
+ result[key] = [line]
return result
def get_metadata(self, address):
@@ -154,9 +171,18 @@ class CloudController(object):
'zoneState': 'available'}]}
def describe_regions(self, context, region_name=None, **kwargs):
- # TODO(vish): region_name is an array. Support filtering
- return {'regionInfo': [{'regionName': 'nova',
- 'regionUrl': FLAGS.ec2_url}]}
+ if FLAGS.region_list:
+ regions = []
+ for region in FLAGS.region_list:
+ name, _sep, url = region.partition('=')
+ regions.append({'regionName': name,
+ 'regionEndpoint': url})
+ else:
+ regions = [{'regionName': 'nova',
+ 'regionEndpoint': FLAGS.ec2_url}]
+ if region_name:
+ regions = [r for r in regions if r['regionName'] in region_name]
+ return {'regionInfo': regions }
def describe_snapshots(self,
context,
@@ -174,31 +200,35 @@ class CloudController(object):
'description': 'fixme'}]}
def describe_key_pairs(self, context, key_name=None, **kwargs):
- key_pairs = context.user.get_key_pairs()
+ key_pairs = db.key_pair_get_all_by_user(context, context.user.id)
if not key_name is None:
- key_pairs = [x for x in key_pairs if x.name in key_name]
+ key_pairs = [x for x in key_pairs if x['name'] in key_name]
result = []
for key_pair in key_pairs:
# filter out the vpn keys
suffix = FLAGS.vpn_key_suffix
- if context.user.is_admin() or not key_pair.name.endswith(suffix):
+ if context.user.is_admin() or not key_pair['name'].endswith(suffix):
result.append({
- 'keyName': key_pair.name,
- 'keyFingerprint': key_pair.fingerprint,
+ 'keyName': key_pair['name'],
+ 'keyFingerprint': key_pair['fingerprint'],
})
return {'keypairsSet': result}
def create_key_pair(self, context, key_name, **kwargs):
- data = _gen_key(context.user.id, key_name)
+ data = _gen_key(None, context.user.id, key_name)
return {'keyName': key_name,
'keyFingerprint': data['fingerprint'],
'keyMaterial': data['private_key']}
+ # TODO(vish): when context is no longer an object, pass it here
def delete_key_pair(self, context, key_name, **kwargs):
- context.user.delete_key_pair(key_name)
- # aws returns true even if the key doens't exist
+ try:
+ db.key_pair_destroy(context, context.user.id, key_name)
+ except exception.NotFound:
+ # aws returns true even if the key doesn't exist
+ pass
return True
def describe_security_groups(self, context, group_names, **kwargs):
@@ -259,7 +289,6 @@ class CloudController(object):
def create_volume(self, context, size, **kwargs):
# check quota
- size = int(size)
if quota.allowed_volumes(context, 1, size) < 1:
logging.warn("Quota exceeeded for %s, tried to create %sG volume",
context.project.id, size)
@@ -525,11 +554,10 @@ class CloudController(object):
launch_time = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
key_data = None
if kwargs.has_key('key_name'):
- key_pair = context.user.get_key_pair(kwargs['key_name'])
- if not key_pair:
- raise exception.ApiError('Key Pair %s not found' %
- kwargs['key_name'])
- key_data = key_pair.public_key
+ key_pair_ref = db.key_pair_get(context,
+ context.user.id,
+ kwargs['key_name'])
+ key_data = key_pair_ref['public_key']
# TODO: Get the real security group of launch in here
security_group = "default"
diff --git a/nova/auth/fakeldap.py b/nova/auth/fakeldap.py
index bfc3433c5..2791dfde6 100644
--- a/nova/auth/fakeldap.py
+++ b/nova/auth/fakeldap.py
@@ -33,6 +33,7 @@ SCOPE_ONELEVEL = 1 # not implemented
SCOPE_SUBTREE = 2
MOD_ADD = 0
MOD_DELETE = 1
+MOD_REPLACE = 2
class NO_SUCH_OBJECT(Exception): # pylint: disable-msg=C0103
@@ -175,7 +176,7 @@ class FakeLDAP(object):
Args:
dn -- a dn
attrs -- a list of tuples in the following form:
- ([MOD_ADD | MOD_DELETE], attribute, value)
+ ([MOD_ADD | MOD_DELETE | MOD_REPACE], attribute, value)
"""
redis = datastore.Redis.instance()
@@ -185,6 +186,8 @@ class FakeLDAP(object):
values = _from_json(redis.hget(key, k))
if cmd == MOD_ADD:
values.append(v)
+ elif cmd == MOD_REPLACE:
+ values = [v]
else:
values.remove(v)
values = redis.hset(key, k, _to_json(values))
diff --git a/nova/auth/ldapdriver.py b/nova/auth/ldapdriver.py
index 74ba011b5..021851ebf 100644
--- a/nova/auth/ldapdriver.py
+++ b/nova/auth/ldapdriver.py
@@ -99,13 +99,6 @@ class LdapDriver(object):
dn = FLAGS.ldap_user_subtree
return self.__to_user(self.__find_object(dn, query))
- def get_key_pair(self, uid, key_name):
- """Retrieve key pair by uid and key name"""
- dn = 'cn=%s,%s' % (key_name,
- self.__uid_to_dn(uid))
- attr = self.__find_object(dn, '(objectclass=novaKeyPair)')
- return self.__to_key_pair(uid, attr)
-
def get_project(self, pid):
"""Retrieve project by id"""
dn = 'cn=%s,%s' % (pid,
@@ -119,12 +112,6 @@ class LdapDriver(object):
'(objectclass=novaUser)')
return [self.__to_user(attr) for attr in attrs]
- def get_key_pairs(self, uid):
- """Retrieve list of key pairs"""
- attrs = self.__find_objects(self.__uid_to_dn(uid),
- '(objectclass=novaKeyPair)')
- return [self.__to_key_pair(uid, attr) for attr in attrs]
-
def get_projects(self, uid=None):
"""Retrieve list of projects"""
pattern = '(objectclass=novaProject)'
@@ -154,21 +141,6 @@ class LdapDriver(object):
self.conn.add_s(self.__uid_to_dn(name), attr)
return self.__to_user(dict(attr))
- def create_key_pair(self, uid, key_name, public_key, fingerprint):
- """Create a key pair"""
- # TODO(vish): possibly refactor this to store keys in their own ou
- # and put dn reference in the user object
- attr = [
- ('objectclass', ['novaKeyPair']),
- ('cn', [key_name]),
- ('sshPublicKey', [public_key]),
- ('keyFingerprint', [fingerprint]),
- ]
- self.conn.add_s('cn=%s,%s' % (key_name,
- self.__uid_to_dn(uid)),
- attr)
- return self.__to_key_pair(uid, dict(attr))
-
def create_project(self, name, manager_uid,
description=None, member_uids=None):
"""Create a project"""
@@ -202,6 +174,24 @@ class LdapDriver(object):
self.conn.add_s('cn=%s,%s' % (name, FLAGS.ldap_project_subtree), attr)
return self.__to_project(dict(attr))
+ def modify_project(self, project_id, manager_uid=None, description=None):
+ """Modify an existing project"""
+ if not manager_uid and not description:
+ return
+ attr = []
+ if manager_uid:
+ if not self.__user_exists(manager_uid):
+ raise exception.NotFound("Project can't be modified because "
+ "manager %s doesn't exist" %
+ manager_uid)
+ manager_dn = self.__uid_to_dn(manager_uid)
+ attr.append((self.ldap.MOD_REPLACE, 'projectManager', manager_dn))
+ if description:
+ attr.append((self.ldap.MOD_REPLACE, 'description', description))
+ self.conn.modify_s('cn=%s,%s' % (project_id,
+ FLAGS.ldap_project_subtree),
+ attr)
+
def add_to_project(self, uid, project_id):
"""Add user to project"""
dn = 'cn=%s,%s' % (project_id, FLAGS.ldap_project_subtree)
@@ -265,19 +255,10 @@ class LdapDriver(object):
"""Delete a user"""
if not self.__user_exists(uid):
raise exception.NotFound("User %s doesn't exist" % uid)
- self.__delete_key_pairs(uid)
self.__remove_from_all(uid)
self.conn.delete_s('uid=%s,%s' % (uid,
FLAGS.ldap_user_subtree))
- def delete_key_pair(self, uid, key_name):
- """Delete a key pair"""
- if not self.__key_pair_exists(uid, key_name):
- raise exception.NotFound("Key Pair %s doesn't exist for user %s" %
- (key_name, uid))
- self.conn.delete_s('cn=%s,uid=%s,%s' % (key_name, uid,
- FLAGS.ldap_user_subtree))
-
def delete_project(self, project_id):
"""Delete a project"""
project_dn = 'cn=%s,%s' % (project_id, FLAGS.ldap_project_subtree)
@@ -288,10 +269,6 @@ class LdapDriver(object):
"""Check if user exists"""
return self.get_user(uid) != None
- def __key_pair_exists(self, uid, key_name):
- """Check if key pair exists"""
- return self.get_key_pair(uid, key_name) != None
-
def __project_exists(self, project_id):
"""Check if project exists"""
return self.get_project(project_id) != None
@@ -341,13 +318,6 @@ class LdapDriver(object):
"""Check if group exists"""
return self.__find_object(dn, '(objectclass=groupOfNames)') != None
- def __delete_key_pairs(self, uid):
- """Delete all key pairs for user"""
- keys = self.get_key_pairs(uid)
- if keys != None:
- for key in keys:
- self.delete_key_pair(uid, key['name'])
-
@staticmethod
def __role_to_dn(role, project_id=None):
"""Convert role to corresponding dn"""
@@ -472,18 +442,6 @@ class LdapDriver(object):
'secret': attr['secretKey'][0],
'admin': (attr['isAdmin'][0] == 'TRUE')}
- @staticmethod
- def __to_key_pair(owner, attr):
- """Convert ldap attributes to KeyPair object"""
- if attr == None:
- return None
- return {
- 'id': attr['cn'][0],
- 'name': attr['cn'][0],
- 'owner_id': owner,
- 'public_key': attr['sshPublicKey'][0],
- 'fingerprint': attr['keyFingerprint'][0]}
-
def __to_project(self, attr):
"""Convert ldap attributes to Project object"""
if attr == None:
diff --git a/nova/auth/manager.py b/nova/auth/manager.py
index d5fbec7c5..bc3a8a12e 100644
--- a/nova/auth/manager.py
+++ b/nova/auth/manager.py
@@ -128,24 +128,6 @@ class User(AuthBase):
def is_project_manager(self, project):
return AuthManager().is_project_manager(self, project)
- def generate_key_pair(self, name):
- return AuthManager().generate_key_pair(self.id, name)
-
- def create_key_pair(self, name, public_key, fingerprint):
- return AuthManager().create_key_pair(self.id,
- name,
- public_key,
- fingerprint)
-
- def get_key_pair(self, name):
- return AuthManager().get_key_pair(self.id, name)
-
- def delete_key_pair(self, name):
- return AuthManager().delete_key_pair(self.id, name)
-
- def get_key_pairs(self):
- return AuthManager().get_key_pairs(self.id)
-
def __repr__(self):
return "User('%s', '%s', '%s', '%s', %s)" % (self.id,
self.name,
@@ -154,29 +136,6 @@ class User(AuthBase):
self.admin)
-class KeyPair(AuthBase):
- """Represents an ssh key returned from the datastore
-
- Even though this object is named KeyPair, only the public key and
- fingerprint is stored. The user's private key is not saved.
- """
-
- def __init__(self, id, name, owner_id, public_key, fingerprint):
- AuthBase.__init__(self)
- self.id = id
- self.name = name
- self.owner_id = owner_id
- self.public_key = public_key
- self.fingerprint = fingerprint
-
- def __repr__(self):
- return "KeyPair('%s', '%s', '%s', '%s', '%s')" % (self.id,
- self.name,
- self.owner_id,
- self.public_key,
- self.fingerprint)
-
-
class Project(AuthBase):
"""Represents a Project returned from the datastore"""
@@ -533,6 +492,26 @@ class AuthManager(object):
raise
return project
+ def modify_project(self, project, manager_user=None, description=None):
+ """Modify a project
+
+ @type name: Project or project_id
+ @param project: The project to modify.
+
+ @type manager_user: User or uid
+ @param manager_user: This user will be the new project manager.
+
+ @type description: str
+ @param project: This will be the new description of the project.
+
+ """
+ if manager_user:
+ manager_user = User.safe_id(manager_user)
+ with self.driver() as drv:
+ drv.modify_project(Project.safe_id(project),
+ manager_user,
+ description)
+
def add_to_project(self, user, project):
"""Add user to project"""
with self.driver() as drv:
@@ -643,67 +622,13 @@ class AuthManager(object):
return User(**user_dict)
def delete_user(self, user):
- """Deletes a user"""
- with self.driver() as drv:
- drv.delete_user(User.safe_id(user))
-
- def generate_key_pair(self, user, key_name):
- """Generates a key pair for a user
-
- Generates a public and private key, stores the public key using the
- key_name, and returns the private key and fingerprint.
-
- @type user: User or uid
- @param user: User for which to create key pair.
+ """Deletes a user
- @type key_name: str
- @param key_name: Name to use for the generated KeyPair.
-
- @rtype: tuple (private_key, fingerprint)
- @return: A tuple containing the private_key and fingerprint.
- """
- # NOTE(vish): generating key pair is slow so check for legal
- # creation before creating keypair
+ Additionally deletes all users key_pairs"""
uid = User.safe_id(user)
+ db.key_pair_destroy_all_by_user(None, uid)
with self.driver() as drv:
- if not drv.get_user(uid):
- raise exception.NotFound("User %s doesn't exist" % user)
- if drv.get_key_pair(uid, key_name):
- raise exception.Duplicate("The keypair %s already exists"
- % key_name)
- private_key, public_key, fingerprint = crypto.generate_key_pair()
- self.create_key_pair(uid, key_name, public_key, fingerprint)
- return private_key, fingerprint
-
- def create_key_pair(self, user, key_name, public_key, fingerprint):
- """Creates a key pair for user"""
- with self.driver() as drv:
- kp_dict = drv.create_key_pair(User.safe_id(user),
- key_name,
- public_key,
- fingerprint)
- if kp_dict:
- return KeyPair(**kp_dict)
-
- def get_key_pair(self, user, key_name):
- """Retrieves a key pair for user"""
- with self.driver() as drv:
- kp_dict = drv.get_key_pair(User.safe_id(user), key_name)
- if kp_dict:
- return KeyPair(**kp_dict)
-
- def get_key_pairs(self, user):
- """Retrieves all key pairs for user"""
- with self.driver() as drv:
- kp_list = drv.get_key_pairs(User.safe_id(user))
- if not kp_list:
- return []
- return [KeyPair(**kp_dict) for kp_dict in kp_list]
-
- def delete_key_pair(self, user, key_name):
- """Deletes a key pair for user"""
- with self.driver() as drv:
- drv.delete_key_pair(User.safe_id(user), key_name)
+ drv.delete_user(uid)
def get_credentials(self, user, project=None):
"""Get credential zip for user in project"""
diff --git a/nova/cloudpipe/pipelib.py b/nova/cloudpipe/pipelib.py
index 97272eda6..b13a60292 100644
--- a/nova/cloudpipe/pipelib.py
+++ b/nova/cloudpipe/pipelib.py
@@ -58,7 +58,7 @@ class CloudPipe(object):
z.write(FLAGS.boot_script_template,'autorun.sh')
z.close()
- key_name = self.setup_keypair(project.project_manager_id, project_id)
+ key_name = self.setup_key_pair(project.project_manager_id, project_id)
zippy = open(zippath, "r")
context = context.APIRequestContext(user=project.project_manager, project=project)
@@ -74,7 +74,7 @@ class CloudPipe(object):
security_groups=["vpn-secgroup"])
zippy.close()
- def setup_keypair(self, user_id, project_id):
+ def setup_key_pair(self, user_id, project_id):
key_name = '%s%s' % (project_id, FLAGS.vpn_key_suffix)
try:
private_key, fingerprint = self.manager.generate_key_pair(user_id, key_name)
diff --git a/nova/crypto.py b/nova/crypto.py
index b05548ea1..1c6fe57ad 100644
--- a/nova/crypto.py
+++ b/nova/crypto.py
@@ -18,7 +18,7 @@
"""
Wrappers around standard crypto, including root and intermediate CAs,
-SSH keypairs and x509 certificates.
+SSH key_pairs and x509 certificates.
"""
import base64
diff --git a/nova/db/api.py b/nova/db/api.py
index 9f6ff99c3..8e418f9f0 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -296,6 +296,34 @@ def instance_update(context, instance_id, values):
return IMPL.instance_update(context, instance_id, values)
+###################
+
+
+def key_pair_create(context, values):
+ """Create a key_pair from the values dictionary."""
+ return IMPL.key_pair_create(context, values)
+
+
+def key_pair_destroy(context, user_id, name):
+ """Destroy the key_pair or raise if it does not exist."""
+ return IMPL.key_pair_destroy(context, user_id, name)
+
+
+def key_pair_destroy_all_by_user(context, user_id):
+ """Destroy all key_pairs by user."""
+ return IMPL.key_pair_destroy_all_by_user(context, user_id)
+
+
+def key_pair_get(context, user_id, name):
+ """Get a key_pair or raise if it does not exist."""
+ return IMPL.key_pair_get(context, user_id, name)
+
+
+def key_pair_get_all_by_user(context, user_id):
+ """Get all key_pairs by user."""
+ return IMPL.key_pair_get_all_by_user(context, user_id)
+
+
####################
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index d612fe669..30c550105 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -462,6 +462,46 @@ def instance_update(_context, instance_id, values):
###################
+def key_pair_create(_context, values):
+ key_pair_ref = models.KeyPair()
+ for (key, value) in values.iteritems():
+ key_pair_ref[key] = value
+ key_pair_ref.save()
+ return key_pair_ref
+
+
+def key_pair_destroy(_context, user_id, name):
+ session = get_session()
+ with session.begin():
+ key_pair_ref = models.KeyPair.find_by_args(user_id,
+ name,
+ session=session)
+ key_pair_ref.delete(session=session)
+
+
+def key_pair_destroy_all_by_user(_context, user_id):
+ session = get_session()
+ with session.begin():
+ # TODO(vish): do we have to use sql here?
+ session.execute('update key_pairs set deleted=1 where user_id=:id',
+ {'id': user_id})
+
+
+def key_pair_get(_context, user_id, name):
+ return models.KeyPair.find_by_args(user_id, name)
+
+
+def key_pair_get_all_by_user(_context, user_id):
+ session = get_session()
+ return session.query(models.KeyPair
+ ).filter_by(user_id=user_id
+ ).filter_by(deleted=False
+ ).all()
+
+
+###################
+
+
def network_count(_context):
return models.Network.count()
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index 41013f41b..f62b79af8 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -323,6 +323,42 @@ class ExportDevice(BASE, NovaBase):
uselist=False))
+class KeyPair(BASE, NovaBase):
+ """Represents a public key pair for ssh"""
+ __tablename__ = 'key_pairs'
+ id = Column(Integer, primary_key=True)
+ name = Column(String(255))
+
+ user_id = Column(String(255))
+
+ fingerprint = Column(String(255))
+ public_key = Column(Text)
+
+ @property
+ def str_id(self):
+ return '%s.%s' % (self.user_id, self.name)
+
+ @classmethod
+ def find_by_str(cls, str_id, session=None, deleted=False):
+ user_id, _sep, name = str_id.partition('.')
+ return cls.find_by_str(user_id, name, session, deleted)
+
+ @classmethod
+ def find_by_args(cls, user_id, name, session=None, deleted=False):
+ if not session:
+ session = get_session()
+ try:
+ return session.query(cls
+ ).filter_by(user_id=user_id
+ ).filter_by(name=name
+ ).filter_by(deleted=deleted
+ ).one()
+ except exc.NoResultFound:
+ new_exc = exception.NotFound("No model for user %s, name %s" %
+ (user_id, name))
+ raise new_exc.__class__, new_exc, sys.exc_info()[2]
+
+
class Network(BASE, NovaBase):
"""Represents a network"""
__tablename__ = 'networks'
diff --git a/nova/flags.py b/nova/flags.py
index ed0baee65..6a1c14490 100644
--- a/nova/flags.py
+++ b/nova/flags.py
@@ -167,6 +167,9 @@ def DECLARE(name, module_string, flag_values=FLAGS):
# Define any app-specific flags in their own files, docs at:
# http://code.google.com/p/python-gflags/source/browse/trunk/gflags.py#39
+DEFINE_list('region_list',
+ [],
+ 'list of region=url pairs separated by commas')
DEFINE_string('connection_type', 'libvirt', 'libvirt, xenapi or fake')
DEFINE_integer('s3_port', 3333, 's3 port')
DEFINE_string('s3_host', '127.0.0.1', 's3 host')
diff --git a/nova/quota.py b/nova/quota.py
index f0e51feeb..edbb83111 100644
--- a/nova/quota.py
+++ b/nova/quota.py
@@ -37,7 +37,7 @@ flags.DEFINE_integer('quota_gigabytes', 1000,
flags.DEFINE_integer('quota_floating_ips', 10,
'number of floating ips allowed per project')
-def _get_quota(context, project_id):
+def get_quota(context, project_id):
rval = {'instances': FLAGS.quota_instances,
'cores': FLAGS.quota_cores,
'volumes': FLAGS.quota_volumes,
@@ -57,7 +57,7 @@ def allowed_instances(context, num_instances, instance_type):
project_id = context.project.id
used_instances, used_cores = db.instance_data_get_for_project(context,
project_id)
- quota = _get_quota(context, project_id)
+ quota = get_quota(context, project_id)
allowed_instances = quota['instances'] - used_instances
allowed_cores = quota['cores'] - used_cores
type_cores = instance_types.INSTANCE_TYPES[instance_type]['vcpus']
@@ -72,9 +72,10 @@ def allowed_volumes(context, num_volumes, size):
project_id = context.project.id
used_volumes, used_gigabytes = db.volume_data_get_for_project(context,
project_id)
- quota = _get_quota(context, project_id)
+ quota = get_quota(context, project_id)
allowed_volumes = quota['volumes'] - used_volumes
allowed_gigabytes = quota['gigabytes'] - used_gigabytes
+ size = int(size)
num_gigabytes = num_volumes * size
allowed_volumes = min(allowed_volumes,
int(allowed_gigabytes // size))
@@ -85,7 +86,7 @@ def allowed_floating_ips(context, num_floating_ips):
"""Check quota and return min(num_floating_ips, allowed_floating_ips)"""
project_id = context.project.id
used_floating_ips = db.floating_ip_count_by_project(context, project_id)
- quota = _get_quota(context, project_id)
+ quota = get_quota(context, project_id)
allowed_floating_ips = quota['floating_ips'] - used_floating_ips
return min(num_floating_ips, allowed_floating_ips)
diff --git a/nova/tests/api_unittest.py b/nova/tests/api_unittest.py
index ffc78e71d..3d2724d12 100644
--- a/nova/tests/api_unittest.py
+++ b/nova/tests/api_unittest.py
@@ -124,7 +124,8 @@ class ApiEc2TestCase(test.BaseTestCase):
for x in range(random.randint(4, 8)))
user = self.manager.create_user('fake', 'fake', 'fake')
project = self.manager.create_project('fake', 'fake', 'fake')
- self.manager.generate_key_pair(user.id, keyname)
+ # NOTE(vish): create depends on pool, so call helper directly
+ cloud._gen_key(None, user.id, keyname)
rv = self.ec2.get_all_key_pairs()
results = [k for k in rv if k.name == keyname]
diff --git a/nova/tests/auth_unittest.py b/nova/tests/auth_unittest.py
index 23c6356d9..3235dea39 100644
--- a/nova/tests/auth_unittest.py
+++ b/nova/tests/auth_unittest.py
@@ -17,8 +17,6 @@
# under the License.
import logging
-from M2Crypto import BIO
-from M2Crypto import RSA
from M2Crypto import X509
import unittest
@@ -65,35 +63,6 @@ class AuthTestCase(test.BaseTestCase):
'export S3_URL="http://127.0.0.1:3333/"\n' +
'export EC2_USER_ID="test1"\n')
- def test_006_test_key_storage(self):
- user = self.manager.get_user('test1')
- user.create_key_pair('public', 'key', 'fingerprint')
- key = user.get_key_pair('public')
- self.assertEqual('key', key.public_key)
- self.assertEqual('fingerprint', key.fingerprint)
-
- def test_007_test_key_generation(self):
- user = self.manager.get_user('test1')
- private_key, fingerprint = user.generate_key_pair('public2')
- key = RSA.load_key_string(private_key, callback=lambda: None)
- bio = BIO.MemoryBuffer()
- public_key = user.get_key_pair('public2').public_key
- key.save_pub_key_bio(bio)
- converted = crypto.ssl_pub_to_ssh_pub(bio.read())
- # assert key fields are equal
- self.assertEqual(public_key.split(" ")[1].strip(),
- converted.split(" ")[1].strip())
-
- def test_008_can_list_key_pairs(self):
- keys = self.manager.get_user('test1').get_key_pairs()
- self.assertTrue(filter(lambda k: k.name == 'public', keys))
- self.assertTrue(filter(lambda k: k.name == 'public2', keys))
-
- def test_009_can_delete_key_pair(self):
- self.manager.get_user('test1').delete_key_pair('public')
- keys = self.manager.get_user('test1').get_key_pairs()
- self.assertFalse(filter(lambda k: k.name == 'public', keys))
-
def test_010_can_list_users(self):
users = self.manager.get_users()
logging.warn(users)
@@ -204,6 +173,12 @@ class AuthTestCase(test.BaseTestCase):
self.assert_(len(self.manager.get_projects()) > 1)
self.assertEqual(len(self.manager.get_projects('test2')), 1)
+ def test_220_can_modify_project(self):
+ self.manager.modify_project('testproj', 'test2', 'new description')
+ project = self.manager.get_project('testproj')
+ self.assertEqual(project.project_manager_id, 'test2')
+ self.assertEqual(project.description, 'new description')
+
def test_299_can_delete_project(self):
self.manager.delete_project('testproj')
self.assertFalse(filter(lambda p: p.name == 'testproj', self.manager.get_projects()))
diff --git a/nova/tests/cloud_unittest.py b/nova/tests/cloud_unittest.py
index afb8ad408..247ccc093 100644
--- a/nova/tests/cloud_unittest.py
+++ b/nova/tests/cloud_unittest.py
@@ -17,13 +17,18 @@
# under the License.
import logging
+from M2Crypto import BIO
+from M2Crypto import RSA
import StringIO
import time
+
from tornado import ioloop
from twisted.internet import defer
import unittest
from xml.etree import ElementTree
+from nova import crypto
+from nova import db
from nova import flags
from nova import rpc
from nova import test
@@ -55,16 +60,21 @@ class CloudTestCase(test.BaseTestCase):
proxy=self.compute)
self.injected.append(self.compute_consumer.attach_to_tornado(self.ioloop))
- try:
- manager.AuthManager().create_user('admin', 'admin', 'admin')
- except: pass
- admin = manager.AuthManager().get_user('admin')
- project = manager.AuthManager().create_project('proj', 'admin', 'proj')
- self.context = context.APIRequestContext(project=project,user=admin)
+ self.manager = manager.AuthManager()
+ self.user = self.manager.create_user('admin', 'admin', 'admin', True)
+ self.project = self.manager.create_project('proj', 'admin', 'proj')
+ self.context = context.APIRequestContext(handler=None,
+ user=self.user,
+ project=self.project)
def tearDown(self):
- manager.AuthManager().delete_project('proj')
- manager.AuthManager().delete_user('admin')
+ self.manager.delete_project(self.project)
+ self.manager.delete_user(self.user)
+ super(CloudTestCase, self).setUp()
+
+ def _create_key(self, name):
+ # NOTE(vish): create depends on pool, so just call helper directly
+ return cloud._gen_key(self.context, self.context.user.id, name)
def test_console_output(self):
if FLAGS.connection_type == 'fake':
@@ -77,6 +87,33 @@ class CloudTestCase(test.BaseTestCase):
self.assert_(output)
rv = yield self.compute.terminate_instance(instance_id)
+
+ def test_key_generation(self):
+ result = self._create_key('test')
+ private_key = result['private_key']
+ key = RSA.load_key_string(private_key, callback=lambda: None)
+ bio = BIO.MemoryBuffer()
+ public_key = db.key_pair_get(self.context,
+ self.context.user.id,
+ 'test')['public_key']
+ key.save_pub_key_bio(bio)
+ converted = crypto.ssl_pub_to_ssh_pub(bio.read())
+ # assert key fields are equal
+ self.assertEqual(public_key.split(" ")[1].strip(),
+ converted.split(" ")[1].strip())
+
+ def test_describe_key_pairs(self):
+ self._create_key('test1')
+ self._create_key('test2')
+ result = self.cloud.describe_key_pairs(self.context)
+ keys = result["keypairsSet"]
+ self.assertTrue(filter(lambda k: k['keyName'] == 'test1', keys))
+ self.assertTrue(filter(lambda k: k['keyName'] == 'test2', keys))
+
+ def test_delete_key_pair(self):
+ self._create_key('test')
+ self.cloud.delete_key_pair(self.context, 'test')
+
def test_run_instances(self):
if FLAGS.connection_type == 'fake':
logging.debug("Can't test instances without a real virtual env.")