summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJosh Kearney <josh.kearney@rackspace.com>2010-12-08 11:22:12 -0600
committerJosh Kearney <josh.kearney@rackspace.com>2010-12-08 11:22:12 -0600
commiteeac485381f9beaf3df68e8f47b5837a3f48558c (patch)
tree4e8b38554310bab015da7f38cb24a7ee1284f895
parenta107c6f69237cd7488b9ff716e370dd01b8dd8bd (diff)
parent3bb38e64ed5144db00d77488243b6b70023a3c44 (diff)
Merged trunk
-rwxr-xr-xcontrib/nova.sh10
-rw-r--r--nova/api/cloud.py58
-rw-r--r--nova/api/ec2/cloud.py7
-rw-r--r--nova/api/openstack/servers.py7
-rw-r--r--nova/auth/ldapdriver.py138
-rwxr-xr-xnova/auth/opendj.sh119
-rw-r--r--nova/compute/api.py27
7 files changed, 264 insertions, 102 deletions
diff --git a/contrib/nova.sh b/contrib/nova.sh
index 1a9f93a3b..7eb934eca 100755
--- a/contrib/nova.sh
+++ b/contrib/nova.sh
@@ -22,6 +22,8 @@ USE_MYSQL=${USE_MYSQL:-0}
MYSQL_PASS=${MYSQL_PASS:-nova}
TEST=${TEST:-0}
USE_LDAP=${USE_LDAP:-0}
+# Use OpenDJ instead of OpenLDAP when using LDAP
+USE_OPENDJ=${USE_OPENDJ:-0}
LIBVIRT_TYPE=${LIBVIRT_TYPE:-qemu}
NET_MAN=${NET_MAN:-VlanManager}
# NOTE(vish): If you are using FlatDHCP on multiple hosts, set the interface
@@ -113,7 +115,13 @@ if [ "$CMD" == "run" ]; then
rm $NOVA_DIR/nova.sqlite
fi
if [ "$USE_LDAP" == 1 ]; then
- sudo $NOVA_DIR/nova/auth/slap.sh
+ if [ "$USE_OPENDJ" == 1 ]; then
+ echo '--ldap_user_dn=cn=Directory Manager' >> \
+ /etc/nova/nova-manage.conf
+ sudo $NOVA_DIR/nova/auth/opendj.sh
+ else
+ sudo $NOVA_DIR/nova/auth/slap.sh
+ fi
fi
rm -rf $NOVA_DIR/instances
mkdir -p $NOVA_DIR/instances
diff --git a/nova/api/cloud.py b/nova/api/cloud.py
deleted file mode 100644
index b8f15019f..000000000
--- a/nova/api/cloud.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# 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.
-
-"""
-Methods for API calls to control instances via AMQP.
-"""
-
-
-from nova import db
-from nova import flags
-from nova import rpc
-
-FLAGS = flags.FLAGS
-
-
-def reboot(instance_id, context=None):
- """Reboot the given instance."""
- instance_ref = db.instance_get_by_internal_id(context, instance_id)
- host = instance_ref['host']
- rpc.cast(context,
- db.queue_get_for(context, FLAGS.compute_topic, host),
- {"method": "reboot_instance",
- "args": {"instance_id": instance_ref['id']}})
-
-
-def rescue(instance_id, context):
- """Rescue the given instance."""
- instance_ref = db.instance_get_by_internal_id(context, instance_id)
- host = instance_ref['host']
- rpc.cast(context,
- db.queue_get_for(context, FLAGS.compute_topic, host),
- {"method": "rescue_instance",
- "args": {"instance_id": instance_ref['id']}})
-
-
-def unrescue(instance_id, context):
- """Unrescue the given instance."""
- instance_ref = db.instance_get_by_internal_id(context, instance_id)
- host = instance_ref['host']
- rpc.cast(context,
- db.queue_get_for(context, FLAGS.compute_topic, host),
- {"method": "unrescue_instance",
- "args": {"instance_id": instance_ref['id']}})
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index e50906ae1..161d2d038 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -41,7 +41,6 @@ from nova import rpc
from nova import utils
from nova.compute import api as compute_api
from nova.compute import instance_types
-from nova.api import cloud
from nova.image.s3 import S3ImageService
@@ -834,19 +833,19 @@ class CloudController(object):
"""instance_id is a list of instance ids"""
for ec2_id in instance_id:
internal_id = ec2_id_to_internal_id(ec2_id)
- cloud.reboot(internal_id, context=context)
+ self.compute_api.reboot(context, internal_id)
return True
def rescue_instance(self, context, instance_id, **kwargs):
"""This is an extension to the normal ec2_api"""
internal_id = ec2_id_to_internal_id(instance_id)
- cloud.rescue(internal_id, context=context)
+ self.compute_api.rescue(context, internal_id)
return True
def unrescue_instance(self, context, instance_id, **kwargs):
"""This is an extension to the normal ec2_api"""
internal_id = ec2_id_to_internal_id(instance_id)
- cloud.unrescue(internal_id, context=context)
+ self.compute_api.unrescue(context, internal_id)
return True
def update_instance(self, context, ec2_id, **kwargs):
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index e7f765c02..519250e60 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -23,7 +23,6 @@ from nova import rpc
from nova import utils
from nova import wsgi
from nova import context
-from nova.api import cloud
from nova.api.openstack import faults
from nova.compute import api as compute_api
from nova.compute import instance_types
@@ -183,9 +182,9 @@ class Controller(wsgi.Controller):
inst_ref = self.db.instance_get_by_internal_id(ctxt, int(id))
if not inst_ref or (inst_ref and not inst_ref.user_id == user_id):
return faults.Fault(exc.HTTPUnprocessableEntity())
- #TODO(gundlach): pass reboot_type, support soft reboot in
- #virt driver
- cloud.reboot(id)
+ # TODO(gundlach): pass reboot_type, support soft reboot in
+ # virt driver
+ self.compute_api.reboot(ctxt, id)
def _get_network_topic(self, context):
"""Retrieves the network host for a project"""
diff --git a/nova/auth/ldapdriver.py b/nova/auth/ldapdriver.py
index ceade1d65..c10939d74 100644
--- a/nova/auth/ldapdriver.py
+++ b/nova/auth/ldapdriver.py
@@ -40,6 +40,8 @@ flags.DEFINE_string('ldap_user_dn', 'cn=Manager,dc=example,dc=com',
flags.DEFINE_string('ldap_user_unit', 'Users', 'OID for Users')
flags.DEFINE_string('ldap_user_subtree', 'ou=Users,dc=example,dc=com',
'OU for Users')
+flags.DEFINE_boolean('ldap_user_modify_only', False,
+ 'Modify attributes for users instead of creating/deleting')
flags.DEFINE_string('ldap_project_subtree', 'ou=Groups,dc=example,dc=com',
'OU for Projects')
flags.DEFINE_string('role_project_subtree', 'ou=Groups,dc=example,dc=com',
@@ -89,8 +91,7 @@ class LdapDriver(object):
def get_user(self, uid):
"""Retrieve user by id"""
- attr = self.__find_object(self.__uid_to_dn(uid),
- '(objectclass=novaUser)')
+ attr = self.__get_ldap_user(uid)
return self.__to_user(attr)
def get_user_from_access_key(self, access):
@@ -110,7 +111,12 @@ class LdapDriver(object):
"""Retrieve list of users"""
attrs = self.__find_objects(FLAGS.ldap_user_subtree,
'(objectclass=novaUser)')
- return [self.__to_user(attr) for attr in attrs]
+ users = []
+ for attr in attrs:
+ user = self.__to_user(attr)
+ if user is not None:
+ users.append(user)
+ return users
def get_projects(self, uid=None):
"""Retrieve list of projects"""
@@ -125,21 +131,52 @@ class LdapDriver(object):
"""Create a user"""
if self.__user_exists(name):
raise exception.Duplicate("LDAP user %s already exists" % name)
- attr = [
- ('objectclass', ['person',
- 'organizationalPerson',
- 'inetOrgPerson',
- 'novaUser']),
- ('ou', [FLAGS.ldap_user_unit]),
- ('uid', [name]),
- ('sn', [name]),
- ('cn', [name]),
- ('secretKey', [secret_key]),
- ('accessKey', [access_key]),
- ('isAdmin', [str(is_admin).upper()]),
- ]
- self.conn.add_s(self.__uid_to_dn(name), attr)
- return self.__to_user(dict(attr))
+ if FLAGS.ldap_user_modify_only:
+ if self.__ldap_user_exists(name):
+ # Retrieve user by name
+ user = self.__get_ldap_user(name)
+ # Entry could be malformed, test for missing attrs.
+ # Malformed entries are useless, replace attributes found.
+ attr = []
+ if 'secretKey' in user.keys():
+ attr.append((self.ldap.MOD_REPLACE, 'secretKey', \
+ [secret_key]))
+ else:
+ attr.append((self.ldap.MOD_ADD, 'secretKey', \
+ [secret_key]))
+ if 'accessKey' in user.keys():
+ attr.append((self.ldap.MOD_REPLACE, 'accessKey', \
+ [access_key]))
+ else:
+ attr.append((self.ldap.MOD_ADD, 'accessKey', \
+ [access_key]))
+ if 'isAdmin' in user.keys():
+ attr.append((self.ldap.MOD_REPLACE, 'isAdmin', \
+ [str(is_admin).upper()]))
+ else:
+ attr.append((self.ldap.MOD_ADD, 'isAdmin', \
+ [str(is_admin).upper()]))
+ self.conn.modify_s(self.__uid_to_dn(name), attr)
+ return self.get_user(name)
+ else:
+ raise exception.NotFound("LDAP object for %s doesn't exist"
+ % name)
+ else:
+ attr = [
+ ('objectclass', ['person',
+ 'organizationalPerson',
+ 'inetOrgPerson',
+ 'novaUser']),
+ ('ou', [FLAGS.ldap_user_unit]),
+ ('uid', [name]),
+ ('sn', [name]),
+ ('cn', [name]),
+ ('secretKey', [secret_key]),
+ ('accessKey', [access_key]),
+ ('isAdmin', [str(is_admin).upper()]),
+ ]
+ self.conn.add_s(self.__uid_to_dn(name), attr)
+ return self.__to_user(dict(attr))
def create_project(self, name, manager_uid,
description=None, member_uids=None):
@@ -155,7 +192,7 @@ class LdapDriver(object):
if description is None:
description = name
members = []
- if member_uids != None:
+ if member_uids is not None:
for member_uid in member_uids:
if not self.__user_exists(member_uid):
raise exception.NotFound("Project can't be created "
@@ -256,7 +293,24 @@ class LdapDriver(object):
if not self.__user_exists(uid):
raise exception.NotFound("User %s doesn't exist" % uid)
self.__remove_from_all(uid)
- self.conn.delete_s(self.__uid_to_dn(uid))
+ if FLAGS.ldap_user_modify_only:
+ # Delete attributes
+ attr = []
+ # Retrieve user by name
+ user = self.__get_ldap_user(uid)
+ if 'secretKey' in user.keys():
+ attr.append((self.ldap.MOD_DELETE, 'secretKey', \
+ user['secretKey']))
+ if 'accessKey' in user.keys():
+ attr.append((self.ldap.MOD_DELETE, 'accessKey', \
+ user['accessKey']))
+ if 'isAdmin' in user.keys():
+ attr.append((self.ldap.MOD_DELETE, 'isAdmin', \
+ user['isAdmin']))
+ self.conn.modify_s(self.__uid_to_dn(uid), attr)
+ else:
+ # Delete entry
+ self.conn.delete_s(self.__uid_to_dn(uid))
def delete_project(self, project_id):
"""Delete a project"""
@@ -265,7 +319,7 @@ class LdapDriver(object):
self.__delete_group(project_dn)
def modify_user(self, uid, access_key=None, secret_key=None, admin=None):
- """Modify an existing project"""
+ """Modify an existing user"""
if not access_key and not secret_key and admin is None:
return
attr = []
@@ -279,11 +333,21 @@ class LdapDriver(object):
def __user_exists(self, uid):
"""Check if user exists"""
- return self.get_user(uid) != None
+ return self.get_user(uid) is not None
+
+ def __ldap_user_exists(self, uid):
+ """Check if the user exists in ldap"""
+ return self.__get_ldap_user(uid) is not None
def __project_exists(self, project_id):
"""Check if project exists"""
- return self.get_project(project_id) != None
+ return self.get_project(project_id) is not None
+
+ def __get_ldap_user(self, uid):
+ """Retrieve LDAP user entry by id"""
+ attr = self.__find_object(self.__uid_to_dn(uid),
+ '(objectclass=novaUser)')
+ return attr
def __find_object(self, dn, query=None, scope=None):
"""Find an object by dn and query"""
@@ -330,12 +394,12 @@ class LdapDriver(object):
def __group_exists(self, dn):
"""Check if group exists"""
- return self.__find_object(dn, '(objectclass=groupOfNames)') != None
+ return self.__find_object(dn, '(objectclass=groupOfNames)') is not None
@staticmethod
def __role_to_dn(role, project_id=None):
"""Convert role to corresponding dn"""
- if project_id == None:
+ if project_id is None:
return FLAGS.__getitem__("ldap_%s" % role).value
else:
return 'cn=%s,cn=%s,%s' % (role,
@@ -349,7 +413,7 @@ class LdapDriver(object):
raise exception.Duplicate("Group can't be created because "
"group %s already exists" % name)
members = []
- if member_uids != None:
+ if member_uids is not None:
for member_uid in member_uids:
if not self.__user_exists(member_uid):
raise exception.NotFound("Group can't be created "
@@ -375,7 +439,7 @@ class LdapDriver(object):
res = self.__find_object(group_dn,
'(member=%s)' % self.__uid_to_dn(uid),
self.ldap.SCOPE_BASE)
- return res != None
+ return res is not None
def __add_to_group(self, uid, group_dn):
"""Add user to group"""
@@ -447,18 +511,22 @@ class LdapDriver(object):
@staticmethod
def __to_user(attr):
"""Convert ldap attributes to User object"""
- if attr == None:
+ if attr is None:
+ return None
+ if ('accessKey' in attr.keys() and 'secretKey' in attr.keys() \
+ and 'isAdmin' in attr.keys()):
+ return {
+ 'id': attr['uid'][0],
+ 'name': attr['cn'][0],
+ 'access': attr['accessKey'][0],
+ 'secret': attr['secretKey'][0],
+ 'admin': (attr['isAdmin'][0] == 'TRUE')}
+ else:
return None
- return {
- 'id': attr['uid'][0],
- 'name': attr['cn'][0],
- 'access': attr['accessKey'][0],
- 'secret': attr['secretKey'][0],
- 'admin': (attr['isAdmin'][0] == 'TRUE')}
def __to_project(self, attr):
"""Convert ldap attributes to Project object"""
- if attr == None:
+ if attr is None:
return None
member_dns = attr.get('member', [])
return {
diff --git a/nova/auth/opendj.sh b/nova/auth/opendj.sh
new file mode 100755
index 000000000..8052c077d
--- /dev/null
+++ b/nova/auth/opendj.sh
@@ -0,0 +1,119 @@
+#!/usr/bin/env bash
+# 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.
+# LDAP INSTALL SCRIPT - IS IDEMPOTENT, does not scrub users
+
+apt-get install -y ldap-utils python-ldap openjdk-6-jre
+
+if [ ! -d "/usr/opendj" ]
+then
+ # TODO(rlane): Wikimedia Foundation is the current package maintainer.
+ # After the package is included in Ubuntu's channel, change this.
+ wget http://apt.wikimedia.org/wikimedia/pool/main/o/opendj/opendj_2.4.0-7_amd64.deb
+ dpkg -i opendj_2.4.0-7_amd64.deb
+fi
+
+abspath=`dirname "$(cd "${0%/*}" 2>/dev/null; echo "$PWD"/"${0##*/}")"`
+schemapath='/var/opendj/instance/config/schema'
+cp $abspath/openssh-lpk_sun.schema $schemapath/97-openssh-lpk_sun.ldif
+cp $abspath/nova_sun.schema $schemapath/98-nova_sun.ldif
+chown opendj:opendj $schemapath/97-openssh-lpk_sun.ldif
+chown opendj:opendj $schemapath/98-nova_sun.ldif
+
+cat >/etc/ldap/ldap.conf <<LDAP_CONF_EOF
+# LDAP Client Settings
+URI ldap://localhost
+BASE dc=example,dc=com
+BINDDN cn=Directory Manager
+SIZELIMIT 0
+TIMELIMIT 0
+LDAP_CONF_EOF
+
+cat >/etc/ldap/base.ldif <<BASE_LDIF_EOF
+# This is the root of the directory tree
+dn: dc=example,dc=com
+description: Example.Com, your trusted non-existent corporation.
+dc: example
+o: Example.Com
+objectClass: top
+objectClass: dcObject
+objectClass: organization
+
+# Subtree for users
+dn: ou=Users,dc=example,dc=com
+ou: Users
+description: Users
+objectClass: organizationalUnit
+
+# Subtree for groups
+dn: ou=Groups,dc=example,dc=com
+ou: Groups
+description: Groups
+objectClass: organizationalUnit
+
+# Subtree for system accounts
+dn: ou=System,dc=example,dc=com
+ou: System
+description: Special accounts used by software applications.
+objectClass: organizationalUnit
+
+# Special Account for Authentication:
+dn: uid=authenticate,ou=System,dc=example,dc=com
+uid: authenticate
+ou: System
+description: Special account for authenticating users
+userPassword: {MD5}TLnIqASP0CKUR3/LGkEZGg==
+objectClass: account
+objectClass: simpleSecurityObject
+
+# create the sysadmin entry
+
+dn: cn=developers,ou=Groups,dc=example,dc=com
+objectclass: groupOfNames
+cn: developers
+description: IT admin group
+member: uid=admin,ou=Users,dc=example,dc=com
+
+dn: cn=sysadmins,ou=Groups,dc=example,dc=com
+objectclass: groupOfNames
+cn: sysadmins
+description: IT admin group
+member: uid=admin,ou=Users,dc=example,dc=com
+
+dn: cn=netadmins,ou=Groups,dc=example,dc=com
+objectclass: groupOfNames
+cn: netadmins
+description: Network admin group
+member: uid=admin,ou=Users,dc=example,dc=com
+
+dn: cn=cloudadmins,ou=Groups,dc=example,dc=com
+objectclass: groupOfNames
+cn: cloudadmins
+description: Cloud admin group
+member: uid=admin,ou=Users,dc=example,dc=com
+
+dn: cn=itsec,ou=Groups,dc=example,dc=com
+objectclass: groupOfNames
+cn: itsec
+description: IT security users group
+member: uid=admin,ou=Users,dc=example,dc=com
+BASE_LDIF_EOF
+
+/etc/init.d/opendj stop
+su - opendj -c '/usr/opendj/setup -i -b "dc=example,dc=com" -l /etc/ldap/base.ldif -S -w changeme -O -n --noPropertiesFile'
+/etc/init.d/opendj start
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 929342a1e..da01ca61a 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -210,3 +210,30 @@ class ComputeAPI(base.Base):
"""
self.db.instance_update(context, instance_id, kwargs)
+
+ def reboot(self, context, instance_id):
+ """Reboot the given instance."""
+ instance = self.db.instance_get_by_internal_id(context, instance_id)
+ host = instance['host']
+ rpc.cast(context,
+ self.db.queue_get_for(context, FLAGS.compute_topic, host),
+ {"method": "reboot_instance",
+ "args": {"instance_id": instance['id']}})
+
+ def rescue(self, context, instance_id):
+ """Rescue the given instance."""
+ instance = self.db.instance_get_by_internal_id(context, instance_id)
+ host = instance['host']
+ rpc.cast(context,
+ self.db.queue_get_for(context, FLAGS.compute_topic, host),
+ {"method": "rescue_instance",
+ "args": {"instance_id": instance['id']}})
+
+ def unrescue(self, context, instance_id):
+ """Unrescue the given instance."""
+ instance = self.db.instance_get_by_internal_id(context, instance_id)
+ host = instance['host']
+ rpc.cast(context,
+ self.db.queue_get_for(context, FLAGS.compute_topic, host),
+ {"method": "unrescue_instance",
+ "args": {"instance_id": instance['id']}})