Merge from trunk: process replaced with util

This commit is contained in:
Chiradeep Vittal
2011-01-03 11:39:50 -08:00
63 changed files with 1608 additions and 1870 deletions

View File

@@ -6,6 +6,7 @@ keys
networks networks
nova.sqlite nova.sqlite
CA/cacert.pem CA/cacert.pem
CA/crl.pem
CA/index.txt* CA/index.txt*
CA/openssl.cnf CA/openssl.cnf
CA/serial* CA/serial*

View File

@@ -19,11 +19,14 @@
<mordred@inaugust.com> <mordred@hudson> <mordred@inaugust.com> <mordred@hudson>
<paul@openstack.org> <pvoccio@castor.local> <paul@openstack.org> <pvoccio@castor.local>
<paul@openstack.org> <paul.voccio@rackspace.com> <paul@openstack.org> <paul.voccio@rackspace.com>
<soren.hansen@rackspace.com> <soren@linux2go.dk>
<todd@ansolabs.com> <todd@lapex> <todd@ansolabs.com> <todd@lapex>
<todd@ansolabs.com> <todd@rubidine.com> <todd@ansolabs.com> <todd@rubidine.com>
<vishvananda@gmail.com> <vishvananda@yahoo.com> <vishvananda@gmail.com> <vishvananda@yahoo.com>
<vishvananda@gmail.com> <root@mirror.nasanebula.net> <vishvananda@gmail.com> <root@mirror.nasanebula.net>
# These are from people who failed to set a proper committer <vishvananda@gmail.com> <root@ubuntu>
. <root@tonbuntu> <sleepsonthefloor@gmail.com> <root@tonbuntu>
. <laner@controller> <rlane@wikimedia.org> <laner@controller>
. <root@ubuntu> <rconradharris@gmail.com> <rick.harris@rackspace.com>
<corywright@gmail.com> <cory.wright@rackspace.com>
<ant@openstack.org> <amesserl@rackspace.com>

11
Authors
View File

@@ -1,11 +1,16 @@
Andy Smith <code@term.ie> Andy Smith <code@term.ie>
Anne Gentle <anne@openstack.org> Anne Gentle <anne@openstack.org>
Anthony Young <sleepsonthefloor@gmail.com> Anthony Young <sleepsonthefloor@gmail.com>
Antony Messerli <ant@openstack.org>
Armando Migliaccio <Armando.Migliaccio@eu.citrix.com> Armando Migliaccio <Armando.Migliaccio@eu.citrix.com>
Chris Behrens <cbehrens@codestud.com> Chris Behrens <cbehrens@codestud.com>
Chmouel Boudjnah <chmouel@chmouel.com> Chmouel Boudjnah <chmouel@chmouel.com>
Cory Wright <corywright@gmail.com>
David Pravec <David.Pravec@danix.org>
Dean Troyer <dtroyer@gmail.com> Dean Troyer <dtroyer@gmail.com>
Devin Carlen <devin.carlen@gmail.com> Devin Carlen <devin.carlen@gmail.com>
Ed Leafe <ed@leafe.com>
Eldar Nugaev <enugaev@griddynamics.com>
Eric Day <eday@oddments.org> Eric Day <eday@oddments.org>
Ewan Mellor <ewan.mellor@citrix.com> Ewan Mellor <ewan.mellor@citrix.com>
Hisaki Ohara <hisaki.ohara@intel.com> Hisaki Ohara <hisaki.ohara@intel.com>
@@ -13,6 +18,7 @@ Jay Pipes <jaypipes@gmail.com>
Jesse Andrews <anotherjesse@gmail.com> Jesse Andrews <anotherjesse@gmail.com>
Joe Heck <heckj@mac.com> Joe Heck <heckj@mac.com>
Joel Moore <joelbm24@gmail.com> Joel Moore <joelbm24@gmail.com>
Jonathan Bryce <jbryce@jbryce.com>
Josh Kearney <josh.kearney@rackspace.com> Josh Kearney <josh.kearney@rackspace.com>
Joshua McKenty <jmckenty@gmail.com> Joshua McKenty <jmckenty@gmail.com>
Justin Santa Barbara <justin@fathomdb.com> Justin Santa Barbara <justin@fathomdb.com>
@@ -21,11 +27,16 @@ Michael Gundlach <michael.gundlach@rackspace.com>
Monty Taylor <mordred@inaugust.com> Monty Taylor <mordred@inaugust.com>
Paul Voccio <paul@openstack.org> Paul Voccio <paul@openstack.org>
Rick Clark <rick@openstack.org> Rick Clark <rick@openstack.org>
Rick Harris <rconradharris@gmail.com>
Ryan Lane <rlane@wikimedia.org>
Ryan Lucio <rlucio@internap.com> Ryan Lucio <rlucio@internap.com>
Salvatore Orlando <salvatore.orlando@eu.citrix.com>
Sandy Walsh <sandy.walsh@rackspace.com> Sandy Walsh <sandy.walsh@rackspace.com>
Soren Hansen <soren.hansen@rackspace.com> Soren Hansen <soren.hansen@rackspace.com>
Thierry Carrez <thierry@openstack.org>
Todd Willey <todd@ansolabs.com> Todd Willey <todd@ansolabs.com>
Trey Morris <trey.morris@rackspace.com> Trey Morris <trey.morris@rackspace.com>
Vishvananda Ishaya <vishvananda@gmail.com> Vishvananda Ishaya <vishvananda@gmail.com>
Youcef Laribi <Youcef.Laribi@eu.citrix.com> Youcef Laribi <Youcef.Laribi@eu.citrix.com>
Zhixue Wu <Zhixue.Wu@citrix.com> Zhixue Wu <Zhixue.Wu@citrix.com>

View File

@@ -16,16 +16,24 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
# ARG is the id of the user # $1 is the id of the project and $2 is the subject of the cert
export SUBJ="/C=US/ST=California/L=MountainView/O=AnsoLabs/OU=NovaDev/CN=customer-intCA-$1" NAME=$1
mkdir INTER/$1 SUBJ=$2
cd INTER/$1 mkdir -p projects/$NAME
cd projects/$NAME
cp ../../openssl.cnf.tmpl openssl.cnf 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 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 echo "10" > serial
touch index.txt touch index.txt
openssl genrsa -out private/cakey.pem 1024 -config ./openssl.cnf -batch -nodes # NOTE(vish): Disabling intermediate ca's because we don't actually need them.
openssl req -new -sha2 -key private/cakey.pem -out ../../reqs/inter$1.csr -batch -subj "$SUBJ" # It makes more sense to have each project have its own root ca.
cd ../../ # openssl genrsa -out private/cakey.pem 1024 -config ./openssl.cnf -batch -nodes
openssl ca -extensions v3_ca -days 365 -out INTER/$1/cacert.pem -in reqs/inter$1.csr -config openssl.cnf -batch # 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

View File

@@ -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 openssl req -new -x509 -extensions v3_ca -keyout private/cakey.pem -out cacert.pem -days 365 -config ./openssl.cnf -batch -nodes
touch index.txt touch index.txt
echo "10" > serial echo "10" > serial
openssl ca -gencrl -config ./openssl.cnf -out crl.pem
fi fi

34
nova/tests/validator_unittest.py → CA/genvpn.sh Normal file → Executable file
View File

@@ -1,3 +1,4 @@
#!/bin/bash
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the # Copyright 2010 United States Government as represented by the
@@ -16,27 +17,20 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import logging # This gets zipped and run on the cloudpipe-managed OpenVPN server
import unittest NAME=$1
SUBJ=$2
from nova import flags mkdir -p projects/$NAME
from nova import test cd projects/$NAME
from nova import validate
# generate a server priv key
openssl genrsa -out server.key 2048
class ValidationTestCase(test.TrialTestCase): # generate a server CSR
def setUp(self): openssl req -new -key server.key -out server.csr -batch -subj "$SUBJ"
super(ValidationTestCase, self).setUp()
def tearDown(self): novauid=`getent passwd nova | awk -F: '{print $3}'`
super(ValidationTestCase, self).tearDown() if [ ! -z "${novauid}" ] && [ "`id -u`" != "${novauid}" ]; then
sudo chown -R nova:nogroup .
def test_type_validation(self): fi
self.assertTrue(type_case("foo", 5, 1))
self.assertRaises(TypeError, type_case, "bar", "5", 1)
self.assertRaises(TypeError, type_case, None, 5, 1)
@validate.typetest(instanceid=str, size=int, number_of_instances=int)
def type_case(instanceid, size, number_of_instances):
return True

View File

@@ -24,7 +24,6 @@ dir = .
[ ca ] [ ca ]
default_ca = CA_default default_ca = CA_default
unique_subject = no
[ CA_default ] [ CA_default ]
serial = $dir/serial serial = $dir/serial
@@ -32,6 +31,8 @@ database = $dir/index.txt
new_certs_dir = $dir/newcerts new_certs_dir = $dir/newcerts
certificate = $dir/cacert.pem certificate = $dir/cacert.pem
private_key = $dir/private/cakey.pem private_key = $dir/private/cakey.pem
unique_subject = no
default_crl_days = 365
default_days = 365 default_days = 365
default_md = md5 default_md = md5
preserve = no preserve = no

View File

@@ -13,7 +13,7 @@ include nova/cloudpipe/client.ovpn.template
include nova/compute/fakevirtinstance.xml include nova/compute/fakevirtinstance.xml
include nova/compute/interfaces.template include nova/compute/interfaces.template
include nova/virt/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/
include nova/tests/CA/cacert.pem include nova/tests/CA/cacert.pem
include nova/tests/CA/private/ include nova/tests/CA/private/

View File

@@ -17,10 +17,10 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
"""
Nova API daemon.
"""
"""Starter script for Nova API."""
import gettext
import os import os
import sys import sys
@@ -32,9 +32,13 @@ 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')): if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
sys.path.insert(0, possible_topdir) sys.path.insert(0, possible_topdir)
gettext.install('nova', unicode=1)
from nova import api
from nova import flags from nova import flags
from nova import utils from nova import utils
from nova import server from nova import wsgi
FLAGS = flags.FLAGS FLAGS = flags.FLAGS
flags.DEFINE_integer('osapi_port', 8774, 'OpenStack API port') flags.DEFINE_integer('osapi_port', 8774, 'OpenStack API port')
@@ -43,15 +47,10 @@ flags.DEFINE_integer('ec2api_port', 8773, 'EC2 API port')
flags.DEFINE_string('ec2api_host', '0.0.0.0', 'EC2 API host') flags.DEFINE_string('ec2api_host', '0.0.0.0', 'EC2 API host')
def main(_args): if __name__ == '__main__':
from nova import api utils.default_flagfile()
from nova import wsgi FLAGS(sys.argv)
server = wsgi.Server() server = wsgi.Server()
server.start(api.API('os'), FLAGS.osapi_port, host=FLAGS.osapi_host) server.start(api.API('os'), FLAGS.osapi_port, host=FLAGS.osapi_host)
server.start(api.API('ec2'), FLAGS.ec2api_port, host=FLAGS.ec2api_host) server.start(api.API('ec2'), FLAGS.ec2api_port, host=FLAGS.ec2api_host)
server.wait() server.wait()
if __name__ == '__main__':
utils.default_flagfile()
server.serve('nova-api', main)

68
bin/nova-combined Executable file
View File

@@ -0,0 +1,68 @@
#!/usr/bin/env python
# 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.
"""Combined starter script for Nova services."""
import eventlet
eventlet.monkey_patch()
import gettext
import os
import sys
# If ../nova/__init__.py exists, add ../ to Python search path, so that
# it will override what happens to be installed in /usr/(local/)lib/python...
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
os.pardir,
os.pardir))
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
from nova import utils
from nova import wsgi
FLAGS = flags.FLAGS
flags.DEFINE_integer('osapi_port', 8774, 'OpenStack API port')
flags.DEFINE_string('osapi_host', '0.0.0.0', 'OpenStack API host')
flags.DEFINE_integer('ec2api_port', 8773, 'EC2 API port')
flags.DEFINE_string('ec2api_host', '0.0.0.0', 'EC2 API host')
if __name__ == '__main__':
utils.default_flagfile()
FLAGS(sys.argv)
compute = service.Service.create(binary='nova-compute')
network = service.Service.create(binary='nova-network')
volume = service.Service.create(binary='nova-volume')
scheduler = service.Service.create(binary='nova-scheduler')
#objectstore = service.Service.create(binary='nova-objectstore')
service.serve(compute, network, volume, scheduler)
server = wsgi.Server()
server.start(api.API('os'), FLAGS.osapi_port, host=FLAGS.osapi_host)
server.start(api.API('ec2'), FLAGS.ec2api_port, host=FLAGS.ec2api_host)
server.wait()

View File

@@ -17,10 +17,12 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
""" """Starter script for Nova Compute."""
Twistd daemon for the nova compute nodes.
"""
import eventlet
eventlet.monkey_patch()
import gettext
import os import os
import sys import sys
@@ -32,14 +34,12 @@ 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')): if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
sys.path.insert(0, possible_topdir) sys.path.insert(0, possible_topdir)
from nova import service gettext.install('nova', unicode=1)
from nova import twistd
from nova import utils
from nova import service
from nova import utils
if __name__ == '__main__': if __name__ == '__main__':
utils.default_flagfile() utils.default_flagfile()
twistd.serve(__file__) service.serve()
service.wait()
if __name__ == '__builtin__':
application = service.Service.create() # pylint: disable=C0103

View File

@@ -21,6 +21,7 @@
Handle lease database updates from DHCP servers. Handle lease database updates from DHCP servers.
""" """
import gettext
import logging import logging
import os import os
import sys 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')): if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
sys.path.insert(0, possible_topdir) sys.path.insert(0, possible_topdir)
gettext.install('nova', unicode=1)
from nova import context from nova import context
from nova import db from nova import db
from nova import flags from nova import flags
@@ -107,7 +110,6 @@ def main():
FLAGS.num_networks = 5 FLAGS.num_networks = 5
path = os.path.abspath(os.path.join(os.path.dirname(__file__), path = os.path.abspath(os.path.join(os.path.dirname(__file__),
'..', '..',
'_trial_temp',
'nova.sqlite')) 'nova.sqlite'))
FLAGS.sql_connection = 'sqlite:///%s' % path FLAGS.sql_connection = 'sqlite:///%s' % path
action = argv[1] action = argv[1]

View File

@@ -21,6 +21,7 @@
Download images from Canonical Image Store Download images from Canonical Image Store
""" """
import gettext
import json import json
import os import os
import tempfile import tempfile
@@ -37,6 +38,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')): if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
sys.path.insert(0, possible_topdir) sys.path.insert(0, possible_topdir)
gettext.install('nova', unicode=1)
from nova import flags from nova import flags
from nova import utils from nova import utils
from nova.objectstore import image from nova.objectstore import image

View File

@@ -21,6 +21,7 @@
Daemon for Nova RRD based instance resource monitoring. Daemon for Nova RRD based instance resource monitoring.
""" """
import gettext
import os import os
import logging import logging
import sys import sys
@@ -34,6 +35,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')): if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
sys.path.insert(0, possible_topdir) sys.path.insert(0, possible_topdir)
gettext.install('nova', unicode=1)
from nova import utils from nova import utils
from nova import twistd from nova import twistd
from nova.compute import monitor from nova.compute import monitor

View File

@@ -53,6 +53,7 @@
CLI interface for nova management. CLI interface for nova management.
""" """
import gettext
import logging import logging
import os import os
import sys import sys
@@ -68,7 +69,10 @@ 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')): if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
sys.path.insert(0, possible_topdir) sys.path.insert(0, possible_topdir)
gettext.install('nova', unicode=1)
from nova import context from nova import context
from nova import crypto
from nova import db from nova import db
from nova import exception from nova import exception
from nova import flags from nova import flags
@@ -93,47 +97,43 @@ class VpnCommands(object):
self.manager = manager.AuthManager() self.manager = manager.AuthManager()
self.pipe = pipelib.CloudPipe() self.pipe = pipelib.CloudPipe()
def list(self): def list(self, project=None):
"""Print a listing of the VPNs for all projects.""" """Print a listing of the VPN data for one or all projects.
args: [project=all]"""
print "%-12s\t" % 'project', print "%-12s\t" % 'project',
print "%-20s\t" % 'ip:port', print "%-20s\t" % 'ip:port',
print "%-20s\t" % 'private_ip',
print "%s" % 'state' 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, print "%-12s\t" % project.name,
ipport = "%s:%s" % (project.vpn_ip, project.vpn_port)
try: print "%-20s\t" % ipport,
s = "%s:%s" % (project.vpn_ip, project.vpn_port) ctxt = context.get_admin_context()
except exception.NotFound: vpn = db.instance_get_project_vpn(ctxt, project.id)
s = "None"
print "%-20s\t" % s,
vpn = self._vpn_for(project.id)
if vpn: if vpn:
command = "ping -c1 -w1 %s > /dev/null; echo $?" address = None
out, _err = utils.execute(command % vpn['private_dns_name'], state = 'down'
check_exit_code=False) if vpn.get('fixed_ip', None):
if out.strip() == '0': address = vpn['fixed_ip']['address']
net = 'up' if project.vpn_ip and utils.vpn_ping(project.vpn_ip,
else: project.vpn_port):
net = 'down' state = 'up'
print vpn['private_dns_name'], print address,
print vpn['node_name'], print vpn['host'],
print vpn['instance_id'], print vpn['ec2_id'],
print vpn['state_description'], print vpn['state_description'],
print net print state
else: else:
print None 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): def spawn(self):
"""Run all VPNs.""" """Run all VPNs."""
for p in reversed(self.manager.get_projects()): for p in reversed(self.manager.get_projects()):
@@ -146,6 +146,21 @@ class VpnCommands(object):
"""Start the VPN for a given project.""" """Start the VPN for a given project."""
self.pipe.launch_vpn_instance(project_id) 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): class ShellCommands(object):
def bpython(self): def bpython(self):
@@ -292,6 +307,14 @@ class UserCommands(object):
is_admin = False is_admin = False
self.manager.modify_user(name, access_key, secret_key, is_admin) 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 ProjectCommands(object):
"""Class for managing projects.""" """Class for managing projects."""

View File

@@ -17,10 +17,12 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
""" """Starter script for Nova Network."""
Twistd daemon for the nova network nodes.
"""
import eventlet
eventlet.monkey_patch()
import gettext
import os import os
import sys import sys
@@ -32,14 +34,12 @@ 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')): if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
sys.path.insert(0, possible_topdir) sys.path.insert(0, possible_topdir)
from nova import service gettext.install('nova', unicode=1)
from nova import twistd
from nova import utils
from nova import service
from nova import utils
if __name__ == '__main__': if __name__ == '__main__':
utils.default_flagfile() utils.default_flagfile()
twistd.serve(__file__) service.serve()
service.wait()
if __name__ == '__builtin__':
application = service.Service.create() # pylint: disable-msg=C0103

View File

@@ -21,6 +21,7 @@
Twisted daemon for nova objectstore. Supports S3 API. Twisted daemon for nova objectstore. Supports S3 API.
""" """
import gettext
import os import os
import sys import sys
@@ -32,6 +33,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')): if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
sys.path.insert(0, possible_topdir) sys.path.insert(0, possible_topdir)
gettext.install('nova', unicode=1)
from nova import flags from nova import flags
from nova import utils from nova import utils
from nova import twistd from nova import twistd

View File

@@ -17,10 +17,12 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
""" """Starter script for Nova Scheduler."""
Twistd daemon for the nova scheduler nodes.
"""
import eventlet
eventlet.monkey_patch()
import gettext
import os import os
import sys import sys
@@ -32,14 +34,12 @@ 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')): if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
sys.path.insert(0, possible_topdir) sys.path.insert(0, possible_topdir)
from nova import service gettext.install('nova', unicode=1)
from nova import twistd
from nova import utils
from nova import service
from nova import utils
if __name__ == '__main__': if __name__ == '__main__':
utils.default_flagfile() utils.default_flagfile()
twistd.serve(__file__) service.serve()
service.wait()
if __name__ == '__builtin__':
application = service.Service.create()

