diff options
| author | Ewan Mellor <ewan.mellor@citrix.com> | 2010-07-29 22:06:17 +0100 |
|---|---|---|
| committer | Ewan Mellor <ewan.mellor@citrix.com> | 2010-07-29 22:06:17 +0100 |
| commit | e588b82a991107720137d21d89f0fb24f55fdf50 (patch) | |
| tree | 057ef6bedf567ff072af12358e1e3b1cbd2c97c5 | |
| parent | 04a6a0267e7dc0f4e587e43f23b4acf0dcef52fc (diff) | |
| parent | 73a47dfecf4b1ba66a45421bbd925f3e0db054c5 (diff) | |
| download | nova-e588b82a991107720137d21d89f0fb24f55fdf50.tar.gz nova-e588b82a991107720137d21d89f0fb24f55fdf50.tar.xz nova-e588b82a991107720137d21d89f0fb24f55fdf50.zip | |
Merged with trunk.
| -rw-r--r-- | .bzrignore | 1 | ||||
| -rw-r--r-- | MANIFEST.in | 1 | ||||
| -rw-r--r-- | doc/source/conf.py | 4 | ||||
| -rw-r--r-- | nova/adminclient.py | 163 | ||||
| -rw-r--r-- | nova/auth/manager.py | 24 | ||||
| -rw-r--r-- | nova/endpoint/admin.py | 74 | ||||
| -rw-r--r-- | nova/endpoint/cloud.py | 8 | ||||
| -rw-r--r-- | nova/process.py | 12 | ||||
| -rw-r--r-- | nova/tests/process_unittest.py | 11 | ||||
| -rw-r--r-- | nova/utils.py | 10 | ||||
| -rw-r--r-- | setup.py | 25 |
11 files changed, 295 insertions, 38 deletions
diff --git a/.bzrignore b/.bzrignore index c3a502a1a..ab099d3e3 100644 --- a/.bzrignore +++ b/.bzrignore @@ -1,2 +1,3 @@ run_tests.err.log .nova-venv +ChangeLog diff --git a/MANIFEST.in b/MANIFEST.in index e917077c5..4eb28bde6 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,6 @@ include HACKING LICENSE run_tests.py run_tests.sh include README builddeb.sh exercise_rsapi.py +include ChangeLog graft CA graft doc graft smoketests diff --git a/doc/source/conf.py b/doc/source/conf.py index 1c1ae7f48..349d23af2 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -47,9 +47,9 @@ copyright = u'2010, United States Government as represented by the Administrator # built documents. # # The short X.Y version. -version = '0.42' +version = '0.9' # The full version, including alpha/beta/rc tags. -release = '0.42' +release = '0.9.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/nova/adminclient.py b/nova/adminclient.py index db392feb1..fceeac274 100644 --- a/nova/adminclient.py +++ b/nova/adminclient.py @@ -23,6 +23,7 @@ import base64 import boto from boto.ec2.regioninfo import RegionInfo + class UserInfo(object): """ Information about a Nova user, as parsed through SAX @@ -56,6 +57,64 @@ class UserInfo(object): elif name == 'secretkey': self.secretkey = str(value) +class ProjectInfo(object): + """ + Information about a Nova project, as parsed through SAX + Fields include: + projectname + description + projectManagerId + memberIds + """ + + def __init__(self, connection=None): + self.connection = connection + self.projectname = None + self.description = None + self.projectManagerId = None + self.memberIds = [] + + def __repr__(self): + return 'ProjectInfo:%s' % self.projectname + + def startElement(self, name, attrs, connection): + return None + + def endElement(self, name, value, connection): + if name == 'projectname': + self.projectname = value + elif name == 'description': + self.description = value + elif name == 'projectManagerId': + self.projectManagerId = value + elif name == 'memberId': + self.memberIds.append(value) + else: + setattr(self, name, str(value)) + +class ProjectMember(object): + """ + Information about a Nova project member, as parsed through SAX. + Fields include: + memberId + """ + def __init__(self, connection=None): + self.connection = connection + self.memberId = None + + def __repr__(self): + return 'ProjectMember:%s' % self.memberId + + def startElement(self, name, attrs, connection): + return None + + def endElement(self, name, value, connection): + if name == 'member': + self.memberId = value + else: + setattr(self, name, str(value)) + + class HostInfo(object): """ Information about a Nova Host, as parsed through SAX: @@ -99,20 +158,20 @@ class NovaAdminClient(object): **kwargs) self.apiconn.APIVersion = 'nova' - def connection_for(self, username, **kwargs): + def connection_for(self, username, project, **kwargs): """ Returns a boto ec2 connection for the given username. """ user = self.get_user(username) + access_key = '%s:%s' % (user.accesskey, project) return boto.connect_ec2( - aws_access_key_id=user.accesskey, + aws_access_key_id=access_key, aws_secret_access_key=user.secretkey, is_secure=False, region=RegionInfo(None, self.region, self.clc_ip), port=8773, path='/services/Cloud', - **kwargs - ) + **kwargs) def get_users(self): """ grabs the list of all users """ @@ -137,6 +196,102 @@ class NovaAdminClient(object): """ deletes a user """ return self.apiconn.get_object('DeregisterUser', {'Name': username}, UserInfo) + def add_user_role(self, user, role, project=None): + """ + Add a role to a user either globally or for a specific project. + """ + return self.modify_user_role(user, role, project=project, + operation='add') + + def remove_user_role(self, user, role, project=None): + """ + Remove a role from a user either globally or for a specific project. + """ + return self.modify_user_role(user, role, project=project, + operation='remove') + + def modify_user_role(self, user, role, project=None, operation='add', + **kwargs): + """ + Add or remove a role for a user and project. + """ + params = {'User': user, + 'Role': role, + 'Project': project, + 'Operation': operation} + return self.apiconn.get_status('ModifyUserRole', params) + + def get_projects(self, user=None): + """ + Returns a list of all projects. + """ + if user: + params = {'User': user} + else: + params = {} + return self.apiconn.get_list('DescribeProjects', + params, + [('item', ProjectInfo)]) + + def get_project(self, name): + """ + Returns a single project with the specified name. + """ + project = self.apiconn.get_object('DescribeProject', + {'Name': name}, + ProjectInfo) + + if project.projectname != None: + return project + + def create_project(self, projectname, manager_user, description=None, + member_users=None): + """ + Creates a new project. + """ + params = {'Name': projectname, + 'ManagerUser': manager_user, + 'Description': description, + 'MemberUsers': member_users} + return self.apiconn.get_object('RegisterProject', params, ProjectInfo) + + def delete_project(self, projectname): + """ + Permanently deletes the specified project. + """ + return self.apiconn.get_object('DeregisterProject', + {'Name': projectname}, + ProjectInfo) + + def get_project_members(self, name): + """ + Returns a list of members of a project. + """ + return self.apiconn.get_list('DescribeProjectMembers', + {'Name': name}, + [('item', ProjectMember)]) + + def add_project_member(self, user, project): + """ + Adds a user to a project. + """ + return self.modify_project_member(user, project, operation='add') + + def remove_project_member(self, user, project): + """ + Removes a user from a project. + """ + return self.modify_project_member(user, project, operation='remove') + + def modify_project_member(self, user, project, operation='add'): + """ + Adds or removes a user from a project. + """ + params = {'User': user, + 'Project': project, + 'Operation': operation} + return self.apiconn.get_status('ModifyProjectMember', params) + def get_zip(self, username): """ returns the content of a zip file containing novarc and access credentials. """ return self.apiconn.get_object('GenerateX509ForUser', {'Name': username}, UserInfo).file diff --git a/nova/auth/manager.py b/nova/auth/manager.py index 7307f673b..2da53a736 100644 --- a/nova/auth/manager.py +++ b/nova/auth/manager.py @@ -323,25 +323,21 @@ class AuthManager(object): """ _instance=None def __new__(cls, *args, **kwargs): - """Returns the AuthManager singleton with driver set - - __init__ is run every time AuthManager() is called, so we need to do - any constructor related stuff here. The driver that is specified - in the flagfile is loaded here. - """ + """Returns the AuthManager singleton""" if not cls._instance: cls._instance = super(AuthManager, cls).__new__( cls, *args, **kwargs) - mod_str, sep, driver_str = FLAGS.auth_driver.rpartition('.') - try: - __import__(mod_str) - cls._instance.driver = getattr(sys.modules[mod_str], - driver_str) - except (ImportError, AttributeError): - raise exception.Error('Auth driver %s cannot be found' - % FLAGS.auth_driver) return cls._instance + def __init__(self, driver=None, *args, **kwargs): + """Inits the driver from parameter or flag + + __init__ is run every time AuthManager() is called, so we only + reset the driver if it is not set or a new driver is specified. + """ + if driver or not getattr(self, 'driver', None): + self.driver = utils.import_class(driver or FLAGS.auth_driver) + def authenticate(self, access, signature, params, verb='GET', server_string='127.0.0.1:8773', path='/', check_type='ec2', headers=None): diff --git a/nova/endpoint/admin.py b/nova/endpoint/admin.py index 55a8e4238..c4b8c05ca 100644 --- a/nova/endpoint/admin.py +++ b/nova/endpoint/admin.py @@ -25,6 +25,7 @@ import base64 from nova.auth import manager from nova.compute import model + def user_dict(user, base64_file=None): """Convert the user object to a result dict""" if user: @@ -32,8 +33,17 @@ def user_dict(user, base64_file=None): 'username': user.id, 'accesskey': user.access, 'secretkey': user.secret, - 'file': base64_file, - } + 'file': base64_file} + else: + return {} + +def project_dict(project): + """Convert the project object to a result dict""" + if project: + return { + 'projectname': project.id, + 'project_manager_id': project.project_manager_id, + 'description': project.description} else: return {} @@ -93,6 +103,19 @@ class AdminController(object): return True @admin_only + def modify_user_role(self, context, user, role, project=None, + operation='add', **kwargs): + """Add or remove a role for a user and project.""" + if operation == 'add': + manager.AuthManager().add_role(user, role, project) + elif operation == 'remove': + manager.AuthManager().remove_role(user, role, project) + else: + raise exception.ApiError('operation must be add or remove') + + return True + + @admin_only def generate_x509_for_user(self, _context, name, project=None, **kwargs): """Generates and returns an x509 certificate for a single user. Is usually called from a client that will wrap this with @@ -105,6 +128,53 @@ class AdminController(object): return user_dict(user, base64.b64encode(project.get_credentials(user))) @admin_only + def describe_project(self, context, name, **kwargs): + """Returns project data, including member ids.""" + return project_dict(manager.AuthManager().get_project(name)) + + @admin_only + def describe_projects(self, context, user=None, **kwargs): + """Returns all projects - should be changed to deal with a list.""" + return {'projectSet': + [project_dict(u) for u in + manager.AuthManager().get_projects(user=user)]} + + @admin_only + def register_project(self, context, name, manager_user, description=None, + member_users=None, **kwargs): + """Creates a new project""" + return project_dict( + manager.AuthManager().create_project( + name, + manager_user, + description=None, + member_users=None)) + + @admin_only + def deregister_project(self, context, name): + """Permanently deletes a project.""" + manager.AuthManager().delete_project(name) + return True + + @admin_only + def describe_project_members(self, context, name, **kwargs): + project = manager.AuthManager().get_project(name) + result = { + 'members': [{'member': m} for m in project.member_ids]} + return result + + @admin_only + def modify_project_member(self, context, user, project, operation, **kwargs): + """Add or remove a user from a project.""" + if operation =='add': + manager.AuthManager().add_to_project(user, project) + elif operation == 'remove': + manager.AuthManager().remove_from_project(user, project) + else: + raise exception.ApiError('operation must be add or remove') + return True + + @admin_only def describe_hosts(self, _context, **_kwargs): """Returns status info for all nodes. Includes: * Disk Space diff --git a/nova/endpoint/cloud.py b/nova/endpoint/cloud.py index 8a4edbc0b..67fc04502 100644 --- a/nova/endpoint/cloud.py +++ b/nova/endpoint/cloud.py @@ -519,14 +519,14 @@ class CloudController(object): kernel_id = image.get('kernelId', FLAGS.default_kernel) ramdisk_id = image.get('ramdiskId', FLAGS.default_ramdisk) - # make sure we have access to kernel and ramdisk - self._get_image(context, kernel_id) - self._get_image(context, ramdisk_id) - # API parameters overrides of defaults kernel_id = kwargs.get('kernel_id', kernel_id) ramdisk_id = kwargs.get('ramdisk_id', ramdisk_id) + # make sure we have access to kernel and ramdisk + self._get_image(context, kernel_id) + self._get_image(context, ramdisk_id) + logging.debug("Going to run instances...") reservation_id = utils.generate_uid('r') launch_time = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()) diff --git a/nova/process.py b/nova/process.py index d3558ed2e..2dc56372f 100644 --- a/nova/process.py +++ b/nova/process.py @@ -205,13 +205,13 @@ class ProcessPool(object): self._pool.release() return rv -class SharedPool(ProcessPool): +class SharedPool(object): _instance = None - def __new__(cls, *args, **kwargs): - if not cls._instance: - cls._instance = super(SharedPool, cls).__new__( - cls, *args, **kwargs) - return cls._instance + def __init__(self): + if SharedPool._instance is None: + self.__class__._instance = ProcessPool() + def __getattr__(self, key): + return getattr(self._instance, key) def simple_execute(cmd, **kwargs): return SharedPool().simple_execute(cmd, **kwargs) diff --git a/nova/tests/process_unittest.py b/nova/tests/process_unittest.py index 1c15b69a0..75187e1fc 100644 --- a/nova/tests/process_unittest.py +++ b/nova/tests/process_unittest.py @@ -118,5 +118,12 @@ class ProcessTestCase(test.TrialTestCase): def test_shared_pool_is_singleton(self): pool1 = process.SharedPool() pool2 = process.SharedPool() - self.assert_(id(pool1) == id(pool2)) - + self.assertEqual(id(pool1._instance), id(pool2._instance)) + + def test_shared_pool_works_as_singleton(self): + d1 = process.simple_execute('sleep 1') + d2 = process.simple_execute('sleep 0.005') + # lp609749: would have failed with + # exceptions.AssertionError: Someone released me too many times: + # too many tokens! + return d1 diff --git a/nova/utils.py b/nova/utils.py index a1eb0a092..0016b656e 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -29,11 +29,21 @@ import subprocess import socket import sys +from nova import exception from nova import flags FLAGS = flags.FLAGS TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ" +def import_class(import_str): + """Returns a class from a string including module and class""" + mod_str, _sep, class_str = import_str.rpartition('.') + try: + __import__(mod_str) + return getattr(sys.modules[mod_str], class_str) + except (ImportError, AttributeError): + raise exception.NotFound('Class %s cannot be found' % class_str) + def fetchfile(url, target): logging.debug("Fetching %s" % url) # c = pycurl.Curl() @@ -17,6 +17,24 @@ # under the License. from setuptools import setup, find_packages +from setuptools.command.sdist import sdist + +import os +import subprocess + + +class local_sdist(sdist): + """Customized sdist hook - builds the ChangeLog file from VC first""" + + def run(self): + if os.path.isdir('.bzr'): + # We're in a bzr branch + log_cmd = subprocess.Popen(["bzr", "log", "--gnu"], + stdout=subprocess.PIPE) + changelog = log_cmd.communicate()[0] + with open("ChangeLog", "w") as changelog_file: + changelog_file.write(changelog) + sdist.run(self) setup(name='nova', version='0.9.1', @@ -24,7 +42,8 @@ setup(name='nova', author='OpenStack', author_email='nova@lists.launchpad.net', url='http://www.openstack.org/', - packages = find_packages(exclude=['bin','smoketests']), + cmdclass={'sdist': local_sdist}, + packages=find_packages(exclude=['bin', 'smoketests']), scripts=['bin/nova-api', 'bin/nova-compute', 'bin/nova-dhcpbridge', @@ -34,6 +53,4 @@ setup(name='nova', 'bin/nova-network', 'bin/nova-objectstore', 'bin/nova-rsapi', - 'bin/nova-volume', - ] - ) + 'bin/nova-volume']) |
