Merged trunk and resolved conflicts.
This commit is contained in:
		
							
								
								
									
										8
									
								
								.mailmap
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								.mailmap
									
									
									
									
									
								
							@@ -19,11 +19,11 @@
 | 
			
		||||
<mordred@inaugust.com> <mordred@hudson>
 | 
			
		||||
<paul@openstack.org> <pvoccio@castor.local>
 | 
			
		||||
<paul@openstack.org> <paul.voccio@rackspace.com>
 | 
			
		||||
<soren.hansen@rackspace.com> <soren@linux2go.dk>
 | 
			
		||||
<todd@ansolabs.com> <todd@lapex>
 | 
			
		||||
<todd@ansolabs.com> <todd@rubidine.com>
 | 
			
		||||
<vishvananda@gmail.com> <vishvananda@yahoo.com>
 | 
			
		||||
<vishvananda@gmail.com> <root@mirror.nasanebula.net>
 | 
			
		||||
# These are from people who failed to set a proper committer
 | 
			
		||||
. <root@tonbuntu>
 | 
			
		||||
. <laner@controller>
 | 
			
		||||
. <root@ubuntu>
 | 
			
		||||
<vishvananda@gmail.com> <root@ubuntu>
 | 
			
		||||
<sleepsonthefloor@gmail.com> <root@tonbuntu>
 | 
			
		||||
<rlane@wikimedia.org> <laner@controller>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								Authors
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								Authors
									
									
									
									
									
								
							@@ -6,6 +6,7 @@ Chris Behrens <cbehrens@codestud.com>
 | 
			
		||||
Chmouel Boudjnah <chmouel@chmouel.com>
 | 
			
		||||
Dean Troyer <dtroyer@gmail.com>
 | 
			
		||||
Devin Carlen <devin.carlen@gmail.com>
 | 
			
		||||
Ed Leafe <ed@leafe.com>
 | 
			
		||||
Eldar Nugaev <enugaev@griddynamics.com>
 | 
			
		||||
Eric Day <eday@oddments.org>
 | 
			
		||||
Ewan Mellor <ewan.mellor@citrix.com>
 | 
			
		||||
@@ -14,6 +15,7 @@ Jay Pipes <jaypipes@gmail.com>
 | 
			
		||||
Jesse Andrews <anotherjesse@gmail.com>
 | 
			
		||||
Joe Heck <heckj@mac.com>
 | 
			
		||||
Joel Moore <joelbm24@gmail.com>
 | 
			
		||||
Jonathan Bryce <jbryce@jbryce.com>
 | 
			
		||||
Josh Kearney <josh.kearney@rackspace.com>
 | 
			
		||||
Joshua McKenty <jmckenty@gmail.com>
 | 
			
		||||
Justin Santa Barbara <justin@fathomdb.com>
 | 
			
		||||
@@ -22,9 +24,11 @@ Michael Gundlach <michael.gundlach@rackspace.com>
 | 
			
		||||
Monty Taylor <mordred@inaugust.com>
 | 
			
		||||
Paul Voccio <paul@openstack.org>
 | 
			
		||||
Rick Clark <rick@openstack.org>
 | 
			
		||||
Ryan Lane <rlane@wikimedia.org>
 | 
			
		||||
Ryan Lucio <rlucio@internap.com>
 | 
			
		||||
Sandy Walsh <sandy.walsh@rackspace.com>
 | 
			
		||||
Soren Hansen <soren.hansen@rackspace.com>
 | 
			
		||||
Thierry Carrez <thierry@openstack.org>
 | 
			
		||||
Todd Willey <todd@ansolabs.com>
 | 
			
		||||
Trey Morris <trey.morris@rackspace.com>
 | 
			
		||||
Vishvananda Ishaya <vishvananda@gmail.com>
 | 
			
		||||
 
 | 
			
		||||
@@ -16,16 +16,24 @@
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
# ARG is the id of the user
 | 
			
		||||
export SUBJ="/C=US/ST=California/L=MountainView/O=AnsoLabs/OU=NovaDev/CN=customer-intCA-$1"
 | 
			
		||||
mkdir INTER/$1
 | 
			
		||||
cd INTER/$1
 | 
			
		||||
# $1 is the id of the project and $2 is the subject of the cert
 | 
			
		||||
NAME=$1
 | 
			
		||||
SUBJ=$2
 | 
			
		||||
mkdir -p projects/$NAME
 | 
			
		||||
cd projects/$NAME
 | 
			
		||||
cp ../../openssl.cnf.tmpl openssl.cnf
 | 
			
		||||
sed -i -e s/%USERNAME%/$1/g openssl.cnf
 | 
			
		||||
sed -i -e s/%USERNAME%/$NAME/g openssl.cnf
 | 
			
		||||
mkdir certs crl newcerts private
 | 
			
		||||
openssl req -new -x509 -extensions v3_ca -keyout private/cakey.pem -out cacert.pem -days 365 -config ./openssl.cnf -batch -nodes
 | 
			
		||||
echo "10" > serial
 | 
			
		||||
touch index.txt
 | 
			
		||||
openssl genrsa -out private/cakey.pem 1024 -config ./openssl.cnf -batch -nodes
 | 
			
		||||
openssl req -new -sha2 -key private/cakey.pem -out ../../reqs/inter$1.csr -batch -subj "$SUBJ"
 | 
			
		||||
cd ../../
 | 
			
		||||
openssl ca -extensions v3_ca -days 365 -out INTER/$1/cacert.pem -in reqs/inter$1.csr -config openssl.cnf -batch
 | 
			
		||||
# NOTE(vish): Disabling intermediate ca's because we don't actually need them.
 | 
			
		||||
#             It makes more sense to have each project have its own root ca.
 | 
			
		||||
# openssl genrsa -out private/cakey.pem 1024 -config ./openssl.cnf -batch -nodes
 | 
			
		||||
# openssl req -new -sha256 -key private/cakey.pem -out ../../reqs/inter$NAME.csr -batch -subj "$SUBJ"
 | 
			
		||||
openssl ca -gencrl -config ./openssl.cnf -out crl.pem
 | 
			
		||||
if [ "`id -u`" != "`grep nova /etc/passwd | cut -d':' -f3`" ]; then
 | 
			
		||||
    sudo chown -R nova:nogroup .
 | 
			
		||||
fi
 | 
			
		||||
# cd ../../
 | 
			
		||||
# openssl ca -extensions v3_ca -days 365 -out INTER/$NAME/cacert.pem -in reqs/inter$NAME.csr -config openssl.cnf -batch
 | 
			
		||||
 
 | 
			
		||||
@@ -25,4 +25,5 @@ else
 | 
			
		||||
    openssl req -new -x509 -extensions v3_ca -keyout private/cakey.pem -out cacert.pem -days 365 -config ./openssl.cnf -batch -nodes
 | 
			
		||||
    touch index.txt
 | 
			
		||||
    echo "10" > serial
 | 
			
		||||
    openssl ca -gencrl -config ./openssl.cnf -out crl.pem
 | 
			
		||||
fi
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										36
									
								
								CA/genvpn.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										36
									
								
								CA/genvpn.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,36 @@
 | 
			
		||||
#!/bin/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.
 | 
			
		||||
 | 
			
		||||
# This gets zipped and run on the cloudpipe-managed OpenVPN server
 | 
			
		||||
NAME=$1
 | 
			
		||||
SUBJ=$2
 | 
			
		||||
 | 
			
		||||
mkdir -p projects/$NAME
 | 
			
		||||
cd projects/$NAME
 | 
			
		||||
 | 
			
		||||
# generate a server priv key
 | 
			
		||||
openssl genrsa -out server.key 2048
 | 
			
		||||
 | 
			
		||||
# generate a server CSR
 | 
			
		||||
openssl req -new -key server.key -out server.csr -batch -subj "$SUBJ"
 | 
			
		||||
 | 
			
		||||
novauid=`getent passwd nova | awk -F: '{print $3}'`
 | 
			
		||||
if [ ! -z "${novauid}" ] && [ "`id -u`" != "${novauid}" ]; then
 | 
			
		||||
    sudo chown -R nova:nogroup .
 | 
			
		||||
fi
 | 
			
		||||
@@ -24,7 +24,6 @@ dir			= .
 | 
			
		||||
 | 
			
		||||
[ ca ]
 | 
			
		||||
default_ca		= CA_default
 | 
			
		||||
unique_subject		= no
 | 
			
		||||
 | 
			
		||||
[ CA_default ]
 | 
			
		||||
serial			= $dir/serial
 | 
			
		||||
@@ -32,6 +31,8 @@ database		= $dir/index.txt
 | 
			
		||||
new_certs_dir		= $dir/newcerts
 | 
			
		||||
certificate		= $dir/cacert.pem
 | 
			
		||||
private_key		= $dir/private/cakey.pem
 | 
			
		||||
unique_subject		= no
 | 
			
		||||
default_crl_days	= 365
 | 
			
		||||
default_days		= 365
 | 
			
		||||
default_md		= md5
 | 
			
		||||
preserve		= no
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@ include nova/cloudpipe/client.ovpn.template
 | 
			
		||||
include nova/compute/fakevirtinstance.xml
 | 
			
		||||
include nova/compute/interfaces.template
 | 
			
		||||
include nova/virt/interfaces.template
 | 
			
		||||
include nova/virt/libvirt.*.xml.template
 | 
			
		||||
include nova/virt/libvirt*.xml.template
 | 
			
		||||
include nova/tests/CA/
 | 
			
		||||
include nova/tests/CA/cacert.pem
 | 
			
		||||
include nova/tests/CA/private/
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,7 @@
 | 
			
		||||
import eventlet
 | 
			
		||||
eventlet.monkey_patch()
 | 
			
		||||
 | 
			
		||||
import gettext
 | 
			
		||||
import os
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
@@ -33,6 +34,8 @@ possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
 | 
			
		||||
if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
 | 
			
		||||
    sys.path.insert(0, possible_topdir)
 | 
			
		||||
 | 
			
		||||
gettext.install('nova', unicode=1)
 | 
			
		||||
 | 
			
		||||
from nova import api
 | 
			
		||||
from nova import flags
 | 
			
		||||
from nova import service
 | 
			
		||||
 
 | 
			
		||||
@@ -72,6 +72,7 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
 | 
			
		||||
gettext.install('nova', unicode=1)
 | 
			
		||||
 | 
			
		||||
from nova import context
 | 
			
		||||
from nova import crypto
 | 
			
		||||
from nova import db
 | 
			
		||||
from nova import exception
 | 
			
		||||
from nova import flags
 | 
			
		||||
@@ -96,47 +97,43 @@ class VpnCommands(object):
 | 
			
		||||
        self.manager = manager.AuthManager()
 | 
			
		||||
        self.pipe = pipelib.CloudPipe()
 | 
			
		||||
 | 
			
		||||
    def list(self):
 | 
			
		||||
        """Print a listing of the VPNs for all projects."""
 | 
			
		||||
    def list(self, project=None):
 | 
			
		||||
        """Print a listing of the VPN data for one or all projects.
 | 
			
		||||
 | 
			
		||||
        args: [project=all]"""
 | 
			
		||||
        print "%-12s\t" % 'project',
 | 
			
		||||
        print "%-20s\t" % 'ip:port',
 | 
			
		||||
        print "%-20s\t" % 'private_ip',
 | 
			
		||||
        print "%s" % 'state'
 | 
			
		||||
        for project in self.manager.get_projects():
 | 
			
		||||
        if project:
 | 
			
		||||
            projects = [self.manager.get_project(project)]
 | 
			
		||||
        else:
 | 
			
		||||
            projects = self.manager.get_projects()
 | 
			
		||||
            # NOTE(vish): This hits the database a lot.  We could optimize
 | 
			
		||||
            #             by getting all networks in one query and all vpns
 | 
			
		||||
            #             in aother query, then doing lookups by project
 | 
			
		||||
        for project in projects:
 | 
			
		||||
            print "%-12s\t" % project.name,
 | 
			
		||||
 | 
			
		||||
            try:
 | 
			
		||||
                s = "%s:%s" % (project.vpn_ip, project.vpn_port)
 | 
			
		||||
            except exception.NotFound:
 | 
			
		||||
                s = "None"
 | 
			
		||||
            print "%-20s\t" % s,
 | 
			
		||||
 | 
			
		||||
            vpn = self._vpn_for(project.id)
 | 
			
		||||
            ipport = "%s:%s" % (project.vpn_ip, project.vpn_port)
 | 
			
		||||
            print "%-20s\t" % ipport,
 | 
			
		||||
            ctxt = context.get_admin_context()
 | 
			
		||||
            vpn = db.instance_get_project_vpn(ctxt, project.id)
 | 
			
		||||
            if vpn:
 | 
			
		||||
                command = "ping -c1 -w1 %s > /dev/null; echo $?"
 | 
			
		||||
                out, _err = utils.execute(command % vpn['private_dns_name'],
 | 
			
		||||
                                          check_exit_code=False)
 | 
			
		||||
                if out.strip() == '0':
 | 
			
		||||
                    net = 'up'
 | 
			
		||||
                else:
 | 
			
		||||
                    net = 'down'
 | 
			
		||||
                print vpn['private_dns_name'],
 | 
			
		||||
                print vpn['node_name'],
 | 
			
		||||
                print vpn['instance_id'],
 | 
			
		||||
                address = None
 | 
			
		||||
                state = 'down'
 | 
			
		||||
                if vpn.get('fixed_ip', None):
 | 
			
		||||
                    address = vpn['fixed_ip']['address']
 | 
			
		||||
                if project.vpn_ip and utils.vpn_ping(project.vpn_ip,
 | 
			
		||||
                                                     project.vpn_port):
 | 
			
		||||
                    state = 'up'
 | 
			
		||||
                print address,
 | 
			
		||||
                print vpn['host'],
 | 
			
		||||
                print vpn['ec2_id'],
 | 
			
		||||
                print vpn['state_description'],
 | 
			
		||||
                print net
 | 
			
		||||
 | 
			
		||||
                print state
 | 
			
		||||
            else:
 | 
			
		||||
                print None
 | 
			
		||||
 | 
			
		||||
    def _vpn_for(self, project_id):
 | 
			
		||||
        """Get the VPN instance for a project ID."""
 | 
			
		||||
        for instance in db.instance_get_all(context.get_admin_context()):
 | 
			
		||||
            if (instance['image_id'] == FLAGS.vpn_image_id
 | 
			
		||||
                and not instance['state_description'] in
 | 
			
		||||
                    ['shutting_down', 'shutdown']
 | 
			
		||||
                and instance['project_id'] == project_id):
 | 
			
		||||
                return instance
 | 
			
		||||
 | 
			
		||||
    def spawn(self):
 | 
			
		||||
        """Run all VPNs."""
 | 
			
		||||
        for p in reversed(self.manager.get_projects()):
 | 
			
		||||
@@ -149,6 +146,21 @@ class VpnCommands(object):
 | 
			
		||||
        """Start the VPN for a given project."""
 | 
			
		||||
        self.pipe.launch_vpn_instance(project_id)
 | 
			
		||||
 | 
			
		||||
    def change(self, project_id, ip, port):
 | 
			
		||||
        """Change the ip and port for a vpn.
 | 
			
		||||
 | 
			
		||||
        args: project, ip, port"""
 | 
			
		||||
        project = self.manager.get_project(project_id)
 | 
			
		||||
        if not project:
 | 
			
		||||
            print 'No project %s' % (project_id)
 | 
			
		||||
            return
 | 
			
		||||
        admin = context.get_admin_context()
 | 
			
		||||
        network_ref = db.project_get_network(admin, project_id)
 | 
			
		||||
        db.network_update(admin,
 | 
			
		||||
                          network_ref['id'],
 | 
			
		||||
                          {'vpn_public_address': ip,
 | 
			
		||||
                           'vpn_public_port': int(port)})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ShellCommands(object):
 | 
			
		||||
    def bpython(self):
 | 
			
		||||