View File

@@ -17,10 +17,12 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
""" """Starter script for Nova Volume."""
Twistd daemon for the nova volume nodes.
"""
import eventlet
eventlet.monkey_patch()
import gettext
import os import os
import sys import sys
@@ -32,14 +34,12 @@ 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')): if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
sys.path.insert(0, possible_topdir) sys.path.insert(0, possible_topdir)
from nova import service gettext.install('nova', unicode=1)
from nova import twistd
from nova import utils
from nova import service
from nova import utils
if __name__ == '__main__': if __name__ == '__main__':
utils.default_flagfile() utils.default_flagfile()
twistd.serve(__file__) service.serve()
service.wait()
if __name__ == '__builtin__':
application = service.Service.create() # pylint: disable-msg=C0103

View File

@@ -194,6 +194,7 @@ class HostInfo(object):
class NovaAdminClient(object): class NovaAdminClient(object):
def __init__( def __init__(
self, self,
clc_url=DEFAULT_CLC_URL, clc_url=DEFAULT_CLC_URL,

View File

@@ -37,7 +37,6 @@ class DbDriver(object):
def __init__(self): def __init__(self):
"""Imports the LDAP module""" """Imports the LDAP module"""
pass pass
db
def __enter__(self): def __enter__(self):
return self return self
@@ -83,7 +82,7 @@ class DbDriver(object):
user_ref = db.user_create(context.get_admin_context(), values) user_ref = db.user_create(context.get_admin_context(), values)
return self._db_user_to_auth_user(user_ref) return self._db_user_to_auth_user(user_ref)
except exception.Duplicate, e: 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): def _db_user_to_auth_user(self, user_ref):
return {'id': user_ref['id'], return {'id': user_ref['id'],
@@ -105,8 +104,9 @@ class DbDriver(object):
"""Create a project""" """Create a project"""
manager = db.user_get(context.get_admin_context(), manager_uid) manager = db.user_get(context.get_admin_context(), manager_uid)
if not manager: if not manager:
raise exception.NotFound("Project can't be created because " raise exception.NotFound(_("Project can't be created because "
"manager %s doesn't exist" % manager_uid) "manager %s doesn't exist")
% manager_uid)
# description is a required attribute # description is a required attribute
if description is None: if description is None:
@@ -133,8 +133,8 @@ class DbDriver(object):
try: try:
project = db.project_create(context.get_admin_context(), values) project = db.project_create(context.get_admin_context(), values)
except exception.Duplicate: except exception.Duplicate:
raise exception.Duplicate("Project can't be created because " raise exception.Duplicate(_("Project can't be created because "
"project %s already exists" % name) "project %s already exists") % name)
for member in members: for member in members:
db.project_add_member(context.get_admin_context(), db.project_add_member(context.get_admin_context(),
@@ -155,8 +155,8 @@ class DbDriver(object):
if manager_uid: if manager_uid:
manager = db.user_get(context.get_admin_context(), manager_uid) manager = db.user_get(context.get_admin_context(), manager_uid)
if not manager: if not manager:
raise exception.NotFound("Project can't be modified because " raise exception.NotFound(_("Project can't be modified because "
"manager %s doesn't exist" % "manager %s doesn't exist") %
manager_uid) manager_uid)
values['project_manager'] = manager['id'] values['project_manager'] = manager['id']
if description: if description:
@@ -243,8 +243,8 @@ class DbDriver(object):
def _validate_user_and_project(self, user_id, project_id): def _validate_user_and_project(self, user_id, project_id):
user = db.user_get(context.get_admin_context(), user_id) user = db.user_get(context.get_admin_context(), user_id)
if not user: 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) project = db.project_get(context.get_admin_context(), project_id)
if not project: if not project:
raise exception.NotFound('Project "%s" not found' % project_id) raise exception.NotFound(_('Project "%s" not found') % project_id)
return user, project return user, project

View File

@@ -15,7 +15,7 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # 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 This class does very little error checking, and knows nothing about ldap
class definitions. It implements the minimum emulation of the python 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 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): def __init__(self):
if hasattr(self.__class__, '_instance'): if hasattr(self.__class__, '_instance'):
raise Exception('Attempted to instantiate singleton') raise Exception(_('Attempted to instantiate singleton'))
@classmethod @classmethod
def instance(cls): def instance(cls):
if not hasattr(cls, '_instance'): if not hasattr(cls, '_instance'):
inst = redis.Redis(host=FLAGS.redis_host, cls._instance = _StorageDict()
port=FLAGS.redis_port,
db=FLAGS.redis_db)
cls._instance = inst
return cls._instance 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_BASE = 0
SCOPE_ONELEVEL = 1 # Not implemented SCOPE_ONELEVEL = 1 # Not implemented
SCOPE_SUBTREE = 2 SCOPE_SUBTREE = 2
@@ -119,6 +150,9 @@ def _match(key, value, attrs):
"""Match a given key and value against an attribute list.""" """Match a given key and value against an attribute list."""
if key not in attrs: if key not in attrs:
return False return False
# This is a wild card search. Implemented as all or nothing for now.
if value == "*":
return True
if key != "objectclass": if key != "objectclass":
return value in attrs[key] return value in attrs[key]
# it is an objectclass check, so check subclasses # it is an objectclass check, so check subclasses
@@ -169,8 +203,6 @@ def _to_json(unencoded):
class FakeLDAP(object): class FakeLDAP(object):
#TODO(vish): refactor this class to use a wrapper instead of accessing
# redis directly
"""Fake LDAP connection.""" """Fake LDAP connection."""
def simple_bind_s(self, dn, password): def simple_bind_s(self, dn, password):
@@ -183,14 +215,13 @@ class FakeLDAP(object):
def add_s(self, dn, attr): def add_s(self, dn, attr):
"""Add an object with the specified attributes at dn.""" """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]) 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): def delete_s(self, dn):
"""Remove the ldap object at specified 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): def modify_s(self, dn, attrs):
"""Modify the object at dn using the attribute list. """Modify the object at dn using the attribute list.
@@ -201,18 +232,18 @@ class FakeLDAP(object):
([MOD_ADD | MOD_DELETE | MOD_REPACE], attribute, value) ([MOD_ADD | MOD_DELETE | MOD_REPACE], attribute, value)
""" """
redis = Redis.instance() store = Store.instance()
key = "%s%s" % (self.__redis_prefix, dn) key = "%s%s" % (self.__prefix, dn)
for cmd, k, v in attrs: for cmd, k, v in attrs:
values = _from_json(redis.hget(key, k)) values = _from_json(store.hget(key, k))
if cmd == MOD_ADD: if cmd == MOD_ADD:
values.append(v) values.append(v)
elif cmd == MOD_REPLACE: elif cmd == MOD_REPLACE:
values = [v] values = [v]
else: else:
values.remove(v) 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): def search_s(self, dn, scope, query=None, fields=None):
"""Search for all matching objects under dn using the query. """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: if scope != SCOPE_BASE and scope != SCOPE_SUBTREE:
raise NotImplementedError(str(scope)) raise NotImplementedError(str(scope))
redis = Redis.instance() store = Store.instance()
if scope == SCOPE_BASE: if scope == SCOPE_BASE:
keys = ["%s%s" % (self.__redis_prefix, dn)] keys = ["%s%s" % (self.__prefix, dn)]
else: else:
keys = redis.keys("%s*%s" % (self.__redis_prefix, dn)) keys = store.keys("%s*%s" % (self.__prefix, dn))
objects = [] objects = []
for key in keys: for key in keys:
# get the attributes from redis # get the attributes from the store
attrs = redis.hgetall(key) attrs = store.hgetall(key)
# turn the values from redis into lists # turn the values from the store into lists
# pylint: disable-msg=E1103 # pylint: disable-msg=E1103
attrs = dict([(k, _from_json(v)) attrs = dict([(k, _from_json(v))
for k, v in attrs.iteritems()]) for k, v in attrs.iteritems()])
@@ -244,13 +276,13 @@ class FakeLDAP(object):
# filter the attributes by fields # filter the attributes by fields
attrs = dict([(k, v) for k, v in attrs.iteritems() attrs = dict([(k, v) for k, v in attrs.iteritems()
if not fields or k in fields]) 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 # pylint: enable-msg=E1103
if objects == []: if objects == []:
raise NO_SUCH_OBJECT() raise NO_SUCH_OBJECT()
return objects return objects
@property @property
def __redis_prefix(self): # pylint: disable-msg=R0201 def __prefix(self): # pylint: disable-msg=R0201
"""Get the prefix to use for all redis keys.""" """Get the prefix to use for all keys."""
return 'ldap:' return 'ldap:'

View File

@@ -32,11 +32,16 @@ from nova import flags
FLAGS = flags.FLAGS FLAGS = flags.FLAGS
flags.DEFINE_integer('ldap_schema_version', 2,
'Current version of the LDAP schema')
flags.DEFINE_string('ldap_url', 'ldap://localhost', flags.DEFINE_string('ldap_url', 'ldap://localhost',
'Point this at your ldap server') 'Point this at your ldap server')
flags.DEFINE_string('ldap_password', 'changeme', 'LDAP password') flags.DEFINE_string('ldap_password', 'changeme', 'LDAP password')
flags.DEFINE_string('ldap_user_dn', 'cn=Manager,dc=example,dc=com', flags.DEFINE_string('ldap_user_dn', 'cn=Manager,dc=example,dc=com',
'DN of admin user') '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_unit', 'Users', 'OID for Users')
flags.DEFINE_string('ldap_user_subtree', 'ou=Users,dc=example,dc=com', flags.DEFINE_string('ldap_user_subtree', 'ou=Users,dc=example,dc=com',
'OU for Users') 'OU for Users')
@@ -73,10 +78,20 @@ class LdapDriver(object):
Defines enter and exit and therefore supports the with/as syntax. 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): def __init__(self):
"""Imports the LDAP module""" """Imports the LDAP module"""
self.ldap = __import__('ldap') self.ldap = __import__('ldap')
self.conn = None 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): def __enter__(self):
"""Creates the connection to LDAP""" """Creates the connection to LDAP"""
@@ -104,13 +119,13 @@ class LdapDriver(object):
"""Retrieve project by id""" """Retrieve project by id"""
dn = 'cn=%s,%s' % (pid, dn = 'cn=%s,%s' % (pid,
FLAGS.ldap_project_subtree) FLAGS.ldap_project_subtree)
attr = self.__find_object(dn, '(objectclass=novaProject)') attr = self.__find_object(dn, LdapDriver.project_pattern)
return self.__to_project(attr) return self.__to_project(attr)
def get_users(self): def get_users(self):
"""Retrieve list of users""" """Retrieve list of users"""
attrs = self.__find_objects(FLAGS.ldap_user_subtree, attrs = self.__find_objects(FLAGS.ldap_user_subtree,
'(objectclass=novaUser)') '(objectclass=novaUser)')
users = [] users = []
for attr in attrs: for attr in attrs:
user = self.__to_user(attr) user = self.__to_user(attr)
@@ -120,7 +135,7 @@ class LdapDriver(object):
def get_projects(self, uid=None): def get_projects(self, uid=None):
"""Retrieve list of projects""" """Retrieve list of projects"""
pattern = '(objectclass=novaProject)' pattern = LdapDriver.project_pattern
if uid: if uid:
pattern = "(&%s(member=%s))" % (pattern, self.__uid_to_dn(uid)) pattern = "(&%s(member=%s))" % (pattern, self.__uid_to_dn(uid))
attrs = self.__find_objects(FLAGS.ldap_project_subtree, attrs = self.__find_objects(FLAGS.ldap_project_subtree,
@@ -139,27 +154,29 @@ class LdapDriver(object):
# Malformed entries are useless, replace attributes found. # Malformed entries are useless, replace attributes found.
attr = [] attr = []
if 'secretKey' in user.keys(): if 'secretKey' in user.keys():
attr.append((self.ldap.MOD_REPLACE, 'secretKey', \ attr.append((self.ldap.MOD_REPLACE, 'secretKey',
[secret_key])) [secret_key]))
else: else:
attr.append((self.ldap.MOD_ADD, 'secretKey', \ attr.append((self.ldap.MOD_ADD, 'secretKey',
[secret_key])) [secret_key]))
if 'accessKey' in user.keys(): if 'accessKey' in user.keys():
attr.append((self.ldap.MOD_REPLACE, 'accessKey', \ attr.append((self.ldap.MOD_REPLACE, 'accessKey',
[access_key])) [access_key]))
else: else:
attr.append((self.ldap.MOD_ADD, 'accessKey', \ attr.append((self.ldap.MOD_ADD, 'accessKey',
[access_key])) [access_key]))
if 'isAdmin' in user.keys(): if LdapDriver.isadmin_attribute in user.keys():
attr.append((self.ldap.MOD_REPLACE, 'isAdmin', \ attr.append((self.ldap.MOD_REPLACE,
[str(is_admin).upper()])) LdapDriver.isadmin_attribute,
[str(is_admin).upper()]))
else: else:
attr.append((self.ldap.MOD_ADD, 'isAdmin', \ attr.append((self.ldap.MOD_ADD,
[str(is_admin).upper()])) LdapDriver.isadmin_attribute,
[str(is_admin).upper()]))
self.conn.modify_s(self.__uid_to_dn(name), attr) self.conn.modify_s(self.__uid_to_dn(name), attr)
return self.get_user(name) return self.get_user(name)
else: else:
raise exception.NotFound("LDAP object for %s doesn't exist" raise exception.NotFound(_("LDAP object for %s doesn't exist")
% name) % name)
else: else:
attr = [ attr = [
@@ -168,12 +185,12 @@ class LdapDriver(object):
'inetOrgPerson', 'inetOrgPerson',
'novaUser']), 'novaUser']),
('ou', [FLAGS.ldap_user_unit]), ('ou', [FLAGS.ldap_user_unit]),
('uid', [name]), (FLAGS.ldap_user_id_attribute, [name]),
('sn', [name]), ('sn', [name]),
('cn', [name]), (FLAGS.ldap_user_name_attribute, [name]),
('secretKey', [secret_key]), ('secretKey', [secret_key]),
('accessKey', [access_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) self.conn.add_s(self.__uid_to_dn(name), attr)
return self.__to_user(dict(attr)) return self.__to_user(dict(attr))
@@ -182,11 +199,12 @@ class LdapDriver(object):
description=None, member_uids=None): description=None, member_uids=None):
"""Create a project""" """Create a project"""
if self.__project_exists(name): if self.__project_exists(name):
raise exception.Duplicate("Project can't be created because " raise exception.Duplicate(_("Project can't be created because "
"project %s already exists" % name) "project %s already exists") % name)
if not self.__user_exists(manager_uid): if not self.__user_exists(manager_uid):
raise exception.NotFound("Project can't be created because " raise exception.NotFound(_("Project can't be created because "
"manager %s doesn't exist" % manager_uid) "manager %s doesn't exist")
% manager_uid)
manager_dn = self.__uid_to_dn(manager_uid) manager_dn = self.__uid_to_dn(manager_uid)
# description is a required attribute # description is a required attribute
if description is None: if description is None:
@@ -195,18 +213,18 @@ class LdapDriver(object):
if member_uids is not None: if member_uids is not None:
for member_uid in member_uids: for member_uid in member_uids:
if not self.__user_exists(member_uid): if not self.__user_exists(member_uid):
raise exception.NotFound("Project can't be created " raise exception.NotFound(_("Project can't be created "
"because user %s doesn't exist" "because user %s doesn't exist")
% member_uid) % member_uid)
members.append(self.__uid_to_dn(member_uid)) members.append(self.__uid_to_dn(member_uid))
# always add the manager as a member because members is required # always add the manager as a member because members is required
if not manager_dn in members: if not manager_dn in members:
members.append(manager_dn) members.append(manager_dn)
attr = [ attr = [
('objectclass', ['novaProject']), ('objectclass', [LdapDriver.project_objectclass]),
('cn', [name]), ('cn', [name]),
('description', [description]), ('description', [description]),
('projectManager', [manager_dn]), (LdapDriver.project_attribute, [manager_dn]),
('member', members)] ('member', members)]
self.conn.add_s('cn=%s,%s' % (name, FLAGS.ldap_project_subtree), attr) self.conn.add_s('cn=%s,%s' % (name, FLAGS.ldap_project_subtree), attr)
return self.__to_project(dict(attr)) return self.__to_project(dict(attr))
@@ -218,11 +236,12 @@ class LdapDriver(object):
attr = [] attr = []
if manager_uid: if manager_uid:
if not self.__user_exists(manager_uid): if not self.__user_exists(manager_uid):
raise exception.NotFound("Project can't be modified because " raise exception.NotFound(_("Project can't be modified because "
"manager %s doesn't exist" % "manager %s doesn't exist")
manager_uid) % manager_uid)
manager_dn = self.__uid_to_dn(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: if description:
attr.append((self.ldap.MOD_REPLACE, 'description', description)) attr.append((self.ldap.MOD_REPLACE, 'description', description))
self.conn.modify_s('cn=%s,%s' % (project_id, self.conn.modify_s('cn=%s,%s' % (project_id,
@@ -282,10 +301,9 @@ class LdapDriver(object):
return roles return roles
else: else:
project_dn = 'cn=%s,%s' % (project_id, FLAGS.ldap_project_subtree) project_dn = 'cn=%s,%s' % (project_id, FLAGS.ldap_project_subtree)
roles = self.__find_objects(project_dn, query = ('(&(&(objectclass=groupOfNames)(!%s))(member=%s))' %
'(&(&(objectclass=groupOfNames)' (LdapDriver.project_pattern, self.__uid_to_dn(uid)))
'(!(objectclass=novaProject)))' roles = self.__find_objects(project_dn, query)
'(member=%s))' % self.__uid_to_dn(uid))
return [role['cn'][0] for role in roles] return [role['cn'][0] for role in roles]
def delete_user(self, uid): def delete_user(self, uid):
@@ -299,14 +317,15 @@ class LdapDriver(object):
# Retrieve user by name # Retrieve user by name
user = self.__get_ldap_user(uid) user = self.__get_ldap_user(uid)
if 'secretKey' in user.keys(): if 'secretKey' in user.keys():
attr.append((self.ldap.MOD_DELETE, 'secretKey', \ attr.append((self.ldap.MOD_DELETE, 'secretKey',
user['secretKey'])) user['secretKey']))
if 'accessKey' in user.keys(): if 'accessKey' in user.keys():
attr.append((self.ldap.MOD_DELETE, 'accessKey', \ attr.append((self.ldap.MOD_DELETE, 'accessKey',
user['accessKey'])) user['accessKey']))
if 'isAdmin' in user.keys(): if LdapDriver.isadmin_attribute in user.keys():
attr.append((self.ldap.MOD_DELETE, 'isAdmin', \ attr.append((self.ldap.MOD_DELETE,
user['isAdmin'])) LdapDriver.isadmin_attribute,
user[LdapDriver.isadmin_attribute]))
self.conn.modify_s(self.__uid_to_dn(uid), attr) self.conn.modify_s(self.__uid_to_dn(uid), attr)
else: else:
# Delete entry # Delete entry
@@ -328,7 +347,8 @@ class LdapDriver(object):
if secret_key: if secret_key:
attr.append((self.ldap.MOD_REPLACE, 'secretKey', secret_key)) attr.append((self.ldap.MOD_REPLACE, 'secretKey', secret_key))
if admin is not None: 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) self.conn.modify_s(self.__uid_to_dn(uid), attr)
def __user_exists(self, uid): def __user_exists(self, uid):
@@ -346,7 +366,7 @@ class LdapDriver(object):
def __get_ldap_user(self, uid): def __get_ldap_user(self, uid):
"""Retrieve LDAP user entry by id""" """Retrieve LDAP user entry by id"""
attr = self.__find_object(self.__uid_to_dn(uid), attr = self.__find_object(self.__uid_to_dn(uid),
'(objectclass=novaUser)') '(objectclass=novaUser)')
return attr return attr
def __find_object(self, dn, query=None, scope=None): def __find_object(self, dn, query=None, scope=None):
@@ -382,19 +402,21 @@ class LdapDriver(object):
def __find_role_dns(self, tree): def __find_role_dns(self, tree):
"""Find dns of role objects in given tree""" """Find dns of role objects in given tree"""
return self.__find_dns(tree, query = ('(&(objectclass=groupOfNames)(!%s))' %
'(&(objectclass=groupOfNames)(!(objectclass=novaProject)))') LdapDriver.project_pattern)
return self.__find_dns(tree, query)
def __find_group_dns_with_member(self, tree, uid): def __find_group_dns_with_member(self, tree, uid):
"""Find dns of group objects in a given tree that contain member""" """Find dns of group objects in a given tree that contain member"""
dns = self.__find_dns(tree, query = ('(&(objectclass=groupOfNames)(member=%s))' %
'(&(objectclass=groupOfNames)(member=%s))' % self.__uid_to_dn(uid))
self.__uid_to_dn(uid)) dns = self.__find_dns(tree, query)
return dns return dns
def __group_exists(self, dn): def __group_exists(self, dn):
"""Check if group exists""" """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 @staticmethod
def __role_to_dn(role, project_id=None): def __role_to_dn(role, project_id=None):
@@ -417,7 +439,8 @@ class LdapDriver(object):
for member_uid in member_uids: for member_uid in member_uids:
if not self.__user_exists(member_uid): if not self.__user_exists(member_uid):
raise exception.NotFound("Group can't be created " 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)) members.append(self.__uid_to_dn(member_uid))
dn = self.__uid_to_dn(uid) dn = self.__uid_to_dn(uid)
if not dn in members: if not dn in members:
@@ -433,7 +456,7 @@ class LdapDriver(object):
"""Check if user is in group""" """Check if user is in group"""
if not self.__user_exists(uid): if not self.__user_exists(uid):
raise exception.NotFound("User %s can't be searched in group " 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): if not self.__group_exists(group_dn):
return False return False
res = self.__find_object(group_dn, res = self.__find_object(group_dn,
@@ -445,13 +468,13 @@ class LdapDriver(object):
"""Add user to group""" """Add user to group"""
if not self.__user_exists(uid): if not self.__user_exists(uid):
raise exception.NotFound("User %s can't be added to the group " 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): if not self.__group_exists(group_dn):
raise exception.NotFound("The group at dn %s doesn't exist" % raise exception.NotFound("The group at dn %s doesn't exist" %
(group_dn,)) group_dn)
if self.__is_in_group(uid, group_dn): if self.__is_in_group(uid, group_dn):
raise exception.Duplicate("User %s is already a member of " raise exception.Duplicate(_("User %s is already a member of "
"the group %s" % (uid, group_dn)) "the group %s") % (uid, group_dn))
attr = [(self.ldap.MOD_ADD, 'member', self.__uid_to_dn(uid))] attr = [(self.ldap.MOD_ADD, 'member', self.__uid_to_dn(uid))]
self.conn.modify_s(group_dn, attr) self.conn.modify_s(group_dn, attr)
@@ -459,16 +482,16 @@ class LdapDriver(object):
"""Remove user from group""" """Remove user from group"""
if not self.__group_exists(group_dn): if not self.__group_exists(group_dn):
raise exception.NotFound("The group at dn %s doesn't exist" % raise exception.NotFound("The group at dn %s doesn't exist" %
(group_dn,)) group_dn)
if not self.__user_exists(uid): if not self.__user_exists(uid):
raise exception.NotFound("User %s can't be removed from the " 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): if not self.__is_in_group(uid, group_dn):
raise exception.NotFound("User %s is not a member of the group" % raise exception.NotFound("User %s is not a member of the group" %
(uid,)) uid)
# NOTE(vish): remove user from group and any sub_groups # NOTE(vish): remove user from group and any sub_groups
sub_dns = self.__find_group_dns_with_member( sub_dns = self.__find_group_dns_with_member(group_dn, uid)
group_dn, uid)
for sub_dn in sub_dns: for sub_dn in sub_dns:
self.__safe_remove_from_group(uid, sub_dn) self.__safe_remove_from_group(uid, sub_dn)
@@ -479,15 +502,15 @@ class LdapDriver(object):
try: try:
self.conn.modify_s(group_dn, attr) self.conn.modify_s(group_dn, attr)
except self.ldap.OBJECT_CLASS_VIOLATION: except self.ldap.OBJECT_CLASS_VIOLATION:
logging.debug("Attempted to remove the last member of a group. " logging.debug(_("Attempted to remove the last member of a group. "
"Deleting the group at %s instead.", group_dn) "Deleting the group at %s instead."), group_dn)
self.__delete_group(group_dn) self.__delete_group(group_dn)
def __remove_from_all(self, uid): def __remove_from_all(self, uid):
"""Remove user from all roles and projects""" """Remove user from all roles and projects"""
if not self.__user_exists(uid): if not self.__user_exists(uid):
raise exception.NotFound("User %s can't be removed from all " 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( role_dns = self.__find_group_dns_with_member(
FLAGS.role_project_subtree, uid) FLAGS.role_project_subtree, uid)
for role_dn in role_dns: for role_dn in role_dns:
@@ -500,7 +523,8 @@ class LdapDriver(object):
def __delete_group(self, group_dn): def __delete_group(self, group_dn):
"""Delete Group""" """Delete Group"""
if not self.__group_exists(group_dn): 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) self.conn.delete_s(group_dn)
def __delete_roles(self, project_dn): def __delete_roles(self, project_dn):
@@ -514,13 +538,13 @@ class LdapDriver(object):
if attr is None: if attr is None:
return None return None
if ('accessKey' in attr.keys() and 'secretKey' in attr.keys() \ if ('accessKey' in attr.keys() and 'secretKey' in attr.keys() \
and 'isAdmin' in attr.keys()): and LdapDriver.isadmin_attribute in attr.keys()):
return { return {
'id': attr['uid'][0], 'id': attr[FLAGS.ldap_user_id_attribute][0],
'name': attr['cn'][0], 'name': attr[FLAGS.ldap_user_name_attribute][0],
'access': attr['accessKey'][0], 'access': attr['accessKey'][0],
'secret': attr['secretKey'][0], 'secret': attr['secretKey'][0],
'admin': (attr['isAdmin'][0] == 'TRUE')} 'admin': (attr[LdapDriver.isadmin_attribute][0] == 'TRUE')}
else: else:
return None return None
@@ -532,7 +556,8 @@ class LdapDriver(object):
return { return {
'id': attr['cn'][0], 'id': attr['cn'][0],
'name': 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], 'description': attr.get('description', [None])[0],
'member_ids': [self.__dn_to_uid(x) for x in member_dns]} '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] return dn.split(',')[0].split('=')[1]
@staticmethod @staticmethod
def __uid_to_dn(dn): def __uid_to_dn(uid):
"""Convert uid to dn""" """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): class FakeLdapDriver(LdapDriver):

View File

@@ -64,12 +64,9 @@ flags.DEFINE_string('credential_key_file', 'pk.pem',
'Filename of private key in credentials zip') 'Filename of private key in credentials zip')
flags.DEFINE_string('credential_cert_file', 'cert.pem', flags.DEFINE_string('credential_cert_file', 'cert.pem',
'Filename of certificate in credentials zip') 'Filename of certificate in credentials zip')
flags.DEFINE_string('credential_rc_file', 'novarc', flags.DEFINE_string('credential_rc_file', '%src',
'Filename of rc in credentials zip') 'Filename of rc in credentials zip, %s will be '
flags.DEFINE_string('credential_cert_subject', 'replaced by name of the region (nova by default)')
'/C=US/ST=California/L=MountainView/O=AnsoLabs/'
'OU=NovaDev/CN=%s-%s',
'Subject for certificate for users')
flags.DEFINE_string('auth_driver', 'nova.auth.dbdriver.DbDriver', flags.DEFINE_string('auth_driver', 'nova.auth.dbdriver.DbDriver',
'Driver that auth manager uses') 'Driver that auth manager uses')
@@ -257,12 +254,12 @@ class AuthManager(object):
# TODO(vish): check for valid timestamp # TODO(vish): check for valid timestamp
(access_key, _sep, project_id) = access.partition(':') (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) user = self.get_user_from_access_key(access_key)
logging.info('user: %r', user) logging.info('user: %r', user)
if user == None: if user == None:
raise exception.NotFound('No user found for access key %s' % raise exception.NotFound(_('No user found for access key %s')
access_key) % access_key)
# NOTE(vish): if we stop using project name as id we need better # NOTE(vish): if we stop using project name as id we need better
# logic to find a default project for user # logic to find a default project for user
@@ -271,12 +268,12 @@ class AuthManager(object):
project = self.get_project(project_id) project = self.get_project(project_id)
if project == None: if project == None:
raise exception.NotFound('No project called %s could be found' % raise exception.NotFound(_('No project called %s could be found')
project_id) % project_id)
if not self.is_admin(user) and not self.is_project_member(user, if not self.is_admin(user) and not self.is_project_member(user,
project): project):
raise exception.NotFound('User %s is not a member of project %s' % raise exception.NotFound(_('User %s is not a member of project %s')
(user.id, project.id)) % (user.id, project.id))
if check_type == 's3': if check_type == 's3':
sign = signer.Signer(user.secret.encode()) sign = signer.Signer(user.secret.encode())
expected_signature = sign.s3_authorization(headers, verb, path) expected_signature = sign.s3_authorization(headers, verb, path)
@@ -284,7 +281,7 @@ class AuthManager(object):
logging.debug('expected_signature: %s', expected_signature) logging.debug('expected_signature: %s', expected_signature)
logging.debug('signature: %s', signature) logging.debug('signature: %s', signature)
if signature != expected_signature: if signature != expected_signature:
raise exception.NotAuthorized('Signature does not match') raise exception.NotAuthorized(_('Signature does not match'))
elif check_type == 'ec2': elif check_type == 'ec2':
# NOTE(vish): hmac can't handle unicode, so encode ensures that # NOTE(vish): hmac can't handle unicode, so encode ensures that
# secret isn't unicode # secret isn't unicode
@@ -294,7 +291,7 @@ class AuthManager(object):
logging.debug('expected_signature: %s', expected_signature) logging.debug('expected_signature: %s', expected_signature)
logging.debug('signature: %s', signature) logging.debug('signature: %s', signature)
if signature != expected_signature: if signature != expected_signature:
raise exception.NotAuthorized('Signature does not match') raise exception.NotAuthorized(_('Signature does not match'))
return (user, project) return (user, project)
def get_access_key(self, user, project): def get_access_key(self, user, project):
@@ -364,7 +361,7 @@ class AuthManager(object):
with self.driver() as drv: with self.driver() as drv:
if role == 'projectmanager': if role == 'projectmanager':
if not project: if not project:
raise exception.Error("Must specify project") raise exception.Error(_("Must specify project"))
return self.is_project_manager(user, project) return self.is_project_manager(user, project)
global_role = drv.has_role(User.safe_id(user), 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. @param project: Project in which to add local role.
""" """
if role not in FLAGS.allowed_roles: 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: 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: with self.driver() as drv:
drv.add_role(User.safe_id(user), role, Project.safe_id(project)) 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(), 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']: if not network_ref:
raise exception.NotFound('project network data has not been set') return (None, None)
return (network_ref['vpn_public_address'], return (network_ref['vpn_public_address'],
network_ref['vpn_public_port']) network_ref['vpn_public_port'])
@@ -628,27 +625,37 @@ class AuthManager(object):
def get_key_pairs(context): def get_key_pairs(context):
return db.key_pair_get_all_by_user(context.elevated(), context.user_id) 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""" """Get credential zip for user in project"""
if not isinstance(user, User): if not isinstance(user, User):
user = self.get_user(user) user = self.get_user(user)
if project is None: if project is None:
project = user.id project = user.id
pid = Project.safe_id(project) pid = Project.safe_id(project)
rc = self.__generate_rc(user.access, user.secret, pid) private_key, signed_cert = crypto.generate_x509_cert(user.id, pid)
private_key, signed_cert = self._generate_x509_cert(user.id, pid)
tmpdir = tempfile.mkdtemp() tmpdir = tempfile.mkdtemp()
zf = os.path.join(tmpdir, "temp.zip") zf = os.path.join(tmpdir, "temp.zip")
zippy = zipfile.ZipFile(zf, 'w') 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_key_file, private_key)
zippy.writestr(FLAGS.credential_cert_file, signed_cert) zippy.writestr(FLAGS.credential_cert_file, signed_cert)
try: (vpn_ip, vpn_port) = self.get_project_vpn_data(project)
(vpn_ip, vpn_port) = self.get_project_vpn_data(project)
except exception.NotFound:
vpn_ip = None
if vpn_ip: if vpn_ip:
configfile = open(FLAGS.vpn_client_template, "r") configfile = open(FLAGS.vpn_client_template, "r")
s = string.Template(configfile.read()) s = string.Template(configfile.read())
@@ -659,10 +666,9 @@ class AuthManager(object):
port=vpn_port) port=vpn_port)
zippy.writestr(FLAGS.credential_vpn_file, config) zippy.writestr(FLAGS.credential_vpn_file, config)
else: else:
logging.warn("No vpn data for project %s" % logging.warn(_("No vpn data for project %s"), pid)
pid)
zippy.writestr(FLAGS.ca_file, crypto.fetch_ca(user.id)) zippy.writestr(FLAGS.ca_file, crypto.fetch_ca(pid))
zippy.close() zippy.close()
with open(zf, 'rb') as f: with open(zf, 'rb') as f:
read_buffer = f.read() read_buffer = f.read()
@@ -670,38 +676,38 @@ class AuthManager(object):
shutil.rmtree(tmpdir) shutil.rmtree(tmpdir)
return read_buffer 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""" """Get credential zip for user in project"""
if not isinstance(user, User): if not isinstance(user, User):
user = self.get_user(user) user = self.get_user(user)
if project is None: if project is None:
project = user.id project = user.id
pid = Project.safe_id(project) 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 @staticmethod
def __generate_rc(access, secret, pid): def __generate_rc(access, secret, pid, use_dmz=True, host=None):
"""Generate rc file for user""" """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 = open(FLAGS.credentials_template).read()
rc = rc % {'access': access, rc = rc % {'access': access,
'project': pid, 'project': pid,
'secret': secret, 'secret': secret,
'ec2': FLAGS.ec2_url, 'ec2': '%s://%s:%s%s' % (FLAGS.ec2_prefix,
's3': 'http://%s:%s' % (FLAGS.s3_host, FLAGS.s3_port), cc_host,
FLAGS.cc_port,
FLAGS.ec2_suffix),
's3': 'http://%s:%s' % (s3_host, FLAGS.s3_port),
'nova': FLAGS.ca_file, 'nova': FLAGS.ca_file,
'cert': FLAGS.credential_cert_file, 'cert': FLAGS.credential_cert_file,
'key': FLAGS.credential_key_file} 'key': FLAGS.credential_key_file}
return rc 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())

View File

@@ -1,7 +1,9 @@
# #
# Person object for Nova # Person object for Nova
# inetorgperson with extra attributes # 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 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 ( attributetype (
novaAttrs:4 novaAttrs:4
NAME 'isAdmin' NAME 'isNovaAdmin'
DESC 'Is user an administrator?' DESC 'Is user an nova administrator?'
EQUALITY booleanMatch EQUALITY booleanMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
SINGLE-VALUE 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 ( objectClass (
novaOCs:1 novaOCs:1
NAME 'novaUser' NAME 'novaUser'
DESC 'access and secret keys' DESC 'access and secret keys'
AUXILIARY AUXILIARY
MUST ( uid ) MAY ( accessKey $ secretKey $ isNovaAdmin )
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 )
) )

View File

@@ -1,16 +1,13 @@
# #
# Person object for Nova # Person object for Nova
# inetorgperson with extra attributes # inetorgperson with extra attributes
# Author: Vishvananda Ishaya <vishvananda@yahoo.com> # Schema version: 2
# Modified for strict RFC 4512 compatibility by: Ryan Lane <ryan@ryandlane.com> # Authors: Vishvananda Ishaya <vishvananda@gmail.com>
# Ryan Lane <rlane@wikimedia.org>
# #
# using internet experimental oid arc as per BP64 3.1 # using internet experimental oid arc as per BP64 3.1
dn: cn=schema 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.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.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 'isNovaAdmin' DESC 'Is user a nova 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.4 NAME 'isAdmin' DESC 'Is user an 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 ) )
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 ) )

View File

@@ -32,7 +32,6 @@ abspath=`dirname "$(cd "${0%/*}" 2>/dev/null; echo "$PWD"/"${0##*/}")"`
schemapath='/var/opendj/instance/config/schema' schemapath='/var/opendj/instance/config/schema'
cp $abspath/openssh-lpk_sun.schema $schemapath/97-openssh-lpk_sun.ldif cp $abspath/openssh-lpk_sun.schema $schemapath/97-openssh-lpk_sun.ldif
cp $abspath/nova_sun.schema $schemapath/98-nova_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 chown opendj:opendj $schemapath/98-nova_sun.ldif
cat >/etc/ldap/ldap.conf <<LDAP_CONF_EOF cat >/etc/ldap/ldap.conf <<LDAP_CONF_EOF

View File

@@ -22,7 +22,7 @@ apt-get install -y slapd ldap-utils python-ldap
abspath=`dirname "$(cd "${0%/*}" 2>/dev/null; echo "$PWD"/"${0##*/}")"` 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/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 mv /etc/ldap/slapd.conf /etc/ldap/slapd.conf.orig
cat >/etc/ldap/slapd.conf <<SLAPD_CONF_EOF 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/core.schema
include /etc/ldap/schema/cosine.schema include /etc/ldap/schema/cosine.schema
include /etc/ldap/schema/inetorgperson.schema include /etc/ldap/schema/inetorgperson.schema
include /etc/ldap/schema/openssh-lpk_openldap.schema
include /etc/ldap/schema/nova.schema include /etc/ldap/schema/nova.schema
pidfile /var/run/slapd/slapd.pid pidfile /var/run/slapd/slapd.pid
argsfile /var/run/slapd/slapd.args argsfile /var/run/slapd/slapd.args

59
nova/fakememcache.py Normal file
View 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

View File

@@ -25,6 +25,10 @@ from carrot.backends import base
from eventlet import greenthread from eventlet import greenthread
EXCHANGES = {}
QUEUES = {}
class Message(base.BaseMessage): class Message(base.BaseMessage):
pass pass
@@ -37,12 +41,12 @@ class Exchange(object):
self._routes = {} self._routes = {}
def publish(self, message, routing_key=None): 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) self.name, routing_key, message)
routing_key = routing_key.split('.')[0] routing_key = routing_key.split('.')[0]
if routing_key in self._routes: if routing_key in self._routes:
for f in self._routes[routing_key]: 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) f(message, routing_key=routing_key)
def bind(self, callback, routing_key): def bind(self, callback, routing_key):
@@ -68,81 +72,63 @@ class Queue(object):
return self._queue.get() return self._queue.get()
class Backend(object): class Backend(base.BaseBackend):
""" Singleton backend for testing """ def queue_declare(self, queue, **kwargs):
class __impl(base.BaseBackend): global QUEUES
def __init__(self, *args, **kwargs): if queue not in QUEUES:
#super(__impl, self).__init__(*args, **kwargs) logging.debug(_('Declaring queue %s'), queue)
self._exchanges = {} QUEUES[queue] = Queue(queue)
self._queues = {}
def _reset_all(self): def exchange_declare(self, exchange, type, *args, **kwargs):
self._exchanges = {} global EXCHANGES
self._queues = {} if exchange not in EXCHANGES:
logging.debug(_('Declaring exchange %s'), exchange)
EXCHANGES[exchange] = Exchange(exchange, type)
def queue_declare(self, queue, **kwargs): def queue_bind(self, queue, exchange, routing_key, **kwargs):
if queue not in self._queues: global EXCHANGES
logging.debug('Declaring queue %s', queue) global QUEUES
self._queues[queue] = Queue(queue) 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): def declare_consumer(self, queue, callback, *args, **kwargs):
if exchange not in self._exchanges: self.current_queue = queue
logging.debug('Declaring exchange %s', exchange) self.current_callback = callback
self._exchanges[exchange] = Exchange(exchange, type)
def queue_bind(self, queue, exchange, routing_key, **kwargs): def consume(self, limit=None):
logging.debug('Binding %s to %s with key %s', while True:
queue, exchange, routing_key) item = self.get(self.current_queue)
self._exchanges[exchange].bind(self._queues[queue].push, if item:
routing_key) self.current_callback(item)
raise StopIteration()
greenthread.sleep(0)
def declare_consumer(self, queue, callback, *args, **kwargs): def get(self, queue, no_ack=False):
self.current_queue = queue global QUEUES
self.current_callback = callback 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): def prepare_message(self, message_data, delivery_mode,
while True: content_type, content_encoding, **kwargs):
item = self.get(self.current_queue) """Prepare message for sending."""
if item: return (message_data, content_type, content_encoding)
self.current_callback(item)
raise StopIteration()
greenthread.sleep(0)
def get(self, queue, no_ack=False): def publish(self, message, exchange, routing_key, **kwargs):
if not queue in self._queues or not self._queues[queue].size(): global EXCHANGES
return None if exchange in EXCHANGES:
(message_data, content_type, content_encoding) = \ EXCHANGES[exchange].publish(message, routing_key=routing_key)
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 reset_all(): def reset_all():
Backend()._reset_all() global EXCHANGES
global QUEUES
EXCHANGES = {}
QUEUES = {}

View File

@@ -29,6 +29,8 @@ import sys
import gflags import gflags
from nova import utils
class FlagValues(gflags.FlagValues): class FlagValues(gflags.FlagValues):
"""Extension of gflags.FlagValues that allows undefined and runtime flags. """Extension of gflags.FlagValues that allows undefined and runtime flags.
@@ -159,6 +161,7 @@ class StrWrapper(object):
return str(val) return str(val)
raise KeyError(name) raise KeyError(name)
FLAGS = FlagValues() FLAGS = FlagValues()
gflags.FLAGS = FLAGS gflags.FLAGS = FLAGS
gflags.DEFINE_flag(gflags.HelpFlag(), FLAGS) gflags.DEFINE_flag(gflags.HelpFlag(), FLAGS)
@@ -183,6 +186,12 @@ DEFINE_list = _wrapper(gflags.DEFINE_list)
DEFINE_spaceseplist = _wrapper(gflags.DEFINE_spaceseplist) DEFINE_spaceseplist = _wrapper(gflags.DEFINE_spaceseplist)
DEFINE_multistring = _wrapper(gflags.DEFINE_multistring) DEFINE_multistring = _wrapper(gflags.DEFINE_multistring)
DEFINE_multi_int = _wrapper(gflags.DEFINE_multi_int) DEFINE_multi_int = _wrapper(gflags.DEFINE_multi_int)
DEFINE_flag = _wrapper(gflags.DEFINE_flag)
HelpFlag = gflags.HelpFlag
HelpshortFlag = gflags.HelpshortFlag
HelpXMLFlag = gflags.HelpXMLFlag
def DECLARE(name, module_string, flag_values=FLAGS): def DECLARE(name, module_string, flag_values=FLAGS):
@@ -203,8 +212,11 @@ DEFINE_list('region_list',
DEFINE_string('connection_type', 'libvirt', 'libvirt, xenapi or fake') DEFINE_string('connection_type', 'libvirt', 'libvirt, xenapi or fake')
DEFINE_string('aws_access_key_id', 'admin', 'AWS Access ID') DEFINE_string('aws_access_key_id', 'admin', 'AWS Access ID')
DEFINE_string('aws_secret_access_key', 'admin', 'AWS Access Key') DEFINE_string('aws_secret_access_key', 'admin', 'AWS Access Key')
DEFINE_integer('glance_port', 9292, 'glance port')
DEFINE_string('glance_host', utils.get_my_ip(), 'glance host')
DEFINE_integer('s3_port', 3333, 's3 port') 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('compute_topic', 'compute', 'the topic compute nodes listen on')
DEFINE_string('scheduler_topic', 'scheduler', DEFINE_string('scheduler_topic', 'scheduler',
'the topic scheduler nodes listen on') 'the topic scheduler nodes listen on')
@@ -223,22 +235,25 @@ DEFINE_string('rabbit_virtual_host', '/', 'rabbit virtual host')
DEFINE_integer('rabbit_retry_interval', 10, 'rabbit connection retry interval') DEFINE_integer('rabbit_retry_interval', 10, 'rabbit connection retry interval')
DEFINE_integer('rabbit_max_retries', 12, 'rabbit connection attempts') DEFINE_integer('rabbit_max_retries', 12, 'rabbit connection attempts')
DEFINE_string('control_exchange', 'nova', 'the main exchange to connect to') DEFINE_string('control_exchange', 'nova', 'the main exchange to connect to')
DEFINE_string('ec2_url', 'http://127.0.0.1:8773/services/Cloud', DEFINE_string('ec2_prefix', 'http', 'prefix for ec2')
'Url to ec2 api server') 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_project', 'openstack', 'default project for openstack')
DEFINE_string('default_image', 'ami-11111', DEFINE_string('default_image', 'ami-11111',
'default image to use, testing only') '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', DEFINE_string('default_instance_type', 'm1.small',
'default instance type to use, testing only') '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', DEFINE_string('vpn_key_suffix',
'-key', '-vpn',
'Suffix to add to project name for vpn key') 'Suffix to add to project name for vpn key and secgroups')
DEFINE_integer('auth_token_ttl', 3600, 'Seconds for auth tokens to linger') DEFINE_integer('auth_token_ttl', 3600, 'Seconds for auth tokens to linger')

View File

@@ -1,209 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# Copyright 2010 FathomDB Inc.
# 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.
"""
Process pool using twisted threading
"""
import logging
import StringIO
from twisted.internet import defer
from twisted.internet import error
from twisted.internet import protocol
from twisted.internet import reactor
from nova import flags
from nova.exception import ProcessExecutionError
FLAGS = flags.FLAGS
flags.DEFINE_integer('process_pool_size', 4,
'Number of processes to use in the process pool')
# This is based on _BackRelay from twister.internal.utils, but modified to
# capture both stdout and stderr, without odd stderr handling, and also to
# handle stdin
class BackRelayWithInput(protocol.ProcessProtocol):
"""
Trivial protocol for communicating with a process and turning its output
into the result of a L{Deferred}.
@ivar deferred: A L{Deferred} which will be called back with all of stdout
and all of stderr as well (as a tuple). C{terminate_on_stderr} is true
and any bytes are received over stderr, this will fire with an
L{_ProcessExecutionError} instance and the attribute will be set to
C{None}.
@ivar onProcessEnded: If C{terminate_on_stderr} is false and bytes are
received over stderr, this attribute will refer to a L{Deferred} which
will be called back when the process ends. This C{Deferred} is also
associated with the L{_ProcessExecutionError} which C{deferred} fires
with earlier in this case so that users can determine when the process
has actually ended, in addition to knowing when bytes have been
received via stderr.
"""
def __init__(self, deferred, cmd, started_deferred=None,
terminate_on_stderr=False, check_exit_code=True,
process_input=None):
self.deferred = deferred
self.cmd = cmd
self.stdout = StringIO.StringIO()
self.stderr = StringIO.StringIO()
self.started_deferred = started_deferred
self.terminate_on_stderr = terminate_on_stderr
self.check_exit_code = check_exit_code
self.process_input = process_input
self.on_process_ended = None
def _build_execution_error(self, exit_code=None):
return ProcessExecutionError(cmd=self.cmd,
exit_code=exit_code,
stdout=self.stdout.getvalue(),
stderr=self.stderr.getvalue())
def errReceived(self, text):
self.stderr.write(text)
if self.terminate_on_stderr and (self.deferred is not None):
self.on_process_ended = defer.Deferred()
self.deferred.errback(self._build_execution_error())
self.deferred = None
self.transport.loseConnection()
def outReceived(self, text):
self.stdout.write(text)
def processEnded(self, reason):
if self.deferred is not None:
stdout, stderr = self.stdout.getvalue(), self.stderr.getvalue()
exit_code = reason.value.exitCode
if self.check_exit_code and exit_code != 0:
self.deferred.errback(self._build_execution_error(exit_code))
else:
try:
if self.check_exit_code:
reason.trap(error.ProcessDone)
self.deferred.callback((stdout, stderr))
except:
# NOTE(justinsb): This logic is a little suspicious to me.
# If the callback throws an exception, then errback will
# be called also. However, this is what the unit tests
# test for.
exec_error = self._build_execution_error(exit_code)
self.deferred.errback(exec_error)
elif self.on_process_ended is not None:
self.on_process_ended.errback(reason)
def connectionMade(self):
if self.started_deferred:
self.started_deferred.callback(self)
if self.process_input:
self.transport.write(str(self.process_input))
self.transport.closeStdin()
def get_process_output(executable, args=None, env=None, path=None,
process_reactor=None, check_exit_code=True,
process_input=None, started_deferred=None,
terminate_on_stderr=False):
if process_reactor is None:
process_reactor = reactor
args = args and args or ()
env = env and env and {}
deferred = defer.Deferred()
cmd = executable
if args:
cmd = " ".join([cmd] + args)
logging.debug("Running cmd: %s", cmd)
process_handler = BackRelayWithInput(
deferred,
cmd,
started_deferred=started_deferred,
check_exit_code=check_exit_code,
process_input=process_input,
terminate_on_stderr=terminate_on_stderr)
# NOTE(vish): commands come in as unicode, but self.executes needs
# strings or process.spawn raises a deprecation warning
executable = str(executable)
if not args is None:
args = [str(x) for x in args]
process_reactor.spawnProcess(process_handler, executable,
(executable,) + tuple(args), env, path)
return deferred
class ProcessPool(object):
""" A simple process pool implementation using Twisted's Process bits.
This is pretty basic right now, but hopefully the API will be the correct
one so that it can be optimized later.
"""
def __init__(self, size=None):
self.size = size and size or FLAGS.process_pool_size
self._pool = defer.DeferredSemaphore(self.size)
def simple_execute(self, cmd, **kw):
""" Weak emulation of the old utils.execute() function.
This only exists as a way to quickly move old execute methods to
this new style of code.
NOTE(termie): This will break on args with spaces in them.
"""
parsed = cmd.split(' ')
executable, args = parsed[0], parsed[1:]
return self.execute(executable, args, **kw)
def execute(self, *args, **kw):
deferred = self._pool.acquire()
def _associate_process(proto):
deferred.process = proto.transport
return proto.transport
started = defer.Deferred()
started.addCallback(_associate_process)
kw.setdefault('started_deferred', started)
deferred.process = None
deferred.started = started
deferred.addCallback(lambda _: get_process_output(*args, **kw))
deferred.addBoth(self._release)
return deferred
def _release(self, retval=None):
self._pool.release()
return retval
class SharedPool(object):
_instance = None
def __init__(self):
if SharedPool._instance is None:
self.__class__._instance = ProcessPool()
def __getattr__(self, key):
return getattr(self._instance, key)
def simple_execute(cmd, **kwargs):
return SharedPool().simple_execute(cmd, **kwargs)

View File

@@ -25,18 +25,18 @@ import json
import logging import logging
import sys import sys
import time import time
import traceback
import uuid import uuid
from carrot import connection as carrot_connection from carrot import connection as carrot_connection
from carrot import messaging from carrot import messaging
from eventlet import greenthread from eventlet import greenthread
from twisted.internet import defer
from twisted.internet import task
from nova import context
from nova import exception from nova import exception
from nova import fakerabbit from nova import fakerabbit
from nova import flags from nova import flags
from nova import context from nova import utils
FLAGS = flags.FLAGS FLAGS = flags.FLAGS
@@ -91,15 +91,15 @@ class Consumer(messaging.Consumer):
self.failed_connection = False self.failed_connection = False
break break
except: # Catching all because carrot sucks except: # Catching all because carrot sucks
logging.exception("AMQP server on %s:%d is unreachable." \ logging.exception(_("AMQP server on %s:%d is unreachable."
" Trying again in %d seconds." % ( " Trying again in %d seconds.") % (
FLAGS.rabbit_host, FLAGS.rabbit_host,
FLAGS.rabbit_port, FLAGS.rabbit_port,
FLAGS.rabbit_retry_interval)) FLAGS.rabbit_retry_interval))
self.failed_connection = True self.failed_connection = True
if self.failed_connection: if self.failed_connection:
logging.exception("Unable to connect to AMQP server" \ logging.exception(_("Unable to connect to AMQP server"
" after %d tries. Shutting down." % FLAGS.rabbit_max_retries) " after %d tries. Shutting down.") % FLAGS.rabbit_max_retries)
sys.exit(1) sys.exit(1)
def fetch(self, no_ack=None, auto_ack=None, enable_callbacks=False): def fetch(self, no_ack=None, auto_ack=None, enable_callbacks=False):
@@ -116,29 +116,21 @@ class Consumer(messaging.Consumer):
self.declare() self.declare()
super(Consumer, self).fetch(no_ack, auto_ack, enable_callbacks) super(Consumer, self).fetch(no_ack, auto_ack, enable_callbacks)
if self.failed_connection: if self.failed_connection:
logging.error("Reconnected to queue") logging.error(_("Reconnected to queue"))
self.failed_connection = False self.failed_connection = False
# NOTE(vish): This is catching all errors because we really don't # NOTE(vish): This is catching all errors because we really don't
# exceptions to be logged 10 times a second if some # exceptions to be logged 10 times a second if some
# persistent failure occurs. # persistent failure occurs.
except Exception: # pylint: disable-msg=W0703 except Exception: # pylint: disable-msg=W0703
if not self.failed_connection: 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 self.failed_connection = True
def attach_to_eventlet(self): def attach_to_eventlet(self):
"""Only needed for unit tests!""" """Only needed for unit tests!"""
def fetch_repeatedly(): timer = utils.LoopingCall(self.fetch, enable_callbacks=True)
while True: timer.start(0.1)
self.fetch(enable_callbacks=True) return timer
greenthread.sleep(0.1)
greenthread.spawn(fetch_repeatedly)
def attach_to_twisted(self):
"""Attach a callback to twisted that fires 10 times a second"""
loop = task.LoopingCall(self.fetch, enable_callbacks=True)
loop.start(interval=0.1)
return loop
class Publisher(messaging.Publisher): class Publisher(messaging.Publisher):
@@ -161,7 +153,7 @@ class TopicConsumer(Consumer):
class AdapterConsumer(TopicConsumer): class AdapterConsumer(TopicConsumer):
"""Calls methods on a proxy object based on method and args""" """Calls methods on a proxy object based on method and args"""
def __init__(self, connection=None, topic="broadcast", proxy=None): 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 self.proxy = proxy
super(AdapterConsumer, self).__init__(connection=connection, super(AdapterConsumer, self).__init__(connection=connection,
topic=topic) topic=topic)
@@ -176,7 +168,7 @@ class AdapterConsumer(TopicConsumer):
Example: {'method': 'echo', 'args': {'value': 42}} 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) msg_id = message_data.pop('_msg_id', None)
ctxt = _unpack_context(message_data) ctxt = _unpack_context(message_data)
@@ -189,18 +181,20 @@ class AdapterConsumer(TopicConsumer):
# messages stay in the queue indefinitely, so for now # messages stay in the queue indefinitely, so for now
# we just log the message and send an error string # we just log the message and send an error string
# back to the caller # back to the caller
LOG.warn('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) msg_reply(msg_id, _('No method for message: %s') % message_data)
return return
node_func = getattr(self.proxy, str(method)) node_func = getattr(self.proxy, str(method))
node_args = dict((str(k), v) for k, v in args.iteritems()) node_args = dict((str(k), v) for k, v in args.iteritems())
# NOTE(vish): magic is fun! # NOTE(vish): magic is fun!
# pylint: disable-msg=W0142 try:
d = defer.maybeDeferred(node_func, context=ctxt, **node_args) rval = node_func(context=ctxt, **node_args)
if msg_id: if msg_id:
d.addCallback(lambda rval: msg_reply(msg_id, rval, None)) msg_reply(msg_id, rval, None)
d.addErrback(lambda e: msg_reply(msg_id, None, e)) except Exception as e:
if msg_id:
msg_reply(msg_id, None, sys.exc_info())
return return
@@ -242,14 +236,16 @@ class DirectPublisher(Publisher):
def msg_reply(msg_id, reply=None, failure=None): def msg_reply(msg_id, reply=None, failure=None):
"""Sends a reply or an error on the channel signified by msg_id """Sends a reply or an error on the channel signified by msg_id
failure should be a twisted failure object""" failure should be a sys.exc_info() tuple.
"""
if failure: if failure:
message = failure.getErrorMessage() message = str(failure[1])
traceback = failure.getTraceback() tb = traceback.format_exception(*failure)
logging.error("Returning exception %s to caller", message) logging.error(_("Returning exception %s to caller"), message)
logging.error(traceback) logging.error(tb)
failure = (failure.type.__name__, str(failure.value), traceback) failure = (failure[0].__name__, str(failure[1]), tb)
conn = Connection.instance() conn = Connection.instance(True)
publisher = DirectPublisher(connection=conn, msg_id=msg_id) publisher = DirectPublisher(connection=conn, msg_id=msg_id)
try: try:
publisher.send({'result': reply, 'failure': failure}) publisher.send({'result': reply, 'failure': failure})
@@ -287,7 +283,7 @@ def _unpack_context(msg):
if key.startswith('_context_'): if key.startswith('_context_'):
value = msg.pop(key) value = msg.pop(key)
context_dict[key[9:]] = value 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) return context.RequestContext.from_dict(context_dict)
@@ -306,14 +302,13 @@ def _pack_context(msg, context):
def call(context, topic, msg): def call(context, topic, msg):
"""Sends a message on a topic and wait for a response""" """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_id = uuid.uuid4().hex
msg.update({'_msg_id': msg_id}) 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) _pack_context(msg, context)
class WaitMessage(object): class WaitMessage(object):
def __call__(self, data, message): def __call__(self, data, message):
"""Acks message and sets result.""" """Acks message and sets result."""
message.ack() message.ack()
@@ -337,41 +332,15 @@ def call(context, topic, msg):
except StopIteration: except StopIteration:
pass pass
consumer.close() consumer.close()
# NOTE(termie): this is a little bit of a change from the original
# non-eventlet code where returning a Failure
# instance from a deferred call is very similar to
# raising an exception
if isinstance(wait_msg.result, Exception):
raise wait_msg.result
return wait_msg.result return wait_msg.result
def call_twisted(context, topic, msg):
"""Sends a message on a topic and wait for a response"""
LOG.debug("Making asynchronous call...")
msg_id = uuid.uuid4().hex
msg.update({'_msg_id': msg_id})
LOG.debug("MSG_ID is %s" % (msg_id))
_pack_context(msg, context)
conn = Connection.instance()
d = defer.Deferred()
consumer = DirectConsumer(connection=conn, msg_id=msg_id)
def deferred_receive(data, message):
"""Acks message and callbacks or errbacks"""
message.ack()
if data['failure']:
return d.errback(RemoteError(*data['failure']))
else:
return d.callback(data['result'])
consumer.register_callback(deferred_receive)
injected = consumer.attach_to_twisted()
# clean up after the injected listened and return x
d.addCallback(lambda x: injected.stop() and x or x)
publisher = TopicPublisher(connection=conn, topic=topic)
publisher.send(msg)
publisher.close()
return d
def cast(context, topic, msg): def cast(context, topic, msg):
"""Sends a message on a topic without waiting for a response""" """Sends a message on a topic without waiting for a response"""
LOG.debug("Making asynchronous cast...") LOG.debug("Making asynchronous cast...")
@@ -384,7 +353,7 @@ def cast(context, topic, msg):
def generic_response(message_data, message): def generic_response(message_data, message):
"""Logs a result and exits""" """Logs a result and exits"""
LOG.debug('response %s', message_data) LOG.debug(_('response %s'), message_data)
message.ack() message.ack()
sys.exit(0) sys.exit(0)
@@ -393,8 +362,8 @@ def send_message(topic, message, wait=True):
"""Sends a message for testing""" """Sends a message for testing"""
msg_id = uuid.uuid4().hex msg_id = uuid.uuid4().hex
message.update({'_msg_id': msg_id}) message.update({'_msg_id': msg_id})
LOG.debug('topic is %s', topic) LOG.debug(_('topic is %s'), topic)
LOG.debug('message %s', message) LOG.debug(_('message %s'), message)
if wait: if wait:
consumer = messaging.Consumer(connection=Connection.instance(), consumer = messaging.Consumer(connection=Connection.instance(),

View File

@@ -47,7 +47,7 @@ class SimpleScheduler(chance.ChanceScheduler):
for result in results: for result in results:
(service, instance_cores) = result (service, instance_cores) = result
if instance_cores + instance_ref['vcpus'] > FLAGS.max_cores: 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): if self.service_is_up(service):
# NOTE(vish): this probably belongs in the manager, if we # NOTE(vish): this probably belongs in the manager, if we
# can generalize this somehow # can generalize this somehow
@@ -57,7 +57,7 @@ class SimpleScheduler(chance.ChanceScheduler):
{'host': service['host'], {'host': service['host'],
'scheduled_at': now}) 'scheduled_at': now})
return service['host'] 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): def schedule_create_volume(self, context, volume_id, *_args, **_kwargs):
"""Picks a host that is up and has the fewest volumes.""" """Picks a host that is up and has the fewest volumes."""
@@ -66,7 +66,8 @@ class SimpleScheduler(chance.ChanceScheduler):
for result in results: for result in results:
(service, volume_gigabytes) = result (service, volume_gigabytes) = result
if volume_gigabytes + volume_ref['size'] > FLAGS.max_gigabytes: 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): if self.service_is_up(service):
# NOTE(vish): this probably belongs in the manager, if we # NOTE(vish): this probably belongs in the manager, if we
# can generalize this somehow # can generalize this somehow
@@ -76,7 +77,7 @@ class SimpleScheduler(chance.ChanceScheduler):
{'host': service['host'], {'host': service['host'],
'scheduled_at': now}) 'scheduled_at': now})
return service['host'] return service['host']
raise driver.NoValidHost("No hosts found") raise driver.NoValidHost(_("No hosts found"))
def schedule_set_network_host(self, context, *_args, **_kwargs): def schedule_set_network_host(self, context, *_args, **_kwargs):
"""Picks a host that is up and has the fewest networks.""" """Picks a host that is up and has the fewest networks."""
@@ -85,7 +86,7 @@ class SimpleScheduler(chance.ChanceScheduler):
for result in results: for result in results:
(service, instance_count) = result (service, instance_count) = result
if instance_count >= FLAGS.max_networks: 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): if self.service_is_up(service):
return service['host'] return service['host']
raise driver.NoValidHost("No hosts found") raise driver.NoValidHost(_("No hosts found"))

