summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjaypipes@gmail.com <>2010-10-04 16:51:03 -0400
committerjaypipes@gmail.com <>2010-10-04 16:51:03 -0400
commitf8c64fba51c1ca93c612d84bb9f303c8f2ff2fed (patch)
tree1971c05189888135699ee1ad7c8aa05481160ce5
parent32bd6c198a4ed96768649f58628e22fb25a95855 (diff)
parent44d9fac26aff296f2846b00df98ddaf646c52158 (diff)
downloadnova-f8c64fba51c1ca93c612d84bb9f303c8f2ff2fed.tar.gz
nova-f8c64fba51c1ca93c612d84bb9f303c8f2ff2fed.tar.xz
nova-f8c64fba51c1ca93c612d84bb9f303c8f2ff2fed.zip
Merge trunk
-rwxr-xr-xbin/nova-manage7
-rw-r--r--nova/auth/dbdriver.py236
-rw-r--r--nova/auth/manager.py2
-rw-r--r--nova/db/api.py119
-rw-r--r--nova/db/sqlalchemy/api.py223
-rw-r--r--nova/db/sqlalchemy/models.py73
-rw-r--r--nova/network/linux_net.py50
-rw-r--r--nova/network/manager.py10
-rw-r--r--nova/service.py12
-rw-r--r--nova/tests/auth_unittest.py9
-rw-r--r--nova/tests/fake_flags.py2
-rw-r--r--nova/tests/network_unittest.py4
-rw-r--r--nova/tests/scheduler_unittest.py10
-rw-r--r--nova/tests/service_unittest.py3
-rw-r--r--nova/virt/xenapi.py33
15 files changed, 741 insertions, 52 deletions
diff --git a/bin/nova-manage b/bin/nova-manage
index bf3c67612..5b72c170f 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -52,6 +52,7 @@
CLI interface for nova management.
"""
+import logging
import os
import sys
import time
@@ -114,7 +115,7 @@ class VpnCommands(object):
def _vpn_for(self, project_id):
"""Get the VPN instance for a project ID."""
- for instance in db.instance_get_all():
+ for instance in db.instance_get_all(None):
if (instance['image_id'] == FLAGS.vpn_image_id
and not instance['state_description'] in
['shutting_down', 'shutdown']
@@ -417,6 +418,10 @@ def main():
"""Parse options and call the appropriate class/method."""
utils.default_flagfile('/etc/nova/nova-manage.conf')
argv = FLAGS(sys.argv)
+
+ if FLAGS.verbose:
+ logging.getLogger().setLevel(logging.DEBUG)
+
script_name = argv.pop(0)
if len(argv) < 1:
print script_name + " category action [<args>]"
diff --git a/nova/auth/dbdriver.py b/nova/auth/dbdriver.py
new file mode 100644
index 000000000..09d15018b
--- /dev/null
+++ b/nova/auth/dbdriver.py
@@ -0,0 +1,236 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 United States Government as represented by the
+# Administrator of the National Aeronautics and Space Administration.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""
+Auth driver using the DB as its backend.
+"""
+
+import logging
+import sys
+
+from nova import exception
+from nova import db
+
+
+class DbDriver(object):
+ """DB Auth driver
+
+ Defines enter and exit and therefore supports the with/as syntax.
+ """
+
+ def __init__(self):
+ """Imports the LDAP module"""
+ pass
+ db
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ pass
+
+ def get_user(self, uid):
+ """Retrieve user by id"""
+ return self._db_user_to_auth_user(db.user_get({}, uid))
+
+ def get_user_from_access_key(self, access):
+ """Retrieve user by access key"""
+ return self._db_user_to_auth_user(db.user_get_by_access_key({}, access))
+
+ def get_project(self, pid):
+ """Retrieve project by id"""
+ return self._db_project_to_auth_projectuser(db.project_get({}, pid))
+
+ def get_users(self):
+ """Retrieve list of users"""
+ return [self._db_user_to_auth_user(user) for user in db.user_get_all({})]
+
+ def get_projects(self, uid=None):
+ """Retrieve list of projects"""
+ if uid:
+ result = db.project_get_by_user({}, uid)
+ else:
+ result = db.project_get_all({})
+ return [self._db_project_to_auth_projectuser(proj) for proj in result]
+
+ def create_user(self, name, access_key, secret_key, is_admin):
+ """Create a user"""
+ values = { 'id' : name,
+ 'access_key' : access_key,
+ 'secret_key' : secret_key,
+ 'is_admin' : is_admin
+ }
+ try:
+ user_ref = db.user_create({}, values)
+ return self._db_user_to_auth_user(user_ref)
+ except exception.Duplicate, e:
+ raise exception.Duplicate('User %s already exists' % name)
+
+ def _db_user_to_auth_user(self, user_ref):
+ return { 'id' : user_ref['id'],
+ 'name' : user_ref['id'],
+ 'access' : user_ref['access_key'],
+ 'secret' : user_ref['secret_key'],
+ 'admin' : user_ref['is_admin'] }
+
+ def _db_project_to_auth_projectuser(self, project_ref):
+ return { 'id' : project_ref['id'],
+ 'name' : project_ref['name'],
+ 'project_manager_id' : project_ref['project_manager'],
+ 'description' : project_ref['description'],
+ 'member_ids' : [member['id'] for member in project_ref['members']] }
+
+ def create_project(self, name, manager_uid,
+ description=None, member_uids=None):
+ """Create a project"""
+ manager = db.user_get({}, manager_uid)
+ if not manager:
+ raise exception.NotFound("Project can't be created because "
+ "manager %s doesn't exist" % manager_uid)
+
+ # description is a required attribute
+ if description is None:
+ description = name
+
+ # First, we ensure that all the given users exist before we go
+ # on to create the project. This way we won't have to destroy
+ # the project again because a user turns out to be invalid.
+ members = set([manager])
+ if member_uids != None:
+ for member_uid in member_uids:
+ member = db.user_get({}, member_uid)
+ if not member:
+ raise exception.NotFound("Project can't be created "
+ "because user %s doesn't exist"
+ % member_uid)
+ members.add(member)
+
+ values = { 'id' : name,
+ 'name' : name,
+ 'project_manager' : manager['id'],
+ 'description': description }
+
+ try:
+ project = db.project_create({}, values)
+ except exception.Duplicate:
+ raise exception.Duplicate("Project can't be created because "
+ "project %s already exists" % name)
+
+ for member in members:
+ db.project_add_member({}, project['id'], member['id'])
+
+ # This looks silly, but ensures that the members element has been
+ # correctly populated
+ project_ref = db.project_get({}, project['id'])
+ return self._db_project_to_auth_projectuser(project_ref)
+
+ def modify_project(self, project_id, manager_uid=None, description=None):
+ """Modify an existing project"""
+ if not manager_uid and not description:
+ return
+ values = {}
+ if manager_uid:
+ manager = db.user_get({}, manager_uid)
+ if not manager:
+ raise exception.NotFound("Project can't be modified because "
+ "manager %s doesn't exist" %
+ manager_uid)
+ values['project_manager'] = manager['id']
+ if description:
+ values['description'] = description
+
+ db.project_update({}, project_id, values)
+
+ def add_to_project(self, uid, project_id):
+ """Add user to project"""
+ user, project = self._validate_user_and_project(uid, project_id)
+ db.project_add_member({}, project['id'], user['id'])
+
+ def remove_from_project(self, uid, project_id):
+ """Remove user from project"""
+ user, project = self._validate_user_and_project(uid, project_id)
+ db.project_remove_member({}, project['id'], user['id'])
+
+ def is_in_project(self, uid, project_id):
+ """Check if user is in project"""
+ user, project = self._validate_user_and_project(uid, project_id)
+ return user in project.members
+
+ def has_role(self, uid, role, project_id=None):
+ """Check if user has role
+
+ If project is specified, it checks for local role, otherwise it
+ checks for global role
+ """
+
+ return role in self.get_user_roles(uid, project_id)
+
+ def add_role(self, uid, role, project_id=None):
+ """Add role for user (or user and project)"""
+ if not project_id:
+ db.user_add_role({}, uid, role)
+ return
+ db.user_add_project_role({}, uid, project_id, role)
+
+ def remove_role(self, uid, role, project_id=None):
+ """Remove role for user (or user and project)"""
+ if not project_id:
+ db.user_remove_role({}, uid, role)
+ return
+ db.user_remove_project_role({}, uid, project_id, role)
+
+ def get_user_roles(self, uid, project_id=None):
+ """Retrieve list of roles for user (or user and project)"""
+ if project_id is None:
+ roles = db.user_get_roles({}, uid)
+ return roles
+ else:
+ roles = db.user_get_roles_for_project({}, uid, project_id)
+ return roles
+
+ def delete_user(self, id):
+ """Delete a user"""
+ user = db.user_get({}, id)
+ db.user_delete({}, user['id'])
+
+ def delete_project(self, project_id):
+ """Delete a project"""
+ db.project_delete({}, project_id)
+
+ def modify_user(self, uid, access_key=None, secret_key=None, admin=None):
+ """Modify an existing user"""
+ if not access_key and not secret_key and admin is None:
+ return
+ values = {}
+ if access_key:
+ values['access_key'] = access_key
+ if secret_key:
+ values['secret_key'] = secret_key
+ if admin is not None:
+ values['is_admin'] = admin
+ db.user_update({}, uid, values)
+
+ def _validate_user_and_project(self, user_id, project_id):
+ user = db.user_get({}, user_id)
+ if not user:
+ raise exception.NotFound('User "%s" not found' % user_id)
+ project = db.project_get({}, project_id)
+ if not project:
+ raise exception.NotFound('Project "%s" not found' % project_id)
+ return user, project
+
diff --git a/nova/auth/manager.py b/nova/auth/manager.py
index 0bc12c80f..ce8a294df 100644
--- a/nova/auth/manager.py
+++ b/nova/auth/manager.py
@@ -69,7 +69,7 @@ flags.DEFINE_string('credential_cert_subject',
'/C=US/ST=California/L=MountainView/O=AnsoLabs/'
'OU=NovaDev/CN=%s-%s',
'Subject for certificate for users')
-flags.DEFINE_string('auth_driver', 'nova.auth.ldapdriver.FakeLdapDriver',
+flags.DEFINE_string('auth_driver', 'nova.auth.dbdriver.DbDriver',
'Driver that auth manager uses')
diff --git a/nova/db/api.py b/nova/db/api.py
index 5c935b561..a6d1f405a 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -565,3 +565,122 @@ def volume_update(context, volume_id, values):
"""
return IMPL.volume_update(context, volume_id, values)
+
+
+###################
+
+
+def user_get(context, id):
+ """Get user by id"""
+ return IMPL.user_get(context, id)
+
+
+def user_get_by_uid(context, uid):
+ """Get user by uid"""
+ return IMPL.user_get_by_uid(context, uid)
+
+
+def user_get_by_access_key(context, access_key):
+ """Get user by access key"""
+ return IMPL.user_get_by_access_key(context, access_key)
+
+
+def user_create(context, values):
+ """Create a new user"""
+ return IMPL.user_create(context, values)
+
+
+def user_delete(context, id):
+ """Delete a user"""
+ return IMPL.user_delete(context, id)
+
+
+def user_get_all(context):
+ """Create a new user"""
+ return IMPL.user_get_all(context)
+
+
+def user_add_role(context, user_id, role):
+ """Add another global role for user"""
+ return IMPL.user_add_role(context, user_id, role)
+
+
+def user_remove_role(context, user_id, role):
+ """Remove global role from user"""
+ return IMPL.user_remove_role(context, user_id, role)
+
+
+def user_get_roles(context, user_id):
+ """Get global roles for user"""
+ return IMPL.user_get_roles(context, user_id)
+
+
+def user_add_project_role(context, user_id, project_id, role):
+ """Add project role for user"""
+ return IMPL.user_add_project_role(context, user_id, project_id, role)
+
+
+def user_remove_project_role(context, user_id, project_id, role):
+ """Remove project role from user"""
+ return IMPL.user_remove_project_role(context, user_id, project_id, role)
+
+
+def user_get_roles_for_project(context, user_id, project_id):
+ """Return list of roles a user holds on project"""
+ return IMPL.user_get_roles_for_project(context, user_id, project_id)
+
+
+def user_update(context, user_id, values):
+ """Update user"""
+ return IMPL.user_update(context, user_id, values)
+
+
+def project_get(context, id):
+ """Get project by id"""
+ return IMPL.project_get(context, id)
+
+
+def project_create(context, values):
+ """Create a new project"""
+ return IMPL.project_create(context, values)
+
+
+def project_add_member(context, project_id, user_id):
+ """Add user to project"""
+ return IMPL.project_add_member(context, project_id, user_id)
+
+
+def project_get_all(context):
+ """Get all projects"""
+ return IMPL.project_get_all(context)
+
+
+def project_get_by_user(context, user_id):
+ """Get all projects of which the given user is a member"""
+ return IMPL.project_get_by_user(context, user_id)
+
+
+def project_remove_member(context, project_id, user_id):
+ """Remove the given user from the given project"""
+ return IMPL.project_remove_member(context, project_id, user_id)
+
+
+def project_update(context, project_id, values):
+ """Update Remove the given user from the given project"""
+ return IMPL.project_update(context, project_id, values)
+
+
+def project_delete(context, project_id):
+ """Delete project"""
+ return IMPL.project_delete(context, project_id)
+
+
+###################
+
+
+def host_get_networks(context, host):
+ """Return all networks for which the given host is the designated
+ network host
+ """
+ return IMPL.host_get_networks(context, host)
+
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 7f72f66b9..e0c6a34b8 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -240,7 +240,7 @@ def service_create(context, values):
def service_update(context, service_id, values):
session = get_session()
with session.begin():
- service_ref = session_get(context, service_id, session=session)
+ service_ref = service_get(context, service_id, session=session)
for (key, value) in values.iteritems():
service_ref[key] = value
service_ref.save(session=session)
@@ -1273,3 +1273,224 @@ def volume_update(context, volume_id, values):
for (key, value) in values.iteritems():
volume_ref[key] = value
volume_ref.save(session=session)
+
+
+###################
+
+
+@require_admin_context
+def user_get(context, id, session=None):
+ if not session:
+ session = get_session()
+
+ result = session.query(models.User
+ ).filter_by(id=id
+ ).filter_by(deleted=can_read_deleted(context)
+ ).first()
+
+ if not result:
+ raise exception.NotFound('No user for id %s' % id)
+
+ return result
+
+
+@require_admin_context
+def user_get_by_access_key(context, access_key, session=None):
+ if not session:
+ session = get_session()
+
+ result = session.query(models.User
+ ).filter_by(access_key=access_key
+ ).filter_by(deleted=can_read_deleted(context)
+ ).first()
+
+ if not result:
+ raise exception.NotFound('No user for id %s' % id)
+
+ return result
+
+
+@require_admin_context
+def user_create(_context, values):
+ user_ref = models.User()
+ for (key, value) in values.iteritems():
+ user_ref[key] = value
+ user_ref.save()
+ return user_ref
+
+
+@require_admin_context
+def user_delete(context, id):
+ session = get_session()
+ with session.begin():
+ session.execute('delete from user_project_association where user_id=:id',
+ {'id': id})
+ session.execute('delete from user_role_association where user_id=:id',
+ {'id': id})
+ session.execute('delete from user_project_role_association where user_id=:id',
+ {'id': id})
+ user_ref = user_get(context, id, session=session)
+ session.delete(user_ref)
+
+
+def user_get_all(context):
+ session = get_session()
+ return session.query(models.User
+ ).filter_by(deleted=can_read_deleted(context)
+ ).all()
+
+
+def project_create(_context, values):
+ project_ref = models.Project()
+ for (key, value) in values.iteritems():
+ project_ref[key] = value
+ project_ref.save()
+ return project_ref
+
+
+def project_add_member(context, project_id, user_id):
+ session = get_session()
+ with session.begin():
+ project_ref = project_get(context, project_id, session=session)
+ user_ref = user_get(context, user_id, session=session)
+
+ project_ref.members += [user_ref]
+ project_ref.save(session=session)
+
+
+def project_get(context, id, session=None):
+ if not session:
+ session = get_session()
+
+ result = session.query(models.Project
+ ).filter_by(deleted=False
+ ).filter_by(id=id
+ ).options(joinedload_all('members')
+ ).first()
+
+ if not result:
+ raise exception.NotFound("No project with id %s" % id)
+
+ return result
+
+
+def project_get_all(context):
+ session = get_session()
+ return session.query(models.Project
+ ).filter_by(deleted=can_read_deleted(context)
+ ).options(joinedload_all('members')
+ ).all()
+
+
+def project_get_by_user(context, user_id):
+ session = get_session()
+ user = session.query(models.User
+ ).filter_by(deleted=can_read_deleted(context)
+ ).options(joinedload_all('projects')
+ ).first()
+ return user.projects
+
+
+def project_remove_member(context, project_id, user_id):
+ session = get_session()
+ project = project_get(context, project_id, session=session)
+ user = user_get(context, user_id, session=session)
+
+ if user in project.members:
+ project.members.remove(user)
+ project.save(session=session)
+
+
+def user_update(context, user_id, values):
+ session = get_session()
+ with session.begin():
+ user_ref = user_get(context, user_id, session=session)
+ for (key, value) in values.iteritems():
+ user_ref[key] = value
+ user_ref.save(session=session)
+
+
+def project_update(context, project_id, values):
+ session = get_session()
+ with session.begin():
+ project_ref = project_get(context, project_id, session=session)
+ for (key, value) in values.iteritems():
+ project_ref[key] = value
+ project_ref.save(session=session)
+
+
+def project_delete(context, id):
+ session = get_session()
+ with session.begin():
+ session.execute('delete from user_project_association where project_id=:id',
+ {'id': id})
+ session.execute('delete from user_project_role_association where project_id=:id',
+ {'id': id})
+ project_ref = project_get(context, id, session=session)
+ session.delete(project_ref)
+
+
+def user_get_roles(context, user_id):
+ session = get_session()
+ with session.begin():
+ user_ref = user_get(context, user_id, session=session)
+ return [role.role for role in user_ref['roles']]
+
+
+def user_get_roles_for_project(context, user_id, project_id):
+ session = get_session()
+ with session.begin():
+ res = session.query(models.UserProjectRoleAssociation
+ ).filter_by(user_id=user_id
+ ).filter_by(project_id=project_id
+ ).all()
+ return [association.role for association in res]
+
+def user_remove_project_role(context, user_id, project_id, role):
+ session = get_session()
+ with session.begin():
+ session.execute('delete from user_project_role_association where ' + \
+ 'user_id=:user_id and project_id=:project_id and ' + \
+ 'role=:role', { 'user_id' : user_id,
+ 'project_id' : project_id,
+ 'role' : role })
+
+
+def user_remove_role(context, user_id, role):
+ session = get_session()
+ with session.begin():
+ res = session.query(models.UserRoleAssociation
+ ).filter_by(user_id=user_id
+ ).filter_by(role=role
+ ).all()
+ for role in res:
+ session.delete(role)
+
+
+def user_add_role(context, user_id, role):
+ session = get_session()
+ with session.begin():
+ user_ref = user_get(context, user_id, session=session)
+ models.UserRoleAssociation(user=user_ref, role=role).save(session=session)
+
+
+def user_add_project_role(context, user_id, project_id, role):
+ session = get_session()
+ with session.begin():
+ user_ref = user_get(context, user_id, session=session)
+ project_ref = project_get(context, project_id, session=session)
+ models.UserProjectRoleAssociation(user_id=user_ref['id'],
+ project_id=project_ref['id'],
+ role=role).save(session=session)
+
+
+###################
+
+
+def host_get_networks(context, host):
+ session = get_session()
+ with session.begin():
+ return session.query(models.Network
+ ).filter_by(deleted=False
+ ).filter_by(host=host
+ ).all()
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index 1837a7584..673c8e94f 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -27,7 +27,9 @@ import datetime
from sqlalchemy.orm import relationship, backref, exc, object_mapper
from sqlalchemy import Column, Integer, String
from sqlalchemy import ForeignKey, DateTime, Boolean, Text
+from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.schema import ForeignKeyConstraint
from nova.db.sqlalchemy.session import get_session
@@ -60,7 +62,13 @@ class NovaBase(object):
if not session:
session = get_session()
session.add(self)
- session.flush()
+ try:
+ session.flush()
+ except IntegrityError, e:
+ if str(e).endswith('is not unique'):
+ raise exception.Duplicate(str(e))
+ else:
+ raise
def delete(self, session=None):
"""Delete this object"""
@@ -374,6 +382,67 @@ class FixedIp(BASE, NovaBase):
return self.address
+class User(BASE, NovaBase):
+ """Represents a user"""
+ __tablename__ = 'users'
+ id = Column(String(255), primary_key=True)
+
+ name = Column(String(255))
+ access_key = Column(String(255))
+ secret_key = Column(String(255))
+
+ is_admin = Column(Boolean)
+
+
+class Project(BASE, NovaBase):
+ """Represents a project"""
+ __tablename__ = 'projects'
+ id = Column(String(255), primary_key=True)
+ name = Column(String(255))
+ description = Column(String(255))
+
+ project_manager = Column(String(255), ForeignKey(User.id))
+
+ members = relationship(User,
+ secondary='user_project_association',
+ backref='projects')
+
+
+class UserProjectRoleAssociation(BASE, NovaBase):
+ __tablename__ = 'user_project_role_association'
+ user_id = Column(String(255), primary_key=True)
+ user = relationship(User,
+ primaryjoin=user_id==User.id,
+ foreign_keys=[User.id],
+ uselist=False)
+
+ project_id = Column(String(255), primary_key=True)
+ project = relationship(Project,
+ primaryjoin=project_id==Project.id,
+ foreign_keys=[Project.id],
+ uselist=False)
+
+ role = Column(String(255), primary_key=True)
+ ForeignKeyConstraint(['user_id',
+ 'project_id'],
+ ['user_project_association.user_id',
+ 'user_project_association.project_id'])
+
+
+class UserRoleAssociation(BASE, NovaBase):
+ __tablename__ = 'user_role_association'
+ user_id = Column(String(255), ForeignKey('users.id'), primary_key=True)
+ user = relationship(User, backref='roles')
+ role = Column(String(255), primary_key=True)
+
+
+class UserProjectAssociation(BASE, NovaBase):
+ __tablename__ = 'user_project_association'
+ user_id = Column(String(255), ForeignKey(User.id), primary_key=True)
+ project_id = Column(String(255), ForeignKey(Project.id), primary_key=True)
+
+
+
class FloatingIp(BASE, NovaBase):
"""Represents a floating ip that dynamically forwards to a fixed ip"""
__tablename__ = 'floating_ips'
@@ -394,7 +463,7 @@ def register_models():
from sqlalchemy import create_engine
models = (Service, Instance, Volume, ExportDevice,
FixedIp, FloatingIp, Network, NetworkIndex,
- AuthToken) # , Image, Host)
+ AuthToken, UserProjectAssociation, User, Project) # , Image, Host)
engine = create_engine(FLAGS.sql_connection, echo=False)
for model in models:
model.metadata.create_all(engine)
diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py
index 709195ba4..37f9c8253 100644
--- a/nova/network/linux_net.py
+++ b/nova/network/linux_net.py
@@ -28,6 +28,11 @@ from nova import flags
from nova import utils
+def _bin_file(script):
+ """Return the absolute path to scipt in the bin directory"""
+ return os.path.abspath(os.path.join(__file__, "../../../bin", script))
+
+
FLAGS = flags.FLAGS
flags.DEFINE_string('dhcpbridge_flagfile',
'/etc/nova/nova-dhcpbridge.conf',
@@ -38,7 +43,9 @@ flags.DEFINE_string('networks_path', utils.abspath('../networks'),
flags.DEFINE_string('public_interface', 'vlan1',
'Interface for public IP addresses')
flags.DEFINE_string('bridge_dev', 'eth0',
- 'network device for bridges')
+ 'network device for bridges')
+flags.DEFINE_string('dhcpbridge', _bin_file('nova-dhcpbridge'),
+ 'location of nova-dhcpbridge')
flags.DEFINE_string('routing_source_ip', '127.0.0.1',
'Public IP of network host')
flags.DEFINE_bool('use_nova_chains', False,
@@ -139,16 +146,16 @@ def ensure_bridge(bridge, interface, net_attrs=None):
# _execute("sudo brctl setageing %s 10" % bridge)
_execute("sudo brctl stp %s off" % bridge)
_execute("sudo brctl addif %s %s" % (bridge, interface))
- if net_attrs:
- _execute("sudo ifconfig %s %s broadcast %s netmask %s up" % \
- (bridge,
- net_attrs['gateway'],
- net_attrs['broadcast'],
- net_attrs['netmask']))
- else:
- _execute("sudo ifconfig %s up" % bridge)
- _confirm_rule("FORWARD", "--in-interface %s -j ACCEPT" % bridge)
- _confirm_rule("FORWARD", "--out-interface %s -j ACCEPT" % bridge)
+ if net_attrs:
+ _execute("sudo ifconfig %s %s broadcast %s netmask %s up" % \
+ (bridge,
+ net_attrs['gateway'],
+ net_attrs['broadcast'],
+ net_attrs['netmask']))
+ else:
+ _execute("sudo ifconfig %s up" % bridge)
+ _confirm_rule("FORWARD", "--in-interface %s -j ACCEPT" % bridge)
+ _confirm_rule("FORWARD", "--out-interface %s -j ACCEPT" % bridge)
def get_dhcp_hosts(context, network_id):
@@ -172,9 +179,14 @@ def update_dhcp(context, network_id):
signal causing it to reload, otherwise spawn a new instance
"""
network_ref = db.network_get(context, network_id)
- with open(_dhcp_file(network_ref['vlan'], 'conf'), 'w') as f:
+
+ conffile = _dhcp_file(network_ref['vlan'], 'conf')
+ with open(conffile, 'w') as f:
f.write(get_dhcp_hosts(context, network_id))
+ # Make sure dnsmasq can actually read it (it setuid()s to "nobody")
+ os.chmod(conffile, 0644)
+
pid = _dnsmasq_pid_for(network_ref['vlan'])
# if dnsmasq is already running, then tell it to reload
@@ -182,7 +194,7 @@ def update_dhcp(context, network_id):
# TODO(ja): use "/proc/%d/cmdline" % (pid) to determine if pid refers
# correct dnsmasq process
try:
- os.kill(pid, signal.SIGHUP)
+ _execute('sudo kill -HUP %d' % pid)
return
except Exception as exc: # pylint: disable-msg=W0703
logging.debug("Hupping dnsmasq threw %s", exc)
@@ -243,7 +255,7 @@ def _dnsmasq_cmd(net):
' --except-interface=lo',
' --dhcp-range=%s,static,120s' % net['dhcp_start'],
' --dhcp-hostsfile=%s' % _dhcp_file(net['vlan'], 'conf'),
- ' --dhcp-script=%s' % _bin_file('nova-dhcpbridge'),
+ ' --dhcp-script=%s' % FLAGS.dhcpbridge,
' --leasefile-ro']
return ''.join(cmd)
@@ -254,7 +266,7 @@ def _stop_dnsmasq(network):
if pid:
try:
- os.kill(pid, signal.SIGTERM)
+ _execute('sudo kill -TERM %d' % pid)
except Exception as exc: # pylint: disable-msg=W0703
logging.debug("Killing dnsmasq threw %s", exc)
@@ -262,12 +274,10 @@ def _stop_dnsmasq(network):
def _dhcp_file(vlan, kind):
"""Return path to a pid, leases or conf file for a vlan"""
- return os.path.abspath("%s/nova-%s.%s" % (FLAGS.networks_path, vlan, kind))
-
+ if not os.path.exists(FLAGS.networks_path):
+ os.makedirs(FLAGS.networks_path)
-def _bin_file(script):
- """Return the absolute path to scipt in the bin directory"""
- return os.path.abspath(os.path.join(__file__, "../../../bin", script))
+ return os.path.abspath("%s/nova-%s.%s" % (FLAGS.networks_path, vlan, kind))
def _dnsmasq_pid_for(vlan):
diff --git a/nova/network/manager.py b/nova/network/manager.py
index ef1d01138..9c1846dd9 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -85,6 +85,12 @@ class NetworkManager(manager.Manager):
self.driver = utils.import_object(network_driver)
super(NetworkManager, self).__init__(*args, **kwargs)
+ def init_host(self):
+ # Set up networking for the projects for which we're already
+ # the designated network host.
+ for network in self.db.host_get_networks(None, self.host):
+ self._on_set_network_host(None, network['id'])
+
def set_network_host(self, context, project_id):
"""Safely sets the host of the projects network"""
logging.debug("setting network host")
@@ -230,7 +236,7 @@ class VlanManager(NetworkManager):
now = datetime.datetime.utcnow()
timeout = FLAGS.fixed_ip_disassociate_timeout
time = now - datetime.timedelta(seconds=timeout)
- num = self.db.fixed_ip_disassociate_all_by_timeout(self,
+ num = self.db.fixed_ip_disassociate_all_by_timeout(context,
self.host,
time)
if num:
@@ -240,6 +246,7 @@ class VlanManager(NetworkManager):
"""Do any initialization that needs to be run if this is a
standalone service.
"""
+ super(VlanManager, self).init_host()
self.driver.init_host()
def allocate_fixed_ip(self, context, instance_id, *args, **kwargs):
@@ -367,6 +374,7 @@ class VlanManager(NetworkManager):
self.driver.ensure_vlan_bridge(network_ref['vlan'],
network_ref['bridge'],
network_ref)
+ self.driver.update_dhcp(context, network_id)
@property
def _bottom_reserved_ips(self):
diff --git a/nova/service.py b/nova/service.py
index a6c186896..115e0ff32 100644
--- a/nova/service.py
+++ b/nova/service.py
@@ -52,11 +52,17 @@ class Service(object, service.Service):
self.host = host
self.binary = binary
self.topic = topic
- manager_class = utils.import_class(manager)
- self.manager = manager_class(host=host, *args, **kwargs)
+ self.manager_class_name = manager
+ super(Service, self).__init__(*args, **kwargs)
+ self.saved_args, self.saved_kwargs = args, kwargs
+
+
+ def startService(self): # pylint: disable-msg C0103
+ manager_class = utils.import_class(self.manager_class_name)
+ self.manager = manager_class(host=self.host, *self.saved_args,
+ **self.saved_kwargs)
self.manager.init_host()
self.model_disconnected = False
- super(Service, self).__init__(*args, **kwargs)
try:
service_ref = db.service_get_by_args(None,
self.host,
diff --git a/nova/tests/auth_unittest.py b/nova/tests/auth_unittest.py
index 1955bb417..99f7ab599 100644
--- a/nova/tests/auth_unittest.py
+++ b/nova/tests/auth_unittest.py
@@ -75,8 +75,9 @@ class user_and_project_generator(object):
self.manager.delete_user(self.user)
self.manager.delete_project(self.project)
-class AuthManagerTestCase(test.TrialTestCase):
+class AuthManagerTestCase(object):
def setUp(self):
+ FLAGS.auth_driver = self.auth_driver
super(AuthManagerTestCase, self).setUp()
self.flags(connection_type='fake')
self.manager = manager.AuthManager()
@@ -320,6 +321,12 @@ class AuthManagerTestCase(test.TrialTestCase):
self.assertEqual('secret', user.secret)
self.assertTrue(user.is_admin())
+class AuthManagerLdapTestCase(AuthManagerTestCase, test.TrialTestCase):
+ auth_driver = 'nova.auth.ldapdriver.FakeLdapDriver'
+
+class AuthManagerDbTestCase(AuthManagerTestCase, test.TrialTestCase):
+ auth_driver = 'nova.auth.dbdriver.DbDriver'
+
if __name__ == "__main__":
# TODO: Implement use_fake as an option
diff --git a/nova/tests/fake_flags.py b/nova/tests/fake_flags.py
index 8f4754650..4bbef8832 100644
--- a/nova/tests/fake_flags.py
+++ b/nova/tests/fake_flags.py
@@ -24,7 +24,7 @@ flags.DECLARE('volume_driver', 'nova.volume.manager')
FLAGS.volume_driver = 'nova.volume.driver.FakeAOEDriver'
FLAGS.connection_type = 'fake'
FLAGS.fake_rabbit = True
-FLAGS.auth_driver = 'nova.auth.ldapdriver.FakeLdapDriver'
+FLAGS.auth_driver = 'nova.auth.dbdriver.DbDriver'
flags.DECLARE('network_size', 'nova.network.manager')
flags.DECLARE('num_networks', 'nova.network.manager')
flags.DECLARE('fake_network', 'nova.network.manager')
diff --git a/nova/tests/network_unittest.py b/nova/tests/network_unittest.py
index 5370966d2..59b0a36e4 100644
--- a/nova/tests/network_unittest.py
+++ b/nova/tests/network_unittest.py
@@ -56,8 +56,8 @@ class NetworkTestCase(test.TrialTestCase):
'netuser',
name))
# create the necessary network data for the project
- user_context = context.APIRequestContext(project=self.projects[i],
- user=self.user)
+ user_context = context.get_admin_context(user=self.user)
+
self.network.set_network_host(user_context, self.projects[i].id)
instance_ref = self._create_instance(0)
self.instance_id = instance_ref['id']
diff --git a/nova/tests/scheduler_unittest.py b/nova/tests/scheduler_unittest.py
index fde30f81e..53a8be144 100644
--- a/nova/tests/scheduler_unittest.py
+++ b/nova/tests/scheduler_unittest.py
@@ -117,10 +117,12 @@ class SimpleDriverTestCase(test.TrialTestCase):
'nova-compute',
'compute',
FLAGS.compute_manager)
+ compute1.startService()
compute2 = service.Service('host2',
'nova-compute',
'compute',
FLAGS.compute_manager)
+ compute2.startService()
hosts = self.scheduler.driver.hosts_up(self.context, 'compute')
self.assertEqual(len(hosts), 2)
compute1.kill()
@@ -132,10 +134,12 @@ class SimpleDriverTestCase(test.TrialTestCase):
'nova-compute',
'compute',
FLAGS.compute_manager)
+ compute1.startService()
compute2 = service.Service('host2',
'nova-compute',
'compute',
FLAGS.compute_manager)
+ compute2.startService()
instance_id1 = self._create_instance()
compute1.run_instance(self.context, instance_id1)
instance_id2 = self._create_instance()
@@ -153,10 +157,12 @@ class SimpleDriverTestCase(test.TrialTestCase):
'nova-compute',
'compute',
FLAGS.compute_manager)
+ compute1.startService()
compute2 = service.Service('host2',
'nova-compute',
'compute',
FLAGS.compute_manager)
+ compute2.startService()
instance_ids1 = []
instance_ids2 = []
for index in xrange(FLAGS.max_cores):
@@ -184,10 +190,12 @@ class SimpleDriverTestCase(test.TrialTestCase):
'nova-volume',
'volume',
FLAGS.volume_manager)
+ volume1.startService()
volume2 = service.Service('host2',
'nova-volume',
'volume',
FLAGS.volume_manager)
+ volume2.startService()
volume_id1 = self._create_volume()
volume1.create_volume(self.context, volume_id1)
volume_id2 = self._create_volume()
@@ -205,10 +213,12 @@ class SimpleDriverTestCase(test.TrialTestCase):
'nova-volume',
'volume',
FLAGS.volume_manager)
+ volume1.startService()
volume2 = service.Service('host2',
'nova-volume',
'volume',
FLAGS.volume_manager)
+ volume2.startService()
volume_ids1 = []
volume_ids2 = []
for index in xrange(FLAGS.max_gigabytes):
diff --git a/nova/tests/service_unittest.py b/nova/tests/service_unittest.py
index 06f80e82c..6afeec377 100644
--- a/nova/tests/service_unittest.py
+++ b/nova/tests/service_unittest.py
@@ -22,6 +22,8 @@ Unit Tests for remote procedure calls using queue
import mox
+from twisted.application.app import startApplication
+
from nova import exception
from nova import flags
from nova import rpc
@@ -96,6 +98,7 @@ class ServiceTestCase(test.BaseTestCase):
self.mox.ReplayAll()
app = service.Service.create(host=host, binary=binary)
+ startApplication(app, False)
self.assert_(app)
# We're testing sort of weird behavior in how report_state decides
diff --git a/nova/virt/xenapi.py b/nova/virt/xenapi.py
index 0d06b1fce..118e0b687 100644
--- a/nova/virt/xenapi.py
+++ b/nova/virt/xenapi.py
@@ -42,10 +42,12 @@ from twisted.internet import defer
from twisted.internet import reactor
from twisted.internet import task
+from nova import db
from nova import flags
from nova import process
from nova import utils
from nova.auth.manager import AuthManager
+from nova.compute import instance_types
from nova.compute import power_state
from nova.virt import images
@@ -113,32 +115,24 @@ class XenAPIConnection(object):
raise Exception('Attempted to create non-unique name %s' %
instance.name)
- if 'bridge_name' in instance.datamodel:
- network_ref = \
- yield self._find_network_with_bridge(
- instance.datamodel['bridge_name'])
- else:
- network_ref = None
-
- if 'mac_address' in instance.datamodel:
- mac_address = instance.datamodel['mac_address']
- else:
- mac_address = ''
+ network = db.project_get_network(None, instance.project_id)
+ network_ref = \
+ yield self._find_network_with_bridge(network.bridge)
- user = AuthManager().get_user(instance.datamodel['user_id'])
- project = AuthManager().get_project(instance.datamodel['project_id'])
+ user = AuthManager().get_user(instance.user_id)
+ project = AuthManager().get_project(instance.project_id)
vdi_uuid = yield self._fetch_image(
- instance.datamodel['image_id'], user, project, True)
+ instance.image_id, user, project, True)
kernel = yield self._fetch_image(
- instance.datamodel['kernel_id'], user, project, False)
+ instance.kernel_id, user, project, False)
ramdisk = yield self._fetch_image(
- instance.datamodel['ramdisk_id'], user, project, False)
+ instance.ramdisk_id, user, project, False)
vdi_ref = yield self._call_xenapi('VDI.get_by_uuid', vdi_uuid)
vm_ref = yield self._create_vm(instance, kernel, ramdisk)
yield self._create_vbd(vm_ref, vdi_ref, 0, True)
if network_ref:
- yield self._create_vif(vm_ref, network_ref, mac_address)
+ yield self._create_vif(vm_ref, network_ref, instance.mac_address)
logging.debug('Starting VM %s...', vm_ref)
yield self._call_xenapi('VM.start', vm_ref, False, False)
logging.info('Spawning VM %s created %s.', instance.name, vm_ref)
@@ -148,8 +142,9 @@ class XenAPIConnection(object):
"""Create a VM record. Returns a Deferred that gives the new
VM reference."""
- mem = str(long(instance.datamodel['memory_kb']) * 1024)
- vcpus = str(instance.datamodel['vcpus'])
+ instance_type = instance_types.INSTANCE_TYPES[instance.instance_type]
+ mem = str(long(instance_type['memory_mb']) * 1024 * 1024)
+ vcpus = str(instance_type['vcpus'])
rec = {
'name_label': instance.name,
'name_description': '',