@@ -295,6 +307,14 @@ class UserCommands(object):
 | 
			
		||||
            is_admin = False
 | 
			
		||||
        self.manager.modify_user(name, access_key, secret_key, is_admin)
 | 
			
		||||
 | 
			
		||||
    def revoke(self, user_id, project_id=None):
 | 
			
		||||
        """revoke certs for a user
 | 
			
		||||
        arguments: user_id [project_id]"""
 | 
			
		||||
        if project_id:
 | 
			
		||||
            crypto.revoke_certs_by_user_and_project(user_id, project_id)
 | 
			
		||||
        else:
 | 
			
		||||
            crypto.revoke_certs_by_user(user_id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ProjectCommands(object):
 | 
			
		||||
    """Class for managing projects."""
 | 
			
		||||
 
 | 
			
		||||
@@ -194,6 +194,7 @@ class HostInfo(object):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NovaAdminClient(object):
 | 
			
		||||
 | 
			
		||||
    def __init__(
 | 
			
		||||
            self,
 | 
			
		||||
            clc_url=DEFAULT_CLC_URL,
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,6 @@ class DbDriver(object):
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        """Imports the LDAP module"""
 | 
			
		||||
        pass
 | 
			
		||||
        db
 | 
			
		||||
 | 
			
		||||
    def __enter__(self):
 | 
			
		||||
        return self
 | 
			
		||||
@@ -83,7 +82,7 @@ class DbDriver(object):
 | 
			
		||||
            user_ref = db.user_create(context.get_admin_context(), values)
 | 
			
		||||
            return self._db_user_to_auth_user(user_ref)
 | 
			
		||||
        except exception.Duplicate, e:
 | 
			
		||||
            raise exception.Duplicate('User %s already exists' % name)
 | 
			
		||||
            raise exception.Duplicate(_('User %s already exists') % name)
 | 
			
		||||
 | 
			
		||||
    def _db_user_to_auth_user(self, user_ref):
 | 
			
		||||
        return {'id': user_ref['id'],
 | 
			
		||||
@@ -105,8 +104,9 @@ class DbDriver(object):
 | 
			
		||||
        """Create a project"""
 | 
			
		||||
        manager = db.user_get(context.get_admin_context(), manager_uid)
 | 
			
		||||
        if not manager:
 | 
			
		||||
            raise exception.NotFound("Project can't be created because "
 | 
			
		||||
                                     "manager %s doesn't exist" % manager_uid)
 | 
			
		||||
            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:
 | 
			
		||||
@@ -133,8 +133,8 @@ class DbDriver(object):
 | 
			
		||||
        try:
 | 
			
		||||
            project = db.project_create(context.get_admin_context(), values)
 | 
			
		||||
        except exception.Duplicate:
 | 
			
		||||
            raise exception.Duplicate("Project can't be created because "
 | 
			
		||||
                                      "project %s already exists" % name)
 | 
			
		||||
            raise exception.Duplicate(_("Project can't be created because "
 | 
			
		||||
                                        "project %s already exists") % name)
 | 
			
		||||
 | 
			
		||||
        for member in members:
 | 
			
		||||
            db.project_add_member(context.get_admin_context(),
 | 
			
		||||
@@ -155,8 +155,8 @@ class DbDriver(object):
 | 
			
		||||
        if manager_uid:
 | 
			
		||||
            manager = db.user_get(context.get_admin_context(), manager_uid)
 | 
			
		||||
            if not manager:
 | 
			
		||||
                raise exception.NotFound("Project can't be modified because "
 | 
			
		||||
                                          "manager %s doesn't exist" %
 | 
			
		||||
                raise exception.NotFound(_("Project can't be modified because "
 | 
			
		||||
                                           "manager %s doesn't exist") %
 | 
			
		||||
                                          manager_uid)
 | 
			
		||||
            values['project_manager'] = manager['id']
 | 
			
		||||
        if description:
 | 
			
		||||
@@ -243,8 +243,8 @@ class DbDriver(object):
 | 
			
		||||
    def _validate_user_and_project(self, user_id, project_id):
 | 
			
		||||
        user = db.user_get(context.get_admin_context(), user_id)
 | 
			
		||||
        if not user:
 | 
			
		||||
            raise exception.NotFound('User "%s" not found' % user_id)
 | 
			
		||||
            raise exception.NotFound(_('User "%s" not found') % user_id)
 | 
			
		||||
        project = db.project_get(context.get_admin_context(), project_id)
 | 
			
		||||
        if not project:
 | 
			
		||||
            raise exception.NotFound('Project "%s" not found' % project_id)
 | 
			
		||||
            raise exception.NotFound(_('Project "%s" not found') % project_id)
 | 
			
		||||
        return user, project
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
"""Fake LDAP server for test harness, backs to ReDIS.
 | 
			
		||||
"""Fake LDAP server for test harness.
 | 
			
		||||
 | 
			
		||||
This class does very little error checking, and knows nothing about ldap
 | 
			
		||||
class definitions.  It implements the minimum emulation of the python ldap
 | 
			
		||||
@@ -23,34 +23,65 @@ library to work with nova.
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import fnmatch
 | 
			
		||||
import json
 | 
			
		||||
import redis
 | 
			
		||||
 | 
			
		||||
from nova import flags
 | 
			
		||||
 | 
			
		||||
FLAGS = flags.FLAGS
 | 
			
		||||
flags.DEFINE_string('redis_host', '127.0.0.1',
 | 
			
		||||
                    'Host that redis is running on.')
 | 
			
		||||
flags.DEFINE_integer('redis_port', 6379,
 | 
			
		||||
                    'Port that redis is running on.')
 | 
			
		||||
flags.DEFINE_integer('redis_db', 0, 'Multiple DB keeps tests away')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Redis(object):
 | 
			
		||||
class Store(object):
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        if hasattr(self.__class__, '_instance'):
 | 
			
		||||
            raise Exception('Attempted to instantiate singleton')
 | 
			
		||||
            raise Exception(_('Attempted to instantiate singleton'))
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def instance(cls):
 | 
			
		||||
        if not hasattr(cls, '_instance'):
 | 
			
		||||
            inst = redis.Redis(host=FLAGS.redis_host,
 | 
			
		||||
                               port=FLAGS.redis_port,
 | 
			
		||||
                               db=FLAGS.redis_db)
 | 
			
		||||
            cls._instance = inst
 | 
			
		||||
            cls._instance = _StorageDict()
 | 
			
		||||
        return cls._instance
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class _StorageDict(dict):
 | 
			
		||||
    def keys(self, pat=None):
 | 
			
		||||
        ret = super(_StorageDict, self).keys()
 | 
			
		||||
        if pat is not None:
 | 
			
		||||
            ret = fnmatch.filter(ret, pat)
 | 
			
		||||
        return ret
 | 
			
		||||
 | 
			
		||||
    def delete(self, key):
 | 
			
		||||
        try:
 | 
			
		||||
            del self[key]
 | 
			
		||||
        except KeyError:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
    def flushdb(self):
 | 
			
		||||
        self.clear()
 | 
			
		||||
 | 
			
		||||
    def hgetall(self, key):
 | 
			
		||||
        """Returns the hash for the given key; creates
 | 
			
		||||
        the hash if the key doesn't exist."""
 | 
			
		||||
        try:
 | 
			
		||||
            return self[key]
 | 
			
		||||
        except KeyError:
 | 
			
		||||
            self[key] = {}
 | 
			
		||||
            return self[key]
 | 
			
		||||
 | 
			
		||||
    def hget(self, key, field):
 | 
			
		||||
        hashdict = self.hgetall(key)
 | 
			
		||||
        try:
 | 
			
		||||
            return hashdict[field]
 | 
			
		||||
        except KeyError:
 | 
			
		||||
            hashdict[field] = {}
 | 
			
		||||
            return hashdict[field]
 | 
			
		||||
 | 
			
		||||
    def hset(self, key, field, val):
 | 
			
		||||
        hashdict = self.hgetall(key)
 | 
			
		||||
        hashdict[field] = val
 | 
			
		||||
 | 
			
		||||
    def hmset(self, key, value_dict):
 | 
			
		||||
        hashdict = self.hgetall(key)
 | 
			
		||||
        for field, val in value_dict.items():
 | 
			
		||||
            hashdict[field] = val
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
SCOPE_BASE = 0
 | 
			
		||||
SCOPE_ONELEVEL = 1  # Not implemented
 | 
			
		||||
SCOPE_SUBTREE = 2
 | 
			
		||||
@@ -119,6 +150,9 @@ def _match(key, value, attrs):
 | 
			
		||||
    """Match a given key and value against an attribute list."""
 | 
			
		||||
    if key not in attrs:
 | 
			
		||||
        return False
 | 
			
		||||
    # This is a wild card search. Implemented as all or nothing for now.
 | 
			
		||||
    if value == "*":
 | 
			
		||||
        return True
 | 
			
		||||
    if key != "objectclass":
 | 
			
		||||
        return value in attrs[key]
 | 
			
		||||
    # it is an objectclass check, so check subclasses
 | 
			
		||||
@@ -169,8 +203,6 @@ def _to_json(unencoded):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FakeLDAP(object):
 | 
			
		||||
    #TODO(vish): refactor this class to use a wrapper instead of accessing
 | 
			
		||||
    #            redis directly
 | 
			
		||||
    """Fake LDAP connection."""
 | 
			
		||||
 | 
			
		||||
    def simple_bind_s(self, dn, password):
 | 
			
		||||
@@ -183,14 +215,13 @@ class FakeLDAP(object):
 | 
			
		||||
 | 
			
		||||
    def add_s(self, dn, attr):
 | 
			
		||||
        """Add an object with the specified attributes at dn."""
 | 
			
		||||
        key = "%s%s" % (self.__redis_prefix, dn)
 | 
			
		||||
 | 
			
		||||
        key = "%s%s" % (self.__prefix, dn)
 | 
			
		||||
        value_dict = dict([(k, _to_json(v)) for k, v in attr])
 | 
			
		||||
        Redis.instance().hmset(key, value_dict)
 | 
			
		||||
        Store.instance().hmset(key, value_dict)
 | 
			
		||||
 | 
			
		||||
    def delete_s(self, dn):
 | 
			
		||||
        """Remove the ldap object at specified dn."""
 | 
			
		||||
        Redis.instance().delete("%s%s" % (self.__redis_prefix, dn))
 | 
			
		||||
        Store.instance().delete("%s%s" % (self.__prefix, dn))
 | 
			
		||||
 | 
			
		||||
    def modify_s(self, dn, attrs):
 | 
			
		||||
        """Modify the object at dn using the attribute list.
 | 
			
		||||
@@ -201,18 +232,18 @@ class FakeLDAP(object):
 | 
			
		||||
            ([MOD_ADD | MOD_DELETE | MOD_REPACE], attribute, value)
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        redis = Redis.instance()
 | 
			
		||||
        key = "%s%s" % (self.__redis_prefix, dn)
 | 
			
		||||
        store = Store.instance()
 | 
			
		||||
        key = "%s%s" % (self.__prefix, dn)
 | 
			
		||||
 | 
			
		||||
        for cmd, k, v in attrs:
 | 
			
		||||
            values = _from_json(redis.hget(key, k))
 | 
			
		||||
            values = _from_json(store.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))
 | 
			
		||||
            values = store.hset(key, k, _to_json(values))
 | 
			
		||||
 | 
			
		||||
    def search_s(self, dn, scope, query=None, fields=None):
 | 
			
		||||
        """Search for all matching objects under dn using the query.
 | 
			
		||||
@@ -226,16 +257,17 @@ class FakeLDAP(object):
 | 
			
		||||
        """
 | 
			
		||||
        if scope != SCOPE_BASE and scope != SCOPE_SUBTREE:
 | 
			
		||||
            raise NotImplementedError(str(scope))
 | 
			
		||||
        redis = Redis.instance()
 | 
			
		||||
        store = Store.instance()
 | 
			
		||||
        if scope == SCOPE_BASE:
 | 
			
		||||
            keys = ["%s%s" % (self.__redis_prefix, dn)]
 | 
			
		||||
            keys = ["%s%s" % (self.__prefix, dn)]
 | 
			
		||||
        else:
 | 
			
		||||
            keys = redis.keys("%s*%s" % (self.__redis_prefix, dn))
 | 
			
		||||
            keys = store.keys("%s*%s" % (self.__prefix, dn))
 | 
			
		||||
 | 
			
		||||
        objects = []
 | 
			
		||||
        for key in keys:
 | 
			
		||||
            # get the attributes from redis
 | 
			
		||||
            attrs = redis.hgetall(key)
 | 
			
		||||
            # turn the values from redis into lists
 | 
			
		||||
            # get the attributes from the store
 | 
			
		||||
            attrs = store.hgetall(key)
 | 
			
		||||
            # turn the values from the store into lists
 | 
			
		||||
            # pylint: disable-msg=E1103
 | 
			
		||||
            attrs = dict([(k, _from_json(v))
 | 
			
		||||
                          for k, v in attrs.iteritems()])
 | 
			
		||||
@@ -244,13 +276,13 @@ class FakeLDAP(object):
 | 
			
		||||
                # filter the attributes by fields
 | 
			
		||||
                attrs = dict([(k, v) for k, v in attrs.iteritems()
 | 
			
		||||
                              if not fields or k in fields])
 | 
			
		||||
                objects.append((key[len(self.__redis_prefix):], attrs))
 | 
			
		||||
                objects.append((key[len(self.__prefix):], attrs))
 | 
			
		||||
            # pylint: enable-msg=E1103
 | 
			
		||||
        if objects == []:
 | 
			
		||||
            raise NO_SUCH_OBJECT()
 | 
			
		||||
        return objects
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def __redis_prefix(self):  # pylint: disable-msg=R0201
 | 
			
		||||
        """Get the prefix to use for all redis keys."""
 | 
			
		||||
    def __prefix(self):  # pylint: disable-msg=R0201
 | 
			
		||||
        """Get the prefix to use for all keys."""
 | 
			
		||||
        return 'ldap:'
 | 
			
		||||
 
 | 
			
		||||
@@ -32,11 +32,16 @@ from nova import flags
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FLAGS = flags.FLAGS
 | 
			
		||||
flags.DEFINE_integer('ldap_schema_version', 2,
 | 
			
		||||
                     'Current version of the LDAP schema')
 | 
			
		||||
flags.DEFINE_string('ldap_url', 'ldap://localhost',
 | 
			
		||||
                    'Point this at your ldap server')
 | 
			
		||||
flags.DEFINE_string('ldap_password', 'changeme', 'LDAP password')
 | 
			
		||||
flags.DEFINE_string('ldap_user_dn', 'cn=Manager,dc=example,dc=com',
 | 
			
		||||
                    'DN of admin user')
 | 
			
		||||
flags.DEFINE_string('ldap_user_id_attribute', 'uid', 'Attribute to use as id')
 | 
			
		||||
flags.DEFINE_string('ldap_user_name_attribute', 'cn',
 | 
			
		||||
                    'Attribute to use as name')
 | 
			
		||||
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')
 | 
			
		||||
@@ -73,10 +78,20 @@ class LdapDriver(object):
 | 
			
		||||
    Defines enter and exit and therefore supports the with/as syntax.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    project_pattern = '(owner=*)'
 | 
			
		||||
    isadmin_attribute = 'isNovaAdmin'
 | 
			
		||||
    project_attribute = 'owner'
 | 
			
		||||
    project_objectclass = 'groupOfNames'
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        """Imports the LDAP module"""
 | 
			
		||||
        self.ldap = __import__('ldap')
 | 
			
		||||
        self.conn = None
 | 
			
		||||
        if FLAGS.ldap_schema_version == 1:
 | 
			
		||||
            LdapDriver.project_pattern = '(objectclass=novaProject)'
 | 
			
		||||
            LdapDriver.isadmin_attribute = 'isAdmin'
 | 
			
		||||
            LdapDriver.project_attribute = 'projectManager'
 | 
			
		||||
            LdapDriver.project_objectclass = 'novaProject'
 | 
			
		||||
 | 
			
		||||
    def __enter__(self):
 | 
			
		||||
        """Creates the connection to LDAP"""
 | 
			
		||||
@@ -104,13 +119,13 @@ class LdapDriver(object):
 | 
			
		||||
        """Retrieve project by id"""
 | 
			
		||||
        dn = 'cn=%s,%s' % (pid,
 | 
			
		||||
                           FLAGS.ldap_project_subtree)
 | 
			
		||||
        attr = self.__find_object(dn, '(objectclass=novaProject)')
 | 
			
		||||
        attr = self.__find_object(dn, LdapDriver.project_pattern)
 | 
			
		||||
        return self.__to_project(attr)
 | 
			
		||||
 | 
			
		||||
    def get_users(self):
 | 
			
		||||
        """Retrieve list of users"""
 | 
			
		||||
        attrs = self.__find_objects(FLAGS.ldap_user_subtree,
 | 
			
		||||
                                  '(objectclass=novaUser)')
 | 
			
		||||
                                    '(objectclass=novaUser)')
 | 
			
		||||
        users = []
 | 
			
		||||
        for attr in attrs:
 | 
			
		||||
            user = self.__to_user(attr)
 | 
			
		||||
@@ -120,7 +135,7 @@ class LdapDriver(object):
 | 
			
		||||
 | 
			
		||||
    def get_projects(self, uid=None):
 | 
			
		||||
        """Retrieve list of projects"""
 | 
			
		||||
        pattern = '(objectclass=novaProject)'
 | 
			
		||||
        pattern = LdapDriver.project_pattern
 | 
			
		||||
        if uid:
 | 
			
		||||
            pattern = "(&%s(member=%s))" % (pattern, self.__uid_to_dn(uid))
 | 
			
		||||
        attrs = self.__find_objects(FLAGS.ldap_project_subtree,
 | 
			
		||||
@@ -139,27 +154,29 @@ class LdapDriver(object):
 | 
			
		||||
                # Malformed entries are useless, replace attributes found.
 | 
			
		||||
                attr = []
 | 
			
		||||
                if 'secretKey' in user.keys():
 | 
			
		||||
                    attr.append((self.ldap.MOD_REPLACE, 'secretKey', \
 | 
			
		||||
                    [secret_key]))
 | 
			
		||||
                    attr.append((self.ldap.MOD_REPLACE, 'secretKey',
 | 
			
		||||
                                 [secret_key]))
 | 
			
		||||
                else:
 | 
			
		||||
                    attr.append((self.ldap.MOD_ADD, 'secretKey', \
 | 
			
		||||
                    [secret_key]))
 | 
			
		||||
                    attr.append((self.ldap.MOD_ADD, 'secretKey',
 | 
			
		||||
                                 [secret_key]))
 | 
			
		||||
                if 'accessKey' in user.keys():
 | 
			
		||||
                    attr.append((self.ldap.MOD_REPLACE, 'accessKey', \
 | 
			
		||||
                    [access_key]))
 | 
			
		||||
                    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()]))
 | 
			
		||||
                    attr.append((self.ldap.MOD_ADD, 'accessKey',
 | 
			
		||||
                                 [access_key]))
 | 
			
		||||
                if LdapDriver.isadmin_attribute in user.keys():
 | 
			
		||||
                    attr.append((self.ldap.MOD_REPLACE,
 | 
			
		||||
                                 LdapDriver.isadmin_attribute,
 | 
			
		||||
                                 [str(is_admin).upper()]))
 | 
			
		||||
                else:
 | 
			
		||||
                    attr.append((self.ldap.MOD_ADD, 'isAdmin', \
 | 
			
		||||
                    [str(is_admin).upper()]))
 | 
			
		||||
                    attr.append((self.ldap.MOD_ADD,
 | 
			
		||||
                                 LdapDriver.isadmin_attribute,
 | 
			
		||||
                                 [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"
 | 
			
		||||
                raise exception.NotFound(_("LDAP object for %s doesn't exist")
 | 
			
		||||
                                         % name)
 | 
			
		||||
        else:
 | 
			
		||||
            attr = [
 | 
			
		||||
@@ -168,12 +185,12 @@ class LdapDriver(object):
 | 
			
		||||
                                 'inetOrgPerson',
 | 
			
		||||
                                 'novaUser']),
 | 
			
		||||
                ('ou', [FLAGS.ldap_user_unit]),
 | 
			
		||||
                ('uid', [name]),
 | 
			
		||||
                (FLAGS.ldap_user_id_attribute, [name]),
 | 
			
		||||
                ('sn', [name]),
 | 
			
		||||
                ('cn', [name]),
 | 
			
		||||
                (FLAGS.ldap_user_name_attribute, [name]),
 | 
			
		||||
                ('secretKey', [secret_key]),
 | 
			
		||||
                ('accessKey', [access_key]),
 | 
			
		||||
                ('isAdmin', [str(is_admin).upper()]),
 | 
			
		||||
                (LdapDriver.isadmin_attribute, [str(is_admin).upper()]),
 | 
			
		||||
            ]
 | 
			
		||||
            self.conn.add_s(self.__uid_to_dn(name), attr)
 | 
			
		||||
            return self.__to_user(dict(attr))
 | 
			
		||||
@@ -182,11 +199,12 @@ class LdapDriver(object):
 | 
			
		||||
                       description=None, member_uids=None):
 | 
			
		||||
        """Create a project"""
 | 
			
		||||
        if self.__project_exists(name):
 | 
			
		||||
            raise exception.Duplicate("Project can't be created because "
 | 
			
		||||
                                      "project %s already exists" % name)
 | 
			
		||||
            raise exception.Duplicate(_("Project can't be created because "
 | 
			
		||||
                                        "project %s already exists") % name)
 | 
			
		||||
        if not self.__user_exists(manager_uid):
 | 
			
		||||
            raise exception.NotFound("Project can't be created because "
 | 
			
		||||
                                     "manager %s doesn't exist" % manager_uid)
 | 
			
		||||
            raise exception.NotFound(_("Project can't be created because "
 | 
			
		||||
                                       "manager %s doesn't exist")
 | 
			
		||||
                                     % manager_uid)
 | 
			
		||||
        manager_dn = self.__uid_to_dn(manager_uid)
 | 
			
		||||
        # description is a required attribute
 | 
			
		||||
        if description is None:
 | 
			
		||||
@@ -195,18 +213,18 @@ class LdapDriver(object):
 | 
			
		||||
        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 "
 | 
			
		||||
                                             "because user %s doesn't exist"
 | 
			
		||||
                    raise exception.NotFound(_("Project can't be created "
 | 
			
		||||
                                               "because user %s doesn't exist")
 | 
			
		||||
                                             % member_uid)
 | 
			
		||||
                members.append(self.__uid_to_dn(member_uid))
 | 
			
		||||
        # always add the manager as a member because members is required
 | 
			
		||||
        if not manager_dn in members:
 | 
			
		||||
            members.append(manager_dn)
 | 
			
		||||
        attr = [
 | 
			
		||||
            ('objectclass', ['novaProject']),
 | 
			
		||||
            ('objectclass', [LdapDriver.project_objectclass]),
 | 
			
		||||
            ('cn', [name]),
 | 
			
		||||
            ('description', [description]),
 | 
			
		||||
            ('projectManager', [manager_dn]),
 | 
			
		||||
            (LdapDriver.project_attribute, [manager_dn]),
 | 
			
		||||
            ('member', members)]
 | 
			
		||||
        self.conn.add_s('cn=%s,%s' % (name, FLAGS.ldap_project_subtree), attr)
 | 
			
		||||
        return self.__to_project(dict(attr))
 | 
			
		||||
@@ -218,11 +236,12 @@ class LdapDriver(object):
 | 
			
		||||
        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)
 | 
			
		||||
                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))
 | 
			
		||||
            attr.append((self.ldap.MOD_REPLACE, LdapDriver.project_attribute,
 | 
			
		||||
                         manager_dn))
 | 
			
		||||
        if description:
 | 
			
		||||
            attr.append((self.ldap.MOD_REPLACE, 'description', description))
 | 
			
		||||
        self.conn.modify_s('cn=%s,%s' % (project_id,
 | 
			
		||||
@@ -282,10 +301,9 @@ class LdapDriver(object):
 | 
			
		||||
            return roles
 | 
			
		||||
        else:
 | 
			
		||||
            project_dn = 'cn=%s,%s' % (project_id, FLAGS.ldap_project_subtree)
 | 
			
		||||
            roles = self.__find_objects(project_dn,
 | 
			
		||||
                                        '(&(&(objectclass=groupOfNames)'
 | 
			
		||||
                                        '(!(objectclass=novaProject)))'
 | 
			
		||||
                                        '(member=%s))' % self.__uid_to_dn(uid))
 | 
			
		||||
            query = ('(&(&(objectclass=groupOfNames)(!%s))(member=%s))' %
 | 
			
		||||
                     (LdapDriver.project_pattern, self.__uid_to_dn(uid)))
 | 
			
		||||
            roles = self.__find_objects(project_dn, query)
 | 
			
		||||
            return [role['cn'][0] for role in roles]
 | 
			
		||||
 | 
			
		||||
    def delete_user(self, uid):
 | 
			
		||||
@@ -299,14 +317,15 @@ class LdapDriver(object):
 | 
			
		||||
            # Retrieve user by name
 | 
			
		||||
            user = self.__get_ldap_user(uid)
 | 
			
		||||
            if 'secretKey' in user.keys():
 | 
			
		||||
                attr.append((self.ldap.MOD_DELETE, 'secretKey', \
 | 
			
		||||
                user['secretKey']))
 | 
			
		||||
                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']))
 | 
			
		||||
                attr.append((self.ldap.MOD_DELETE, 'accessKey',
 | 
			
		||||
                             user['accessKey']))
 | 
			
		||||
            if LdapDriver.isadmin_attribute in user.keys():
 | 
			
		||||
                attr.append((self.ldap.MOD_DELETE,
 | 
			
		||||
                             LdapDriver.isadmin_attribute,
 | 
			
		||||
                             user[LdapDriver.isadmin_attribute]))
 | 
			
		||||
            self.conn.modify_s(self.__uid_to_dn(uid), attr)
 | 
			
		||||
        else:
 | 
			
		||||
            # Delete entry
 | 
			
		||||
@@ -328,7 +347,8 @@ class LdapDriver(object):
 | 
			
		||||
        if secret_key:
 | 
			
		||||
            attr.append((self.ldap.MOD_REPLACE, 'secretKey', secret_key))
 | 
			
		||||
        if admin is not None:
 | 
			
		||||
            attr.append((self.ldap.MOD_REPLACE, 'isAdmin', str(admin).upper()))
 | 
			
		||||
            attr.append((self.ldap.MOD_REPLACE, LdapDriver.isadmin_attribute,
 | 
			
		||||
                         str(admin).upper()))
 | 
			
		||||
        self.conn.modify_s(self.__uid_to_dn(uid), attr)
 | 
			
		||||
 | 
			
		||||
    def __user_exists(self, uid):
 | 
			
		||||
@@ -346,7 +366,7 @@ class LdapDriver(object):
 | 
			
		||||
    def __get_ldap_user(self, uid):
 | 
			
		||||
        """Retrieve LDAP user entry by id"""
 | 
			
		||||
        attr = self.__find_object(self.__uid_to_dn(uid),
 | 
			
		||||
                                '(objectclass=novaUser)')
 | 
			
		||||
                                  '(objectclass=novaUser)')
 | 
			
		||||
        return attr
 | 
			
		||||
 | 
			
		||||
    def __find_object(self, dn, query=None, scope=None):
 | 
			
		||||
@@ -382,19 +402,21 @@ class LdapDriver(object):
 | 
			
		||||
 | 
			
		||||
    def __find_role_dns(self, tree):
 | 
			
		||||
        """Find dns of role objects in given tree"""
 | 
			
		||||
        return self.__find_dns(tree,
 | 
			
		||||
                '(&(objectclass=groupOfNames)(!(objectclass=novaProject)))')
 | 
			
		||||
        query = ('(&(objectclass=groupOfNames)(!%s))' %
 | 
			
		||||
                 LdapDriver.project_pattern)
 | 
			
		||||
        return self.__find_dns(tree, query)
 | 
			
		||||
 | 
			
		||||
    def __find_group_dns_with_member(self, tree, uid):
 | 
			
		||||
        """Find dns of group objects in a given tree that contain member"""
 | 
			
		||||
        dns = self.__find_dns(tree,
 | 
			
		||||
                            '(&(objectclass=groupOfNames)(member=%s))' %
 | 
			
		||||
                            self.__uid_to_dn(uid))
 | 
			
		||||
        query = ('(&(objectclass=groupOfNames)(member=%s))' %
 | 
			
		||||
                 self.__uid_to_dn(uid))
 | 
			
		||||
        dns = self.__find_dns(tree, query)
 | 
			
		||||
        return dns
 | 
			
		||||
 | 
			
		||||
    def __group_exists(self, dn):
 | 
			
		||||
        """Check if group exists"""
 | 
			
		||||
        return self.__find_object(dn, '(objectclass=groupOfNames)') is not None
 | 
			
		||||
        query = '(objectclass=groupOfNames)'
 | 
			
		||||
        return self.__find_object(dn, query) is not None
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def __role_to_dn(role, project_id=None):
 | 
			
		||||
@@ -417,7 +439,8 @@ class LdapDriver(object):
 | 
			
		||||
            for member_uid in member_uids:
 | 
			
		||||
                if not self.__user_exists(member_uid):
 | 
			
		||||
                    raise exception.NotFound("Group can't be created "
 | 
			
		||||
                            "because user %s doesn't exist" % member_uid)
 | 
			
		||||
                                             "because user %s doesn't exist" %
 | 
			
		||||
                                             member_uid)
 | 
			
		||||
                members.append(self.__uid_to_dn(member_uid))
 | 
			
		||||
        dn = self.__uid_to_dn(uid)
 | 
			
		||||
        if not dn in members:
 | 
			
		||||
@@ -433,7 +456,7 @@ class LdapDriver(object):
 | 
			
		||||
        """Check if user is in group"""
 | 
			
		||||
        if not self.__user_exists(uid):
 | 
			
		||||
            raise exception.NotFound("User %s can't be searched in group "
 | 
			
		||||
                    "becuase the user doesn't exist" % (uid,))
 | 
			
		||||
                                     "because the user doesn't exist" % uid)
 | 
			
		||||
        if not self.__group_exists(group_dn):
 | 
			
		||||
            return False
 | 
			
		||||
        res = self.__find_object(group_dn,
 | 
			
		||||
@@ -445,13 +468,13 @@ class LdapDriver(object):
 | 
			
		||||
        """Add user to group"""
 | 
			
		||||
        if not self.__user_exists(uid):
 | 
			
		||||
            raise exception.NotFound("User %s can't be added to the group "
 | 
			
		||||
                    "becuase the user doesn't exist" % (uid,))
 | 
			
		||||
                                     "because the user doesn't exist" % uid)
 | 
			
		||||
        if not self.__group_exists(group_dn):
 | 
			
		||||
            raise exception.NotFound("The group at dn %s doesn't exist" %
 | 
			
		||||
                                     (group_dn,))
 | 
			
		||||
                                     group_dn)
 | 
			
		||||
        if self.__is_in_group(uid, group_dn):
 | 
			
		||||
            raise exception.Duplicate("User %s is already a member of "
 | 
			
		||||
                                      "the group %s" % (uid, group_dn))
 | 
			
		||||
            raise exception.Duplicate(_("User %s is already a member of "
 | 
			
		||||
                                        "the group %s") % (uid, group_dn))
 | 
			
		||||
        attr = [(self.ldap.MOD_ADD, 'member', self.__uid_to_dn(uid))]
 | 
			
		||||
        self.conn.modify_s(group_dn, attr)
 | 
			
		||||
 | 
			
		||||
@@ -459,16 +482,16 @@ class LdapDriver(object):
 | 
			
		||||
        """Remove user from group"""
 | 
			
		||||
        if not self.__group_exists(group_dn):
 | 
			
		||||
            raise exception.NotFound("The group at dn %s doesn't exist" %
 | 
			
		||||
                                     (group_dn,))
 | 
			
		||||
                                     group_dn)
 | 
			
		||||
        if not self.__user_exists(uid):
 | 
			
		||||
            raise exception.NotFound("User %s can't be removed from the "
 | 
			
		||||
                    "group because the user doesn't exist" % (uid,))
 | 
			
		||||
                                     "group because the user doesn't exist" %
 | 
			
		||||
                                     uid)
 | 
			
		||||
        if not self.__is_in_group(uid, group_dn):
 | 
			
		||||
            raise exception.NotFound("User %s is not a member of the group" %
 | 
			
		||||
                                     (uid,))
 | 
			
		||||
                                     uid)
 | 
			
		||||
        # NOTE(vish): remove user from group and any sub_groups
 | 
			
		||||
        sub_dns = self.__find_group_dns_with_member(
 | 
			
		||||
                group_dn, uid)
 | 
			
		||||
        sub_dns = self.__find_group_dns_with_member(group_dn, uid)
 | 
			
		||||
        for sub_dn in sub_dns:
 | 
			
		||||
            self.__safe_remove_from_group(uid, sub_dn)
 | 
			
		||||
 | 
			
		||||
@@ -479,15 +502,15 @@ class LdapDriver(object):
 | 
			
		||||
        try:
 | 
			
		||||
            self.conn.modify_s(group_dn, attr)
 | 
			
		||||
        except self.ldap.OBJECT_CLASS_VIOLATION:
 | 
			
		||||
            logging.debug("Attempted to remove the last member of a group. "
 | 
			
		||||
                          "Deleting the group at %s instead.", group_dn)
 | 
			
		||||
            logging.debug(_("Attempted to remove the last member of a group. "
 | 
			
		||||
                            "Deleting the group at %s instead."), group_dn)
 | 
			
		||||
            self.__delete_group(group_dn)
 | 
			
		||||
 | 
			
		||||
    def __remove_from_all(self, uid):
 | 
			
		||||
        """Remove user from all roles and projects"""
 | 
			
		||||
        if not self.__user_exists(uid):
 | 
			
		||||
            raise exception.NotFound("User %s can't be removed from all "
 | 
			
		||||
                    "because the user doesn't exist" % (uid,))
 | 
			
		||||
                                     "because the user doesn't exist" % uid)
 | 
			
		||||
        role_dns = self.__find_group_dns_with_member(
 | 
			
		||||
                FLAGS.role_project_subtree, uid)
 | 
			
		||||
        for role_dn in role_dns:
 | 
			
		||||
@@ -500,7 +523,8 @@ class LdapDriver(object):
 | 
			
		||||
    def __delete_group(self, group_dn):
 | 
			
		||||
        """Delete Group"""
 | 
			
		||||
        if not self.__group_exists(group_dn):
 | 
			
		||||
            raise exception.NotFound("Group at dn %s doesn't exist" % group_dn)
 | 
			
		||||
            raise exception.NotFound(_("Group at dn %s doesn't exist")
 | 
			
		||||
                                     % group_dn)
 | 
			
		||||
        self.conn.delete_s(group_dn)
 | 
			
		||||
 | 
			
		||||
    def __delete_roles(self, project_dn):
 | 
			
		||||
@@ -514,13 +538,13 @@ class LdapDriver(object):
 | 
			
		||||
        if attr is None:
 | 
			
		||||
            return None
 | 
			
		||||
        if ('accessKey' in attr.keys() and 'secretKey' in attr.keys() \
 | 
			
		||||
            and 'isAdmin' in attr.keys()):
 | 
			
		||||
            and LdapDriver.isadmin_attribute in attr.keys()):
 | 
			
		||||
            return {
 | 
			
		||||
                'id': attr['uid'][0],
 | 
			
		||||
                'name': attr['cn'][0],
 | 
			
		||||
                'id': attr[FLAGS.ldap_user_id_attribute][0],
 | 
			
		||||
                'name': attr[FLAGS.ldap_user_name_attribute][0],
 | 
			
		||||
                'access': attr['accessKey'][0],
 | 
			
		||||
                'secret': attr['secretKey'][0],
 | 
			
		||||
                'admin': (attr['isAdmin'][0] == 'TRUE')}
 | 
			
		||||
                'admin': (attr[LdapDriver.isadmin_attribute][0] == 'TRUE')}
 | 
			
		||||
        else:
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
@@ -532,7 +556,8 @@ class LdapDriver(object):
 | 
			
		||||
        return {
 | 
			
		||||
            'id': attr['cn'][0],
 | 
			
		||||
            'name': attr['cn'][0],
 | 
			
		||||
            'project_manager_id': self.__dn_to_uid(attr['projectManager'][0]),
 | 
			
		||||
            'project_manager_id':
 | 
			
		||||
                self.__dn_to_uid(attr[LdapDriver.project_attribute][0]),
 | 
			
		||||
            'description': attr.get('description', [None])[0],
 | 
			
		||||
            'member_ids': [self.__dn_to_uid(x) for x in member_dns]}
 | 
			
		||||
 | 
			
		||||
@@ -542,9 +567,10 @@ class LdapDriver(object):
 | 
			
		||||
        return dn.split(',')[0].split('=')[1]
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def __uid_to_dn(dn):
 | 
			
		||||
    def __uid_to_dn(uid):
 | 
			
		||||
        """Convert uid to dn"""
 | 
			
		||||
        return 'uid=%s,%s' % (dn, FLAGS.ldap_user_subtree)
 | 
			
		||||
        return (FLAGS.ldap_user_id_attribute + '=%s,%s'
 | 
			
		||||
                % (uid, FLAGS.ldap_user_subtree))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FakeLdapDriver(LdapDriver):
 | 
			
		||||
 
 | 
			
		||||
@@ -64,12 +64,9 @@ flags.DEFINE_string('credential_key_file', 'pk.pem',
 | 
			
		||||
                    'Filename of private key in credentials zip')
 | 
			
		||||
flags.DEFINE_string('credential_cert_file', 'cert.pem',
 | 
			
		||||
                    'Filename of certificate in credentials zip')
 | 
			
		||||
flags.DEFINE_string('credential_rc_file', 'novarc',
 | 
			
		||||
                    'Filename of rc in credentials zip')
 | 
			
		||||
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('credential_rc_file', '%src',
 | 
			
		||||
                    'Filename of rc in credentials zip, %s will be '
 | 
			
		||||
                    'replaced by name of the region (nova by default)')
 | 
			
		||||
flags.DEFINE_string('auth_driver', 'nova.auth.dbdriver.DbDriver',
 | 
			
		||||
                    'Driver that auth manager uses')
 | 
			
		||||
 | 
			
		||||
@@ -257,12 +254,12 @@ class AuthManager(object):
 | 
			
		||||
        # TODO(vish): check for valid timestamp
 | 
			
		||||
        (access_key, _sep, project_id) = access.partition(':')
 | 
			
		||||
 | 
			
		||||
        logging.info('Looking up user: %r', access_key)
 | 
			
		||||
        logging.info(_('Looking up user: %r'), access_key)
 | 
			
		||||
        user = self.get_user_from_access_key(access_key)
 | 
			
		||||
        logging.info('user: %r', user)
 | 
			
		||||
        if user == None:
 | 
			
		||||
            raise exception.NotFound('No user found for access key %s' %
 | 
			
		||||
                                     access_key)
 | 
			
		||||
            raise exception.NotFound(_('No user found for access key %s')
 | 
			
		||||
                                     % access_key)
 | 
			
		||||
 | 
			
		||||
        # NOTE(vish): if we stop using project name as id we need better
 | 
			
		||||
        #             logic to find a default project for user
 | 
			
		||||
@@ -271,12 +268,12 @@ class AuthManager(object):
 | 
			
		||||
 | 
			
		||||
        project = self.get_project(project_id)
 | 
			
		||||
        if project == None:
 | 
			
		||||
            raise exception.NotFound('No project called %s could be found' %
 | 
			
		||||
                                     project_id)
 | 
			
		||||
            raise exception.NotFound(_('No project called %s could be found')
 | 
			
		||||
                                     % project_id)
 | 
			
		||||
        if not self.is_admin(user) and not self.is_project_member(user,
 | 
			
		||||
                                                                  project):
 | 
			
		||||
            raise exception.NotFound('User %s is not a member of project %s' %
 | 
			
		||||
                                     (user.id, project.id))
 | 
			
		||||
            raise exception.NotFound(_('User %s is not a member of project %s')
 | 
			
		||||
                                     % (user.id, project.id))
 | 
			
		||||
        if check_type == 's3':
 | 
			
		||||
            sign = signer.Signer(user.secret.encode())
 | 
			
		||||
            expected_signature = sign.s3_authorization(headers, verb, path)
 | 
			
		||||
@@ -284,7 +281,7 @@ class AuthManager(object):
 | 
			
		||||
            logging.debug('expected_signature: %s', expected_signature)
 | 
			
		||||
            logging.debug('signature: %s', signature)
 | 
			
		||||
            if signature != expected_signature:
 | 
			
		||||
                raise exception.NotAuthorized('Signature does not match')
 | 
			
		||||
                raise exception.NotAuthorized(_('Signature does not match'))
 | 
			
		||||
        elif check_type == 'ec2':
 | 
			
		||||
            # NOTE(vish): hmac can't handle unicode, so encode ensures that
 | 
			
		||||
            #             secret isn't unicode
 | 
			
		||||
@@ -294,7 +291,7 @@ class AuthManager(object):
 | 
			
		||||
            logging.debug('expected_signature: %s', expected_signature)
 | 
			
		||||
            logging.debug('signature: %s', signature)
 | 
			
		||||
            if signature != expected_signature:
 | 
			
		||||
                raise exception.NotAuthorized('Signature does not match')
 | 
			
		||||
                raise exception.NotAuthorized(_('Signature does not match'))
 | 
			
		||||
        return (user, project)
 | 
			
		||||
 | 
			
		||||
    def get_access_key(self, user, project):
 | 
			
		||||
@@ -364,7 +361,7 @@ class AuthManager(object):
 | 
			
		||||
        with self.driver() as drv:
 | 
			
		||||
            if role == 'projectmanager':
 | 
			
		||||
                if not project:
 | 
			
		||||
                    raise exception.Error("Must specify project")
 | 
			
		||||
                    raise exception.Error(_("Must specify project"))
 | 
			
		||||
                return self.is_project_manager(user, project)
 | 
			
		||||
 | 
			
		||||
            global_role = drv.has_role(User.safe_id(user),
 | 
			
		||||
@@ -398,9 +395,9 @@ class AuthManager(object):
 | 
			
		||||
        @param project: Project in which to add local role.
 | 
			
		||||
        """
 | 
			
		||||
        if role not in FLAGS.allowed_roles:
 | 
			
		||||
            raise exception.NotFound("The %s role can not be found" % role)
 | 
			
		||||
            raise exception.NotFound(_("The %s role can not be found") % role)
 | 
			
		||||
        if project is not None and role in FLAGS.global_roles:
 | 
			
		||||
            raise exception.NotFound("The %s role is global only" % role)
 | 
			
		||||
            raise exception.NotFound(_("The %s role is global only") % role)
 | 
			
		||||
        with self.driver() as drv:
 | 
			
		||||
            drv.add_role(User.safe_id(user), role, Project.safe_id(project))
 | 
			
		||||
 | 
			
		||||
@@ -543,10 +540,10 @@ class AuthManager(object):
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        network_ref = db.project_get_network(context.get_admin_context(),
 | 
			
		||||
                                             Project.safe_id(project))
 | 
			
		||||
                                             Project.safe_id(project), False)
 | 
			
		||||
 | 
			
		||||
        if not network_ref['vpn_public_port']:
 | 
			
		||||
            raise exception.NotFound('project network data has not been set')
 | 
			
		||||
        if not network_ref:
 | 
			
		||||
            return (None, None)
 | 
			
		||||
        return (network_ref['vpn_public_address'],
 | 
			
		||||
                network_ref['vpn_public_port'])
 | 
			
		||||
 | 
			
		||||
@@ -628,27 +625,37 @@ class AuthManager(object):
 | 
			
		||||
    def get_key_pairs(context):
 | 
			
		||||
        return db.key_pair_get_all_by_user(context.elevated(), context.user_id)
 | 
			
		||||
 | 
			
		||||
    def get_credentials(self, user, project=None):
 | 
			
		||||
    def get_credentials(self, user, project=None, use_dmz=True):
 | 
			
		||||
        """Get credential zip for user in project"""
 | 
			
		||||
        if not isinstance(user, User):
 | 
			
		||||
            user = self.get_user(user)
 | 
			
		||||
        if project is None:
 | 
			
		||||
            project = user.id
 | 
			
		||||
        pid = Project.safe_id(project)
 | 
			
		||||
        rc = self.__generate_rc(user.access, user.secret, pid)
 | 
			
		||||
        private_key, signed_cert = self._generate_x509_cert(user.id, pid)
 | 
			
		||||
        private_key, signed_cert = crypto.generate_x509_cert(user.id, pid)
 | 
			
		||||
 | 
			
		||||
        tmpdir = tempfile.mkdtemp()
 | 
			
		||||
        zf = os.path.join(tmpdir, "temp.zip")
 | 
			
		||||
        zippy = zipfile.ZipFile(zf, 'w')
 | 
			
		||||
        zippy.writestr(FLAGS.credential_rc_file, rc)
 | 
			
		||||
        if use_dmz and FLAGS.region_list:
 | 
			
		||||
            regions = {}
 | 
			
		||||
            for item in FLAGS.region_list:
 | 
			
		||||
                region, _sep, region_host = item.partition("=")
 | 
			
		||||
                regions[region] = region_host
 | 
			
		||||
        else:
 | 
			
		||||
            regions = {'nova': FLAGS.cc_host}
 | 
			
		||||
        for region, host in regions.iteritems():
 | 
			
		||||
            rc = self.__generate_rc(user.access,
 | 
			
		||||
                                    user.secret,
 | 
			
		||||
                                    pid,
 | 
			
		||||
                                    use_dmz,
 | 
			
		||||
                                    host)
 | 
			
		||||
            zippy.writestr(FLAGS.credential_rc_file % region, rc)
 | 
			
		||||
 | 
			
		||||
        zippy.writestr(FLAGS.credential_key_file, private_key)
 | 
			
		||||
        zippy.writestr(FLAGS.credential_cert_file, signed_cert)
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            (vpn_ip, vpn_port) = self.get_project_vpn_data(project)
 | 
			
		||||
        except exception.NotFound:
 | 
			
		||||
            vpn_ip = None
 | 
			
		||||
        (vpn_ip, vpn_port) = self.get_project_vpn_data(project)
 | 
			
		||||
        if vpn_ip:
 | 
			
		||||
            configfile = open(FLAGS.vpn_client_template, "r")
 | 
			
		||||
            s = string.Template(configfile.read())
 | 
			
		||||
@@ -659,10 +666,9 @@ class AuthManager(object):
 | 
			
		||||
                                  port=vpn_port)
 | 
			
		||||
            zippy.writestr(FLAGS.credential_vpn_file, config)
 | 
			
		||||
        else:
 | 
			
		||||
            logging.warn("No vpn data for project %s" %
 | 
			
		||||
                                  pid)
 | 
			
		||||
            logging.warn(_("No vpn data for project %s"), pid)
 | 
			
		||||
 | 
			
		||||
        zippy.writestr(FLAGS.ca_file, crypto.fetch_ca(user.id))
 | 
			
		||||
        zippy.writestr(FLAGS.ca_file, crypto.fetch_ca(pid))
 | 
			
		||||
        zippy.close()
 | 
			
		||||
        with open(zf, 'rb') as f:
 | 
			
		||||
            read_buffer = f.read()
 | 
			
		||||
@@ -670,38 +676,38 @@ class AuthManager(object):
 | 
			
		||||
        shutil.rmtree(tmpdir)
 | 
			
		||||
        return read_buffer
 | 
			
		||||
 | 
			
		||||
    def get_environment_rc(self, user, project=None):
 | 
			
		||||
    def get_environment_rc(self, user, project=None, use_dmz=True):
 | 
			
		||||
        """Get credential zip for user in project"""
 | 
			
		||||
        if not isinstance(user, User):
 | 
			
		||||
            user = self.get_user(user)
 | 
			
		||||
        if project is None:
 | 
			
		||||
            project = user.id
 | 
			
		||||
        pid = Project.safe_id(project)
 | 
			
		||||
        return self.__generate_rc(user.access, user.secret, pid)
 | 
			
		||||
        return self.__generate_rc(user.access, user.secret, pid, use_dmz)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def __generate_rc(access, secret, pid):
 | 
			
		||||
    def __generate_rc(access, secret, pid, use_dmz=True, host=None):
 | 
			
		||||
        """Generate rc file for user"""
 | 
			
		||||
        if use_dmz:
 | 
			
		||||
            cc_host = FLAGS.cc_dmz
 | 
			
		||||
        else:
 | 
			
		||||
            cc_host = FLAGS.cc_host
 | 
			
		||||
        # NOTE(vish): Always use the dmz since it is used from inside the
 | 
			
		||||
        #             instance
 | 
			
		||||
        s3_host = FLAGS.s3_dmz
 | 
			
		||||
        if host:
 | 
			
		||||
            s3_host = host
 | 
			
		||||
            cc_host = host
 | 
			
		||||
        rc = open(FLAGS.credentials_template).read()
 | 
			
		||||
        rc = rc % {'access': access,
 | 
			
		||||
                   'project': pid,
 | 
			
		||||
                   'secret': secret,
 | 
			
		||||
                   'ec2': FLAGS.ec2_url,
 | 
			
		||||
                   's3': 'http://%s:%s' % (FLAGS.s3_host, FLAGS.s3_port),
 | 
			
		||||
                   'ec2': '%s://%s:%s%s' % (FLAGS.ec2_prefix,
 | 
			
		||||
                                            cc_host,
 | 
			
		||||
                                            FLAGS.cc_port,
 | 
			
		||||
                                            FLAGS.ec2_suffix),
 | 
			
		||||
                   's3': 'http://%s:%s' % (s3_host, FLAGS.s3_port),
 | 
			
		||||
                   'nova': FLAGS.ca_file,
 | 
			
		||||
                   'cert': FLAGS.credential_cert_file,
 | 
			
		||||
                   'key': FLAGS.credential_key_file}
 | 
			
		||||
        return rc
 | 
			
		||||
 | 
			
		||||
    def _generate_x509_cert(self, uid, pid):
 | 
			
		||||
        """Generate x509 cert for user"""
 | 
			
		||||
        (private_key, csr) = crypto.generate_x509_cert(
 | 
			
		||||
                self.__cert_subject(uid))
 | 
			
		||||
        # TODO(joshua): This should be async call back to the cloud controller
 | 
			
		||||
        signed_cert = crypto.sign_csr(csr, pid)
 | 
			
		||||
        return (private_key, signed_cert)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def __cert_subject(uid):
 | 
			
		||||
        """Helper to generate cert subject"""
 | 
			
		||||
        return FLAGS.credential_cert_subject % (uid, utils.isotime())
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,9 @@
 | 
			
		||||
#
 | 
			
		||||
# Person object for Nova
 | 
			
		||||
# inetorgperson with extra attributes
 | 
			
		||||
# Author: Vishvananda Ishaya <vishvananda@yahoo.com>
 | 
			
		||||
# Schema version: 2
 | 
			
		||||
# Authors: Vishvananda Ishaya <vishvananda@gmail.com>
 | 
			
		||||
#          Ryan Lane <rlane@wikimedia.org>
 | 
			
		||||
#
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
@@ -30,55 +32,19 @@ attributetype (
 | 
			
		||||
    SINGLE-VALUE
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
attributetype (
 | 
			
		||||
    novaAttrs:3
 | 
			
		||||
    NAME 'keyFingerprint'
 | 
			
		||||
    DESC 'Fingerprint of private key'
 | 
			
		||||
    EQUALITY caseIgnoreMatch
 | 
			
		||||
    SUBSTR caseIgnoreSubstringsMatch
 | 
			
		||||
    SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
 | 
			
		||||
    SINGLE-VALUE
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
attributetype (
 | 
			
		||||
    novaAttrs:4
 | 
			
		||||
    NAME 'isAdmin'
 | 
			
		||||
    DESC 'Is user an administrator?'
 | 
			
		||||
    NAME 'isNovaAdmin'
 | 
			
		||||
    DESC 'Is user an nova administrator?'
 | 
			
		||||
    EQUALITY booleanMatch
 | 
			
		||||
    SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
 | 
			
		||||
    SINGLE-VALUE
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
attributetype (
 | 
			
		||||
    novaAttrs:5
 | 
			
		||||
    NAME 'projectManager'
 | 
			
		||||
    DESC 'Project Managers of a project'
 | 
			
		||||
    SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
objectClass (
 | 
			
		||||
    novaOCs:1
 | 
			
		||||
    NAME 'novaUser'
 | 
			
		||||
    DESC 'access and secret keys'
 | 
			
		||||
    AUXILIARY
 | 
			
		||||
    MUST ( uid )
 | 
			
		||||
    MAY  ( accessKey $ secretKey $ isAdmin )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
objectClass (
 | 
			
		||||
    novaOCs:2
 | 
			
		||||
    NAME 'novaKeyPair'
 | 
			
		||||
    DESC 'Key pair for User'
 | 
			
		||||
    SUP top
 | 
			
		||||
    STRUCTURAL
 | 
			
		||||
    MUST ( cn $ sshPublicKey $ keyFingerprint )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
objectClass (
 | 
			
		||||
    novaOCs:3
 | 
			
		||||
    NAME 'novaProject'
 | 
			
		||||
    DESC 'Container for project'
 | 
			
		||||
    SUP groupOfNames
 | 
			
		||||
    STRUCTURAL
 | 
			
		||||
    MUST ( cn $ projectManager )
 | 
			
		||||
    MAY  ( accessKey $ secretKey $ isNovaAdmin )
 | 
			
		||||
    )
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,13 @@
 | 
			
		||||
#
 | 
			
		||||
# Person object for Nova
 | 
			
		||||
# inetorgperson with extra attributes
 | 
			
		||||
# Author: Vishvananda Ishaya <vishvananda@yahoo.com>
 | 
			
		||||
# Modified for strict RFC 4512 compatibility by: Ryan Lane <ryan@ryandlane.com>
 | 
			
		||||
# Schema version: 2
 | 
			
		||||
# Authors: Vishvananda Ishaya <vishvananda@gmail.com>
 | 
			
		||||
#          Ryan Lane <rlane@wikimedia.org>
 | 
			
		||||
#
 | 
			
		||||
# using internet experimental oid arc as per BP64 3.1
 | 
			
		||||
dn: cn=schema
 | 
			
		||||
attributeTypes: ( 1.3.6.1.3.1.666.666.3.1 NAME 'accessKey' DESC 'Key for accessing data' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
 | 
			
		||||
attributeTypes: ( 1.3.6.1.3.1.666.666.3.2 NAME 'secretKey' DESC 'Secret key' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
 | 
			
		||||
attributeTypes: ( 1.3.6.1.3.1.666.666.3.3 NAME 'keyFingerprint' DESC 'Fingerprint of private key' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE)
 | 
			
		||||
attributeTypes: ( 1.3.6.1.3.1.666.666.3.4 NAME 'isAdmin' DESC 'Is user an administrator?' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE ) 
 | 
			
		||||
attributeTypes: ( 1.3.6.1.3.1.666.666.3.5 NAME 'projectManager' DESC 'Project Managers of a project' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
 | 
			
		||||
objectClasses: ( 1.3.6.1.3.1.666.666.4.1 NAME 'novaUser' DESC 'access and secret keys' SUP top AUXILIARY MUST ( uid ) MAY  ( accessKey $ secretKey $ isAdmin ) ) 
 | 
			
		||||
objectClasses: ( 1.3.6.1.3.1.666.666.4.2 NAME 'novaKeyPair' DESC 'Key pair for User' SUP top STRUCTURAL MUST ( cn $ sshPublicKey $ keyFingerprint ) )
 | 
			
		||||
objectClasses: ( 1.3.6.1.3.1.666.666.4.3 NAME 'novaProject' DESC 'Container for project' SUP groupOfNames STRUCTURAL MUST ( cn $ projectManager ) )
 | 
			
		||||
attributeTypes: ( 1.3.6.1.3.1.666.666.3.4 NAME 'isNovaAdmin' DESC 'Is user a nova administrator?' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE ) 
 | 
			
		||||
objectClasses: ( 1.3.6.1.3.1.666.666.4.1 NAME 'novaUser' DESC 'access and secret keys' SUP top AUXILIARY MAY  ( accessKey $ secretKey $ isNovaAdmin ) ) 
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,6 @@ 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
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ apt-get install -y slapd ldap-utils python-ldap
 | 
			
		||||
 | 
			
		||||
abspath=`dirname "$(cd "${0%/*}" 2>/dev/null; echo "$PWD"/"${0##*/}")"`
 | 
			
		||||
cp $abspath/openssh-lpk_openldap.schema /etc/ldap/schema/openssh-lpk_openldap.schema
 | 
			
		||||
cp $abspath/nova_openldap.schema /etc/ldap/schema/nova_openldap.schema
 | 
			
		||||
cp $abspath/nova_openldap.schema /etc/ldap/schema/nova.schema
 | 
			
		||||
 | 
			
		||||
mv /etc/ldap/slapd.conf /etc/ldap/slapd.conf.orig
 | 
			
		||||
cat >/etc/ldap/slapd.conf <<SLAPD_CONF_EOF
 | 
			
		||||
@@ -33,7 +33,6 @@ cat >/etc/ldap/slapd.conf <<SLAPD_CONF_EOF
 | 
			
		||||
include /etc/ldap/schema/core.schema
 | 
			
		||||
include /etc/ldap/schema/cosine.schema
 | 
			
		||||
include /etc/ldap/schema/inetorgperson.schema
 | 
			
		||||
include /etc/ldap/schema/openssh-lpk_openldap.schema
 | 
			
		||||
include /etc/ldap/schema/nova.schema
 | 
			
		||||
pidfile /var/run/slapd/slapd.pid
 | 
			
		||||
argsfile /var/run/slapd/slapd.args
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										59
									
								
								nova/fakememcache.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								nova/fakememcache.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
# 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.
 | 
			
		||||
 | 
			
		||||
"""Super simple fake memcache client."""
 | 
			
		||||
 | 
			
		||||
import utils
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Client(object):
 | 
			
		||||
    """Replicates a tiny subset of memcached client interface."""
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        """Ignores the passed in args"""
 | 
			
		||||
        self.cache = {}
 | 
			
		||||
 | 
			
		||||
    def get(self, key):
 | 
			
		||||
        """Retrieves the value for a key or None."""
 | 
			
		||||
        (timeout, value) = self.cache.get(key, (0, None))
 | 
			
		||||
        if timeout == 0 or utils.utcnow_ts() < timeout:
 | 
			
		||||
            return value
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    def set(self, key, value, time=0, min_compress_len=0):
 | 
			
		||||
        """Sets the value for a key."""
 | 
			
		||||
        timeout = 0
 | 
			
		||||
        if time != 0:
 | 
			
		||||
            timeout = utils.utcnow_ts() + time
 | 
			
		||||
        self.cache[key] = (timeout, value)
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def add(self, key, value, time=0, min_compress_len=0):
 | 
			
		||||
        """Sets the value for a key if it doesn't exist."""
 | 
			
		||||
        if not self.get(key) is None:
 | 
			
		||||
            return False
 | 
			
		||||
        return self.set(key, value, time, min_compress_len)
 | 
			
		||||
 | 
			
		||||
    def incr(self, key, delta=1):
 | 
			
		||||
        """Increments the value for a key."""
 | 
			
		||||
        value = self.get(key)
 | 
			
		||||
        if value is None:
 | 
			
		||||
            return None
 | 
			
		||||
        new_value = int(value) + delta
 | 
			
		||||
        self.cache[key] = (self.cache[key][0], str(new_value))
 | 
			
		||||
        return new_value
 | 
			
		||||
@@ -25,6 +25,10 @@ from carrot.backends import base
 | 
			
		||||
from eventlet import greenthread
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
EXCHANGES = {}
 | 
			
		||||
QUEUES = {}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Message(base.BaseMessage):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
@@ -37,12 +41,12 @@ class Exchange(object):
 | 
			
		||||
        self._routes = {}
 | 
			
		||||
 | 
			
		||||
    def publish(self, message, routing_key=None):
 | 
			
		||||
        logging.debug('(%s) publish (key: %s) %s',
 | 
			
		||||
        logging.debug(_('(%s) publish (key: %s) %s'),
 | 
			
		||||
                      self.name, routing_key, message)
 | 
			
		||||
        routing_key = routing_key.split('.')[0]
 | 
			
		||||
        if routing_key in self._routes:
 | 
			
		||||
            for f in self._routes[routing_key]:
 | 
			
		||||
                logging.debug('Publishing to route %s', f)
 | 
			
		||||
                logging.debug(_('Publishing to route %s'), f)
 | 
			
		||||
                f(message, routing_key=routing_key)
 | 
			
		||||
 | 
			
		||||
    def bind(self, callback, routing_key):
 | 
			
		||||
@@ -68,81 +72,63 @@ class Queue(object):
 | 
			
		||||
        return self._queue.get()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Backend(object):
 | 
			
		||||
    """ Singleton backend for testing """
 | 
			
		||||
    class __impl(base.BaseBackend):
 | 
			
		||||
        def __init__(self, *args, **kwargs):
 | 
			
		||||
            #super(__impl, self).__init__(*args, **kwargs)
 | 
			
		||||
            self._exchanges = {}
 | 
			
		||||
            self._queues = {}
 | 
			
		||||
class Backend(base.BaseBackend):
 | 
			
		||||
    def queue_declare(self, queue, **kwargs):
 | 
			
		||||
        global QUEUES
 | 
			
		||||
        if queue not in QUEUES:
 | 
			
		||||
            logging.debug(_('Declaring queue %s'), queue)
 | 
			
		||||
            QUEUES[queue] = Queue(queue)
 | 
			
		||||
 | 
			
		||||
        def _reset_all(self):
 | 
			
		||||
            self._exchanges = {}
 | 
			
		||||
            self._queues = {}
 | 
			
		||||
    def exchange_declare(self, exchange, type, *args, **kwargs):
 | 
			
		||||
        global EXCHANGES
 | 
			
		||||
        if exchange not in EXCHANGES:
 | 
			
		||||
            logging.debug(_('Declaring exchange %s'), exchange)
 | 
			
		||||
            EXCHANGES[exchange] = Exchange(exchange, type)
 | 
			
		||||
 | 
			
		||||
        def queue_declare(self, queue, **kwargs):
 | 
			
		||||
            if queue not in self._queues:
 | 
			
		||||
                logging.debug('Declaring queue %s', queue)
 | 
			
		||||
                self._queues[queue] = Queue(queue)
 | 
			
		||||
    def queue_bind(self, queue, exchange, routing_key, **kwargs):
 | 
			
		||||
        global EXCHANGES
 | 
			
		||||
        global QUEUES
 | 
			
		||||
        logging.debug(_('Binding %s to %s with key %s'),
 | 
			
		||||
                      queue, exchange, routing_key)
 | 
			
		||||
        EXCHANGES[exchange].bind(QUEUES[queue].push, routing_key)
 | 
			
		||||
 | 
			
		||||
        def exchange_declare(self, exchange, type, *args, **kwargs):
 | 
			
		||||
            if exchange not in self._exchanges:
 | 
			
		||||
                logging.debug('Declaring exchange %s', exchange)
 | 
			
		||||
                self._exchanges[exchange] = Exchange(exchange, type)
 | 
			
		||||
    def declare_consumer(self, queue, callback, *args, **kwargs):
 | 
			
		||||
        self.current_queue = queue
 | 
			
		||||
        self.current_callback = callback
 | 
			
		||||
 | 
			
		||||
        def queue_bind(self, queue, exchange, routing_key, **kwargs):
 | 
			
		||||
            logging.debug('Binding %s to %s with key %s',
 | 
			
		||||
                          queue, exchange, routing_key)
 | 
			
		||||
            self._exchanges[exchange].bind(self._queues[queue].push,
 | 
			
		||||
                                           routing_key)
 | 
			
		||||
    def consume(self, limit=None):
 | 
			
		||||
        while True:
 | 
			
		||||
            item = self.get(self.current_queue)
 | 
			
		||||
            if item:
 | 
			
		||||
                self.current_callback(item)
 | 
			
		||||
                raise StopIteration()
 | 
			
		||||
            greenthread.sleep(0)
 | 
			
		||||
 | 
			
		||||
        def declare_consumer(self, queue, callback, *args, **kwargs):
 | 
			
		||||
            self.current_queue = queue
 | 
			
		||||
            self.current_callback = callback
 | 
			
		||||
    def get(self, queue, no_ack=False):
 | 
			
		||||
        global QUEUES
 | 
			
		||||
        if not queue in QUEUES or not QUEUES[queue].size():
 | 
			
		||||
            return None
 | 
			
		||||
        (message_data, content_type, content_encoding) = QUEUES[queue].pop()
 | 
			
		||||
        message = Message(backend=self, body=message_data,
 | 
			
		||||
                          content_type=content_type,
 | 
			
		||||
                          content_encoding=content_encoding)
 | 
			
		||||
        message.result = True
 | 
			
		||||
        logging.debug(_('Getting from %s: %s'), queue, message)
 | 
			
		||||
        return message
 | 
			
		||||
 | 
			
		||||
        def consume(self, *args, **kwargs):
 | 
			
		||||
            while True:
 | 
			
		||||
                item = self.get(self.current_queue)
 | 
			
		||||
                if item:
 | 
			
		||||
                    self.current_callback(item)
 | 
			
		||||
                    raise StopIteration()
 | 
			
		||||
                greenthread.sleep(0)
 | 
			
		||||
    def prepare_message(self, message_data, delivery_mode,
 | 
			
		||||
                        content_type, content_encoding, **kwargs):
 | 
			
		||||
        """Prepare message for sending."""
 | 
			
		||||
        return (message_data, content_type, content_encoding)
 | 
			
		||||
 | 
			
		||||
        def get(self, queue, no_ack=False):
 | 
			
		||||
            if not queue in self._queues or not self._queues[queue].size():
 | 
			
		||||
                return None
 | 
			
		||||
            (message_data, content_type, content_encoding) = \
 | 
			
		||||
                    self._queues[queue].pop()
 | 
			
		||||
            message = Message(backend=self, body=message_data,
 | 
			
		||||
                              content_type=content_type,
 | 
			
		||||
                              content_encoding=content_encoding)
 | 
			
		||||
            message.result = True
 | 
			
		||||
            logging.debug('Getting from %s: %s', queue, message)
 | 
			
		||||
            return message
 | 
			
		||||
 | 
			
		||||
        def prepare_message(self, message_data, delivery_mode,
 | 
			
		||||
                            content_type, content_encoding, **kwargs):
 | 
			
		||||
            """Prepare message for sending."""
 | 
			
		||||
            return (message_data, content_type, content_encoding)
 | 
			
		||||
 | 
			
		||||
        def publish(self, message, exchange, routing_key, **kwargs):
 | 
			
		||||
            if exchange in self._exchanges:
 | 
			
		||||
                self._exchanges[exchange].publish(
 | 
			
		||||
                        message, routing_key=routing_key)
 | 
			
		||||
 | 
			
		||||
    __instance = None
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        if Backend.__instance is None:
 | 
			
		||||
            Backend.__instance = Backend.__impl(*args, **kwargs)
 | 
			
		||||
        self.__dict__['_Backend__instance'] = Backend.__instance
 | 
			
		||||
 | 
			
		||||
    def __getattr__(self, attr):
 | 
			
		||||
        return getattr(self.__instance, attr)
 | 
			
		||||
 | 
			
		||||
    def __setattr__(self, attr, value):
 | 
			
		||||
        return setattr(self.__instance, attr, value)
 | 
			
		||||
    def publish(self, message, exchange, routing_key, **kwargs):
 | 
			
		||||
        global EXCHANGES
 | 
			
		||||
        if exchange in EXCHANGES:
 | 
			
		||||
            EXCHANGES[exchange].publish(message, routing_key=routing_key)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def reset_all():
 | 
			
		||||
    Backend()._reset_all()
 | 
			
		||||
    global EXCHANGES
 | 
			
		||||
    global QUEUES
 | 
			
		||||
    EXCHANGES = {}
 | 
			
		||||
    QUEUES = {}
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,8 @@ import sys
 | 
			
		||||
 | 
			
		||||
import gflags
 | 
			
		||||
 | 
			
		||||
from nova import utils
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FlagValues(gflags.FlagValues):
 | 
			
		||||
    """Extension of gflags.FlagValues that allows undefined and runtime flags.
 | 
			
		||||
@@ -211,7 +213,8 @@ DEFINE_string('connection_type', 'libvirt', 'libvirt, xenapi or fake')
 | 
			
		||||
DEFINE_string('aws_access_key_id', 'admin', 'AWS Access ID')
 | 
			
		||||
DEFINE_string('aws_secret_access_key', 'admin', 'AWS Access Key')
 | 
			
		||||
DEFINE_integer('s3_port', 3333, 's3 port')
 | 
			
		||||
DEFINE_string('s3_host', '127.0.0.1', 's3 host')
 | 
			
		||||
DEFINE_string('s3_host', utils.get_my_ip(), 's3 host (for infrastructure)')
 | 
			
		||||
DEFINE_string('s3_dmz', utils.get_my_ip(), 's3 dmz ip (for instances)')
 | 
			
		||||
DEFINE_string('compute_topic', 'compute', 'the topic compute nodes listen on')
 | 
			
		||||
DEFINE_string('scheduler_topic', 'scheduler',
 | 
			
		||||
              'the topic scheduler nodes listen on')
 | 
			
		||||
@@ -230,22 +233,24 @@ DEFINE_string('rabbit_virtual_host', '/', 'rabbit virtual host')
 | 
			
		||||
DEFINE_integer('rabbit_retry_interval', 10, 'rabbit connection retry interval')
 | 
			
		||||
DEFINE_integer('rabbit_max_retries', 12, 'rabbit connection attempts')
 | 
			
		||||
DEFINE_string('control_exchange', 'nova', 'the main exchange to connect to')
 | 
			
		||||
DEFINE_string('ec2_url', 'http://127.0.0.1:8773/services/Cloud',
 | 
			
		||||
              'Url to ec2 api server')
 | 
			
		||||
DEFINE_string('ec2_prefix', 'http', 'prefix for ec2')
 | 
			
		||||
DEFINE_string('cc_host', utils.get_my_ip(), 'ip of api server')
 | 
			
		||||
DEFINE_string('cc_dmz', utils.get_my_ip(), 'internal ip of api server')
 | 
			
		||||
DEFINE_integer('cc_port', 8773, 'cloud controller port')
 | 
			
		||||
DEFINE_string('ec2_suffix', '/services/Cloud', 'suffix for ec2')
 | 
			
		||||
 | 
			
		||||
DEFINE_string('default_image', 'ami-11111',
 | 
			
		||||
              'default image to use, testing only')
 | 
			
		||||
DEFINE_string('default_kernel', 'aki-11111',
 | 
			
		||||
              'default kernel to use, testing only')
 | 
			
		||||
DEFINE_string('default_ramdisk', 'ari-11111',
 | 
			
		||||
              'default ramdisk to use, testing only')
 | 
			
		||||
DEFINE_string('default_instance_type', 'm1.small',
 | 
			
		||||
              'default instance type to use, testing only')
 | 
			
		||||
DEFINE_string('null_kernel', 'nokernel',
 | 
			
		||||
              'kernel image that indicates not to use a kernel,'
 | 
			
		||||
              ' but to use a raw disk image instead')
 | 
			
		||||
 | 
			
		||||
DEFINE_string('vpn_image_id', 'ami-CLOUDPIPE', 'AMI for cloudpipe vpn server')
 | 
			
		||||
DEFINE_string('vpn_image_id', 'ami-cloudpipe', 'AMI for cloudpipe vpn server')
 | 
			
		||||
DEFINE_string('vpn_key_suffix',
 | 
			
		||||
              '-key',
 | 
			
		||||
              'Suffix to add to project name for vpn key')
 | 
			
		||||
              '-vpn',
 | 
			
		||||
              'Suffix to add to project name for vpn key and secgroups')
 | 
			
		||||
 | 
			
		||||
DEFINE_integer('auth_token_ttl', 3600, 'Seconds for auth tokens to linger')
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										36
									
								
								nova/rpc.py
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								nova/rpc.py
									
									
									
									
									
								
							@@ -91,15 +91,15 @@ class Consumer(messaging.Consumer):
 | 
			
		||||
                self.failed_connection = False
 | 
			
		||||
                break
 | 
			
		||||
            except:  # Catching all because carrot sucks
 | 
			
		||||
                logging.exception("AMQP server on %s:%d is unreachable." \
 | 
			
		||||
                    " Trying again in %d seconds." % (
 | 
			
		||||
                logging.exception(_("AMQP server on %s:%d is unreachable."
 | 
			
		||||
                    " Trying again in %d seconds.") % (
 | 
			
		||||
                    FLAGS.rabbit_host,
 | 
			
		||||
                    FLAGS.rabbit_port,
 | 
			
		||||
                    FLAGS.rabbit_retry_interval))
 | 
			
		||||
                self.failed_connection = True
 | 
			
		||||
        if self.failed_connection:
 | 
			
		||||
            logging.exception("Unable to connect to AMQP server" \
 | 
			
		||||
                " after %d tries. Shutting down." % FLAGS.rabbit_max_retries)
 | 
			
		||||
            logging.exception(_("Unable to connect to AMQP server"
 | 
			
		||||
                " after %d tries. Shutting down.") % FLAGS.rabbit_max_retries)
 | 
			
		||||
            sys.exit(1)
 | 
			
		||||
 | 
			
		||||
    def fetch(self, no_ack=None, auto_ack=None, enable_callbacks=False):
 | 
			
		||||
@@ -116,14 +116,14 @@ class Consumer(messaging.Consumer):
 | 
			
		||||
                self.declare()
 | 
			
		||||
            super(Consumer, self).fetch(no_ack, auto_ack, enable_callbacks)
 | 
			
		||||
            if self.failed_connection:
 | 
			
		||||
                logging.error("Reconnected to queue")
 | 
			
		||||
                logging.error(_("Reconnected to queue"))
 | 
			
		||||
                self.failed_connection = False
 | 
			
		||||
        # NOTE(vish): This is catching all errors because we really don't
 | 
			
		||||
        #             exceptions to be logged 10 times a second if some
 | 
			
		||||
        #             persistent failure occurs.
 | 
			
		||||
        except Exception:  # pylint: disable-msg=W0703
 | 
			
		||||
            if not self.failed_connection:
 | 
			
		||||
                logging.exception("Failed to fetch message from queue")
 | 
			
		||||
                logging.exception(_("Failed to fetch message from queue"))
 | 
			
		||||
                self.failed_connection = True
 | 
			
		||||
 | 
			
		||||
    def attach_to_eventlet(self):
 | 
			
		||||
@@ -153,7 +153,7 @@ class TopicConsumer(Consumer):
 | 
			
		||||
class AdapterConsumer(TopicConsumer):
 | 
			
		||||
    """Calls methods on a proxy object based on method and args"""
 | 
			
		||||
    def __init__(self, connection=None, topic="broadcast", proxy=None):
 | 
			
		||||
        LOG.debug('Initing the Adapter Consumer for %s' % (topic))
 | 
			
		||||
        LOG.debug(_('Initing the Adapter Consumer for %s') % (topic))
 | 
			
		||||
        self.proxy = proxy
 | 
			
		||||
        super(AdapterConsumer, self).__init__(connection=connection,
 | 
			
		||||
                                              topic=topic)
 | 
			
		||||
@@ -168,7 +168,7 @@ class AdapterConsumer(TopicConsumer):
 | 
			
		||||
 | 
			
		||||
        Example: {'method': 'echo', 'args': {'value': 42}}
 | 
			
		||||
        """
 | 
			
		||||
        LOG.debug('received %s' % (message_data))
 | 
			
		||||
        LOG.debug(_('received %s') % (message_data))
 | 
			
		||||
        msg_id = message_data.pop('_msg_id', None)
 | 
			
		||||
 | 
			
		||||
        ctxt = _unpack_context(message_data)
 | 
			
		||||
@@ -181,8 +181,8 @@ class AdapterConsumer(TopicConsumer):
 | 
			
		||||
            #             messages stay in the queue indefinitely, so for now
 | 
			
		||||
            #             we just log the message and send an error string
 | 
			
		||||
            #             back to the caller
 | 
			
		||||
            LOG.warn('no method for message: %s' % (message_data))
 | 
			
		||||
            msg_reply(msg_id, 'No method for message: %s' % message_data)
 | 
			
		||||
            LOG.warn(_('no method for message: %s') % (message_data))
 | 
			
		||||
            msg_reply(msg_id, _('No method for message: %s') % message_data)
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        node_func = getattr(self.proxy, str(method))
 | 
			
		||||
@@ -242,10 +242,10 @@ def msg_reply(msg_id, reply=None, failure=None):
 | 
			
		||||
    if failure:
 | 
			
		||||
        message = str(failure[1])
 | 
			
		||||
        tb = traceback.format_exception(*failure)
 | 
			
		||||
        logging.error("Returning exception %s to caller", message)
 | 
			
		||||
        logging.error(_("Returning exception %s to caller"), message)
 | 
			
		||||
        logging.error(tb)
 | 
			
		||||
        failure = (failure[0].__name__, str(failure[1]), tb)
 | 
			
		||||
    conn = Connection.instance()
 | 
			
		||||
    conn = Connection.instance(True)
 | 
			
		||||
    publisher = DirectPublisher(connection=conn, msg_id=msg_id)
 | 
			
		||||
    try:
 | 
			
		||||
        publisher.send({'result': reply, 'failure': failure})
 | 
			
		||||
@@ -283,7 +283,7 @@ def _unpack_context(msg):
 | 
			
		||||
        if key.startswith('_context_'):
 | 
			
		||||
            value = msg.pop(key)
 | 
			
		||||
            context_dict[key[9:]] = value
 | 
			
		||||
    LOG.debug('unpacked context: %s', context_dict)
 | 
			
		||||
    LOG.debug(_('unpacked context: %s'), context_dict)
 | 
			
		||||
    return context.RequestContext.from_dict(context_dict)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -302,10 +302,10 @@ def _pack_context(msg, context):
 | 
			
		||||
 | 
			
		||||
def call(context, topic, msg):
 | 
			
		||||
    """Sends a message on a topic and wait for a response"""
 | 
			
		||||
    LOG.debug("Making asynchronous call...")
 | 
			
		||||
    LOG.debug(_("Making asynchronous call..."))
 | 
			
		||||
    msg_id = uuid.uuid4().hex
 | 
			
		||||
    msg.update({'_msg_id': msg_id})
 | 
			
		||||
    LOG.debug("MSG_ID is %s" % (msg_id))
 | 
			
		||||
    LOG.debug(_("MSG_ID is %s") % (msg_id))
 | 
			
		||||
    _pack_context(msg, context)
 | 
			
		||||
 | 
			
		||||
    class WaitMessage(object):
 | 
			
		||||
@@ -353,7 +353,7 @@ def cast(context, topic, msg):
 | 
			
		||||
 | 
			
		||||
def generic_response(message_data, message):
 | 
			
		||||
    """Logs a result and exits"""
 | 
			
		||||
    LOG.debug('response %s', message_data)
 | 
			
		||||
    LOG.debug(_('response %s'), message_data)
 | 
			
		||||
    message.ack()
 | 
			
		||||
    sys.exit(0)
 | 
			
		||||
 | 
			
		||||
@@ -362,8 +362,8 @@ def send_message(topic, message, wait=True):
 | 
			
		||||
    """Sends a message for testing"""
 | 
			
		||||
    msg_id = uuid.uuid4().hex
 | 
			
		||||
    message.update({'_msg_id': msg_id})
 | 
			
		||||
    LOG.debug('topic is %s', topic)
 | 
			
		||||
    LOG.debug('message %s', message)
 | 
			
		||||
    LOG.debug(_('topic is %s'), topic)
 | 
			
		||||
    LOG.debug(_('message %s'), message)
 | 
			
		||||
 | 
			
		||||
    if wait:
 | 
			
		||||
        consumer = messaging.Consumer(connection=Connection.instance(),
 | 
			
		||||
 
 | 
			
		||||
@@ -47,7 +47,7 @@ class SimpleScheduler(chance.ChanceScheduler):
 | 
			
		||||
        for result in results:
 | 
			
		||||
            (service, instance_cores) = result
 | 
			
		||||
            if instance_cores + instance_ref['vcpus'] > FLAGS.max_cores:
 | 
			
		||||
                raise driver.NoValidHost("All hosts have too many cores")
 | 
			
		||||
                raise driver.NoValidHost(_("All hosts have too many cores"))
 | 
			
		||||
            if self.service_is_up(service):
 | 
			
		||||
                # NOTE(vish): this probably belongs in the manager, if we
 | 
			
		||||
                #             can generalize this somehow
 | 
			
		||||
@@ -57,7 +57,7 @@ class SimpleScheduler(chance.ChanceScheduler):
 | 
			
		||||
                                   {'host': service['host'],
 | 
			
		||||
                                    'scheduled_at': now})
 | 
			
		||||
                return service['host']
 | 
			
		||||
        raise driver.NoValidHost("No hosts found")
 | 
			
		||||
        raise driver.NoValidHost(_("No hosts found"))
 | 
			
		||||
 | 
			
		||||
    def schedule_create_volume(self, context, volume_id, *_args, **_kwargs):
 | 
			
		||||
        """Picks a host that is up and has the fewest volumes."""
 | 
			
		||||
@@ -66,7 +66,8 @@ class SimpleScheduler(chance.ChanceScheduler):
 | 
			
		||||
        for result in results:
 | 
			
		||||
            (service, volume_gigabytes) = result
 | 
			
		||||
            if volume_gigabytes + volume_ref['size'] > FLAGS.max_gigabytes:
 | 
			
		||||
                raise driver.NoValidHost("All hosts have too many gigabytes")
 | 
			
		||||
                raise driver.NoValidHost(_("All hosts have too many "
 | 
			
		||||
                                           "gigabytes"))
 | 
			
		||||
            if self.service_is_up(service):
 | 
			
		||||
                # NOTE(vish): this probably belongs in the manager, if we
 | 
			
		||||
                #             can generalize this somehow
 | 
			
		||||
@@ -76,7 +77,7 @@ class SimpleScheduler(chance.ChanceScheduler):
 | 
			
		||||
                                 {'host': service['host'],
 | 
			
		||||
                                  'scheduled_at': now})
 | 
			
		||||
                return service['host']
 | 
			
		||||
        raise driver.NoValidHost("No hosts found")
 | 
			
		||||
        raise driver.NoValidHost(_("No hosts found"))
 | 
			
		||||
 | 
			
		||||
    def schedule_set_network_host(self, context, *_args, **_kwargs):
 | 
			
		||||
        """Picks a host that is up and has the fewest networks."""
 | 
			
		||||
@@ -85,7 +86,7 @@ class SimpleScheduler(chance.ChanceScheduler):
 | 
			
		||||
        for result in results:
 | 
			
		||||
            (service, instance_count) = result
 | 
			
		||||
            if instance_count >= FLAGS.max_networks:
 | 
			
		||||
                raise driver.NoValidHost("All hosts have too many networks")
 | 
			
		||||
                raise driver.NoValidHost(_("All hosts have too many networks"))
 | 
			
		||||
            if self.service_is_up(service):
 | 
			
		||||
                return service['host']
 | 
			
		||||
        raise driver.NoValidHost("No hosts found")
 | 
			
		||||
        raise driver.NoValidHost(_("No hosts found"))
 | 
			
		||||
 
 | 
			
		||||
@@ -208,17 +208,13 @@ class AuthManagerTestCase(object):
 | 
			
		||||
        #             so it probably belongs in crypto_unittest
 | 
			
		||||
        #             but I'm leaving it where I found it.
 | 
			
		||||
        with user_and_project_generator(self.manager) as (user, project):
 | 
			
		||||
            # NOTE(todd): Should mention why we must setup controller first
 | 
			
		||||
            #             (somebody please clue me in)
 | 
			
		||||
            cloud_controller = cloud.CloudController()
 | 
			
		||||
            cloud_controller.setup()
 | 
			
		||||
            _key, cert_str = self.manager._generate_x509_cert('test1',
 | 
			
		||||
                                                              'testproj')
 | 
			
		||||
            # NOTE(vish): Setup runs genroot.sh if it hasn't been run
 | 
			
		||||
            cloud.CloudController().setup()
 | 
			
		||||
            _key, cert_str = crypto.generate_x509_cert(user.id, project.id)
 | 
			
		||||
            logging.debug(cert_str)
 | 
			
		||||
 | 
			
		||||
            # Need to verify that it's signed by the right intermediate CA
 | 
			
		||||
            full_chain = crypto.fetch_ca(project_id='testproj', chain=True)
 | 
			
		||||
            int_cert = crypto.fetch_ca(project_id='testproj', chain=False)
 | 
			
		||||
            full_chain = crypto.fetch_ca(project_id=project.id, chain=True)
 | 
			
		||||
            int_cert = crypto.fetch_ca(project_id=project.id, chain=False)
 | 
			
		||||
            cloud_cert = crypto.fetch_ca()
 | 
			
		||||
            logging.debug("CA chain:\n\n =====\n%s\n\n=====" % full_chain)
 | 
			
		||||
            signed_cert = X509.load_cert_string(cert_str)
 | 
			
		||||
@@ -227,7 +223,8 @@ class AuthManagerTestCase(object):
 | 
			
		||||
            cloud_cert = X509.load_cert_string(cloud_cert)
 | 
			
		||||
            self.assertTrue(signed_cert.verify(chain_cert.get_pubkey()))
 | 
			
		||||
            self.assertTrue(signed_cert.verify(int_cert.get_pubkey()))
 | 
			
		||||
            if not FLAGS.use_intermediate_ca:
 | 
			
		||||
 | 
			
		||||
            if not FLAGS.use_project_ca:
 | 
			
		||||
                self.assertTrue(signed_cert.verify(cloud_cert.get_pubkey()))
 | 
			
		||||
            else:
 | 
			
		||||
                self.assertFalse(signed_cert.verify(cloud_cert.get_pubkey()))
 | 
			
		||||
@@ -333,14 +330,10 @@ class AuthManagerLdapTestCase(AuthManagerTestCase, test.TestCase):
 | 
			
		||||
        AuthManagerTestCase.__init__(self)
 | 
			
		||||
        test.TestCase.__init__(self, *args, **kwargs)
 | 
			
		||||
        import nova.auth.fakeldap as fakeldap
 | 
			
		||||
        FLAGS.redis_db = 8
 | 
			
		||||
        if FLAGS.flush_db:
 | 
			
		||||
            logging.info("Flushing redis datastore")
 | 
			
		||||
            try:
 | 
			
		||||
                r = fakeldap.Redis.instance()
 | 
			
		||||
                r.flushdb()
 | 
			
		||||
            except:
 | 
			
		||||
                self.skip = True
 | 
			
		||||
            logging.info("Flushing datastore")
 | 
			
		||||
            r = fakeldap.Store.instance()
 | 
			
		||||
            r.flushdb()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AuthManagerDbTestCase(AuthManagerTestCase, test.TestCase):
 | 
			
		||||
 
 | 
			
		||||
@@ -22,20 +22,18 @@ import logging
 | 
			
		||||
from M2Crypto import BIO
 | 
			
		||||
from M2Crypto import RSA
 | 
			
		||||
import os
 | 
			
		||||
import StringIO
 | 
			
		||||
import tempfile
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
from eventlet import greenthread
 | 
			
		||||
from xml.etree import ElementTree
 | 
			
		||||
 | 
			
		||||
from nova import context
 | 
			
		||||
from nova import crypto
 | 
			
		||||
from nova import db
 | 
			
		||||
from nova import flags
 | 
			
		||||
from nova import rpc
 | 
			
		||||
from nova import service
 | 
			
		||||
from nova import test
 | 
			
		||||
from nova import utils
 | 
			
		||||
from nova.auth import manager
 | 
			
		||||
from nova.compute import power_state
 | 
			
		||||
from nova.api.ec2 import cloud
 | 
			
		||||
@@ -54,7 +52,8 @@ os.makedirs(IMAGES_PATH)
 | 
			
		||||
class CloudTestCase(test.TestCase):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(CloudTestCase, self).setUp()
 | 
			
		||||
        self.flags(connection_type='fake', images_path=IMAGES_PATH)
 | 
			
		||||
        self.flags(connection_type='fake',
 | 
			
		||||
                   images_path=IMAGES_PATH)
 | 
			
		||||
 | 
			
		||||
        self.conn = rpc.Connection.instance()
 | 
			
		||||
        logging.getLogger().setLevel(logging.DEBUG)
 | 
			
		||||
@@ -62,27 +61,23 @@ class CloudTestCase(test.TestCase):
 | 
			
		||||
        # set up our cloud
 | 
			
		||||
        self.cloud = cloud.CloudController()
 | 
			
		||||
 | 
			
		||||
        # set up a service
 | 
			
		||||
        self.compute = utils.import_object(FLAGS.compute_manager)
 | 
			
		||||
        self.compute_consumer = rpc.AdapterConsumer(connection=self.conn,
 | 
			
		||||
                                                    topic=FLAGS.compute_topic,
 | 
			
		||||
                                                    proxy=self.compute)
 | 
			
		||||
        self.compute_consumer.attach_to_eventlet()
 | 
			
		||||
        self.network = utils.import_object(FLAGS.network_manager)
 | 
			
		||||
        self.network_consumer = rpc.AdapterConsumer(connection=self.conn,
 | 
			
		||||
                                                    topic=FLAGS.network_topic,
 | 
			
		||||
                                                    proxy=self.network)
 | 
			
		||||
        self.network_consumer.attach_to_eventlet()
 | 
			
		||||
        # set up services
 | 
			
		||||
        self.compute = service.Service.create(binary='nova-compute')
 | 
			
		||||
        self.compute.start()
 | 
			
		||||
        self.network = service.Service.create(binary='nova-network')
 | 
			
		||||
        self.network.start()
 | 
			
		||||
 | 
			
		||||
        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.RequestContext(user=self.user,
 | 
			
		||||
                                                 project=self.project)
 | 
			
		||||
                                              project=self.project)
 | 
			
		||||
 | 
			
		||||
    def tearDown(self):
 | 
			
		||||
        self.manager.delete_project(self.project)
 | 
			
		||||
        self.manager.delete_user(self.user)
 | 
			
		||||
        self.compute.kill()
 | 
			
		||||
        self.network.kill()
 | 
			
		||||
        super(CloudTestCase, self).tearDown()
 | 
			
		||||
 | 
			
		||||
    def _create_key(self, name):
 | 
			
		||||
@@ -109,12 +104,13 @@ class CloudTestCase(test.TestCase):
 | 
			
		||||
                              {'address': address,
 | 
			
		||||
                               'host': FLAGS.host})
 | 
			
		||||
        self.cloud.allocate_address(self.context)
 | 
			
		||||
        inst = db.instance_create(self.context, {})
 | 
			
		||||
        inst = db.instance_create(self.context, {'host': FLAGS.host})
 | 
			
		||||
        fixed = self.network.allocate_fixed_ip(self.context, inst['id'])
 | 
			
		||||
        ec2_id = cloud.internal_id_to_ec2_id(inst['internal_id'])
 | 
			
		||||
        self.cloud.associate_address(self.context,
 | 
			
		||||
                                     instance_id=ec2_id,
 | 
			
		||||
                                     public_ip=address)
 | 
			
		||||
        greenthread.sleep(0.3)
 | 
			
		||||
        self.cloud.disassociate_address(self.context,
 | 
			
		||||
                                        public_ip=address)
 | 
			
		||||
        self.cloud.release_address(self.context,
 | 
			
		||||
 
 | 
			
		||||
@@ -41,6 +41,7 @@ class ComputeTestCase(test.TestCase):
 | 
			
		||||
        logging.getLogger().setLevel(logging.DEBUG)
 | 
			
		||||
        super(ComputeTestCase, self).setUp()
 | 
			
		||||
        self.flags(connection_type='fake',
 | 
			
		||||
                   stub_network=True,
 | 
			
		||||
                   network_manager='nova.network.manager.FlatManager')
 | 
			
		||||
        self.compute = utils.import_object(FLAGS.compute_manager)
 | 
			
		||||
        self.compute_api = compute_api.ComputeAPI()
 | 
			
		||||
@@ -127,6 +128,14 @@ class ComputeTestCase(test.TestCase):
 | 
			
		||||
        self.assert_(instance_ref['launched_at'] < terminate)
 | 
			
		||||
        self.assert_(instance_ref['deleted_at'] > terminate)
 | 
			
		||||
 | 
			
		||||
    def test_pause(self):
 | 
			
		||||
        """Ensure instance can be paused"""
 | 
			
		||||
        instance_id = self._create_instance()
 | 
			
		||||
        self.compute.run_instance(self.context, instance_id)
 | 
			
		||||
        self.compute.pause_instance(self.context, instance_id)
 | 
			
		||||
        self.compute.unpause_instance(self.context, instance_id)
 | 
			
		||||
        self.compute.terminate_instance(self.context, instance_id)
 | 
			
		||||
 | 
			
		||||
    def test_reboot(self):
 | 
			
		||||
        """Ensure instance can be rebooted"""
 | 
			
		||||
        instance_id = self._create_instance()
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										86
									
								
								nova/tests/test_middleware.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								nova/tests/test_middleware.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
			
		||||
# 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.
 | 
			
		||||
 | 
			
		||||
import datetime
 | 
			
		||||
import webob
 | 
			
		||||
import webob.dec
 | 
			
		||||
import webob.exc
 | 
			
		||||
 | 
			
		||||
from nova.api import ec2
 | 
			
		||||
from nova import flags
 | 
			
		||||
from nova import test
 | 
			
		||||
from nova import utils
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FLAGS = flags.FLAGS
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@webob.dec.wsgify
 | 
			
		||||
def conditional_forbid(req):
 | 
			
		||||
    """Helper wsgi app returns 403 if param 'die' is 1."""
 | 
			
		||||
    if 'die' in req.params and req.params['die'] == '1':
 | 
			
		||||
        raise webob.exc.HTTPForbidden()
 | 
			
		||||
    return 'OK'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LockoutTestCase(test.TrialTestCase):
 | 
			
		||||
    """Test case for the Lockout middleware."""
 | 
			
		||||
    def setUp(self):  # pylint: disable-msg=C0103
 | 
			
		||||
        super(LockoutTestCase, self).setUp()
 | 
			
		||||
        utils.set_time_override()
 | 
			
		||||
        self.lockout = ec2.Lockout(conditional_forbid)
 | 
			
		||||
 | 
			
		||||
    def tearDown(self):  # pylint: disable-msg=C0103
 | 
			
		||||
        utils.clear_time_override()
 | 
			
		||||
        super(LockoutTestCase, self).tearDown()
 | 
			
		||||
 | 
			
		||||
    def _send_bad_attempts(self, access_key, num_attempts=1):
 | 
			
		||||
        """Fail x."""
 | 
			
		||||
        for i in xrange(num_attempts):
 | 
			
		||||
            req = webob.Request.blank('/?AWSAccessKeyId=%s&die=1' % access_key)
 | 
			
		||||
            self.assertEqual(req.get_response(self.lockout).status_int, 403)
 | 
			
		||||
 | 
			
		||||
    def _is_locked_out(self, access_key):
 | 
			
		||||
        """Sends a test request to see if key is locked out."""
 | 
			
		||||
        req = webob.Request.blank('/?AWSAccessKeyId=%s' % access_key)
 | 
			
		||||
        return (req.get_response(self.lockout).status_int == 403)
 | 
			
		||||
 | 
			
		||||
    def test_lockout(self):
 | 
			
		||||
        self._send_bad_attempts('test', FLAGS.lockout_attempts)
 | 
			
		||||
        self.assertTrue(self._is_locked_out('test'))
 | 
			
		||||
 | 
			
		||||
    def test_timeout(self):
 | 
			
		||||
        self._send_bad_attempts('test', FLAGS.lockout_attempts)
 | 
			
		||||
        self.assertTrue(self._is_locked_out('test'))
 | 
			
		||||
        utils.advance_time_seconds(FLAGS.lockout_minutes * 60)
 | 
			
		||||
        self.assertFalse(self._is_locked_out('test'))
 | 
			
		||||
 | 
			
		||||
    def test_multiple_keys(self):
 | 
			
		||||
        self._send_bad_attempts('test1', FLAGS.lockout_attempts)
 | 
			
		||||
        self.assertTrue(self._is_locked_out('test1'))
 | 
			
		||||
        self.assertFalse(self._is_locked_out('test2'))
 | 
			
		||||
        utils.advance_time_seconds(FLAGS.lockout_minutes * 60)
 | 
			
		||||
        self.assertFalse(self._is_locked_out('test1'))
 | 
			
		||||
        self.assertFalse(self._is_locked_out('test2'))
 | 
			
		||||
 | 
			
		||||
    def test_window_timeout(self):
 | 
			
		||||
        self._send_bad_attempts('test', FLAGS.lockout_attempts - 1)
 | 
			
		||||
        self.assertFalse(self._is_locked_out('test'))
 | 
			
		||||
        utils.advance_time_seconds(FLAGS.lockout_window * 60)
 | 
			
		||||
        self._send_bad_attempts('test', FLAGS.lockout_attempts - 1)
 | 
			
		||||
        self.assertFalse(self._is_locked_out('test'))
 | 
			
		||||
@@ -26,6 +26,7 @@ from nova import context
 | 
			
		||||
from nova import db
 | 
			
		||||
from nova import exception
 | 
			
		||||
from nova import flags
 | 
			
		||||
from nova import service
 | 
			
		||||
from nova import test
 | 
			
		||||
from nova import utils
 | 
			
		||||
from nova.auth import manager
 | 
			
		||||
@@ -40,6 +41,7 @@ class NetworkTestCase(test.TestCase):
 | 
			
		||||
        # NOTE(vish): if you change these flags, make sure to change the
 | 
			
		||||
        #             flags in the corresponding section in nova-dhcpbridge
 | 
			
		||||
        self.flags(connection_type='fake',
 | 
			
		||||
                   fake_call=True,
 | 
			
		||||
                   fake_network=True,
 | 
			
		||||
                   network_size=16,
 | 
			
		||||
                   num_networks=5)
 | 
			
		||||
@@ -56,16 +58,13 @@ class NetworkTestCase(test.TestCase):
 | 
			
		||||
            # create the necessary network data for the project
 | 
			
		||||
            user_context = context.RequestContext(project=self.projects[i],
 | 
			
		||||
                                                     user=self.user)
 | 
			
		||||
            network_ref = self.network.get_network(user_context)
 | 
			
		||||
            self.network.set_network_host(context.get_admin_context(),
 | 
			
		||||
                                          network_ref['id'])
 | 
			
		||||
            host = self.network.get_network_host(user_context.elevated())
 | 
			
		||||
        instance_ref = self._create_instance(0)
 | 
			
		||||
        self.instance_id = instance_ref['id']
 | 
			
		||||
        instance_ref = self._create_instance(1)
 | 
			
		||||
        self.instance2_id = instance_ref['id']
 | 
			
		||||
 | 
			
		||||
    def tearDown(self):
 | 
			
		||||
        super(NetworkTestCase, self).tearDown()
 | 
			
		||||
        # TODO(termie): this should really be instantiating clean datastores
 | 
			
		||||
        #               in between runs, one failure kills all the tests
 | 
			
		||||
        db.instance_destroy(context.get_admin_context(), self.instance_id)
 | 
			
		||||
@@ -73,6 +72,7 @@ class NetworkTestCase(test.TestCase):
 | 
			
		||||
        for project in self.projects:
 | 
			
		||||
            self.manager.delete_project(project)
 | 
			
		||||
        self.manager.delete_user(self.user)
 | 
			
		||||
        super(NetworkTestCase, self).tearDown()
 | 
			
		||||
 | 
			
		||||
    def _create_instance(self, project_num, mac=None):
 | 
			
		||||
        if not mac:
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,7 @@ class RpcTestCase(test.TestCase):
 | 
			
		||||
    """Test cases for rpc"""
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(RpcTestCase, self).setUp()
 | 
			
		||||
        self.conn = rpc.Connection.instance()
 | 
			
		||||
        self.conn = rpc.Connection.instance(True)
 | 
			
		||||
        self.receiver = TestReceiver()
 | 
			
		||||
        self.consumer = rpc.AdapterConsumer(connection=self.conn,
 | 
			
		||||
                                            topic='test',
 | 
			
		||||
@@ -79,6 +79,33 @@ class RpcTestCase(test.TestCase):
 | 
			
		||||
        except rpc.RemoteError as exc:
 | 
			
		||||
            self.assertEqual(int(exc.value), value)
 | 
			
		||||
 | 
			
		||||
    def test_nested_calls(self):
 | 
			
		||||
        """Test that we can do an rpc.call inside another call"""
 | 
			
		||||
        class Nested(object):
 | 
			
		||||
            @staticmethod
 | 
			
		||||
            def echo(context, queue, value):
 | 
			
		||||
                """Calls echo in the passed queue"""
 | 
			
		||||
                logging.debug("Nested received %s, %s", queue, value)
 | 
			
		||||
                ret = rpc.call(context,
 | 
			
		||||
                               queue,
 | 
			
		||||
                               {"method": "echo",
 | 
			
		||||
                                "args": {"value": value}})
 | 
			
		||||
                logging.debug("Nested return %s", ret)
 | 
			
		||||
                return value
 | 
			
		||||
 | 
			
		||||
        nested = Nested()
 | 
			
		||||
        conn = rpc.Connection.instance(True)
 | 
			
		||||
        consumer = rpc.AdapterConsumer(connection=conn,
 | 
			
		||||
                                       topic='nested',
 | 
			
		||||
                                       proxy=nested)
 | 
			
		||||
        consumer.attach_to_eventlet()
 | 
			
		||||
        value = 42
 | 
			
		||||
        result = rpc.call(self.context,
 | 
			
		||||
                          'nested', {"method": "echo",
 | 
			
		||||
                                     "args": {"queue": "test",
 | 
			
		||||
                                              "value": value}})
 | 
			
		||||
        self.assertEqual(value, result)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestReceiver(object):
 | 
			
		||||
    """Simple Proxy class so the consumer has methods to call
 | 
			
		||||
 
 | 
			
		||||
@@ -78,6 +78,7 @@ class SimpleDriverTestCase(test.TestCase):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(SimpleDriverTestCase, self).setUp()
 | 
			
		||||
        self.flags(connection_type='fake',
 | 
			
		||||
                   stub_network=True,
 | 
			
		||||
                   max_cores=4,
 | 
			
		||||
                   max_gigabytes=4,
 | 
			
		||||
                   network_manager='nova.network.manager.FlatManager',
 | 
			
		||||
 
 | 
			
		||||
@@ -33,6 +33,7 @@ flags.DECLARE('instances_path', 'nova.compute.manager')
 | 
			
		||||
class LibvirtConnTestCase(test.TestCase):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(LibvirtConnTestCase, self).setUp()
 | 
			
		||||
        self.flags(fake_call=True)
 | 
			
		||||
        self.manager = manager.AuthManager()
 | 
			
		||||
        self.user = self.manager.create_user('fake', 'fake', 'fake',
 | 
			
		||||
                                             admin=True)
 | 
			
		||||
@@ -40,33 +41,64 @@ class LibvirtConnTestCase(test.TestCase):
 | 
			
		||||
        self.network = utils.import_object(FLAGS.network_manager)
 | 
			
		||||
        FLAGS.instances_path = ''
 | 
			
		||||
 | 
			
		||||
    def test_get_uri_and_template(self):
 | 
			
		||||
        ip = '10.11.12.13'
 | 
			
		||||
    test_ip = '10.11.12.13'
 | 
			
		||||
    test_instance = {'memory_kb':     '1024000',
 | 
			
		||||
                     'basepath':      '/some/path',
 | 
			
		||||
                     'bridge_name':   'br100',
 | 
			
		||||
                     'mac_address':   '02:12:34:46:56:67',
 | 
			
		||||
                     'vcpus':         2,
 | 
			
		||||
                     'project_id':    'fake',
 | 
			
		||||
                     'bridge':        'br101',
 | 
			
		||||
                     'instance_type': 'm1.small'}
 | 
			
		||||
 | 
			
		||||
        instance = {'internal_id': 1,
 | 
			
		||||
                    'memory_kb': '1024000',
 | 
			
		||||
                    'basepath': '/some/path',
 | 
			
		||||
                    'bridge_name': 'br100',
 | 
			
		||||
                    'mac_address': '02:12:34:46:56:67',
 | 
			
		||||
                    'vcpus': 2,
 | 
			
		||||
                    'project_id': 'fake',
 | 
			
		||||
                    'bridge': 'br101',
 | 
			
		||||
                    'instance_type': 'm1.small'}
 | 
			
		||||
    def test_xml_and_uri_no_ramdisk_no_kernel(self):
 | 
			
		||||
        instance_data = dict(self.test_instance)
 | 
			
		||||
        self._check_xml_and_uri(instance_data,
 | 
			
		||||
                                expect_kernel=False, expect_ramdisk=False)
 | 
			
		||||
 | 
			
		||||
    def test_xml_and_uri_no_ramdisk(self):
 | 
			
		||||
        instance_data = dict(self.test_instance)
 | 
			
		||||
        instance_data['kernel_id'] = 'aki-deadbeef'
 | 
			
		||||
        self._check_xml_and_uri(instance_data,
 | 
			
		||||
                                expect_kernel=True, expect_ramdisk=False)
 | 
			
		||||
 | 
			
		||||
    def test_xml_and_uri_no_kernel(self):
 | 
			
		||||
        instance_data = dict(self.test_instance)
 | 
			
		||||
        instance_data['ramdisk_id'] = 'ari-deadbeef'
 | 
			
		||||
        self._check_xml_and_uri(instance_data,
 | 
			
		||||
                                expect_kernel=False, expect_ramdisk=False)
 | 
			
		||||
 | 
			
		||||
    def test_xml_and_uri(self):
 | 
			
		||||
        instance_data = dict(self.test_instance)
 | 
			
		||||
        instance_data['ramdisk_id'] = 'ari-deadbeef'
 | 
			
		||||
        instance_data['kernel_id'] = 'aki-deadbeef'
 | 
			
		||||
        self._check_xml_and_uri(instance_data,
 | 
			
		||||
                                expect_kernel=True, expect_ramdisk=True)
 | 
			
		||||
 | 
			
		||||
    def test_xml_and_uri_rescue(self):
 | 
			
		||||
        instance_data = dict(self.test_instance)
 | 
			
		||||
        instance_data['ramdisk_id'] = 'ari-deadbeef'
 | 
			
		||||
        instance_data['kernel_id'] = 'aki-deadbeef'
 | 
			
		||||
        self._check_xml_and_uri(instance_data, expect_kernel=True,
 | 
			
		||||
                                expect_ramdisk=True, rescue=True)
 | 
			
		||||
 | 
			
		||||
    def _check_xml_and_uri(self, instance, expect_ramdisk, expect_kernel,
 | 
			
		||||
                           rescue=False):
 | 
			
		||||
        user_context = context.RequestContext(project=self.project,
 | 
			
		||||
                                              user=self.user)
 | 
			
		||||
        instance_ref = db.instance_create(user_context, instance)
 | 
			
		||||
        network_ref = self.network.get_network(user_context)
 | 
			
		||||
        self.network.set_network_host(context.get_admin_context(),
 | 
			
		||||
                                      network_ref['id'])
 | 
			
		||||
        host = self.network.get_network_host(user_context.elevated())
 | 
			
		||||
        network_ref = db.project_get_network(context.get_admin_context(),
 | 
			
		||||
                                             self.project.id)
 | 
			
		||||
 | 
			
		||||
        fixed_ip = {'address': ip,
 | 
			
		||||
        fixed_ip = {'address':    self.test_ip,
 | 
			
		||||
                    'network_id': network_ref['id']}
 | 
			
		||||
 | 
			
		||||
        ctxt = context.get_admin_context()
 | 
			
		||||
        fixed_ip_ref = db.fixed_ip_create(ctxt, fixed_ip)
 | 
			
		||||
        db.fixed_ip_update(ctxt, ip, {'allocated': True,
 | 
			
		||||
                                      'instance_id': instance_ref['id']})
 | 
			
		||||
        db.fixed_ip_update(ctxt, self.test_ip,
 | 
			
		||||
                                 {'allocated':   True,
 | 
			
		||||
                                  'instance_id': instance_ref['id']})
 | 
			
		||||
 | 
			
		||||
        type_uri_map = {'qemu': ('qemu:///system',
 | 
			
		||||
                             [(lambda t: t.find('.').get('type'), 'qemu'),
 | 
			
		||||
@@ -78,23 +110,73 @@ class LibvirtConnTestCase(test.TestCase):
 | 
			
		||||
                              (lambda t: t.find('./devices/emulator'), None)]),
 | 
			
		||||
                        'uml': ('uml:///system',
 | 
			
		||||
                             [(lambda t: t.find('.').get('type'), 'uml'),
 | 
			
		||||
                              (lambda t: t.find('./os/type').text, 'uml')])}
 | 
			
		||||
                              (lambda t: t.find('./os/type').text, 'uml')]),
 | 
			
		||||
                        'xen': ('xen:///',
 | 
			
		||||
                             [(lambda t: t.find('.').get('type'), 'xen'),
 | 
			
		||||
                              (lambda t: t.find('./os/type').text, 'linux')]),
 | 
			
		||||
                              }
 | 
			
		||||
 | 
			
		||||
        for hypervisor_type in ['qemu', 'kvm', 'xen']:
 | 
			
		||||
            check_list = type_uri_map[hypervisor_type][1]
 | 
			
		||||
 | 
			
		||||
            if rescue:
 | 
			
		||||
                check = (lambda t: t.find('./os/kernel').text.split('/')[1],
 | 
			
		||||
                         'rescue-kernel')
 | 
			
		||||
                check_list.append(check)
 | 
			
		||||
                check = (lambda t: t.find('./os/initrd').text.split('/')[1],
 | 
			
		||||
                         'rescue-ramdisk')
 | 
			
		||||
                check_list.append(check)
 | 
			
		||||
            else:
 | 
			
		||||
                if expect_kernel:
 | 
			
		||||
                    check = (lambda t: t.find('./os/kernel').text.split(
 | 
			
		||||
                        '/')[1], 'kernel')
 | 
			
		||||
                else:
 | 
			
		||||
                    check = (lambda t: t.find('./os/kernel'), None)
 | 
			
		||||
                check_list.append(check)
 | 
			
		||||
 | 
			
		||||
                if expect_ramdisk:
 | 
			
		||||
                    check = (lambda t: t.find('./os/initrd').text.split(
 | 
			
		||||
                        '/')[1], 'ramdisk')
 | 
			
		||||
                else:
 | 
			
		||||
                    check = (lambda t: t.find('./os/initrd'), None)
 | 
			
		||||
                check_list.append(check)
 | 
			
		||||
 | 
			
		||||
        common_checks = [
 | 
			
		||||
            (lambda t: t.find('.').tag, 'domain'),
 | 
			
		||||
            (lambda t: t.find('./devices/interface/filterref/parameter').\
 | 
			
		||||
                         get('name'), 'IP'),
 | 
			
		||||
            (lambda t: t.find('./devices/interface/filterref/parameter').\
 | 
			
		||||
                         get('value'), '10.11.12.13')]
 | 
			
		||||
            (lambda t: t.find(
 | 
			
		||||
                './devices/interface/filterref/parameter').get('name'), 'IP'),
 | 
			
		||||
            (lambda t: t.find(
 | 
			
		||||
                './devices/interface/filterref/parameter').get(
 | 
			
		||||
                    'value'), '10.11.12.13'),
 | 
			
		||||
            (lambda t: t.findall(
 | 
			
		||||
                './devices/interface/filterref/parameter')[1].get(
 | 
			
		||||
                    'name'), 'DHCPSERVER'),
 | 
			
		||||
            (lambda t: t.findall(
 | 
			
		||||
                './devices/interface/filterref/parameter')[1].get(
 | 
			
		||||
                    'value'), '10.0.0.1'),
 | 
			
		||||
            (lambda t: t.find('./devices/serial/source').get(
 | 
			
		||||
                'path').split('/')[1], 'console.log'),
 | 
			
		||||
            (lambda t: t.find('./memory').text, '2097152')]
 | 
			
		||||
 | 
			
		||||
        if rescue:
 | 
			
		||||
            common_checks += [
 | 
			
		||||
                (lambda t: t.findall('./devices/disk/source')[0].get(
 | 
			
		||||
                    'file').split('/')[1], 'rescue-disk'),
 | 
			
		||||
                (lambda t: t.findall('./devices/disk/source')[1].get(
 | 
			
		||||
                    'file').split('/')[1], 'disk')]
 | 
			
		||||
        else:
 | 
			
		||||
            common_checks += [(lambda t: t.findall(
 | 
			
		||||
                './devices/disk/source')[0].get('file').split('/')[1],
 | 
			
		||||
                               'disk')]
 | 
			
		||||
 | 
			
		||||
        for (libvirt_type, (expected_uri, checks)) in type_uri_map.iteritems():
 | 
			
		||||
            FLAGS.libvirt_type = libvirt_type
 | 
			
		||||
            conn = libvirt_conn.LibvirtConnection(True)
 | 
			
		||||
 | 
			
		||||
            uri, _template, _rescue = conn.get_uri_and_templates()
 | 
			
		||||
            uri = conn.get_uri()
 | 
			
		||||
            self.assertEquals(uri, expected_uri)
 | 
			
		||||
 | 
			
		||||
            xml = conn.to_xml(instance_ref)
 | 
			
		||||
            xml = conn.to_xml(instance_ref, rescue)
 | 
			
		||||
            tree = xml_to_tree(xml)
 | 
			
		||||
            for i, (check, expected_result) in enumerate(checks):
 | 
			
		||||
                self.assertEqual(check(tree),
 | 
			
		||||
@@ -106,6 +188,9 @@ class LibvirtConnTestCase(test.TestCase):
 | 
			
		||||
                                 expected_result,
 | 
			
		||||
                                 '%s failed common check %d' % (xml, i))
 | 
			
		||||
 | 
			
		||||
        # This test is supposed to make sure we don't override a specifically
 | 
			
		||||
        # set uri
 | 
			
		||||
        #
 | 
			
		||||
        # Deliberately not just assigning this string to FLAGS.libvirt_uri and
 | 
			
		||||
        # checking against that later on. This way we make sure the
 | 
			
		||||
        # implementation doesn't fiddle around with the FLAGS.
 | 
			
		||||
@@ -114,7 +199,7 @@ class LibvirtConnTestCase(test.TestCase):
 | 
			
		||||
        for (libvirt_type, (expected_uri, checks)) in type_uri_map.iteritems():
 | 
			
		||||
            FLAGS.libvirt_type = libvirt_type
 | 
			
		||||
            conn = libvirt_conn.LibvirtConnection(True)
 | 
			
		||||
            uri, _template, _rescue = conn.get_uri_and_templates()
 | 
			
		||||
            uri = conn.get_uri()
 | 
			
		||||
            self.assertEquals(uri, testuri)
 | 
			
		||||
 | 
			
		||||
    def tearDown(self):
 | 
			
		||||
@@ -252,7 +337,7 @@ class NWFilterTestCase(test.TestCase):
 | 
			
		||||
                                       self.security_group.id)
 | 
			
		||||
        instance = db.instance_get(self.context, inst_id)
 | 
			
		||||
 | 
			
		||||
        d = self.fw.setup_nwfilters_for_instance(instance)
 | 
			
		||||
        self.fw.setup_base_nwfilters()
 | 
			
		||||
        self.fw.setup_nwfilters_for_instance(instance)
 | 
			
		||||
        _ensure_all_called()
 | 
			
		||||
        self.teardown_security_group()
 | 
			
		||||
        return d
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										219
									
								
								nova/tests/test_xenapi.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								nova/tests/test_xenapi.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,219 @@
 | 
			
		||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 | 
			
		||||
 | 
			
		||||
#    Copyright (c) 2010 Citrix Systems, Inc.
 | 
			
		||||
#
 | 
			
		||||
#    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.
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
Test suite for XenAPI
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import stubout
 | 
			
		||||
 | 
			
		||||
from nova import db
 | 
			
		||||
from nova import context
 | 
			
		||||
from nova import flags
 | 
			
		||||
from nova import test
 | 
			
		||||
from nova import utils
 | 
			
		||||
from nova.auth import manager
 | 
			
		||||
from nova.compute import instance_types
 | 
			
		||||
from nova.compute import power_state
 | 
			
		||||
from nova.virt import xenapi_conn
 | 
			
		||||
from nova.virt.xenapi import fake
 | 
			
		||||
from nova.virt.xenapi import volume_utils
 | 
			
		||||
from nova.tests.db import fakes
 | 
			
		||||
from nova.tests.xenapi import stubs
 | 
			
		||||
 | 
			
		||||
FLAGS = flags.FLAGS
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class XenAPIVolumeTestCase(test.TestCase):
 | 
			
		||||
    """
 | 
			
		||||
    Unit tests for Volume operations
 | 
			
		||||
    """
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(XenAPIVolumeTestCase, self).setUp()
 | 
			
		||||
        self.stubs = stubout.StubOutForTesting()
 | 
			
		||||
        FLAGS.target_host = '127.0.0.1'
 | 
			
		||||
        FLAGS.xenapi_connection_url = 'test_url'
 | 
			
		||||
        FLAGS.xenapi_connection_password = 'test_pass'
 | 
			
		||||
        fakes.stub_out_db_instance_api(self.stubs)
 | 
			
		||||
        fake.reset()
 | 
			
		||||
        self.values = {'name': 1, 'id': 1,
 | 
			
		||||
                  'project_id': 'fake',
 | 
			
		||||
                  'user_id': 'fake',
 | 
			
		||||
                  'image_id': 1,
 | 
			
		||||
                  'kernel_id': 2,
 | 
			
		||||
                  'ramdisk_id': 3,
 | 
			
		||||
                  'instance_type': 'm1.large',
 | 
			
		||||
                  'mac_address': 'aa:bb:cc:dd:ee:ff',
 | 
			
		||||
                  }
 | 
			
		||||
 | 
			
		||||
    def _create_volume(self, size='0'):
 | 
			
		||||
        """Create a volume object."""
 | 
			
		||||
        vol = {}
 | 
			
		||||
        vol['size'] = size
 | 
			
		||||
        vol['user_id'] = 'fake'
 | 
			
		||||
        vol['project_id'] = 'fake'
 | 
			
		||||
        vol['host'] = 'localhost'
 | 
			
		||||
        vol['availability_zone'] = FLAGS.storage_availability_zone
 | 
			
		||||
        vol['status'] = "creating"
 | 
			
		||||
        vol['attach_status'] = "detached"
 | 
			
		||||
        return db.volume_create(context.get_admin_context(), vol)
 | 
			
		||||
 | 
			
		||||
    def test_create_iscsi_storage(self):
 | 
			
		||||
        """ This shows how to test helper classes' methods """
 | 
			
		||||
        stubs.stubout_session(self.stubs, stubs.FakeSessionForVolumeTests)
 | 
			
		||||
        session = xenapi_conn.XenAPISession('test_url', 'root', 'test_pass')
 | 
			
		||||
        helper = volume_utils.VolumeHelper
 | 
			
		||||
        helper.XenAPI = session.get_imported_xenapi()
 | 
			
		||||
        vol = self._create_volume()
 | 
			
		||||
        info = helper.parse_volume_info(vol['ec2_id'], '/dev/sdc')
 | 
			
		||||
        label = 'SR-%s' % vol['ec2_id']
 | 
			
		||||
        description = 'Test-SR'
 | 
			
		||||
        sr_ref = helper.create_iscsi_storage(session, info, label, description)
 | 
			
		||||
        srs = fake.get_all('SR')
 | 
			
		||||
        self.assertEqual(sr_ref, srs[0])
 | 
			
		||||
        db.volume_destroy(context.get_admin_context(), vol['id'])
 | 
			
		||||
 | 
			
		||||
    def test_parse_volume_info_raise_exception(self):
 | 
			
		||||
        """ This shows how to test helper classes' methods """
 | 
			
		||||
        stubs.stubout_session(self.stubs, stubs.FakeSessionForVolumeTests)
 | 
			
		||||
        session = xenapi_conn.XenAPISession('test_url', 'root', 'test_pass')
 | 
			
		||||
        helper = volume_utils.VolumeHelper
 | 
			
		||||
        helper.XenAPI = session.get_imported_xenapi()
 | 
			
		||||
        vol = self._create_volume()
 | 
			
		||||
        # oops, wrong mount point!
 | 
			
		||||
        self.assertRaises(volume_utils.StorageError,
 | 
			
		||||
                          helper.parse_volume_info,
 | 
			
		||||
                          vol['ec2_id'],
 | 
			
		||||
                          '/dev/sd')
 | 
			
		||||
        db.volume_destroy(context.get_admin_context(), vol['id'])
 | 
			
		||||
 | 
			
		||||
    def test_attach_volume(self):
 | 
			
		||||
        """ This shows how to test Ops classes' methods """
 | 
			
		||||
        stubs.stubout_session(self.stubs, stubs.FakeSessionForVolumeTests)
 | 
			
		||||
        conn = xenapi_conn.get_connection(False)
 | 
			
		||||
        volume = self._create_volume()
 | 
			
		||||
        instance = db.instance_create(self.values)
 | 
			
		||||
        fake.create_vm(instance.name, 'Running')
 | 
			
		||||
        result = conn.attach_volume(instance.name, volume['ec2_id'],
 | 
			
		||||
                                    '/dev/sdc')
 | 
			
		||||
 | 
			
		||||
        def check():
 | 
			
		||||
            # check that the VM has a VBD attached to it
 | 
			
		||||
            # Get XenAPI reference for the VM
 | 
			
		||||
            vms = fake.get_all('VM')
 | 
			
		||||
            # Get XenAPI record for VBD
 | 
			
		||||
            vbds = fake.get_all('VBD')
 | 
			
		||||
            vbd = fake.get_record('VBD', vbds[0])
 | 
			
		||||
            vm_ref = vbd['VM']
 | 
			
		||||
            self.assertEqual(vm_ref, vms[0])
 | 
			
		||||
 | 
			
		||||
        check()
 | 
			
		||||
 | 
			
		||||
    def test_attach_volume_raise_exception(self):
 | 
			
		||||
        """ This shows how to test when exceptions are raised """
 | 
			
		||||
        stubs.stubout_session(self.stubs,
 | 
			
		||||
                              stubs.FakeSessionForVolumeFailedTests)
 | 
			
		||||
        conn = xenapi_conn.get_connection(False)
 | 
			
		||||
        volume = self._create_volume()
 | 
			
		||||
        instance = db.instance_create(self.values)
 | 
			
		||||
        fake.create_vm(instance.name, 'Running')
 | 
			
		||||
        self.assertRaises(Exception,
 | 
			
		||||
                          conn.attach_volume,
 | 
			
		||||
                          instance.name,
 | 
			
		||||
                          volume['ec2_id'],
 | 
			
		||||
                          '/dev/sdc')
 | 
			
		||||
 | 
			
		||||
    def tearDown(self):
 | 
			
		||||
        super(XenAPIVolumeTestCase, self).tearDown()
 | 
			
		||||
        self.stubs.UnsetAll()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class XenAPIVMTestCase(test.TestCase):
 | 
			
		||||
    """
 | 
			
		||||
    Unit tests for VM operations
 | 
			
		||||
    """
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(XenAPIVMTestCase, self).setUp()
 | 
			
		||||
        self.manager = manager.AuthManager()
 | 
			
		||||
        self.user = self.manager.create_user('fake', 'fake', 'fake',
 | 
			
		||||
                                             admin=True)
 | 
			
		||||
        self.project = self.manager.create_project('fake', 'fake', 'fake')
 | 
			
		||||
        self.network = utils.import_object(FLAGS.network_manager)
 | 
			
		||||
        self.stubs = stubout.StubOutForTesting()
 | 
			
		||||
        FLAGS.xenapi_connection_url = 'test_url'
 | 
			
		||||
        FLAGS.xenapi_connection_password = 'test_pass'
 | 
			
		||||
        fake.reset()
 | 
			
		||||
        fakes.stub_out_db_instance_api(self.stubs)
 | 
			
		||||
        fake.create_network('fake', FLAGS.flat_network_bridge)
 | 
			
		||||
 | 
			
		||||
    def test_list_instances_0(self):
 | 
			
		||||
        stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
 | 
			
		||||
        conn = xenapi_conn.get_connection(False)
 | 
			
		||||
        instances = conn.list_instances()
 | 
			
		||||
        self.assertEquals(instances, [])
 | 
			
		||||
 | 
			
		||||
    def test_spawn(self):
 | 
			
		||||
        stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
 | 
			
		||||
        values = {'name': 1, 'id': 1,
 | 
			
		||||
                  'project_id': self.project.id,
 | 
			
		||||
                  'user_id': self.user.id,
 | 
			
		||||
                  'image_id': 1,
 | 
			
		||||
                  'kernel_id': 2,
 | 
			
		||||
                  'ramdisk_id': 3,
 | 
			
		||||
                  'instance_type': 'm1.large',
 | 
			
		||||
                  'mac_address': 'aa:bb:cc:dd:ee:ff',
 | 
			
		||||
                  }
 | 
			
		||||
        conn = xenapi_conn.get_connection(False)
 | 
			
		||||
        instance = db.instance_create(values)
 | 
			
		||||
        conn.spawn(instance)
 | 
			
		||||
 | 
			
		||||
        def check():
 | 
			
		||||
            instances = conn.list_instances()
 | 
			
		||||
            self.assertEquals(instances, [1])
 | 
			
		||||
 | 
			
		||||
            # Get Nova record for VM
 | 
			
		||||
            vm_info = conn.get_info(1)
 | 
			
		||||
 | 
			
		||||
            # Get XenAPI record for VM
 | 
			
		||||
            vms = fake.get_all('VM')
 | 
			
		||||
            vm = fake.get_record('VM', vms[0])
 | 
			
		||||
 | 
			
		||||
            # Check that m1.large above turned into the right thing.
 | 
			
		||||
            instance_type = instance_types.INSTANCE_TYPES['m1.large']
 | 
			
		||||
            mem_kib = long(instance_type['memory_mb']) << 10
 | 
			
		||||
            mem_bytes = str(mem_kib << 10)
 | 
			
		||||
            vcpus = instance_type['vcpus']
 | 
			
		||||
            self.assertEquals(vm_info['max_mem'], mem_kib)
 | 
			
		||||
            self.assertEquals(vm_info['mem'], mem_kib)
 | 
			
		||||
            self.assertEquals(vm['memory_static_max'], mem_bytes)
 | 
			
		||||
            self.assertEquals(vm['memory_dynamic_max'], mem_bytes)
 | 
			
		||||
            self.assertEquals(vm['memory_dynamic_min'], mem_bytes)
 | 
			
		||||
            self.assertEquals(vm['VCPUs_max'], str(vcpus))
 | 
			
		||||
            self.assertEquals(vm['VCPUs_at_startup'], str(vcpus))
 | 
			
		||||
 | 
			
		||||
            # Check that the VM is running according to Nova
 | 
			
		||||
            self.assertEquals(vm_info['state'], power_state.RUNNING)
 | 
			
		||||
 | 
			
		||||
            # Check that the VM is running according to XenAPI.
 | 
			
		||||
            self.assertEquals(vm['power_state'], 'Running')
 | 
			
		||||
 | 
			
		||||
        check()
 | 
			
		||||
 | 
			
		||||
    def tearDown(self):
 | 
			
		||||
        super(XenAPIVMTestCase, self).tearDown()
 | 
			
		||||
        self.manager.delete_project(self.project)
 | 
			
		||||
        self.manager.delete_user(self.user)
 | 
			
		||||
        self.stubs.UnsetAll()
 | 
			
		||||
							
								
								
									
										20
									
								
								nova/tests/xenapi/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								nova/tests/xenapi/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 | 
			
		||||
 | 
			
		||||
#    Copyright (c) 2010 Citrix Systems, Inc.
 | 
			
		||||
#
 | 
			
		||||
#    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.
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
:mod:`xenapi` -- Stubs for XenAPI
 | 
			
		||||
=================================
 | 
			
		||||
"""
 | 
			
		||||
							
								
								
									
										94
									
								
								nova/tests/xenapi/stubs.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								nova/tests/xenapi/stubs.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,94 @@
 | 
			
		||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 | 
			
		||||
 | 
			
		||||
# Copyright (c) 2010 Citrix Systems, Inc.
 | 
			
		||||
#
 | 
			
		||||
#    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.
 | 
			
		||||
 | 
			
		||||
"""Stubouts, mocks and fixtures for the test suite"""
 | 
			
		||||
 | 
			
		||||
from nova.virt import xenapi_conn
 | 
			
		||||
from nova.virt.xenapi import fake
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def stubout_session(stubs, cls):
 | 
			
		||||
    """ Stubs out two methods from XenAPISession """
 | 
			
		||||
    def fake_import(self):
 | 
			
		||||
        """ Stubs out get_imported_xenapi of XenAPISession """
 | 
			
		||||
        fake_module = 'nova.virt.xenapi.fake'
 | 
			
		||||
        from_list = ['fake']
 | 
			
		||||
        return __import__(fake_module, globals(), locals(), from_list, -1)
 | 
			
		||||
 | 
			
		||||
    stubs.Set(xenapi_conn.XenAPISession, '_create_session',
 | 
			
		||||
                       lambda s, url: cls(url))
 | 
			
		||||
    stubs.Set(xenapi_conn.XenAPISession, 'get_imported_xenapi',
 | 
			
		||||
                       fake_import)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FakeSessionForVMTests(fake.SessionBase):
 | 
			
		||||
    """ Stubs out a XenAPISession for VM tests """
 | 
			
		||||
    def __init__(self, uri):
 | 
			
		||||
        super(FakeSessionForVMTests, self).__init__(uri)
 | 
			
		||||
 | 
			
		||||
    def network_get_all_records_where(self, _1, _2):
 | 
			
		||||
        return self.xenapi.network.get_all_records()
 | 
			
		||||
 | 
			
		||||
    def host_call_plugin(self, _1, _2, _3, _4, _5):
 | 
			
		||||
        return ''
 | 
			
		||||
 | 
			
		||||
    def VM_start(self, _1, ref, _2, _3):
 | 
			
		||||
        vm = fake.get_record('VM', ref)
 | 
			
		||||
        if vm['power_state'] != 'Halted':
 | 
			
		||||
            raise fake.Failure(['VM_BAD_POWER_STATE', ref, 'Halted',
 | 
			
		||||
                                  vm['power_state']])
 | 
			
		||||
        vm['power_state'] = 'Running'
 | 
			
		||||
        vm['is_a_template'] = False
 | 
			
		||||
        vm['is_control_domain'] = False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FakeSessionForVolumeTests(fake.SessionBase):
 | 
			
		||||
    """ Stubs out a XenAPISession for Volume tests """
 | 
			
		||||
    def __init__(self, uri):
 | 
			
		||||
        super(FakeSessionForVolumeTests, self).__init__(uri)
 | 
			
		||||
 | 
			
		||||
    def VBD_plug(self, _1, ref):
 | 
			
		||||
        rec = fake.get_record('VBD', ref)
 | 
			
		||||
        rec['currently-attached'] = True
 | 
			
		||||
 | 
			
		||||
    def VDI_introduce(self, _1, uuid, _2, _3, _4, _5,
 | 
			
		||||
                      _6, _7, _8, _9, _10, _11):
 | 
			
		||||
        valid_vdi = False
 | 
			
		||||
        refs = fake.get_all('VDI')
 | 
			
		||||
        for ref in refs:
 | 
			
		||||
            rec = fake.get_record('VDI', ref)
 | 
			
		||||
            if rec['uuid'] == uuid:
 | 
			
		||||
                valid_vdi = True
 | 
			
		||||
        if not valid_vdi:
 | 
			
		||||
            raise fake.Failure([['INVALID_VDI', 'session', self._session]])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FakeSessionForVolumeFailedTests(FakeSessionForVolumeTests):
 | 
			
		||||
    """ Stubs out a XenAPISession for Volume tests: it injects failures """
 | 
			
		||||
    def __init__(self, uri):
 | 
			
		||||
        super(FakeSessionForVolumeFailedTests, self).__init__(uri)
 | 
			
		||||
 | 
			
		||||
    def VDI_introduce(self, _1, uuid, _2, _3, _4, _5,
 | 
			
		||||
                      _6, _7, _8, _9, _10, _11):
 | 
			
		||||
        # This is for testing failure
 | 
			
		||||
        raise fake.Failure([['INVALID_VDI', 'session', self._session]])
 | 
			
		||||
 | 
			
		||||
    def PBD_unplug(self, _1, ref):
 | 
			
		||||
        rec = fake.get_record('PBD', ref)
 | 
			
		||||
        rec['currently-attached'] = False
 | 
			
		||||
 | 
			
		||||
    def SR_forget(self, _1, ref):
 | 
			
		||||
        pass
 | 
			
		||||
@@ -43,7 +43,7 @@ else:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FLAGS = flags.FLAGS
 | 
			
		||||
flags.DEFINE_string('logdir',  None, 'directory to keep log files in '
 | 
			
		||||
flags.DEFINE_string('logdir', None, 'directory to keep log files in '
 | 
			
		||||
                                     '(will be prepended to $logfile)')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -208,7 +208,7 @@ def stop(pidfile):
 | 
			
		||||
        pid = None
 | 
			
		||||
 | 
			
		||||
    if not pid:
 | 
			
		||||
        message = "pidfile %s does not exist. Daemon not running?\n"
 | 
			
		||||
        message = _("pidfile %s does not exist. Daemon not running?\n")
 | 
			
		||||
        sys.stderr.write(message % pidfile)
 | 
			
		||||
        # Not an error in a restart
 | 
			
		||||
        return
 | 
			
		||||
@@ -229,7 +229,7 @@ def stop(pidfile):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def serve(filename):
 | 
			
		||||
    logging.debug("Serving %s" % filename)
 | 
			
		||||
    logging.debug(_("Serving %s") % filename)
 | 
			
		||||
    name = os.path.basename(filename)
 | 
			
		||||
    OptionsClass = WrapTwistedOptions(TwistdServerOptions)
 | 
			
		||||
    options = OptionsClass()
 | 
			
		||||
@@ -281,7 +281,7 @@ def serve(filename):
 | 
			
		||||
    else:
 | 
			
		||||
        logging.getLogger().setLevel(logging.WARNING)
 | 
			
		||||
 | 
			
		||||
    logging.debug("Full set of FLAGS:")
 | 
			
		||||
    logging.debug(_("Full set of FLAGS:"))
 | 
			
		||||
    for flag in FLAGS:
 | 
			
		||||
        logging.debug("%s : %s" % (flag, FLAGS.get(flag, None)))
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user