View File

@@ -1,151 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Base functionality for nova daemons - gradually being replaced with twistd.py.
"""
import daemon
from daemon import pidlockfile
import logging
import logging.handlers
import os
import signal
import sys
import time
from nova import flags
FLAGS = flags.FLAGS
flags.DEFINE_bool('daemonize', False, 'daemonize this process')
# NOTE(termie): right now I am defaulting to using syslog when we daemonize
# it may be better to do something else -shrug-
# NOTE(Devin): I think we should let each process have its own log file
# and put it in /var/logs/nova/(appname).log
# This makes debugging much easier and cuts down on sys log
# clutter.
flags.DEFINE_bool('use_syslog', True, 'output to syslog when daemonizing')
flags.DEFINE_string('logfile', None, 'log file to output to')
flags.DEFINE_string('logdir', None, 'directory to keep log files in '
'(will be prepended to $logfile)')
flags.DEFINE_string('pidfile', None, 'pid file to output to')
flags.DEFINE_string('working_directory', './', 'working directory...')
flags.DEFINE_integer('uid', os.getuid(), 'uid under which to run')
flags.DEFINE_integer('gid', os.getgid(), 'gid under which to run')
def stop(pidfile):
"""
Stop the daemon
"""
# Get the pid from the pidfile
try:
pid = int(open(pidfile, 'r').read().strip())
except IOError:
message = "pidfile %s does not exist. Daemon not running?\n"
sys.stderr.write(message % pidfile)
return
# Try killing the daemon process
try:
while 1:
os.kill(pid, signal.SIGTERM)
time.sleep(0.1)
except OSError, err:
err = str(err)
if err.find("No such process") > 0:
if os.path.exists(pidfile):
os.remove(pidfile)
else:
print str(err)
sys.exit(1)
def serve(name, main):
"""Controller for server"""
argv = FLAGS(sys.argv)
if not FLAGS.pidfile:
FLAGS.pidfile = '%s.pid' % name
logging.debug("Full set of FLAGS: \n\n\n")
for flag in FLAGS:
logging.debug("%s : %s", flag, FLAGS.get(flag, None))
action = 'start'
if len(argv) > 1:
action = argv.pop()
if action == 'stop':
stop(FLAGS.pidfile)
sys.exit()
elif action == 'restart':
stop(FLAGS.pidfile)
elif action == 'start':
pass
else:
print 'usage: %s [options] [start|stop|restart]' % argv[0]
sys.exit(1)
daemonize(argv, name, main)
def daemonize(args, name, main):
"""Does the work of daemonizing the process"""
logging.getLogger('amqplib').setLevel(logging.WARN)
files_to_keep = []
if FLAGS.daemonize:
logger = logging.getLogger()
formatter = logging.Formatter(
name + '(%(name)s): %(levelname)s %(message)s')
if FLAGS.use_syslog and not FLAGS.logfile:
syslog = logging.handlers.SysLogHandler(address='/dev/log')
syslog.setFormatter(formatter)
logger.addHandler(syslog)
files_to_keep.append(syslog.socket)
else:
if not FLAGS.logfile:
FLAGS.logfile = '%s.log' % name
if FLAGS.logdir:
FLAGS.logfile = os.path.join(FLAGS.logdir, FLAGS.logfile)
logfile = logging.FileHandler(FLAGS.logfile)
logfile.setFormatter(formatter)
logger.addHandler(logfile)
files_to_keep.append(logfile.stream)
stdin, stdout, stderr = None, None, None
else:
stdin, stdout, stderr = sys.stdin, sys.stdout, sys.stderr
if FLAGS.verbose:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.WARNING)
with daemon.DaemonContext(
detach_process=FLAGS.daemonize,
working_directory=FLAGS.working_directory,
pidfile=pidlockfile.TimeoutPIDLockFile(FLAGS.pidfile,
acquire_timeout=1,
threaded=False),
stdin=stdin,
stdout=stdout,
stderr=stderr,
uid=FLAGS.uid,
gid=FLAGS.gid,
files_preserve=files_to_keep):
main(args)

View File

@@ -1,54 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import boto
from boto.ec2.regioninfo import RegionInfo
import unittest
ACCESS_KEY = 'fake'
SECRET_KEY = 'fake'
CLC_IP = '127.0.0.1'
CLC_PORT = 8773
REGION = 'test'
def get_connection():
return boto.connect_ec2(
aws_access_key_id=ACCESS_KEY,
aws_secret_access_key=SECRET_KEY,
is_secure=False,
region=RegionInfo(None, REGION, CLC_IP),
port=CLC_PORT,
path='/services/Cloud',
debug=99)
class APIIntegrationTests(unittest.TestCase):
def test_001_get_all_images(self):
conn = get_connection()
res = conn.get_all_images()
if __name__ == '__main__':
unittest.main()
#print conn.get_all_key_pairs()
#print conn.create_key_pair
#print conn.create_security_group('name', 'description')

View File

@@ -1,52 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 OpenStack LLC
#
# 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 os
from nova import test
from nova.utils import parse_mailmap, str_dict_replace
class ProjectTestCase(test.TrialTestCase):
def test_authors_up_to_date(self):
if os.path.exists('../.bzr'):
contributors = set()
mailmap = parse_mailmap('../.mailmap')
import bzrlib.workingtree
tree = bzrlib.workingtree.WorkingTree.open('..')
tree.lock_read()
parents = tree.get_parent_ids()
g = tree.branch.repository.get_graph()
for p in parents[1:]:
rev_ids = [r for r, _ in g.iter_ancestry(parents)
if r != "null:"]
revs = tree.branch.repository.get_revisions(rev_ids)
for r in revs:
for author in r.get_apparent_authors():
email = author.split(' ')[-1]
contributors.add(str_dict_replace(email, mailmap))
authors_file = open('../Authors', 'r').read()
missing = set()
for contributor in contributors:
if not contributor in authors_file:
missing.add(contributor)
self.assertTrue(len(missing) == 0,
'%r not listed in Authors' % missing)

View File

@@ -54,7 +54,7 @@ os.makedirs(os.path.join(OSS_TEMPDIR, 'images'))
os.makedirs(os.path.join(OSS_TEMPDIR, 'buckets')) os.makedirs(os.path.join(OSS_TEMPDIR, 'buckets'))
class ObjectStoreTestCase(test.TrialTestCase): class ObjectStoreTestCase(test.TestCase):
"""Test objectstore API directly.""" """Test objectstore API directly."""
def setUp(self): def setUp(self):
@@ -191,7 +191,7 @@ class TestSite(server.Site):
protocol = TestHTTPChannel protocol = TestHTTPChannel
class S3APITestCase(test.TrialTestCase): class S3APITestCase(test.TestCase):
"""Test objectstore through S3 API.""" """Test objectstore through S3 API."""
def setUp(self): def setUp(self):

View File

@@ -1,132 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import logging
from twisted.internet import defer
from twisted.internet import reactor
from xml.etree import ElementTree
from nova import exception
from nova import flags
from nova import process
from nova import test
from nova import utils
FLAGS = flags.FLAGS
class ProcessTestCase(test.TrialTestCase):
def setUp(self):
logging.getLogger().setLevel(logging.DEBUG)
super(ProcessTestCase, self).setUp()
def test_execute_stdout(self):
pool = process.ProcessPool(2)
d = pool.simple_execute('echo test')
def _check(rv):
self.assertEqual(rv[0], 'test\n')
self.assertEqual(rv[1], '')
d.addCallback(_check)
d.addErrback(self.fail)
return d
def test_execute_stderr(self):
pool = process.ProcessPool(2)
d = pool.simple_execute('cat BAD_FILE', check_exit_code=False)
def _check(rv):
self.assertEqual(rv[0], '')
self.assert_('No such file' in rv[1])
d.addCallback(_check)
d.addErrback(self.fail)
return d
def test_execute_unexpected_stderr(self):
pool = process.ProcessPool(2)
d = pool.simple_execute('cat BAD_FILE')
d.addCallback(lambda x: self.fail('should have raised an error'))
d.addErrback(lambda failure: failure.trap(IOError))
return d
def test_max_processes(self):
pool = process.ProcessPool(2)
d1 = pool.simple_execute('sleep 0.01')
d2 = pool.simple_execute('sleep 0.01')
d3 = pool.simple_execute('sleep 0.005')
d4 = pool.simple_execute('sleep 0.005')
called = []
def _called(rv, name):
called.append(name)
d1.addCallback(_called, 'd1')
d2.addCallback(_called, 'd2')
d3.addCallback(_called, 'd3')
d4.addCallback(_called, 'd4')
# Make sure that d3 and d4 had to wait on the other two and were called
# in order
# NOTE(termie): there may be a race condition in this test if for some
# reason one of the sleeps takes longer to complete
# than it should
d4.addCallback(lambda x: self.assertEqual(called[2], 'd3'))
d4.addCallback(lambda x: self.assertEqual(called[3], 'd4'))
d4.addErrback(self.fail)
return d4
def test_kill_long_process(self):
pool = process.ProcessPool(2)
d1 = pool.simple_execute('sleep 1')
d2 = pool.simple_execute('sleep 0.005')
timeout = reactor.callLater(0.1, self.fail, 'should have been killed')
# kill d1 and wait on it to end then cancel the timeout
d2.addCallback(lambda _: d1.process.signalProcess('KILL'))
d2.addCallback(lambda _: d1)
d2.addBoth(lambda _: timeout.active() and timeout.cancel())
d2.addErrback(self.fail)
return d2
def test_process_exit_is_contained(self):
pool = process.ProcessPool(2)
d1 = pool.simple_execute('sleep 1')
d1.addCallback(lambda x: self.fail('should have errbacked'))
d1.addErrback(lambda fail: fail.trap(IOError))
reactor.callLater(0.05, d1.process.signalProcess, 'KILL')
return d1
def test_shared_pool_is_singleton(self):
pool1 = process.SharedPool()
pool2 = process.SharedPool()
self.assertEqual(id(pool1._instance), id(pool2._instance))
def test_shared_pool_works_as_singleton(self):
d1 = process.simple_execute('sleep 1')
d2 = process.simple_execute('sleep 0.005')
# lp609749: would have failed with
# exceptions.AssertionError: Someone released me too many times:
# too many tokens!
return d1

View File

@@ -1,153 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import logging
from nova import context
from nova import db
from nova import exception
from nova import flags
from nova import quota
from nova import test
from nova import utils
from nova.auth import manager
from nova.api.ec2 import cloud
FLAGS = flags.FLAGS
class QuotaTestCase(test.TrialTestCase):
def setUp(self):
logging.getLogger().setLevel(logging.DEBUG)
super(QuotaTestCase, self).setUp()
self.flags(connection_type='fake',
quota_instances=2,
quota_cores=4,
quota_volumes=2,
quota_gigabytes=20,
quota_floating_ips=1)
self.cloud = cloud.CloudController()
self.manager = manager.AuthManager()
self.user = self.manager.create_user('admin', 'admin', 'admin', True)
self.project = self.manager.create_project('admin', 'admin', 'admin')
self.network = utils.import_object(FLAGS.network_manager)
self.context = context.RequestContext(project=self.project,
user=self.user)
def tearDown(self):
manager.AuthManager().delete_project(self.project)
manager.AuthManager().delete_user(self.user)
super(QuotaTestCase, self).tearDown()
def _create_instance(self, cores=2):
"""Create a test instance"""
inst = {}
inst['image_id'] = 'ami-test'
inst['reservation_id'] = 'r-fakeres'
inst['user_id'] = self.user.id
inst['project_id'] = self.project.id
inst['instance_type'] = 'm1.large'
inst['vcpus'] = cores
inst['mac_address'] = utils.generate_mac()
return db.instance_create(self.context, inst)['id']
def _create_volume(self, size=10):
"""Create a test volume"""
vol = {}
vol['user_id'] = self.user.id
vol['project_id'] = self.project.id
vol['size'] = size
return db.volume_create(self.context, vol)['id']
def test_quota_overrides(self):
"""Make sure overriding a projects quotas works"""
num_instances = quota.allowed_instances(self.context, 100, 'm1.small')
self.assertEqual(num_instances, 2)
db.quota_create(self.context, {'project_id': self.project.id,
'instances': 10})
num_instances = quota.allowed_instances(self.context, 100, 'm1.small')
self.assertEqual(num_instances, 4)
db.quota_update(self.context, self.project.id, {'cores': 100})
num_instances = quota.allowed_instances(self.context, 100, 'm1.small')
self.assertEqual(num_instances, 10)
db.quota_destroy(self.context, self.project.id)
def test_too_many_instances(self):
instance_ids = []
for i in range(FLAGS.quota_instances):
instance_id = self._create_instance()
instance_ids.append(instance_id)
self.assertRaises(quota.QuotaError, self.cloud.run_instances,
self.context,
min_count=1,
max_count=1,
instance_type='m1.small',
image_id='fake')
for instance_id in instance_ids:
db.instance_destroy(self.context, instance_id)
def test_too_many_cores(self):
instance_ids = []
instance_id = self._create_instance(cores=4)
instance_ids.append(instance_id)
self.assertRaises(quota.QuotaError, self.cloud.run_instances,
self.context,
min_count=1,
max_count=1,
instance_type='m1.small',
image_id='fake')
for instance_id in instance_ids:
db.instance_destroy(self.context, instance_id)
def test_too_many_volumes(self):
volume_ids = []
for i in range(FLAGS.quota_volumes):
volume_id = self._create_volume()
volume_ids.append(volume_id)
self.assertRaises(quota.QuotaError, self.cloud.create_volume,
self.context,
size=10)
for volume_id in volume_ids:
db.volume_destroy(self.context, volume_id)
def test_too_many_gigabytes(self):
volume_ids = []
volume_id = self._create_volume(size=20)
volume_ids.append(volume_id)
self.assertRaises(quota.QuotaError,
self.cloud.create_volume,
self.context,
size=10)
for volume_id in volume_ids:
db.volume_destroy(self.context, volume_id)
def test_too_many_addresses(self):
address = '192.168.0.100'
db.floating_ip_create(context.get_admin_context(),
{'address': address, 'host': FLAGS.host})
float_addr = self.network.allocate_floating_ip(self.context,
self.project.id)
# NOTE(vish): This assert never fails. When cloud attempts to
# make an rpc.call, the test just finishes with OK. It
# appears to be something in the magic inline callbacks
# that is breaking.
self.assertRaises(quota.QuotaError, self.cloud.allocate_address,
self.context)
db.floating_ip_destroy(context.get_admin_context(), address)

View File

@@ -1,245 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Unit Tests for remote procedure calls using queue
"""
import mox
from twisted.application.app import startApplication
from twisted.internet import defer
from nova import exception
from nova import flags
from nova import rpc
from nova import test
from nova import service
from nova import manager
FLAGS = flags.FLAGS
flags.DEFINE_string("fake_manager", "nova.tests.service_unittest.FakeManager",
"Manager for testing")
class FakeManager(manager.Manager):
"""Fake manager for tests"""
def test_method(self):
return 'manager'
class ExtendedService(service.Service):
def test_method(self):
return 'service'
class ServiceManagerTestCase(test.TrialTestCase):
"""Test cases for Services"""
def test_attribute_error_for_no_manager(self):
serv = service.Service('test',
'test',
'test',
'nova.tests.service_unittest.FakeManager')
self.assertRaises(AttributeError, getattr, serv, 'test_method')
def test_message_gets_to_manager(self):
serv = service.Service('test',
'test',
'test',
'nova.tests.service_unittest.FakeManager')
serv.startService()
self.assertEqual(serv.test_method(), 'manager')
def test_override_manager_method(self):
serv = ExtendedService('test',
'test',
'test',
'nova.tests.service_unittest.FakeManager')
serv.startService()
self.assertEqual(serv.test_method(), 'service')
class ServiceTestCase(test.TrialTestCase):
"""Test cases for Services"""
def setUp(self):
super(ServiceTestCase, self).setUp()
self.mox.StubOutWithMock(service, 'db')
def test_create(self):
host = 'foo'
binary = 'nova-fake'
topic = 'fake'
# NOTE(vish): Create was moved out of mox replay to make sure that
# the looping calls are created in StartService.
app = service.Service.create(host=host, binary=binary)
self.mox.StubOutWithMock(rpc,
'AdapterConsumer',
use_mock_anything=True)
self.mox.StubOutWithMock(
service.task, 'LoopingCall', use_mock_anything=True)
rpc.AdapterConsumer(connection=mox.IgnoreArg(),
topic=topic,
proxy=mox.IsA(service.Service)).AndReturn(
rpc.AdapterConsumer)
rpc.AdapterConsumer(connection=mox.IgnoreArg(),
topic='%s.%s' % (topic, host),
proxy=mox.IsA(service.Service)).AndReturn(
rpc.AdapterConsumer)
rpc.AdapterConsumer.attach_to_twisted()
rpc.AdapterConsumer.attach_to_twisted()
# Stub out looping call a bit needlessly since we don't have an easy
# way to cancel it (yet) when the tests finishes
service.task.LoopingCall(mox.IgnoreArg()).AndReturn(
service.task.LoopingCall)
service.task.LoopingCall.start(interval=mox.IgnoreArg(),
now=mox.IgnoreArg())
service.task.LoopingCall(mox.IgnoreArg()).AndReturn(
service.task.LoopingCall)
service.task.LoopingCall.start(interval=mox.IgnoreArg(),
now=mox.IgnoreArg())
service_create = {'host': host,
'binary': binary,
'topic': topic,
'report_count': 0}
service_ref = {'host': host,
'binary': binary,
'report_count': 0,
'id': 1}
service.db.service_get_by_args(mox.IgnoreArg(),
host,
binary).AndRaise(exception.NotFound())
service.db.service_create(mox.IgnoreArg(),
service_create).AndReturn(service_ref)
self.mox.ReplayAll()
startApplication(app, False)
self.assert_(app)
# We're testing sort of weird behavior in how report_state decides
# whether it is disconnected, it looks for a variable on itself called
# 'model_disconnected' and report_state doesn't really do much so this
# these are mostly just for coverage
@defer.inlineCallbacks
def test_report_state_no_service(self):
host = 'foo'
binary = 'bar'
topic = 'test'
service_create = {'host': host,
'binary': binary,
'topic': topic,
'report_count': 0}
service_ref = {'host': host,
'binary': binary,
'topic': topic,
'report_count': 0,
'id': 1}
service.db.service_get_by_args(mox.IgnoreArg(),
host,
binary).AndRaise(exception.NotFound())
service.db.service_create(mox.IgnoreArg(),
service_create).AndReturn(service_ref)
service.db.service_get(mox.IgnoreArg(),
service_ref['id']).AndReturn(service_ref)
service.db.service_update(mox.IgnoreArg(), service_ref['id'],
mox.ContainsKeyValue('report_count', 1))
self.mox.ReplayAll()
serv = service.Service(host,
binary,
topic,
'nova.tests.service_unittest.FakeManager')
serv.startService()
yield serv.report_state()
@defer.inlineCallbacks
def test_report_state_newly_disconnected(self):
host = 'foo'
binary = 'bar'
topic = 'test'
service_create = {'host': host,
'binary': binary,
'topic': topic,
'report_count': 0}
service_ref = {'host': host,
'binary': binary,
'topic': topic,
'report_count': 0,
'id': 1}
service.db.service_get_by_args(mox.IgnoreArg(),
host,
binary).AndRaise(exception.NotFound())
service.db.service_create(mox.IgnoreArg(),
service_create).AndReturn(service_ref)
service.db.service_get(mox.IgnoreArg(),
mox.IgnoreArg()).AndRaise(Exception())
self.mox.ReplayAll()
serv = service.Service(host,
binary,
topic,
'nova.tests.service_unittest.FakeManager')
serv.startService()
yield serv.report_state()
self.assert_(serv.model_disconnected)
@defer.inlineCallbacks
def test_report_state_newly_connected(self):
host = 'foo'
binary = 'bar'
topic = 'test'
service_create = {'host': host,
'binary': binary,
'topic': topic,
'report_count': 0}
service_ref = {'host': host,
'binary': binary,
'topic': topic,
'report_count': 0,
'id': 1}
service.db.service_get_by_args(mox.IgnoreArg(),
host,
binary).AndRaise(exception.NotFound())
service.db.service_create(mox.IgnoreArg(),
service_create).AndReturn(service_ref)
service.db.service_get(mox.IgnoreArg(),
service_ref['id']).AndReturn(service_ref)
service.db.service_update(mox.IgnoreArg(), service_ref['id'],
mox.ContainsKeyValue('report_count', 1))
self.mox.ReplayAll()
serv = service.Service(host,
binary,
topic,
'nova.tests.service_unittest.FakeManager')
serv.startService()
serv.model_disconnected = True
yield serv.report_state()
self.assert_(not serv.model_disconnected)

View File

@@ -35,7 +35,7 @@ class Context(object):
pass pass
class AccessTestCase(test.TrialTestCase): class AccessTestCase(test.TestCase):
def setUp(self): def setUp(self):
super(AccessTestCase, self).setUp() super(AccessTestCase, self).setUp()
um = manager.AuthManager() um = manager.AuthManager()

View File

@@ -208,17 +208,13 @@ class AuthManagerTestCase(object):
# so it probably belongs in crypto_unittest # so it probably belongs in crypto_unittest
# but I'm leaving it where I found it. # but I'm leaving it where I found it.
with user_and_project_generator(self.manager) as (user, project): with user_and_project_generator(self.manager) as (user, project):
# NOTE(todd): Should mention why we must setup controller first # NOTE(vish): Setup runs genroot.sh if it hasn't been run
# (somebody please clue me in) cloud.CloudController().setup()
cloud_controller = cloud.CloudController() _key, cert_str = crypto.generate_x509_cert(user.id, project.id)
cloud_controller.setup()
_key, cert_str = self.manager._generate_x509_cert('test1',
'testproj')
logging.debug(cert_str) logging.debug(cert_str)
# Need to verify that it's signed by the right intermediate CA full_chain = crypto.fetch_ca(project_id=project.id, chain=True)
full_chain = crypto.fetch_ca(project_id='testproj', chain=True) int_cert = crypto.fetch_ca(project_id=project.id, chain=False)
int_cert = crypto.fetch_ca(project_id='testproj', chain=False)
cloud_cert = crypto.fetch_ca() cloud_cert = crypto.fetch_ca()
logging.debug("CA chain:\n\n =====\n%s\n\n=====" % full_chain) logging.debug("CA chain:\n\n =====\n%s\n\n=====" % full_chain)
signed_cert = X509.load_cert_string(cert_str) signed_cert = X509.load_cert_string(cert_str)
@@ -227,7 +223,8 @@ class AuthManagerTestCase(object):
cloud_cert = X509.load_cert_string(cloud_cert) cloud_cert = X509.load_cert_string(cloud_cert)
self.assertTrue(signed_cert.verify(chain_cert.get_pubkey())) self.assertTrue(signed_cert.verify(chain_cert.get_pubkey()))
self.assertTrue(signed_cert.verify(int_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())) self.assertTrue(signed_cert.verify(cloud_cert.get_pubkey()))
else: else:
self.assertFalse(signed_cert.verify(cloud_cert.get_pubkey())) self.assertFalse(signed_cert.verify(cloud_cert.get_pubkey()))
@@ -326,24 +323,20 @@ class AuthManagerTestCase(object):
self.assertTrue(user.is_admin()) self.assertTrue(user.is_admin())
class AuthManagerLdapTestCase(AuthManagerTestCase, test.TrialTestCase): class AuthManagerLdapTestCase(AuthManagerTestCase, test.TestCase):
auth_driver = 'nova.auth.ldapdriver.FakeLdapDriver' auth_driver = 'nova.auth.ldapdriver.FakeLdapDriver'
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
AuthManagerTestCase.__init__(self) AuthManagerTestCase.__init__(self)
test.TrialTestCase.__init__(self, *args, **kwargs) test.TestCase.__init__(self, *args, **kwargs)
import nova.auth.fakeldap as fakeldap import nova.auth.fakeldap as fakeldap
FLAGS.redis_db = 8
if FLAGS.flush_db: if FLAGS.flush_db:
logging.info("Flushing redis datastore") logging.info("Flushing datastore")
try: r = fakeldap.Store.instance()
r = fakeldap.Redis.instance() r.flushdb()
r.flushdb()
except:
self.skip = True
class AuthManagerDbTestCase(AuthManagerTestCase, test.TrialTestCase): class AuthManagerDbTestCase(AuthManagerTestCase, test.TestCase):
auth_driver = 'nova.auth.dbdriver.DbDriver' auth_driver = 'nova.auth.dbdriver.DbDriver'

View File

@@ -22,22 +22,18 @@ import logging
from M2Crypto import BIO from M2Crypto import BIO
from M2Crypto import RSA from M2Crypto import RSA
import os import os
import StringIO
import tempfile import tempfile
import time import time
from eventlet import greenthread from eventlet import greenthread
from twisted.internet import defer
import unittest
from xml.etree import ElementTree
from nova import context from nova import context
from nova import crypto from nova import crypto
from nova import db from nova import db
from nova import flags from nova import flags
from nova import rpc from nova import rpc
from nova import service
from nova import test from nova import test
from nova import utils
from nova.auth import manager from nova.auth import manager
from nova.compute import power_state from nova.compute import power_state
from nova.api.ec2 import cloud from nova.api.ec2 import cloud
@@ -53,10 +49,11 @@ IMAGES_PATH = os.path.join(OSS_TEMPDIR, 'images')
os.makedirs(IMAGES_PATH) os.makedirs(IMAGES_PATH)
class CloudTestCase(test.TrialTestCase): class CloudTestCase(test.TestCase):
def setUp(self): def setUp(self):
super(CloudTestCase, self).setUp() 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() self.conn = rpc.Connection.instance()
logging.getLogger().setLevel(logging.DEBUG) logging.getLogger().setLevel(logging.DEBUG)
@@ -64,27 +61,23 @@ class CloudTestCase(test.TrialTestCase):
# set up our cloud # set up our cloud
self.cloud = cloud.CloudController() self.cloud = cloud.CloudController()
# set up a service # set up services
self.compute = utils.import_object(FLAGS.compute_manager) self.compute = service.Service.create(binary='nova-compute')
self.compute_consumer = rpc.AdapterConsumer(connection=self.conn, self.compute.start()
topic=FLAGS.compute_topic, self.network = service.Service.create(binary='nova-network')
proxy=self.compute) self.network.start()
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()
self.manager = manager.AuthManager() self.manager = manager.AuthManager()
self.user = self.manager.create_user('admin', 'admin', 'admin', True) self.user = self.manager.create_user('admin', 'admin', 'admin', True)
self.project = self.manager.create_project('proj', 'admin', 'proj') self.project = self.manager.create_project('proj', 'admin', 'proj')
self.context = context.RequestContext(user=self.user, self.context = context.RequestContext(user=self.user,
project=self.project) project=self.project)
def tearDown(self): def tearDown(self):
self.manager.delete_project(self.project) self.manager.delete_project(self.project)
self.manager.delete_user(self.user) self.manager.delete_user(self.user)
self.compute.kill()
self.network.kill()
super(CloudTestCase, self).tearDown() super(CloudTestCase, self).tearDown()
def _create_key(self, name): def _create_key(self, name):
@@ -111,12 +104,13 @@ class CloudTestCase(test.TrialTestCase):
{'address': address, {'address': address,
'host': FLAGS.host}) 'host': FLAGS.host})
self.cloud.allocate_address(self.context) 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']) fixed = self.network.allocate_fixed_ip(self.context, inst['id'])
ec2_id = cloud.internal_id_to_ec2_id(inst['internal_id']) ec2_id = cloud.internal_id_to_ec2_id(inst['internal_id'])
self.cloud.associate_address(self.context, self.cloud.associate_address(self.context,
instance_id=ec2_id, instance_id=ec2_id,
public_ip=address) public_ip=address)
greenthread.sleep(0.3)
self.cloud.disassociate_address(self.context, self.cloud.disassociate_address(self.context,
public_ip=address) public_ip=address)
self.cloud.release_address(self.context, self.cloud.release_address(self.context,
@@ -199,7 +193,7 @@ class CloudTestCase(test.TrialTestCase):
logging.debug("Need to watch instance %s until it's running..." % logging.debug("Need to watch instance %s until it's running..." %
instance['instance_id']) instance['instance_id'])
while True: while True:
rv = yield defer.succeed(time.sleep(1)) greenthread.sleep(1)
info = self.cloud._get_instance(instance['instance_id']) info = self.cloud._get_instance(instance['instance_id'])
logging.debug(info['state']) logging.debug(info['state'])
if info['state'] == power_state.RUNNING: if info['state'] == power_state.RUNNING:

View File

@@ -22,8 +22,6 @@ Tests For Compute
import datetime import datetime
import logging import logging
from twisted.internet import defer
from nova import context from nova import context
from nova import db from nova import db
from nova import exception from nova import exception
@@ -33,15 +31,17 @@ from nova import utils
from nova.auth import manager from nova.auth import manager
from nova.compute import api as compute_api from nova.compute import api as compute_api
FLAGS = flags.FLAGS FLAGS = flags.FLAGS
class ComputeTestCase(test.TrialTestCase): class ComputeTestCase(test.TestCase):
"""Test case for compute""" """Test case for compute"""
def setUp(self): def setUp(self):
logging.getLogger().setLevel(logging.DEBUG) logging.getLogger().setLevel(logging.DEBUG)
super(ComputeTestCase, self).setUp() super(ComputeTestCase, self).setUp()
self.flags(connection_type='fake', self.flags(connection_type='fake',
stub_network=True,
network_manager='nova.network.manager.FlatManager') network_manager='nova.network.manager.FlatManager')
self.compute = utils.import_object(FLAGS.compute_manager) self.compute = utils.import_object(FLAGS.compute_manager)
self.compute_api = compute_api.ComputeAPI() self.compute_api = compute_api.ComputeAPI()
@@ -94,24 +94,22 @@ class ComputeTestCase(test.TrialTestCase):
db.security_group_destroy(self.context, group['id']) db.security_group_destroy(self.context, group['id'])
db.instance_destroy(self.context, ref[0]['id']) db.instance_destroy(self.context, ref[0]['id'])
@defer.inlineCallbacks
def test_run_terminate(self): def test_run_terminate(self):
"""Make sure it is possible to run and terminate instance""" """Make sure it is possible to run and terminate instance"""
instance_id = self._create_instance() instance_id = self._create_instance()
yield self.compute.run_instance(self.context, instance_id) self.compute.run_instance(self.context, instance_id)
instances = db.instance_get_all(context.get_admin_context()) instances = db.instance_get_all(context.get_admin_context())
logging.info("Running instances: %s", instances) logging.info(_("Running instances: %s"), instances)
self.assertEqual(len(instances), 1) self.assertEqual(len(instances), 1)
yield self.compute.terminate_instance(self.context, instance_id) self.compute.terminate_instance(self.context, instance_id)
instances = db.instance_get_all(context.get_admin_context()) instances = db.instance_get_all(context.get_admin_context())
logging.info("After terminating instances: %s", instances) logging.info(_("After terminating instances: %s"), instances)
self.assertEqual(len(instances), 0) self.assertEqual(len(instances), 0)
@defer.inlineCallbacks
def test_run_terminate_timestamps(self): def test_run_terminate_timestamps(self):
"""Make sure timestamps are set for launched and destroyed""" """Make sure timestamps are set for launched and destroyed"""
instance_id = self._create_instance() instance_id = self._create_instance()
@@ -119,42 +117,64 @@ class ComputeTestCase(test.TrialTestCase):
self.assertEqual(instance_ref['launched_at'], None) self.assertEqual(instance_ref['launched_at'], None)
self.assertEqual(instance_ref['deleted_at'], None) self.assertEqual(instance_ref['deleted_at'], None)
launch = datetime.datetime.utcnow() launch = datetime.datetime.utcnow()
yield self.compute.run_instance(self.context, instance_id) self.compute.run_instance(self.context, instance_id)
instance_ref = db.instance_get(self.context, instance_id) instance_ref = db.instance_get(self.context, instance_id)
self.assert_(instance_ref['launched_at'] > launch) self.assert_(instance_ref['launched_at'] > launch)
self.assertEqual(instance_ref['deleted_at'], None) self.assertEqual(instance_ref['deleted_at'], None)
terminate = datetime.datetime.utcnow() terminate = datetime.datetime.utcnow()
yield self.compute.terminate_instance(self.context, instance_id) self.compute.terminate_instance(self.context, instance_id)
self.context = self.context.elevated(True) self.context = self.context.elevated(True)
instance_ref = db.instance_get(self.context, instance_id) instance_ref = db.instance_get(self.context, instance_id)
self.assert_(instance_ref['launched_at'] < terminate) self.assert_(instance_ref['launched_at'] < terminate)
self.assert_(instance_ref['deleted_at'] > terminate) self.assert_(instance_ref['deleted_at'] > terminate)
@defer.inlineCallbacks 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_suspend(self):
"""ensure instance can be suspended"""
instance_id = self._create_instance()
self.compute.run_instance(self.context, instance_id)
self.compute.suspend_instance(self.context, instance_id)
self.compute.resume_instance(self.context, instance_id)
self.compute.terminate_instance(self.context, instance_id)
def test_reboot(self): def test_reboot(self):
"""Ensure instance can be rebooted""" """Ensure instance can be rebooted"""
instance_id = self._create_instance() instance_id = self._create_instance()
yield self.compute.run_instance(self.context, instance_id) self.compute.run_instance(self.context, instance_id)
yield self.compute.reboot_instance(self.context, instance_id) self.compute.reboot_instance(self.context, instance_id)
yield self.compute.terminate_instance(self.context, instance_id) self.compute.terminate_instance(self.context, instance_id)
def test_snapshot(self):
"""Ensure instance can be snapshotted"""
instance_id = self._create_instance()
name = "myfakesnapshot"
self.compute.run_instance(self.context, instance_id)
self.compute.snapshot_instance(self.context, instance_id, name)
self.compute.terminate_instance(self.context, instance_id)
@defer.inlineCallbacks
def test_console_output(self): def test_console_output(self):
"""Make sure we can get console output from instance""" """Make sure we can get console output from instance"""
instance_id = self._create_instance() instance_id = self._create_instance()
yield self.compute.run_instance(self.context, instance_id) self.compute.run_instance(self.context, instance_id)
console = yield self.compute.get_console_output(self.context, console = self.compute.get_console_output(self.context,
instance_id) instance_id)
self.assert_(console) self.assert_(console)
yield self.compute.terminate_instance(self.context, instance_id) self.compute.terminate_instance(self.context, instance_id)
@defer.inlineCallbacks
def test_run_instance_existing(self): def test_run_instance_existing(self):
"""Ensure failure when running an instance that already exists""" """Ensure failure when running an instance that already exists"""
instance_id = self._create_instance() instance_id = self._create_instance()
yield self.compute.run_instance(self.context, instance_id) self.compute.run_instance(self.context, instance_id)
self.assertFailure(self.compute.run_instance(self.context, self.assertRaises(exception.Error,
instance_id), self.compute.run_instance,
exception.Error) self.context,
yield self.compute.terminate_instance(self.context, instance_id) instance_id)
self.compute.terminate_instance(self.context, instance_id)

View File

@@ -24,7 +24,7 @@ FLAGS = flags.FLAGS
flags.DEFINE_string('flags_unittest', 'foo', 'for testing purposes only') flags.DEFINE_string('flags_unittest', 'foo', 'for testing purposes only')
class FlagsTestCase(test.TrialTestCase): class FlagsTestCase(test.TestCase):
def setUp(self): def setUp(self):
super(FlagsTestCase, self).setUp() super(FlagsTestCase, self).setUp()

View 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'))

55
nova/tests/test_misc.py Normal file
View File

@@ -0,0 +1,55 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 OpenStack LLC
#
# 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 os
from nova import test
from nova.utils import parse_mailmap, str_dict_replace
class ProjectTestCase(test.TestCase):
def test_authors_up_to_date(self):
if os.path.exists('.bzr'):
contributors = set()
mailmap = parse_mailmap('.mailmap')
import bzrlib.workingtree
tree = bzrlib.workingtree.WorkingTree.open('.')
tree.lock_read()
try:
parents = tree.get_parent_ids()
g = tree.branch.repository.get_graph()
for p in parents[1:]:
rev_ids = [r for r, _ in g.iter_ancestry(parents)
if r != "null:"]
revs = tree.branch.repository.get_revisions(rev_ids)
for r in revs:
for author in r.get_apparent_authors():
email = author.split(' ')[-1]
contributors.add(str_dict_replace(email, mailmap))
authors_file = open('Authors', 'r').read()
missing = set()
for contributor in contributors:
if not contributor in authors_file:
missing.add(contributor)
self.assertTrue(len(missing) == 0,
'%r not listed in Authors' % missing)
finally:
tree.unlock()

View File

@@ -26,6 +26,7 @@ from nova import context
from nova import db from nova import db
from nova import exception from nova import exception
from nova import flags from nova import flags
from nova import service
from nova import test from nova import test
from nova import utils from nova import utils
from nova.auth import manager from nova.auth import manager
@@ -33,13 +34,14 @@ from nova.auth import manager
FLAGS = flags.FLAGS FLAGS = flags.FLAGS
class NetworkTestCase(test.TrialTestCase): class NetworkTestCase(test.TestCase):
"""Test cases for network code""" """Test cases for network code"""
def setUp(self): def setUp(self):
super(NetworkTestCase, self).setUp() super(NetworkTestCase, self).setUp()
# NOTE(vish): if you change these flags, make sure to change the # NOTE(vish): if you change these flags, make sure to change the
# flags in the corresponding section in nova-dhcpbridge # flags in the corresponding section in nova-dhcpbridge
self.flags(connection_type='fake', self.flags(connection_type='fake',
fake_call=True,
fake_network=True, fake_network=True,
network_size=16, network_size=16,
num_networks=5) num_networks=5)
@@ -56,16 +58,13 @@ class NetworkTestCase(test.TrialTestCase):
# create the necessary network data for the project # create the necessary network data for the project
user_context = context.RequestContext(project=self.projects[i], user_context = context.RequestContext(project=self.projects[i],
user=self.user) user=self.user)
network_ref = self.network.get_network(user_context) host = self.network.get_network_host(user_context.elevated())
self.network.set_network_host(context.get_admin_context(),
network_ref['id'])
instance_ref = self._create_instance(0) instance_ref = self._create_instance(0)
self.instance_id = instance_ref['id'] self.instance_id = instance_ref['id']
instance_ref = self._create_instance(1) instance_ref = self._create_instance(1)
self.instance2_id = instance_ref['id'] self.instance2_id = instance_ref['id']
def tearDown(self): def tearDown(self):
super(NetworkTestCase, self).tearDown()
# TODO(termie): this should really be instantiating clean datastores # TODO(termie): this should really be instantiating clean datastores
# in between runs, one failure kills all the tests # in between runs, one failure kills all the tests
db.instance_destroy(context.get_admin_context(), self.instance_id) db.instance_destroy(context.get_admin_context(), self.instance_id)
@@ -73,6 +72,7 @@ class NetworkTestCase(test.TrialTestCase):
for project in self.projects: for project in self.projects:
self.manager.delete_project(project) self.manager.delete_project(project)
self.manager.delete_user(self.user) self.manager.delete_user(self.user)
super(NetworkTestCase, self).tearDown()
def _create_instance(self, project_num, mac=None): def _create_instance(self, project_num, mac=None):
if not mac: if not mac:

View File

@@ -20,8 +20,6 @@ Unit Tests for remote procedure calls using queue
""" """
import logging import logging
from twisted.internet import defer
from nova import context from nova import context
from nova import flags from nova import flags
from nova import rpc from nova import rpc
@@ -31,32 +29,31 @@ from nova import test
FLAGS = flags.FLAGS FLAGS = flags.FLAGS
class RpcTestCase(test.TrialTestCase): class RpcTestCase(test.TestCase):
"""Test cases for rpc""" """Test cases for rpc"""
def setUp(self): def setUp(self):
super(RpcTestCase, self).setUp() super(RpcTestCase, self).setUp()
self.conn = rpc.Connection.instance() self.conn = rpc.Connection.instance(True)
self.receiver = TestReceiver() self.receiver = TestReceiver()
self.consumer = rpc.AdapterConsumer(connection=self.conn, self.consumer = rpc.AdapterConsumer(connection=self.conn,
topic='test', topic='test',
proxy=self.receiver) proxy=self.receiver)
self.consumer.attach_to_twisted() self.consumer.attach_to_eventlet()
self.context = context.get_admin_context() self.context = context.get_admin_context()
def test_call_succeed(self): def test_call_succeed(self):
"""Get a value through rpc call""" """Get a value through rpc call"""
value = 42 value = 42
result = yield rpc.call_twisted(self.context, result = rpc.call(self.context, 'test', {"method": "echo",
'test', {"method": "echo",
"args": {"value": value}}) "args": {"value": value}})
self.assertEqual(value, result) self.assertEqual(value, result)
def test_context_passed(self): def test_context_passed(self):
"""Makes sure a context is passed through rpc call""" """Makes sure a context is passed through rpc call"""
value = 42 value = 42
result = yield rpc.call_twisted(self.context, result = rpc.call(self.context,
'test', {"method": "context", 'test', {"method": "context",
"args": {"value": value}}) "args": {"value": value}})
self.assertEqual(self.context.to_dict(), result) self.assertEqual(self.context.to_dict(), result)
def test_call_exception(self): def test_call_exception(self):
@@ -67,18 +64,48 @@ class RpcTestCase(test.TrialTestCase):
to an int in the test. to an int in the test.
""" """
value = 42 value = 42
self.assertFailure(rpc.call_twisted(self.context, 'test', self.assertRaises(rpc.RemoteError,
{"method": "fail", rpc.call,
"args": {"value": value}}), self.context,
rpc.RemoteError) 'test',
{"method": "fail",
"args": {"value": value}})
try: try:
yield rpc.call_twisted(self.context, rpc.call(self.context,
'test', {"method": "fail", 'test',
"args": {"value": value}}) {"method": "fail",
"args": {"value": value}})
self.fail("should have thrown rpc.RemoteError") self.fail("should have thrown rpc.RemoteError")
except rpc.RemoteError as exc: except rpc.RemoteError as exc:
self.assertEqual(int(exc.value), value) 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): class TestReceiver(object):
"""Simple Proxy class so the consumer has methods to call """Simple Proxy class so the consumer has methods to call
@@ -89,13 +116,13 @@ class TestReceiver(object):
def echo(context, value): def echo(context, value):
"""Simply returns whatever value is sent in""" """Simply returns whatever value is sent in"""
logging.debug("Received %s", value) logging.debug("Received %s", value)
return defer.succeed(value) return value
@staticmethod @staticmethod
def context(context, value): def context(context, value):
"""Returns dictionary version of context""" """Returns dictionary version of context"""
logging.debug("Received %s", context) logging.debug("Received %s", context)
return defer.succeed(context.to_dict()) return context.to_dict()
@staticmethod @staticmethod
def fail(context, value): def fail(context, value):

View File

@@ -33,6 +33,7 @@ from nova.scheduler import driver
FLAGS = flags.FLAGS FLAGS = flags.FLAGS
flags.DECLARE('max_cores', 'nova.scheduler.simple') flags.DECLARE('max_cores', 'nova.scheduler.simple')
flags.DECLARE('stub_network', 'nova.compute.manager')
class TestDriver(driver.Scheduler): class TestDriver(driver.Scheduler):
@@ -44,11 +45,11 @@ class TestDriver(driver.Scheduler):
return 'named_host' return 'named_host'
class SchedulerTestCase(test.TrialTestCase): class SchedulerTestCase(test.TestCase):
"""Test case for scheduler""" """Test case for scheduler"""
def setUp(self): def setUp(self):
super(SchedulerTestCase, self).setUp() super(SchedulerTestCase, self).setUp()
self.flags(scheduler_driver='nova.tests.scheduler_unittest.TestDriver') self.flags(scheduler_driver='nova.tests.test_scheduler.TestDriver')
def test_fallback(self): def test_fallback(self):
scheduler = manager.SchedulerManager() scheduler = manager.SchedulerManager()
@@ -73,11 +74,12 @@ class SchedulerTestCase(test.TrialTestCase):
scheduler.named_method(ctxt, 'topic', num=7) scheduler.named_method(ctxt, 'topic', num=7)
class SimpleDriverTestCase(test.TrialTestCase): class SimpleDriverTestCase(test.TestCase):
"""Test case for simple driver""" """Test case for simple driver"""
def setUp(self): def setUp(self):
super(SimpleDriverTestCase, self).setUp() super(SimpleDriverTestCase, self).setUp()
self.flags(connection_type='fake', self.flags(connection_type='fake',
stub_network=True,
max_cores=4, max_cores=4,
max_gigabytes=4, max_gigabytes=4,
network_manager='nova.network.manager.FlatManager', network_manager='nova.network.manager.FlatManager',
@@ -122,12 +124,12 @@ class SimpleDriverTestCase(test.TrialTestCase):
'nova-compute', 'nova-compute',
'compute', 'compute',
FLAGS.compute_manager) FLAGS.compute_manager)
compute1.startService() compute1.start()
compute2 = service.Service('host2', compute2 = service.Service('host2',
'nova-compute', 'nova-compute',
'compute', 'compute',
FLAGS.compute_manager) FLAGS.compute_manager)
compute2.startService() compute2.start()
hosts = self.scheduler.driver.hosts_up(self.context, 'compute') hosts = self.scheduler.driver.hosts_up(self.context, 'compute')
self.assertEqual(len(hosts), 2) self.assertEqual(len(hosts), 2)
compute1.kill() compute1.kill()
@@ -139,12 +141,12 @@ class SimpleDriverTestCase(test.TrialTestCase):
'nova-compute', 'nova-compute',
'compute', 'compute',
FLAGS.compute_manager) FLAGS.compute_manager)
compute1.startService() compute1.start()
compute2 = service.Service('host2', compute2 = service.Service('host2',
'nova-compute', 'nova-compute',
'compute', 'compute',
FLAGS.compute_manager) FLAGS.compute_manager)
compute2.startService() compute2.start()
instance_id1 = self._create_instance() instance_id1 = self._create_instance()
compute1.run_instance(self.context, instance_id1) compute1.run_instance(self.context, instance_id1)
instance_id2 = self._create_instance() instance_id2 = self._create_instance()
@@ -162,12 +164,12 @@ class SimpleDriverTestCase(test.TrialTestCase):
'nova-compute', 'nova-compute',
'compute', 'compute',
FLAGS.compute_manager) FLAGS.compute_manager)
compute1.startService() compute1.start()
compute2 = service.Service('host2', compute2 = service.Service('host2',
'nova-compute', 'nova-compute',
'compute', 'compute',
FLAGS.compute_manager) FLAGS.compute_manager)
compute2.startService() compute2.start()
instance_ids1 = [] instance_ids1 = []
instance_ids2 = [] instance_ids2 = []
for index in xrange(FLAGS.max_cores): for index in xrange(FLAGS.max_cores):
@@ -195,12 +197,12 @@ class SimpleDriverTestCase(test.TrialTestCase):
'nova-volume', 'nova-volume',
'volume', 'volume',
FLAGS.volume_manager) FLAGS.volume_manager)
volume1.startService() volume1.start()
volume2 = service.Service('host2', volume2 = service.Service('host2',
'nova-volume', 'nova-volume',
'volume', 'volume',
FLAGS.volume_manager) FLAGS.volume_manager)
volume2.startService() volume2.start()
volume_id1 = self._create_volume() volume_id1 = self._create_volume()
volume1.create_volume(self.context, volume_id1) volume1.create_volume(self.context, volume_id1)
volume_id2 = self._create_volume() volume_id2 = self._create_volume()
@@ -218,12 +220,12 @@ class SimpleDriverTestCase(test.TrialTestCase):
'nova-volume', 'nova-volume',
'volume', 'volume',
FLAGS.volume_manager) FLAGS.volume_manager)
volume1.startService() volume1.start()
volume2 = service.Service('host2', volume2 = service.Service('host2',
'nova-volume', 'nova-volume',
'volume', 'volume',
FLAGS.volume_manager) FLAGS.volume_manager)
volume2.startService() volume2.start()
volume_ids1 = [] volume_ids1 = []
volume_ids2 = [] volume_ids2 = []
for index in xrange(FLAGS.max_gigabytes): for index in xrange(FLAGS.max_gigabytes):

View File

@@ -30,9 +30,11 @@ FLAGS = flags.FLAGS
flags.DECLARE('instances_path', 'nova.compute.manager') flags.DECLARE('instances_path', 'nova.compute.manager')
class LibvirtConnTestCase(test.TrialTestCase): class LibvirtConnTestCase(test.TestCase):
def setUp(self): def setUp(self):
super(LibvirtConnTestCase, self).setUp() super(LibvirtConnTestCase, self).setUp()
libvirt_conn._late_load_cheetah()
self.flags(fake_call=True)
self.manager = manager.AuthManager() self.manager = manager.AuthManager()
self.user = self.manager.create_user('fake', 'fake', 'fake', self.user = self.manager.create_user('fake', 'fake', 'fake',
admin=True) admin=True)
@@ -40,33 +42,64 @@ class LibvirtConnTestCase(test.TrialTestCase):
self.network = utils.import_object(FLAGS.network_manager) self.network = utils.import_object(FLAGS.network_manager)
FLAGS.instances_path = '' FLAGS.instances_path = ''
def test_get_uri_and_template(self): test_ip = '10.11.12.13'
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, def test_xml_and_uri_no_ramdisk_no_kernel(self):
'memory_kb': '1024000', instance_data = dict(self.test_instance)
'basepath': '/some/path', self._check_xml_and_uri(instance_data,
'bridge_name': 'br100', expect_kernel=False, expect_ramdisk=False)
'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(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_context = context.RequestContext(project=self.project,
user=self.user) user=self.user)
instance_ref = db.instance_create(user_context, instance) instance_ref = db.instance_create(user_context, instance)
network_ref = self.network.get_network(user_context) host = self.network.get_network_host(user_context.elevated())
self.network.set_network_host(context.get_admin_context(), network_ref = db.project_get_network(context.get_admin_context(),
network_ref['id']) self.project.id)
fixed_ip = {'address': ip, fixed_ip = {'address': self.test_ip,
'network_id': network_ref['id']} 'network_id': network_ref['id']}
ctxt = context.get_admin_context() ctxt = context.get_admin_context()
fixed_ip_ref = db.fixed_ip_create(ctxt, fixed_ip) fixed_ip_ref = db.fixed_ip_create(ctxt, fixed_ip)
db.fixed_ip_update(ctxt, ip, {'allocated': True, db.fixed_ip_update(ctxt, self.test_ip,
'instance_id': instance_ref['id']}) {'allocated': True,
'instance_id': instance_ref['id']})
type_uri_map = {'qemu': ('qemu:///system', type_uri_map = {'qemu': ('qemu:///system',
[(lambda t: t.find('.').get('type'), 'qemu'), [(lambda t: t.find('.').get('type'), 'qemu'),
@@ -78,23 +111,72 @@ class LibvirtConnTestCase(test.TrialTestCase):
(lambda t: t.find('./devices/emulator'), None)]), (lambda t: t.find('./devices/emulator'), None)]),
'uml': ('uml:///system', 'uml': ('uml:///system',
[(lambda t: t.find('.').get('type'), 'uml'), [(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 = [ common_checks = [
(lambda t: t.find('.').tag, 'domain'), (lambda t: t.find('.').tag, 'domain'),
(lambda t: t.find('./devices/interface/filterref/parameter').\ (lambda t: t.find(
get('name'), 'IP'), './devices/interface/filterref/parameter').get('name'), 'IP'),
(lambda t: t.find('./devices/interface/filterref/parameter').\ (lambda t: t.find(
get('value'), '10.11.12.13')] './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(): for (libvirt_type, (expected_uri, checks)) in type_uri_map.iteritems():
FLAGS.libvirt_type = libvirt_type FLAGS.libvirt_type = libvirt_type
conn = libvirt_conn.LibvirtConnection(True) conn = libvirt_conn.LibvirtConnection(True)
uri, _template, _rescue = conn.get_uri_and_templates() uri = conn.get_uri()
self.assertEquals(uri, expected_uri) self.assertEquals(uri, expected_uri)
xml = conn.to_xml(instance_ref) xml = conn.to_xml(instance_ref, rescue)
tree = xml_to_tree(xml) tree = xml_to_tree(xml)
for i, (check, expected_result) in enumerate(checks): for i, (check, expected_result) in enumerate(checks):
self.assertEqual(check(tree), self.assertEqual(check(tree),
@@ -106,6 +188,9 @@ class LibvirtConnTestCase(test.TrialTestCase):
expected_result, expected_result,
'%s failed common check %d' % (xml, i)) '%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 # Deliberately not just assigning this string to FLAGS.libvirt_uri and
# checking against that later on. This way we make sure the # checking against that later on. This way we make sure the
# implementation doesn't fiddle around with the FLAGS. # implementation doesn't fiddle around with the FLAGS.
@@ -114,7 +199,7 @@ class LibvirtConnTestCase(test.TrialTestCase):
for (libvirt_type, (expected_uri, checks)) in type_uri_map.iteritems(): for (libvirt_type, (expected_uri, checks)) in type_uri_map.iteritems():
FLAGS.libvirt_type = libvirt_type FLAGS.libvirt_type = libvirt_type
conn = libvirt_conn.LibvirtConnection(True) conn = libvirt_conn.LibvirtConnection(True)
uri, _template, _rescue = conn.get_uri_and_templates() uri = conn.get_uri()
self.assertEquals(uri, testuri) self.assertEquals(uri, testuri)
def tearDown(self): def tearDown(self):
@@ -123,7 +208,7 @@ class LibvirtConnTestCase(test.TrialTestCase):
self.manager.delete_user(self.user) self.manager.delete_user(self.user)
class NWFilterTestCase(test.TrialTestCase): class NWFilterTestCase(test.TestCase):
def setUp(self): def setUp(self):
super(NWFilterTestCase, self).setUp() super(NWFilterTestCase, self).setUp()
@@ -235,7 +320,7 @@ class NWFilterTestCase(test.TrialTestCase):
'project_id': 'fake'}) 'project_id': 'fake'})
inst_id = instance_ref['id'] inst_id = instance_ref['id']
def _ensure_all_called(_): def _ensure_all_called():
instance_filter = 'nova-instance-%s' % instance_ref['name'] instance_filter = 'nova-instance-%s' % instance_ref['name']
secgroup_filter = 'nova-secgroup-%s' % self.security_group['id'] secgroup_filter = 'nova-secgroup-%s' % self.security_group['id']
for required in [secgroup_filter, 'allow-dhcp-server', for required in [secgroup_filter, 'allow-dhcp-server',
@@ -252,8 +337,7 @@ class NWFilterTestCase(test.TrialTestCase):
self.security_group.id) self.security_group.id)
instance = db.instance_get(self.context, inst_id) instance = db.instance_get(self.context, inst_id)
d = self.fw.setup_nwfilters_for_instance(instance) self.fw.setup_base_nwfilters()
d.addCallback(_ensure_all_called) self.fw.setup_nwfilters_for_instance(instance)
d.addCallback(lambda _: self.teardown_security_group()) _ensure_all_called()
self.teardown_security_group()
return d

View File

@@ -21,8 +21,6 @@ Tests for Volume Code.
""" """
import logging import logging
from twisted.internet import defer
from nova import context from nova import context
from nova import exception from nova import exception
from nova import db from nova import db
@@ -33,7 +31,7 @@ from nova import utils
FLAGS = flags.FLAGS FLAGS = flags.FLAGS
class VolumeTestCase(test.TrialTestCase): class VolumeTestCase(test.TestCase):
"""Test Case for volumes.""" """Test Case for volumes."""
def setUp(self): def setUp(self):
@@ -56,51 +54,48 @@ class VolumeTestCase(test.TrialTestCase):
vol['attach_status'] = "detached" vol['attach_status'] = "detached"
return db.volume_create(context.get_admin_context(), vol)['id'] return db.volume_create(context.get_admin_context(), vol)['id']
@defer.inlineCallbacks
def test_create_delete_volume(self): def test_create_delete_volume(self):
"""Test volume can be created and deleted.""" """Test volume can be created and deleted."""
volume_id = self._create_volume() volume_id = self._create_volume()
yield self.volume.create_volume(self.context, volume_id) self.volume.create_volume(self.context, volume_id)
self.assertEqual(volume_id, db.volume_get(context.get_admin_context(), self.assertEqual(volume_id, db.volume_get(context.get_admin_context(),
volume_id).id) volume_id).id)
yield self.volume.delete_volume(self.context, volume_id) self.volume.delete_volume(self.context, volume_id)
self.assertRaises(exception.NotFound, self.assertRaises(exception.NotFound,
db.volume_get, db.volume_get,
self.context, self.context,
volume_id) volume_id)
@defer.inlineCallbacks
def test_too_big_volume(self): def test_too_big_volume(self):
"""Ensure failure if a too large of a volume is requested.""" """Ensure failure if a too large of a volume is requested."""
# FIXME(vish): validation needs to move into the data layer in # FIXME(vish): validation needs to move into the data layer in
# volume_create # volume_create
defer.returnValue(True) return True
try: try:
volume_id = self._create_volume('1001') volume_id = self._create_volume('1001')
yield self.volume.create_volume(self.context, volume_id) self.volume.create_volume(self.context, volume_id)
self.fail("Should have thrown TypeError") self.fail("Should have thrown TypeError")
except TypeError: except TypeError:
pass pass
@defer.inlineCallbacks
def test_too_many_volumes(self): def test_too_many_volumes(self):
"""Ensure that NoMoreTargets is raised when we run out of volumes.""" """Ensure that NoMoreTargets is raised when we run out of volumes."""
vols = [] vols = []
total_slots = FLAGS.iscsi_num_targets total_slots = FLAGS.iscsi_num_targets
for _index in xrange(total_slots): for _index in xrange(total_slots):
volume_id = self._create_volume() volume_id = self._create_volume()
yield self.volume.create_volume(self.context, volume_id) self.volume.create_volume(self.context, volume_id)
vols.append(volume_id) vols.append(volume_id)
volume_id = self._create_volume() volume_id = self._create_volume()
self.assertFailure(self.volume.create_volume(self.context, self.assertRaises(db.NoMoreTargets,
volume_id), self.volume.create_volume,
db.NoMoreTargets) self.context,
volume_id)
db.volume_destroy(context.get_admin_context(), volume_id) db.volume_destroy(context.get_admin_context(), volume_id)
for volume_id in vols: for volume_id in vols:
yield self.volume.delete_volume(self.context, volume_id) self.volume.delete_volume(self.context, volume_id)
@defer.inlineCallbacks
def test_run_attach_detach_volume(self): def test_run_attach_detach_volume(self):
"""Make sure volume can be attached and detached from instance.""" """Make sure volume can be attached and detached from instance."""
inst = {} inst = {}
@@ -115,15 +110,15 @@ class VolumeTestCase(test.TrialTestCase):
instance_id = db.instance_create(self.context, inst)['id'] instance_id = db.instance_create(self.context, inst)['id']
mountpoint = "/dev/sdf" mountpoint = "/dev/sdf"
volume_id = self._create_volume() volume_id = self._create_volume()
yield self.volume.create_volume(self.context, volume_id) self.volume.create_volume(self.context, volume_id)
if FLAGS.fake_tests: if FLAGS.fake_tests:
db.volume_attached(self.context, volume_id, instance_id, db.volume_attached(self.context, volume_id, instance_id,
mountpoint) mountpoint)
else: else:
yield self.compute.attach_volume(self.context, self.compute.attach_volume(self.context,
instance_id, instance_id,
volume_id, volume_id,
mountpoint) mountpoint)
vol = db.volume_get(context.get_admin_context(), volume_id) vol = db.volume_get(context.get_admin_context(), volume_id)
self.assertEqual(vol['status'], "in-use") self.assertEqual(vol['status'], "in-use")
self.assertEqual(vol['attach_status'], "attached") self.assertEqual(vol['attach_status'], "attached")
@@ -131,25 +126,26 @@ class VolumeTestCase(test.TrialTestCase):
instance_ref = db.volume_get_instance(self.context, volume_id) instance_ref = db.volume_get_instance(self.context, volume_id)
self.assertEqual(instance_ref['id'], instance_id) self.assertEqual(instance_ref['id'], instance_id)
self.assertFailure(self.volume.delete_volume(self.context, volume_id), self.assertRaises(exception.Error,
exception.Error) self.volume.delete_volume,
self.context,
volume_id)
if FLAGS.fake_tests: if FLAGS.fake_tests:
db.volume_detached(self.context, volume_id) db.volume_detached(self.context, volume_id)
else: else:
yield self.compute.detach_volume(self.context, self.compute.detach_volume(self.context,
instance_id, instance_id,
volume_id) volume_id)
vol = db.volume_get(self.context, volume_id) vol = db.volume_get(self.context, volume_id)
self.assertEqual(vol['status'], "available") self.assertEqual(vol['status'], "available")
yield self.volume.delete_volume(self.context, volume_id) self.volume.delete_volume(self.context, volume_id)
self.assertRaises(exception.Error, self.assertRaises(exception.Error,
db.volume_get, db.volume_get,
self.context, self.context,
volume_id) volume_id)
db.instance_destroy(self.context, instance_id) db.instance_destroy(self.context, instance_id)
@defer.inlineCallbacks
def test_concurrent_volumes_get_different_targets(self): def test_concurrent_volumes_get_different_targets(self):
"""Ensure multiple concurrent volumes get different targets.""" """Ensure multiple concurrent volumes get different targets."""
volume_ids = [] volume_ids = []
@@ -164,15 +160,11 @@ class VolumeTestCase(test.TrialTestCase):
self.assert_(iscsi_target not in targets) self.assert_(iscsi_target not in targets)
targets.append(iscsi_target) targets.append(iscsi_target)
logging.debug("Target %s allocated", iscsi_target) logging.debug("Target %s allocated", iscsi_target)
deferreds = []
total_slots = FLAGS.iscsi_num_targets total_slots = FLAGS.iscsi_num_targets
for _index in xrange(total_slots): for _index in xrange(total_slots):
volume_id = self._create_volume() volume_id = self._create_volume()
d = self.volume.create_volume(self.context, volume_id) d = self.volume.create_volume(self.context, volume_id)
d.addCallback(_check) _check(d)
d.addErrback(self.fail)
deferreds.append(d)
yield defer.DeferredList(deferreds)
for volume_id in volume_ids: for volume_id in volume_ids:
self.volume.delete_volume(self.context, volume_id) self.volume.delete_volume(self.context, volume_id)

264
nova/tests/test_xenapi.py Normal file
View File

@@ -0,0 +1,264 @@
# 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 as xenapi_fake
from nova.virt.xenapi import volume_utils
from nova.tests.db import fakes as db_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'
db_fakes.stub_out_db_instance_api(self.stubs)
stubs.stub_out_get_target(self.stubs)
xenapi_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 = xenapi_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)
xenapi_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 = xenapi_fake.get_all('VM')
# Get XenAPI record for VBD
vbds = xenapi_fake.get_all('VBD')
vbd = xenapi_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)
xenapi_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'
xenapi_fake.reset()
db_fakes.stub_out_db_instance_api(self.stubs)
xenapi_fake.create_network('fake', FLAGS.flat_network_bridge)
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
self.conn = xenapi_conn.get_connection(False)
def test_list_instances_0(self):
instances = self.conn.list_instances()
self.assertEquals(instances, [])
def test_get_diagnostics(self):
instance = self._create_instance()
self.conn.get_diagnostics(instance)
def test_instance_snapshot(self):
stubs.stubout_instance_snapshot(self.stubs)
instance = self._create_instance()
name = "MySnapshot"
template_vm_ref = self.conn.snapshot(instance, name)
def ensure_vm_was_torn_down():
vm_labels = []
for vm_ref in xenapi_fake.get_all('VM'):
vm_rec = xenapi_fake.get_record('VM', vm_ref)
if not vm_rec["is_control_domain"]:
vm_labels.append(vm_rec["name_label"])
self.assertEquals(vm_labels, [1])
def ensure_vbd_was_torn_down():
vbd_labels = []
for vbd_ref in xenapi_fake.get_all('VBD'):
vbd_rec = xenapi_fake.get_record('VBD', vbd_ref)
vbd_labels.append(vbd_rec["vm_name_label"])
self.assertEquals(vbd_labels, [1])
def ensure_vdi_was_torn_down():
for vdi_ref in xenapi_fake.get_all('VDI'):
vdi_rec = xenapi_fake.get_record('VDI', vdi_ref)
name_label = vdi_rec["name_label"]
self.assert_(not name_label.endswith('snapshot'))
def check():
ensure_vm_was_torn_down()
ensure_vbd_was_torn_down()
ensure_vdi_was_torn_down()
check()
def test_spawn(self):
instance = self._create_instance()
def check():
instances = self.conn.list_instances()
self.assertEquals(instances, [1])
# Get Nova record for VM
vm_info = self.conn.get_info(1)
# Get XenAPI record for VM
vms = xenapi_fake.get_all('VM')
vm = xenapi_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()
def _create_instance(self):
"""Creates and spawns a test instance"""
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'
}
instance = db.instance_create(values)
self.conn.spawn(instance)
return instance

View 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
=================================
"""

169
nova/tests/xenapi/stubs.py Normal file
View File

@@ -0,0 +1,169 @@
# 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
from nova.virt.xenapi import volume_utils
from nova.virt.xenapi import vm_utils
def stubout_instance_snapshot(stubs):
@classmethod
def fake_fetch_image(cls, session, instance_id, image, user, project,
type):
# Stubout wait_for_task
def fake_wait_for_task(self, id, task):
class FakeEvent:
def send(self, value):
self.rv = value
def wait(self):
return self.rv
done = FakeEvent()
self._poll_task(id, task, done)
rv = done.wait()
return rv
stubs.Set(xenapi_conn.XenAPISession, 'wait_for_task',
fake_wait_for_task)
from nova.virt.xenapi.fake import create_vdi
name_label = "instance-%s" % instance_id
#TODO: create fake SR record
sr_ref = "fakesr"
vdi_ref = create_vdi(name_label=name_label, read_only=False,
sr_ref=sr_ref, sharable=False)
vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref)
vdi_uuid = vdi_rec['uuid']
return vdi_uuid
stubs.Set(vm_utils.VMHelper, 'fetch_image', fake_fetch_image)
def fake_parse_xmlrpc_value(val):
return val
stubs.Set(xenapi_conn, '_parse_xmlrpc_value', fake_parse_xmlrpc_value)
def fake_wait_for_vhd_coalesce(session, instance_id, sr_ref, vdi_ref,
original_parent_uuid):
#TODO(sirp): Should we actually fake out the data here
return "fakeparent"
stubs.Set(vm_utils, 'wait_for_vhd_coalesce', fake_wait_for_vhd_coalesce)
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)
def stub_out_get_target(stubs):
"""Stubs out _get_target in volume_utils"""
def fake_get_target(volume_id):
return (None, None)
stubs.Set(volume_utils, '_get_target', fake_get_target)
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
def VM_snapshot(self, session_ref, vm_ref, label):
status = "Running"
template_vm_ref = fake.create_vm(label, status, is_a_template=True,
is_control_domain=False)
sr_ref = "fakesr"
template_vdi_ref = fake.create_vdi(label, read_only=True,
sr_ref=sr_ref, sharable=False)
template_vbd_ref = fake.create_vbd(template_vm_ref, template_vdi_ref)
return template_vm_ref
def VDI_destroy(self, session_ref, vdi_ref):
fake.destroy_vdi(vdi_ref)
def VM_destroy(self, session_ref, vm_ref):
fake.destroy_vm(vm_ref)
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

View File

@@ -43,7 +43,7 @@ else:
FLAGS = flags.FLAGS 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)') '(will be prepended to $logfile)')
@@ -208,7 +208,7 @@ def stop(pidfile):
pid = None pid = None
if not pid: 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) sys.stderr.write(message % pidfile)
# Not an error in a restart # Not an error in a restart
return return
@@ -229,7 +229,7 @@ def stop(pidfile):
def serve(filename): def serve(filename):
logging.debug("Serving %s" % filename) logging.debug(_("Serving %s") % filename)
name = os.path.basename(filename) name = os.path.basename(filename)
OptionsClass = WrapTwistedOptions(TwistdServerOptions) OptionsClass = WrapTwistedOptions(TwistdServerOptions)
options = OptionsClass() options = OptionsClass()
@@ -284,7 +284,7 @@ def serve(filename):
else: else:
logging.getLogger().setLevel(logging.WARNING) logging.getLogger().setLevel(logging.WARNING)
logging.debug("Full set of FLAGS:") logging.debug(_("Full set of FLAGS:"))
for flag in FLAGS: for flag in FLAGS:
logging.debug("%s : %s" % (flag, FLAGS.get(flag, None))) logging.debug("%s : %s" % (flag, FLAGS.get(flag, None)))

View File

@@ -1,94 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Decorators for argument validation, courtesy of
http://rmi.net/~lutz/rangetest.html"""
def rangetest(**argchecks):
"""Validate ranges for both + defaults"""
def onDecorator(func):
"""onCall remembers func and argchecks"""
import sys
code = func.__code__ if sys.version_info[0] == 3 else func.func_code
allargs = code.co_varnames[:code.co_argcount]
funcname = func.__name__
def onCall(*pargs, **kargs):
# all pargs match first N args by position
# the rest must be in kargs or omitted defaults
positionals = list(allargs)
positionals = positionals[:len(pargs)]
for (argname, (low, high)) in argchecks.items():
# for all args to be checked
if argname in kargs:
# was passed by name
if float(kargs[argname]) < low or \
float(kargs[argname]) > high:
errmsg = '{0} argument "{1}" not in {2}..{3}'
errmsg = errmsg.format(funcname, argname, low, high)
raise TypeError(errmsg)
elif argname in positionals:
# was passed by position
position = positionals.index(argname)
if float(pargs[position]) < low or \
float(pargs[position]) > high:
errmsg = '{0} argument "{1}" with value of {4} ' \
'not in {2}..{3}'
errmsg = errmsg.format(funcname, argname, low, high,
pargs[position])
raise TypeError(errmsg)
else:
pass
return func(*pargs, **kargs) # okay: run original call
return onCall
return onDecorator
def typetest(**argchecks):
def onDecorator(func):
import sys
code = func.__code__ if sys.version_info[0] == 3 else func.func_code
allargs = code.co_varnames[:code.co_argcount]
funcname = func.__name__
def onCall(*pargs, **kargs):
positionals = list(allargs)[:len(pargs)]
for (argname, typeof) in argchecks.items():
if argname in kargs:
if not isinstance(kargs[argname], typeof):
errmsg = '{0} argument "{1}" not of type {2}'
errmsg = errmsg.format(funcname, argname, typeof)
raise TypeError(errmsg)
elif argname in positionals:
position = positionals.index(argname)
if not isinstance(pargs[position], typeof):
errmsg = '{0} argument "{1}" with value of {2} ' \
'not of type {3}'
errmsg = errmsg.format(funcname, argname,
pargs[position], typeof)
raise TypeError(errmsg)
else:
pass
return func(*pargs, **kargs)
return onCall
return onDecorator

View File

@@ -1,122 +1,68 @@
#!/usr/bin/env python
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the # Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration. # Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License");
# not use this file except in compliance with the License. You may obtain # you may not use this file except in compliance with the License.
# a copy of the License at # You may obtain a copy of the License at
# #
# http://www.apache.org/licenses/LICENSE-2.0 # http://www.apache.org/licenses/LICENSE-2.0
# #
# Unless required by applicable law or agreed to in writing, software # Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # distributed under the License is distributed on an "AS IS" BASIS,
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# License for the specific language governing permissions and limitations # See the License for the specific language governing permissions and
# under the License. # limitations under the License.
"""
This is our basic test running framework based on Twisted's Trial.
Usage Examples:
# to run all the tests
python run_tests.py
# to run a specific test suite imported here
python run_tests.py NodeConnectionTestCase
# to run a specific test imported here
python run_tests.py NodeConnectionTestCase.test_reboot
# to run some test suites elsewhere
python run_tests.py nova.tests.node_unittest
python run_tests.py nova.tests.node_unittest.NodeConnectionTestCase
Due to our use of multiprocessing it we frequently get some ignorable
'Interrupted system call' exceptions after test completion.
"""
import __main__
import os import os
import unittest
import sys import sys
from twisted.scripts import trial as trial_script from nose import config
from nose import result
from nova import flags from nose import core
from nova import twistd
from nova.tests.access_unittest import *
from nova.tests.api_unittest import *
from nova.tests.auth_unittest import *
from nova.tests.cloud_unittest import *
from nova.tests.compute_unittest import *
from nova.tests.flags_unittest import *
from nova.tests.misc_unittest import *
from nova.tests.network_unittest import *
from nova.tests.objectstore_unittest import *
from nova.tests.process_unittest import *
from nova.tests.quota_unittest import *
from nova.tests.rpc_unittest import *
from nova.tests.scheduler_unittest import *
from nova.tests.service_unittest import *
from nova.tests.twistd_unittest import *
from nova.tests.validator_unittest import *
from nova.tests.virt_unittest import *
from nova.tests.virt_unittest import *
from nova.tests.volume_unittest import *
FLAGS = flags.FLAGS class NovaTestResult(result.TextTestResult):
flags.DEFINE_bool('flush_db', True, def __init__(self, *args, **kw):
'Flush the database before running fake tests') result.TextTestResult.__init__(self, *args, **kw)
flags.DEFINE_string('tests_stderr', 'run_tests.err.log', self._last_case = None
'Path to where to pipe STDERR during test runs.'
' Default = "run_tests.err.log"') def getDescription(self, test):
return str(test)
def startTest(self, test):
unittest.TestResult.startTest(self, test)
current_case = test.test.__class__.__name__
if self.showAll:
if current_case != self._last_case:
self.stream.writeln(current_case)
self._last_case = current_case
self.stream.write(
' %s' % str(test.test._testMethodName).ljust(60))
self.stream.flush()
class NovaTestRunner(core.TextTestRunner):
def _makeResult(self):
return NovaTestResult(self.stream,
self.descriptions,
self.verbosity,
self.config)
if __name__ == '__main__': if __name__ == '__main__':
OptionsClass = twistd.WrapTwistedOptions(trial_script.Options) c = config.Config(stream=sys.stdout,
config = OptionsClass() env=os.environ,
argv = config.parseOptions() verbosity=3)
FLAGS.verbose = True runner = NovaTestRunner(stream=c.stream,
verbosity=c.verbosity,
# TODO(termie): these should make a call instead of doing work on import config=c)
if FLAGS.fake_tests: sys.exit(not core.run(config=c, testRunner=runner))
from nova.tests.fake_flags import *
else:
from nova.tests.real_flags import *
# Establish redirect for STDERR
sys.stderr.flush()
err = open(FLAGS.tests_stderr, 'w+', 0)
os.dup2(err.fileno(), sys.stderr.fileno())
if len(argv) == 1 and len(config['tests']) == 0:
# If no tests were specified run the ones imported in this file
# NOTE(termie): "tests" is not a flag, just some Trial related stuff
config['tests'].update(['__main__'])
elif len(config['tests']):
# If we specified tests check first whether they are in __main__
for arg in config['tests']:
key = arg.split('.')[0]
if hasattr(__main__, key):
config['tests'].remove(arg)
config['tests'].add('__main__.%s' % arg)
trial_script._initialDebugSetup(config)
trialRunner = trial_script._makeRunner(config)
suite = trial_script._getSuite(config)
if config['until-failure']:
test_result = trialRunner.runUntilFailure(suite)
else:
test_result = trialRunner.run(suite)
if config.tracer:
sys.settrace(None)
results = config.tracer.results()
results.write_results(show_missing=1, summary=False,
coverdir=config.coverdir)
sys.exit(not test_result.wasSuccessful())