Merge from trunk: process replaced with util
This commit is contained in:
@@ -6,6 +6,7 @@ keys
|
||||
networks
|
||||
nova.sqlite
|
||||
CA/cacert.pem
|
||||
CA/crl.pem
|
||||
CA/index.txt*
|
||||
CA/openssl.cnf
|
||||
CA/serial*
|
||||
|
||||
11
.mailmap
11
.mailmap
@@ -19,11 +19,14 @@
|
||||
<mordred@inaugust.com> <mordred@hudson>
|
||||
<paul@openstack.org> <pvoccio@castor.local>
|
||||
<paul@openstack.org> <paul.voccio@rackspace.com>
|
||||
<soren.hansen@rackspace.com> <soren@linux2go.dk>
|
||||
<todd@ansolabs.com> <todd@lapex>
|
||||
<todd@ansolabs.com> <todd@rubidine.com>
|
||||
<vishvananda@gmail.com> <vishvananda@yahoo.com>
|
||||
<vishvananda@gmail.com> <root@mirror.nasanebula.net>
|
||||
# These are from people who failed to set a proper committer
|
||||
. <root@tonbuntu>
|
||||
. <laner@controller>
|
||||
. <root@ubuntu>
|
||||
<vishvananda@gmail.com> <root@ubuntu>
|
||||
<sleepsonthefloor@gmail.com> <root@tonbuntu>
|
||||
<rlane@wikimedia.org> <laner@controller>
|
||||
<rconradharris@gmail.com> <rick.harris@rackspace.com>
|
||||
<corywright@gmail.com> <cory.wright@rackspace.com>
|
||||
<ant@openstack.org> <amesserl@rackspace.com>
|
||||
|
||||
11
Authors
11
Authors
@@ -1,11 +1,16 @@
|
||||
Andy Smith <code@term.ie>
|
||||
Anne Gentle <anne@openstack.org>
|
||||
Anthony Young <sleepsonthefloor@gmail.com>
|
||||
Antony Messerli <ant@openstack.org>
|
||||
Armando Migliaccio <Armando.Migliaccio@eu.citrix.com>
|
||||
Chris Behrens <cbehrens@codestud.com>
|
||||
Chmouel Boudjnah <chmouel@chmouel.com>
|
||||
Cory Wright <corywright@gmail.com>
|
||||
David Pravec <David.Pravec@danix.org>
|
||||
Dean Troyer <dtroyer@gmail.com>
|
||||
Devin Carlen <devin.carlen@gmail.com>
|
||||
Ed Leafe <ed@leafe.com>
|
||||
Eldar Nugaev <enugaev@griddynamics.com>
|
||||
Eric Day <eday@oddments.org>
|
||||
Ewan Mellor <ewan.mellor@citrix.com>
|
||||
Hisaki Ohara <hisaki.ohara@intel.com>
|
||||
@@ -13,6 +18,7 @@ Jay Pipes <jaypipes@gmail.com>
|
||||
Jesse Andrews <anotherjesse@gmail.com>
|
||||
Joe Heck <heckj@mac.com>
|
||||
Joel Moore <joelbm24@gmail.com>
|
||||
Jonathan Bryce <jbryce@jbryce.com>
|
||||
Josh Kearney <josh.kearney@rackspace.com>
|
||||
Joshua McKenty <jmckenty@gmail.com>
|
||||
Justin Santa Barbara <justin@fathomdb.com>
|
||||
@@ -21,11 +27,16 @@ Michael Gundlach <michael.gundlach@rackspace.com>
|
||||
Monty Taylor <mordred@inaugust.com>
|
||||
Paul Voccio <paul@openstack.org>
|
||||
Rick Clark <rick@openstack.org>
|
||||
Rick Harris <rconradharris@gmail.com>
|
||||
Ryan Lane <rlane@wikimedia.org>
|
||||
Ryan Lucio <rlucio@internap.com>
|
||||
Salvatore Orlando <salvatore.orlando@eu.citrix.com>
|
||||
Sandy Walsh <sandy.walsh@rackspace.com>
|
||||
Soren Hansen <soren.hansen@rackspace.com>
|
||||
Thierry Carrez <thierry@openstack.org>
|
||||
Todd Willey <todd@ansolabs.com>
|
||||
Trey Morris <trey.morris@rackspace.com>
|
||||
Vishvananda Ishaya <vishvananda@gmail.com>
|
||||
Youcef Laribi <Youcef.Laribi@eu.citrix.com>
|
||||
Zhixue Wu <Zhixue.Wu@citrix.com>
|
||||
|
||||
|
||||
@@ -16,16 +16,24 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
# ARG is the id of the user
|
||||
export SUBJ="/C=US/ST=California/L=MountainView/O=AnsoLabs/OU=NovaDev/CN=customer-intCA-$1"
|
||||
mkdir INTER/$1
|
||||
cd INTER/$1
|
||||
# $1 is the id of the project and $2 is the subject of the cert
|
||||
NAME=$1
|
||||
SUBJ=$2
|
||||
mkdir -p projects/$NAME
|
||||
cd projects/$NAME
|
||||
cp ../../openssl.cnf.tmpl openssl.cnf
|
||||
sed -i -e s/%USERNAME%/$1/g openssl.cnf
|
||||
sed -i -e s/%USERNAME%/$NAME/g openssl.cnf
|
||||
mkdir certs crl newcerts private
|
||||
openssl req -new -x509 -extensions v3_ca -keyout private/cakey.pem -out cacert.pem -days 365 -config ./openssl.cnf -batch -nodes
|
||||
echo "10" > serial
|
||||
touch index.txt
|
||||
openssl genrsa -out private/cakey.pem 1024 -config ./openssl.cnf -batch -nodes
|
||||
openssl req -new -sha2 -key private/cakey.pem -out ../../reqs/inter$1.csr -batch -subj "$SUBJ"
|
||||
cd ../../
|
||||
openssl ca -extensions v3_ca -days 365 -out INTER/$1/cacert.pem -in reqs/inter$1.csr -config openssl.cnf -batch
|
||||
# NOTE(vish): Disabling intermediate ca's because we don't actually need them.
|
||||
# It makes more sense to have each project have its own root ca.
|
||||
# openssl genrsa -out private/cakey.pem 1024 -config ./openssl.cnf -batch -nodes
|
||||
# openssl req -new -sha256 -key private/cakey.pem -out ../../reqs/inter$NAME.csr -batch -subj "$SUBJ"
|
||||
openssl ca -gencrl -config ./openssl.cnf -out crl.pem
|
||||
if [ "`id -u`" != "`grep nova /etc/passwd | cut -d':' -f3`" ]; then
|
||||
sudo chown -R nova:nogroup .
|
||||
fi
|
||||
# cd ../../
|
||||
# openssl ca -extensions v3_ca -days 365 -out INTER/$NAME/cacert.pem -in reqs/inter$NAME.csr -config openssl.cnf -batch
|
||||
|
||||
@@ -25,4 +25,5 @@ else
|
||||
openssl req -new -x509 -extensions v3_ca -keyout private/cakey.pem -out cacert.pem -days 365 -config ./openssl.cnf -batch -nodes
|
||||
touch index.txt
|
||||
echo "10" > serial
|
||||
openssl ca -gencrl -config ./openssl.cnf -out crl.pem
|
||||
fi
|
||||
|
||||
34
nova/tests/validator_unittest.py → CA/genvpn.sh
Normal file → Executable file
34
nova/tests/validator_unittest.py → CA/genvpn.sh
Normal file → Executable file
@@ -1,3 +1,4 @@
|
||||
#!/bin/bash
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
@@ -16,27 +17,20 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
import unittest
|
||||
# This gets zipped and run on the cloudpipe-managed OpenVPN server
|
||||
NAME=$1
|
||||
SUBJ=$2
|
||||
|
||||
from nova import flags
|
||||
from nova import test
|
||||
from nova import validate
|
||||
mkdir -p projects/$NAME
|
||||
cd projects/$NAME
|
||||
|
||||
# generate a server priv key
|
||||
openssl genrsa -out server.key 2048
|
||||
|
||||
class ValidationTestCase(test.TrialTestCase):
|
||||
def setUp(self):
|
||||
super(ValidationTestCase, self).setUp()
|
||||
# generate a server CSR
|
||||
openssl req -new -key server.key -out server.csr -batch -subj "$SUBJ"
|
||||
|
||||
def tearDown(self):
|
||||
super(ValidationTestCase, self).tearDown()
|
||||
|
||||
def test_type_validation(self):
|
||||
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
|
||||
novauid=`getent passwd nova | awk -F: '{print $3}'`
|
||||
if [ ! -z "${novauid}" ] && [ "`id -u`" != "${novauid}" ]; then
|
||||
sudo chown -R nova:nogroup .
|
||||
fi
|
||||
@@ -24,7 +24,6 @@ dir = .
|
||||
|
||||
[ ca ]
|
||||
default_ca = CA_default
|
||||
unique_subject = no
|
||||
|
||||
[ CA_default ]
|
||||
serial = $dir/serial
|
||||
@@ -32,6 +31,8 @@ database = $dir/index.txt
|
||||
new_certs_dir = $dir/newcerts
|
||||
certificate = $dir/cacert.pem
|
||||
private_key = $dir/private/cakey.pem
|
||||
unique_subject = no
|
||||
default_crl_days = 365
|
||||
default_days = 365
|
||||
default_md = md5
|
||||
preserve = no
|
||||
|
||||
@@ -13,7 +13,7 @@ include nova/cloudpipe/client.ovpn.template
|
||||
include nova/compute/fakevirtinstance.xml
|
||||
include nova/compute/interfaces.template
|
||||
include nova/virt/interfaces.template
|
||||
include nova/virt/libvirt.*.xml.template
|
||||
include nova/virt/libvirt*.xml.template
|
||||
include nova/tests/CA/
|
||||
include nova/tests/CA/cacert.pem
|
||||
include nova/tests/CA/private/
|
||||
|
||||
23
bin/nova-api
23
bin/nova-api
@@ -17,10 +17,10 @@
|
||||
# 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.
|
||||
"""
|
||||
Nova API daemon.
|
||||
"""
|
||||
|
||||
"""Starter script for Nova API."""
|
||||
|
||||
import gettext
|
||||
import os
|
||||
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')):
|
||||
sys.path.insert(0, possible_topdir)
|
||||
|
||||
gettext.install('nova', unicode=1)
|
||||
|
||||
from nova import api
|
||||
from nova import flags
|
||||
from nova import utils
|
||||
from nova import server
|
||||
from nova import wsgi
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
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')
|
||||
|
||||
|
||||
def main(_args):
|
||||
from nova import api
|
||||
from nova import wsgi
|
||||
if __name__ == '__main__':
|
||||
utils.default_flagfile()
|
||||
FLAGS(sys.argv)
|
||||
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()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
utils.default_flagfile()
|
||||
server.serve('nova-api', main)
|
||||
|
||||
68
bin/nova-combined
Executable file
68
bin/nova-combined
Executable 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()
|
||||
@@ -17,10 +17,12 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Twistd daemon for the nova compute nodes.
|
||||
"""
|
||||
"""Starter script for Nova Compute."""
|
||||
|
||||
import eventlet
|
||||
eventlet.monkey_patch()
|
||||
|
||||
import gettext
|
||||
import os
|
||||
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')):
|
||||
sys.path.insert(0, possible_topdir)
|
||||
|
||||
from nova import service
|
||||
from nova import twistd
|
||||
from nova import utils
|
||||
gettext.install('nova', unicode=1)
|
||||
|
||||
from nova import service
|
||||
from nova import utils
|
||||
|
||||
if __name__ == '__main__':
|
||||
utils.default_flagfile()
|
||||
twistd.serve(__file__)
|
||||
|
||||
if __name__ == '__builtin__':
|
||||
application = service.Service.create() # pylint: disable=C0103
|
||||
service.serve()
|
||||
service.wait()
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
Handle lease database updates from DHCP servers.
|
||||
"""
|
||||
|
||||
import gettext
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
@@ -33,6 +34,8 @@ possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
|
||||
if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
|
||||
sys.path.insert(0, possible_topdir)
|
||||
|
||||
gettext.install('nova', unicode=1)
|
||||
|
||||
from nova import context
|
||||
from nova import db
|
||||
from nova import flags
|
||||
@@ -107,7 +110,6 @@ def main():
|
||||
FLAGS.num_networks = 5
|
||||
path = os.path.abspath(os.path.join(os.path.dirname(__file__),
|
||||
'..',
|
||||
'_trial_temp',
|
||||
'nova.sqlite'))
|
||||
FLAGS.sql_connection = 'sqlite:///%s' % path
|
||||
action = argv[1]
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
Download images from Canonical Image Store
|
||||
"""
|
||||
|
||||
import gettext
|
||||
import json
|
||||
import os
|
||||
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')):
|
||||
sys.path.insert(0, possible_topdir)
|
||||
|
||||
gettext.install('nova', unicode=1)
|
||||
|
||||
from nova import flags
|
||||
from nova import utils
|
||||
from nova.objectstore import image
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
Daemon for Nova RRD based instance resource monitoring.
|
||||
"""
|
||||
|
||||
import gettext
|
||||
import os
|
||||
import logging
|
||||
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')):
|
||||
sys.path.insert(0, possible_topdir)
|
||||
|
||||
gettext.install('nova', unicode=1)
|
||||
|
||||
from nova import utils
|
||||
from nova import twistd
|
||||
from nova.compute import monitor
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
CLI interface for nova management.
|
||||
"""
|
||||
|
||||
import gettext
|
||||
import logging
|
||||
import os
|
||||
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')):
|
||||
sys.path.insert(0, possible_topdir)
|
||||
|
||||
gettext.install('nova', unicode=1)
|
||||
|
||||
from nova import context
|
||||
from nova import crypto
|
||||
from nova import db
|
||||
from nova import exception
|
||||
from nova import flags
|
||||
@@ -93,47 +97,43 @@ class VpnCommands(object):
|
||||
self.manager = manager.AuthManager()
|
||||
self.pipe = pipelib.CloudPipe()
|
||||
|
||||
def list(self):
|
||||
"""Print a listing of the VPNs for all projects."""
|
||||
def list(self, project=None):
|
||||
"""Print a listing of the VPN data for one or all projects.
|
||||
|
||||
args: [project=all]"""
|
||||
print "%-12s\t" % 'project',
|
||||
print "%-20s\t" % 'ip:port',
|
||||
print "%-20s\t" % 'private_ip',
|
||||
print "%s" % 'state'
|
||||
for project in self.manager.get_projects():
|
||||
if project:
|
||||
projects = [self.manager.get_project(project)]
|
||||
else:
|
||||
projects = self.manager.get_projects()
|
||||
# NOTE(vish): This hits the database a lot. We could optimize
|
||||
# by getting all networks in one query and all vpns
|
||||
# in aother query, then doing lookups by project
|
||||
for project in projects:
|
||||
print "%-12s\t" % project.name,
|
||||
|
||||
try:
|
||||
s = "%s:%s" % (project.vpn_ip, project.vpn_port)
|
||||
except exception.NotFound:
|
||||
s = "None"
|
||||
print "%-20s\t" % s,
|
||||
|
||||
vpn = self._vpn_for(project.id)
|
||||
ipport = "%s:%s" % (project.vpn_ip, project.vpn_port)
|
||||
print "%-20s\t" % ipport,
|
||||
ctxt = context.get_admin_context()
|
||||
vpn = db.instance_get_project_vpn(ctxt, project.id)
|
||||
if vpn:
|
||||
command = "ping -c1 -w1 %s > /dev/null; echo $?"
|
||||
out, _err = utils.execute(command % vpn['private_dns_name'],
|
||||
check_exit_code=False)
|
||||
if out.strip() == '0':
|
||||
net = 'up'
|
||||
else:
|
||||
net = 'down'
|
||||
print vpn['private_dns_name'],
|
||||
print vpn['node_name'],
|
||||
print vpn['instance_id'],
|
||||
address = None
|
||||
state = 'down'
|
||||
if vpn.get('fixed_ip', None):
|
||||
address = vpn['fixed_ip']['address']
|
||||
if project.vpn_ip and utils.vpn_ping(project.vpn_ip,
|
||||
project.vpn_port):
|
||||
state = 'up'
|
||||
print address,
|
||||
print vpn['host'],
|
||||
print vpn['ec2_id'],
|
||||
print vpn['state_description'],
|
||||
print net
|
||||
|
||||
print state
|
||||
else:
|
||||
print None
|
||||
|
||||
def _vpn_for(self, project_id):
|
||||
"""Get the VPN instance for a project ID."""
|
||||
for instance in db.instance_get_all(context.get_admin_context()):
|
||||
if (instance['image_id'] == FLAGS.vpn_image_id
|
||||
and not instance['state_description'] in
|
||||
['shutting_down', 'shutdown']
|
||||
and instance['project_id'] == project_id):
|
||||
return instance
|
||||
|
||||
def spawn(self):
|
||||
"""Run all VPNs."""
|
||||
for p in reversed(self.manager.get_projects()):
|
||||
@@ -146,6 +146,21 @@ class VpnCommands(object):
|
||||
"""Start the VPN for a given project."""
|
||||
self.pipe.launch_vpn_instance(project_id)
|
||||
|
||||
def change(self, project_id, ip, port):
|
||||
"""Change the ip and port for a vpn.
|
||||
|
||||
args: project, ip, port"""
|
||||
project = self.manager.get_project(project_id)
|
||||
if not project:
|
||||
print 'No project %s' % (project_id)
|
||||
return
|
||||
admin = context.get_admin_context()
|
||||
network_ref = db.project_get_network(admin, project_id)
|
||||
db.network_update(admin,
|
||||
network_ref['id'],
|
||||
{'vpn_public_address': ip,
|
||||
'vpn_public_port': int(port)})
|
||||
|
||||
|
||||
class ShellCommands(object):
|
||||
def bpython(self):
|
||||
@@ -292,6 +307,14 @@ class UserCommands(object):
|
||||
is_admin = False
|
||||
self.manager.modify_user(name, access_key, secret_key, is_admin)
|
||||
|
||||
def revoke(self, user_id, project_id=None):
|
||||
"""revoke certs for a user
|
||||
arguments: user_id [project_id]"""
|
||||
if project_id:
|
||||
crypto.revoke_certs_by_user_and_project(user_id, project_id)
|
||||
else:
|
||||
crypto.revoke_certs_by_user(user_id)
|
||||
|
||||
|
||||
class ProjectCommands(object):
|
||||
"""Class for managing projects."""
|
||||
|
||||
@@ -17,10 +17,12 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Twistd daemon for the nova network nodes.
|
||||
"""
|
||||
"""Starter script for Nova Network."""
|
||||
|
||||
import eventlet
|
||||
eventlet.monkey_patch()
|
||||
|
||||
import gettext
|
||||
import os
|
||||
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')):
|
||||
sys.path.insert(0, possible_topdir)
|
||||
|
||||
from nova import service
|
||||
from nova import twistd
|
||||
from nova import utils
|
||||
gettext.install('nova', unicode=1)
|
||||
|
||||
from nova import service
|
||||
from nova import utils
|
||||
|
||||
if __name__ == '__main__':
|
||||
utils.default_flagfile()
|
||||
twistd.serve(__file__)
|
||||
|
||||
if __name__ == '__builtin__':
|
||||
application = service.Service.create() # pylint: disable-msg=C0103
|
||||
service.serve()
|
||||
service.wait()
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
Twisted daemon for nova objectstore. Supports S3 API.
|
||||
"""
|
||||
|
||||
import gettext
|
||||
import os
|
||||
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')):
|
||||
sys.path.insert(0, possible_topdir)
|
||||
|
||||
gettext.install('nova', unicode=1)
|
||||
|
||||
from nova import flags
|
||||
from nova import utils
|
||||
from nova import twistd
|
||||
|
||||
@@ -17,10 +17,12 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Twistd daemon for the nova scheduler nodes.
|
||||
"""
|
||||
"""Starter script for Nova Scheduler."""
|
||||
|
||||
import eventlet
|
||||
eventlet.monkey_patch()
|
||||
|
||||
import gettext
|
||||
import os
|
||||
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')):
|
||||
sys.path.insert(0, possible_topdir)
|
||||
|
||||
from nova import service
|
||||
from nova import twistd
|
||||
from nova import utils
|
||||
gettext.install('nova', unicode=1)
|
||||
|
||||
from nova import service
|
||||
from nova import utils
|
||||
|
||||
if __name__ == '__main__':
|
||||
utils.default_flagfile()
|
||||
twistd.serve(__file__)
|
||||
|
||||
if __name__ == '__builtin__':
|
||||
application = service.Service.create()
|
||||
service.serve()
|
||||
service.wait()
|
||||
|
||||
@@ -17,10 +17,12 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Twistd daemon for the nova volume nodes.
|
||||
"""
|
||||
"""Starter script for Nova Volume."""
|
||||
|
||||
import eventlet
|
||||
eventlet.monkey_patch()
|
||||
|
||||
import gettext
|
||||
import os
|
||||
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')):
|
||||
sys.path.insert(0, possible_topdir)
|
||||
|
||||
from nova import service
|
||||
from nova import twistd
|
||||
from nova import utils
|
||||
gettext.install('nova', unicode=1)
|
||||
|
||||
from nova import service
|
||||
from nova import utils
|
||||
|
||||
if __name__ == '__main__':
|
||||
utils.default_flagfile()
|
||||
twistd.serve(__file__)
|
||||
|
||||
if __name__ == '__builtin__':
|
||||
application = service.Service.create() # pylint: disable-msg=C0103
|
||||
service.serve()
|
||||
service.wait()
|
||||
|
||||
@@ -194,6 +194,7 @@ class HostInfo(object):
|
||||
|
||||
|
||||
class NovaAdminClient(object):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
clc_url=DEFAULT_CLC_URL,
|
||||
|
||||
@@ -37,7 +37,6 @@ class DbDriver(object):
|
||||
def __init__(self):
|
||||
"""Imports the LDAP module"""
|
||||
pass
|
||||
db
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
@@ -83,7 +82,7 @@ class DbDriver(object):
|
||||
user_ref = db.user_create(context.get_admin_context(), values)
|
||||
return self._db_user_to_auth_user(user_ref)
|
||||
except exception.Duplicate, e:
|
||||
raise exception.Duplicate('User %s already exists' % name)
|
||||
raise exception.Duplicate(_('User %s already exists') % name)
|
||||
|
||||
def _db_user_to_auth_user(self, user_ref):
|
||||
return {'id': user_ref['id'],
|
||||
@@ -105,8 +104,9 @@ class DbDriver(object):
|
||||
"""Create a project"""
|
||||
manager = db.user_get(context.get_admin_context(), manager_uid)
|
||||
if not manager:
|
||||
raise exception.NotFound("Project can't be created because "
|
||||
"manager %s doesn't exist" % manager_uid)
|
||||
raise exception.NotFound(_("Project can't be created because "
|
||||
"manager %s doesn't exist")
|
||||
% manager_uid)
|
||||
|
||||
# description is a required attribute
|
||||
if description is None:
|
||||
@@ -133,8 +133,8 @@ class DbDriver(object):
|
||||
try:
|
||||
project = db.project_create(context.get_admin_context(), values)
|
||||
except exception.Duplicate:
|
||||
raise exception.Duplicate("Project can't be created because "
|
||||
"project %s already exists" % name)
|
||||
raise exception.Duplicate(_("Project can't be created because "
|
||||
"project %s already exists") % name)
|
||||
|
||||
for member in members:
|
||||
db.project_add_member(context.get_admin_context(),
|
||||
@@ -155,8 +155,8 @@ class DbDriver(object):
|
||||
if manager_uid:
|
||||
manager = db.user_get(context.get_admin_context(), manager_uid)
|
||||
if not manager:
|
||||
raise exception.NotFound("Project can't be modified because "
|
||||
"manager %s doesn't exist" %
|
||||
raise exception.NotFound(_("Project can't be modified because "
|
||||
"manager %s doesn't exist") %
|
||||
manager_uid)
|
||||
values['project_manager'] = manager['id']
|
||||
if description:
|
||||
@@ -243,8 +243,8 @@ class DbDriver(object):
|
||||
def _validate_user_and_project(self, user_id, project_id):
|
||||
user = db.user_get(context.get_admin_context(), user_id)
|
||||
if not user:
|
||||
raise exception.NotFound('User "%s" not found' % user_id)
|
||||
raise exception.NotFound(_('User "%s" not found') % user_id)
|
||||
project = db.project_get(context.get_admin_context(), project_id)
|
||||
if not project:
|
||||
raise exception.NotFound('Project "%s" not found' % project_id)
|
||||
raise exception.NotFound(_('Project "%s" not found') % project_id)
|
||||
return user, project
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""Fake LDAP server for test harness, backs to ReDIS.
|
||||
"""Fake LDAP server for test harness.
|
||||
|
||||
This class does very little error checking, and knows nothing about ldap
|
||||
class definitions. It implements the minimum emulation of the python ldap
|
||||
@@ -23,34 +23,65 @@ library to work with nova.
|
||||
|
||||
"""
|
||||
|
||||
import fnmatch
|
||||
import json
|
||||
import redis
|
||||
|
||||
from nova import flags
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
flags.DEFINE_string('redis_host', '127.0.0.1',
|
||||
'Host that redis is running on.')
|
||||
flags.DEFINE_integer('redis_port', 6379,
|
||||
'Port that redis is running on.')
|
||||
flags.DEFINE_integer('redis_db', 0, 'Multiple DB keeps tests away')
|
||||
|
||||
|
||||
class Redis(object):
|
||||
class Store(object):
|
||||
def __init__(self):
|
||||
if hasattr(self.__class__, '_instance'):
|
||||
raise Exception('Attempted to instantiate singleton')
|
||||
raise Exception(_('Attempted to instantiate singleton'))
|
||||
|
||||
@classmethod
|
||||
def instance(cls):
|
||||
if not hasattr(cls, '_instance'):
|
||||
inst = redis.Redis(host=FLAGS.redis_host,
|
||||
port=FLAGS.redis_port,
|
||||
db=FLAGS.redis_db)
|
||||
cls._instance = inst
|
||||
cls._instance = _StorageDict()
|
||||
return cls._instance
|
||||
|
||||
|
||||
class _StorageDict(dict):
|
||||
def keys(self, pat=None):
|
||||
ret = super(_StorageDict, self).keys()
|
||||
if pat is not None:
|
||||
ret = fnmatch.filter(ret, pat)
|
||||
return ret
|
||||
|
||||
def delete(self, key):
|
||||
try:
|
||||
del self[key]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def flushdb(self):
|
||||
self.clear()
|
||||
|
||||
def hgetall(self, key):
|
||||
"""Returns the hash for the given key; creates
|
||||
the hash if the key doesn't exist."""
|
||||
try:
|
||||
return self[key]
|
||||
except KeyError:
|
||||
self[key] = {}
|
||||
return self[key]
|
||||
|
||||
def hget(self, key, field):
|
||||
hashdict = self.hgetall(key)
|
||||
try:
|
||||
return hashdict[field]
|
||||
except KeyError:
|
||||
hashdict[field] = {}
|
||||
return hashdict[field]
|
||||
|
||||
def hset(self, key, field, val):
|
||||
hashdict = self.hgetall(key)
|
||||
hashdict[field] = val
|
||||
|
||||
def hmset(self, key, value_dict):
|
||||
hashdict = self.hgetall(key)
|
||||
for field, val in value_dict.items():
|
||||
hashdict[field] = val
|
||||
|
||||
|
||||
SCOPE_BASE = 0
|
||||
SCOPE_ONELEVEL = 1 # Not implemented
|
||||
SCOPE_SUBTREE = 2
|
||||
@@ -119,6 +150,9 @@ def _match(key, value, attrs):
|
||||
"""Match a given key and value against an attribute list."""
|
||||
if key not in attrs:
|
||||
return False
|
||||
# This is a wild card search. Implemented as all or nothing for now.
|
||||
if value == "*":
|
||||
return True
|
||||
if key != "objectclass":
|
||||
return value in attrs[key]
|
||||
# it is an objectclass check, so check subclasses
|
||||
@@ -169,8 +203,6 @@ def _to_json(unencoded):
|
||||
|
||||
|
||||
class FakeLDAP(object):
|
||||
#TODO(vish): refactor this class to use a wrapper instead of accessing
|
||||
# redis directly
|
||||
"""Fake LDAP connection."""
|
||||
|
||||
def simple_bind_s(self, dn, password):
|
||||
@@ -183,14 +215,13 @@ class FakeLDAP(object):
|
||||
|
||||
def add_s(self, dn, attr):
|
||||
"""Add an object with the specified attributes at dn."""
|
||||
key = "%s%s" % (self.__redis_prefix, dn)
|
||||
|
||||
key = "%s%s" % (self.__prefix, dn)
|
||||
value_dict = dict([(k, _to_json(v)) for k, v in attr])
|
||||
Redis.instance().hmset(key, value_dict)
|
||||
Store.instance().hmset(key, value_dict)
|
||||
|
||||
def delete_s(self, dn):
|
||||
"""Remove the ldap object at specified dn."""
|
||||
Redis.instance().delete("%s%s" % (self.__redis_prefix, dn))
|
||||
Store.instance().delete("%s%s" % (self.__prefix, dn))
|
||||
|
||||
def modify_s(self, dn, attrs):
|
||||
"""Modify the object at dn using the attribute list.
|
||||
@@ -201,18 +232,18 @@ class FakeLDAP(object):
|
||||
([MOD_ADD | MOD_DELETE | MOD_REPACE], attribute, value)
|
||||
|
||||
"""
|
||||
redis = Redis.instance()
|
||||
key = "%s%s" % (self.__redis_prefix, dn)
|
||||
store = Store.instance()
|
||||
key = "%s%s" % (self.__prefix, dn)
|
||||
|
||||
for cmd, k, v in attrs:
|
||||
values = _from_json(redis.hget(key, k))
|
||||
values = _from_json(store.hget(key, k))
|
||||
if cmd == MOD_ADD:
|
||||
values.append(v)
|
||||
elif cmd == MOD_REPLACE:
|
||||
values = [v]
|
||||
else:
|
||||
values.remove(v)
|
||||
values = redis.hset(key, k, _to_json(values))
|
||||
values = store.hset(key, k, _to_json(values))
|
||||
|
||||
def search_s(self, dn, scope, query=None, fields=None):
|
||||
"""Search for all matching objects under dn using the query.
|
||||
@@ -226,16 +257,17 @@ class FakeLDAP(object):
|
||||
"""
|
||||
if scope != SCOPE_BASE and scope != SCOPE_SUBTREE:
|
||||
raise NotImplementedError(str(scope))
|
||||
redis = Redis.instance()
|
||||
store = Store.instance()
|
||||
if scope == SCOPE_BASE:
|
||||
keys = ["%s%s" % (self.__redis_prefix, dn)]
|
||||
keys = ["%s%s" % (self.__prefix, dn)]
|
||||
else:
|
||||
keys = redis.keys("%s*%s" % (self.__redis_prefix, dn))
|
||||
keys = store.keys("%s*%s" % (self.__prefix, dn))
|
||||
|
||||
objects = []
|
||||
for key in keys:
|
||||
# get the attributes from redis
|
||||
attrs = redis.hgetall(key)
|
||||
# turn the values from redis into lists
|
||||
# get the attributes from the store
|
||||
attrs = store.hgetall(key)
|
||||
# turn the values from the store into lists
|
||||
# pylint: disable-msg=E1103
|
||||
attrs = dict([(k, _from_json(v))
|
||||
for k, v in attrs.iteritems()])
|
||||
@@ -244,13 +276,13 @@ class FakeLDAP(object):
|
||||
# filter the attributes by fields
|
||||
attrs = dict([(k, v) for k, v in attrs.iteritems()
|
||||
if not fields or k in fields])
|
||||
objects.append((key[len(self.__redis_prefix):], attrs))
|
||||
objects.append((key[len(self.__prefix):], attrs))
|
||||
# pylint: enable-msg=E1103
|
||||
if objects == []:
|
||||
raise NO_SUCH_OBJECT()
|
||||
return objects
|
||||
|
||||
@property
|
||||
def __redis_prefix(self): # pylint: disable-msg=R0201
|
||||
"""Get the prefix to use for all redis keys."""
|
||||
def __prefix(self): # pylint: disable-msg=R0201
|
||||
"""Get the prefix to use for all keys."""
|
||||
return 'ldap:'
|
||||
|
||||
@@ -32,11 +32,16 @@ from nova import flags
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
flags.DEFINE_integer('ldap_schema_version', 2,
|
||||
'Current version of the LDAP schema')
|
||||
flags.DEFINE_string('ldap_url', 'ldap://localhost',
|
||||
'Point this at your ldap server')
|
||||
flags.DEFINE_string('ldap_password', 'changeme', 'LDAP password')
|
||||
flags.DEFINE_string('ldap_user_dn', 'cn=Manager,dc=example,dc=com',
|
||||
'DN of admin user')
|
||||
flags.DEFINE_string('ldap_user_id_attribute', 'uid', 'Attribute to use as id')
|
||||
flags.DEFINE_string('ldap_user_name_attribute', 'cn',
|
||||
'Attribute to use as name')
|
||||
flags.DEFINE_string('ldap_user_unit', 'Users', 'OID for Users')
|
||||
flags.DEFINE_string('ldap_user_subtree', 'ou=Users,dc=example,dc=com',
|
||||
'OU for Users')
|
||||
@@ -73,10 +78,20 @@ class LdapDriver(object):
|
||||
Defines enter and exit and therefore supports the with/as syntax.
|
||||
"""
|
||||
|
||||
project_pattern = '(owner=*)'
|
||||
isadmin_attribute = 'isNovaAdmin'
|
||||
project_attribute = 'owner'
|
||||
project_objectclass = 'groupOfNames'
|
||||
|
||||
def __init__(self):
|
||||
"""Imports the LDAP module"""
|
||||
self.ldap = __import__('ldap')
|
||||
self.conn = None
|
||||
if FLAGS.ldap_schema_version == 1:
|
||||
LdapDriver.project_pattern = '(objectclass=novaProject)'
|
||||
LdapDriver.isadmin_attribute = 'isAdmin'
|
||||
LdapDriver.project_attribute = 'projectManager'
|
||||
LdapDriver.project_objectclass = 'novaProject'
|
||||
|
||||
def __enter__(self):
|
||||
"""Creates the connection to LDAP"""
|
||||
@@ -104,13 +119,13 @@ class LdapDriver(object):
|
||||
"""Retrieve project by id"""
|
||||
dn = 'cn=%s,%s' % (pid,
|
||||
FLAGS.ldap_project_subtree)
|
||||
attr = self.__find_object(dn, '(objectclass=novaProject)')
|
||||
attr = self.__find_object(dn, LdapDriver.project_pattern)
|
||||
return self.__to_project(attr)
|
||||
|
||||
def get_users(self):
|
||||
"""Retrieve list of users"""
|
||||
attrs = self.__find_objects(FLAGS.ldap_user_subtree,
|
||||
'(objectclass=novaUser)')
|
||||
'(objectclass=novaUser)')
|
||||
users = []
|
||||
for attr in attrs:
|
||||
user = self.__to_user(attr)
|
||||
@@ -120,7 +135,7 @@ class LdapDriver(object):
|
||||
|
||||
def get_projects(self, uid=None):
|
||||
"""Retrieve list of projects"""
|
||||
pattern = '(objectclass=novaProject)'
|
||||
pattern = LdapDriver.project_pattern
|
||||
if uid:
|
||||
pattern = "(&%s(member=%s))" % (pattern, self.__uid_to_dn(uid))
|
||||
attrs = self.__find_objects(FLAGS.ldap_project_subtree,
|
||||
@@ -139,27 +154,29 @@ class LdapDriver(object):
|
||||
# Malformed entries are useless, replace attributes found.
|
||||
attr = []
|
||||
if 'secretKey' in user.keys():
|
||||
attr.append((self.ldap.MOD_REPLACE, 'secretKey', \
|
||||
[secret_key]))
|
||||
attr.append((self.ldap.MOD_REPLACE, 'secretKey',
|
||||
[secret_key]))
|
||||
else:
|
||||
attr.append((self.ldap.MOD_ADD, 'secretKey', \
|
||||
[secret_key]))
|
||||
attr.append((self.ldap.MOD_ADD, 'secretKey',
|
||||
[secret_key]))
|
||||
if 'accessKey' in user.keys():
|
||||
attr.append((self.ldap.MOD_REPLACE, 'accessKey', \
|
||||
[access_key]))
|
||||
attr.append((self.ldap.MOD_REPLACE, 'accessKey',
|
||||
[access_key]))
|
||||
else:
|
||||
attr.append((self.ldap.MOD_ADD, 'accessKey', \
|
||||
[access_key]))
|
||||
if 'isAdmin' in user.keys():
|
||||
attr.append((self.ldap.MOD_REPLACE, 'isAdmin', \
|
||||
[str(is_admin).upper()]))
|
||||
attr.append((self.ldap.MOD_ADD, 'accessKey',
|
||||
[access_key]))
|
||||
if LdapDriver.isadmin_attribute in user.keys():
|
||||
attr.append((self.ldap.MOD_REPLACE,
|
||||
LdapDriver.isadmin_attribute,
|
||||
[str(is_admin).upper()]))
|
||||
else:
|
||||
attr.append((self.ldap.MOD_ADD, 'isAdmin', \
|
||||
[str(is_admin).upper()]))
|
||||
attr.append((self.ldap.MOD_ADD,
|
||||
LdapDriver.isadmin_attribute,
|
||||
[str(is_admin).upper()]))
|
||||
self.conn.modify_s(self.__uid_to_dn(name), attr)
|
||||
return self.get_user(name)
|
||||
else:
|
||||
raise exception.NotFound("LDAP object for %s doesn't exist"
|
||||
raise exception.NotFound(_("LDAP object for %s doesn't exist")
|
||||
% name)
|
||||
else:
|
||||
attr = [
|
||||
@@ -168,12 +185,12 @@ class LdapDriver(object):
|
||||
'inetOrgPerson',
|
||||
'novaUser']),
|
||||
('ou', [FLAGS.ldap_user_unit]),
|
||||
('uid', [name]),
|
||||
(FLAGS.ldap_user_id_attribute, [name]),
|
||||
('sn', [name]),
|
||||
('cn', [name]),
|
||||
(FLAGS.ldap_user_name_attribute, [name]),
|
||||
('secretKey', [secret_key]),
|
||||
('accessKey', [access_key]),
|
||||
('isAdmin', [str(is_admin).upper()]),
|
||||
(LdapDriver.isadmin_attribute, [str(is_admin).upper()]),
|
||||
]
|
||||
self.conn.add_s(self.__uid_to_dn(name), attr)
|
||||
return self.__to_user(dict(attr))
|
||||
@@ -182,11 +199,12 @@ class LdapDriver(object):
|
||||
description=None, member_uids=None):
|
||||
"""Create a project"""
|
||||
if self.__project_exists(name):
|
||||
raise exception.Duplicate("Project can't be created because "
|
||||
"project %s already exists" % name)
|
||||
raise exception.Duplicate(_("Project can't be created because "
|
||||
"project %s already exists") % name)
|
||||
if not self.__user_exists(manager_uid):
|
||||
raise exception.NotFound("Project can't be created because "
|
||||
"manager %s doesn't exist" % manager_uid)
|
||||
raise exception.NotFound(_("Project can't be created because "
|
||||
"manager %s doesn't exist")
|
||||
% manager_uid)
|
||||
manager_dn = self.__uid_to_dn(manager_uid)
|
||||
# description is a required attribute
|
||||
if description is None:
|
||||
@@ -195,18 +213,18 @@ class LdapDriver(object):
|
||||
if member_uids is not None:
|
||||
for member_uid in member_uids:
|
||||
if not self.__user_exists(member_uid):
|
||||
raise exception.NotFound("Project can't be created "
|
||||
"because user %s doesn't exist"
|
||||
raise exception.NotFound(_("Project can't be created "
|
||||
"because user %s doesn't exist")
|
||||
% member_uid)
|
||||
members.append(self.__uid_to_dn(member_uid))
|
||||
# always add the manager as a member because members is required
|
||||
if not manager_dn in members:
|
||||
members.append(manager_dn)
|
||||
attr = [
|
||||
('objectclass', ['novaProject']),
|
||||
('objectclass', [LdapDriver.project_objectclass]),
|
||||
('cn', [name]),
|
||||
('description', [description]),
|
||||
('projectManager', [manager_dn]),
|
||||
(LdapDriver.project_attribute, [manager_dn]),
|
||||
('member', members)]
|
||||
self.conn.add_s('cn=%s,%s' % (name, FLAGS.ldap_project_subtree), attr)
|
||||
return self.__to_project(dict(attr))
|
||||
@@ -218,11 +236,12 @@ class LdapDriver(object):
|
||||
attr = []
|
||||
if manager_uid:
|
||||
if not self.__user_exists(manager_uid):
|
||||
raise exception.NotFound("Project can't be modified because "
|
||||
"manager %s doesn't exist" %
|
||||
manager_uid)
|
||||
raise exception.NotFound(_("Project can't be modified because "
|
||||
"manager %s doesn't exist")
|
||||
% manager_uid)
|
||||
manager_dn = self.__uid_to_dn(manager_uid)
|
||||
attr.append((self.ldap.MOD_REPLACE, 'projectManager', manager_dn))
|
||||
attr.append((self.ldap.MOD_REPLACE, LdapDriver.project_attribute,
|
||||
manager_dn))
|
||||
if description:
|
||||
attr.append((self.ldap.MOD_REPLACE, 'description', description))
|
||||
self.conn.modify_s('cn=%s,%s' % (project_id,
|
||||
@@ -282,10 +301,9 @@ class LdapDriver(object):
|
||||
return roles
|
||||
else:
|
||||
project_dn = 'cn=%s,%s' % (project_id, FLAGS.ldap_project_subtree)
|
||||
roles = self.__find_objects(project_dn,
|
||||
'(&(&(objectclass=groupOfNames)'
|
||||
'(!(objectclass=novaProject)))'
|
||||
'(member=%s))' % self.__uid_to_dn(uid))
|
||||
query = ('(&(&(objectclass=groupOfNames)(!%s))(member=%s))' %
|
||||
(LdapDriver.project_pattern, self.__uid_to_dn(uid)))
|
||||
roles = self.__find_objects(project_dn, query)
|
||||
return [role['cn'][0] for role in roles]
|
||||
|
||||
def delete_user(self, uid):
|
||||
@@ -299,14 +317,15 @@ class LdapDriver(object):
|
||||
# Retrieve user by name
|
||||
user = self.__get_ldap_user(uid)
|
||||
if 'secretKey' in user.keys():
|
||||
attr.append((self.ldap.MOD_DELETE, 'secretKey', \
|
||||
user['secretKey']))
|
||||
attr.append((self.ldap.MOD_DELETE, 'secretKey',
|
||||
user['secretKey']))
|
||||
if 'accessKey' in user.keys():
|
||||
attr.append((self.ldap.MOD_DELETE, 'accessKey', \
|
||||
user['accessKey']))
|
||||
if 'isAdmin' in user.keys():
|
||||
attr.append((self.ldap.MOD_DELETE, 'isAdmin', \
|
||||
user['isAdmin']))
|
||||
attr.append((self.ldap.MOD_DELETE, 'accessKey',
|
||||
user['accessKey']))
|
||||
if LdapDriver.isadmin_attribute in user.keys():
|
||||
attr.append((self.ldap.MOD_DELETE,
|
||||
LdapDriver.isadmin_attribute,
|
||||
user[LdapDriver.isadmin_attribute]))
|
||||
self.conn.modify_s(self.__uid_to_dn(uid), attr)
|
||||
else:
|
||||
# Delete entry
|
||||
@@ -328,7 +347,8 @@ class LdapDriver(object):
|
||||
if secret_key:
|
||||
attr.append((self.ldap.MOD_REPLACE, 'secretKey', secret_key))
|
||||
if admin is not None:
|
||||
attr.append((self.ldap.MOD_REPLACE, 'isAdmin', str(admin).upper()))
|
||||
attr.append((self.ldap.MOD_REPLACE, LdapDriver.isadmin_attribute,
|
||||
str(admin).upper()))
|
||||
self.conn.modify_s(self.__uid_to_dn(uid), attr)
|
||||
|
||||
def __user_exists(self, uid):
|
||||
@@ -346,7 +366,7 @@ class LdapDriver(object):
|
||||
def __get_ldap_user(self, uid):
|
||||
"""Retrieve LDAP user entry by id"""
|
||||
attr = self.__find_object(self.__uid_to_dn(uid),
|
||||
'(objectclass=novaUser)')
|
||||
'(objectclass=novaUser)')
|
||||
return attr
|
||||
|
||||
def __find_object(self, dn, query=None, scope=None):
|
||||
@@ -382,19 +402,21 @@ class LdapDriver(object):
|
||||
|
||||
def __find_role_dns(self, tree):
|
||||
"""Find dns of role objects in given tree"""
|
||||
return self.__find_dns(tree,
|
||||
'(&(objectclass=groupOfNames)(!(objectclass=novaProject)))')
|
||||
query = ('(&(objectclass=groupOfNames)(!%s))' %
|
||||
LdapDriver.project_pattern)
|
||||
return self.__find_dns(tree, query)
|
||||
|
||||
def __find_group_dns_with_member(self, tree, uid):
|
||||
"""Find dns of group objects in a given tree that contain member"""
|
||||
dns = self.__find_dns(tree,
|
||||
'(&(objectclass=groupOfNames)(member=%s))' %
|
||||
self.__uid_to_dn(uid))
|
||||
query = ('(&(objectclass=groupOfNames)(member=%s))' %
|
||||
self.__uid_to_dn(uid))
|
||||
dns = self.__find_dns(tree, query)
|
||||
return dns
|
||||
|
||||
def __group_exists(self, dn):
|
||||
"""Check if group exists"""
|
||||
return self.__find_object(dn, '(objectclass=groupOfNames)') is not None
|
||||
query = '(objectclass=groupOfNames)'
|
||||
return self.__find_object(dn, query) is not None
|
||||
|
||||
@staticmethod
|
||||
def __role_to_dn(role, project_id=None):
|
||||
@@ -417,7 +439,8 @@ class LdapDriver(object):
|
||||
for member_uid in member_uids:
|
||||
if not self.__user_exists(member_uid):
|
||||
raise exception.NotFound("Group can't be created "
|
||||
"because user %s doesn't exist" % member_uid)
|
||||
"because user %s doesn't exist" %
|
||||
member_uid)
|
||||
members.append(self.__uid_to_dn(member_uid))
|
||||
dn = self.__uid_to_dn(uid)
|
||||
if not dn in members:
|
||||
@@ -433,7 +456,7 @@ class LdapDriver(object):
|
||||
"""Check if user is in group"""
|
||||
if not self.__user_exists(uid):
|
||||
raise exception.NotFound("User %s can't be searched in group "
|
||||
"becuase the user doesn't exist" % (uid,))
|
||||
"because the user doesn't exist" % uid)
|
||||
if not self.__group_exists(group_dn):
|
||||
return False
|
||||
res = self.__find_object(group_dn,
|
||||
@@ -445,13 +468,13 @@ class LdapDriver(object):
|
||||
"""Add user to group"""
|
||||
if not self.__user_exists(uid):
|
||||
raise exception.NotFound("User %s can't be added to the group "
|
||||
"becuase the user doesn't exist" % (uid,))
|
||||
"because the user doesn't exist" % uid)
|
||||
if not self.__group_exists(group_dn):
|
||||
raise exception.NotFound("The group at dn %s doesn't exist" %
|
||||
(group_dn,))
|
||||
group_dn)
|
||||
if self.__is_in_group(uid, group_dn):
|
||||
raise exception.Duplicate("User %s is already a member of "
|
||||
"the group %s" % (uid, group_dn))
|
||||
raise exception.Duplicate(_("User %s is already a member of "
|
||||
"the group %s") % (uid, group_dn))
|
||||
attr = [(self.ldap.MOD_ADD, 'member', self.__uid_to_dn(uid))]
|
||||
self.conn.modify_s(group_dn, attr)
|
||||
|
||||
@@ -459,16 +482,16 @@ class LdapDriver(object):
|
||||
"""Remove user from group"""
|
||||
if not self.__group_exists(group_dn):
|
||||
raise exception.NotFound("The group at dn %s doesn't exist" %
|
||||
(group_dn,))
|
||||
group_dn)
|
||||
if not self.__user_exists(uid):
|
||||
raise exception.NotFound("User %s can't be removed from the "
|
||||
"group because the user doesn't exist" % (uid,))
|
||||
"group because the user doesn't exist" %
|
||||
uid)
|
||||
if not self.__is_in_group(uid, group_dn):
|
||||
raise exception.NotFound("User %s is not a member of the group" %
|
||||
(uid,))
|
||||
uid)
|
||||
# NOTE(vish): remove user from group and any sub_groups
|
||||
sub_dns = self.__find_group_dns_with_member(
|
||||
group_dn, uid)
|
||||
sub_dns = self.__find_group_dns_with_member(group_dn, uid)
|
||||
for sub_dn in sub_dns:
|
||||
self.__safe_remove_from_group(uid, sub_dn)
|
||||
|
||||
@@ -479,15 +502,15 @@ class LdapDriver(object):
|
||||
try:
|
||||
self.conn.modify_s(group_dn, attr)
|
||||
except self.ldap.OBJECT_CLASS_VIOLATION:
|
||||
logging.debug("Attempted to remove the last member of a group. "
|
||||
"Deleting the group at %s instead.", group_dn)
|
||||
logging.debug(_("Attempted to remove the last member of a group. "
|
||||
"Deleting the group at %s instead."), group_dn)
|
||||
self.__delete_group(group_dn)
|
||||
|
||||
def __remove_from_all(self, uid):
|
||||
"""Remove user from all roles and projects"""
|
||||
if not self.__user_exists(uid):
|
||||
raise exception.NotFound("User %s can't be removed from all "
|
||||
"because the user doesn't exist" % (uid,))
|
||||
"because the user doesn't exist" % uid)
|
||||
role_dns = self.__find_group_dns_with_member(
|
||||
FLAGS.role_project_subtree, uid)
|
||||
for role_dn in role_dns:
|
||||
@@ -500,7 +523,8 @@ class LdapDriver(object):
|
||||
def __delete_group(self, group_dn):
|
||||
"""Delete Group"""
|
||||
if not self.__group_exists(group_dn):
|
||||
raise exception.NotFound("Group at dn %s doesn't exist" % group_dn)
|
||||
raise exception.NotFound(_("Group at dn %s doesn't exist")
|
||||
% group_dn)
|
||||
self.conn.delete_s(group_dn)
|
||||
|
||||
def __delete_roles(self, project_dn):
|
||||
@@ -514,13 +538,13 @@ class LdapDriver(object):
|
||||
if attr is None:
|
||||
return None
|
||||
if ('accessKey' in attr.keys() and 'secretKey' in attr.keys() \
|
||||
and 'isAdmin' in attr.keys()):
|
||||
and LdapDriver.isadmin_attribute in attr.keys()):
|
||||
return {
|
||||
'id': attr['uid'][0],
|
||||
'name': attr['cn'][0],
|
||||
'id': attr[FLAGS.ldap_user_id_attribute][0],
|
||||
'name': attr[FLAGS.ldap_user_name_attribute][0],
|
||||
'access': attr['accessKey'][0],
|
||||
'secret': attr['secretKey'][0],
|
||||
'admin': (attr['isAdmin'][0] == 'TRUE')}
|
||||
'admin': (attr[LdapDriver.isadmin_attribute][0] == 'TRUE')}
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -532,7 +556,8 @@ class LdapDriver(object):
|
||||
return {
|
||||
'id': attr['cn'][0],
|
||||
'name': attr['cn'][0],
|
||||
'project_manager_id': self.__dn_to_uid(attr['projectManager'][0]),
|
||||
'project_manager_id':
|
||||
self.__dn_to_uid(attr[LdapDriver.project_attribute][0]),
|
||||
'description': attr.get('description', [None])[0],
|
||||
'member_ids': [self.__dn_to_uid(x) for x in member_dns]}
|
||||
|
||||
@@ -542,9 +567,10 @@ class LdapDriver(object):
|
||||
return dn.split(',')[0].split('=')[1]
|
||||
|
||||
@staticmethod
|
||||
def __uid_to_dn(dn):
|
||||
def __uid_to_dn(uid):
|
||||
"""Convert uid to dn"""
|
||||
return 'uid=%s,%s' % (dn, FLAGS.ldap_user_subtree)
|
||||
return (FLAGS.ldap_user_id_attribute + '=%s,%s'
|
||||
% (uid, FLAGS.ldap_user_subtree))
|
||||
|
||||
|
||||
class FakeLdapDriver(LdapDriver):
|
||||
|
||||
@@ -64,12 +64,9 @@ flags.DEFINE_string('credential_key_file', 'pk.pem',
|
||||
'Filename of private key in credentials zip')
|
||||
flags.DEFINE_string('credential_cert_file', 'cert.pem',
|
||||
'Filename of certificate in credentials zip')
|
||||
flags.DEFINE_string('credential_rc_file', 'novarc',
|
||||
'Filename of rc in credentials zip')
|
||||
flags.DEFINE_string('credential_cert_subject',
|
||||
'/C=US/ST=California/L=MountainView/O=AnsoLabs/'
|
||||
'OU=NovaDev/CN=%s-%s',
|
||||
'Subject for certificate for users')
|
||||
flags.DEFINE_string('credential_rc_file', '%src',
|
||||
'Filename of rc in credentials zip, %s will be '
|
||||
'replaced by name of the region (nova by default)')
|
||||
flags.DEFINE_string('auth_driver', 'nova.auth.dbdriver.DbDriver',
|
||||
'Driver that auth manager uses')
|
||||
|
||||
@@ -257,12 +254,12 @@ class AuthManager(object):
|
||||
# TODO(vish): check for valid timestamp
|
||||
(access_key, _sep, project_id) = access.partition(':')
|
||||
|
||||
logging.info('Looking up user: %r', access_key)
|
||||
logging.info(_('Looking up user: %r'), access_key)
|
||||
user = self.get_user_from_access_key(access_key)
|
||||
logging.info('user: %r', user)
|
||||
if user == None:
|
||||
raise exception.NotFound('No user found for access key %s' %
|
||||
access_key)
|
||||
raise exception.NotFound(_('No user found for access key %s')
|
||||
% access_key)
|
||||
|
||||
# NOTE(vish): if we stop using project name as id we need better
|
||||
# logic to find a default project for user
|
||||
@@ -271,12 +268,12 @@ class AuthManager(object):
|
||||
|
||||
project = self.get_project(project_id)
|
||||
if project == None:
|
||||
raise exception.NotFound('No project called %s could be found' %
|
||||
project_id)
|
||||
raise exception.NotFound(_('No project called %s could be found')
|
||||
% project_id)
|
||||
if not self.is_admin(user) and not self.is_project_member(user,
|
||||
project):
|
||||
raise exception.NotFound('User %s is not a member of project %s' %
|
||||
(user.id, project.id))
|
||||
raise exception.NotFound(_('User %s is not a member of project %s')
|
||||
% (user.id, project.id))
|
||||
if check_type == 's3':
|
||||
sign = signer.Signer(user.secret.encode())
|
||||
expected_signature = sign.s3_authorization(headers, verb, path)
|
||||
@@ -284,7 +281,7 @@ class AuthManager(object):
|
||||
logging.debug('expected_signature: %s', expected_signature)
|
||||
logging.debug('signature: %s', signature)
|
||||
if signature != expected_signature:
|
||||
raise exception.NotAuthorized('Signature does not match')
|
||||
raise exception.NotAuthorized(_('Signature does not match'))
|
||||
elif check_type == 'ec2':
|
||||
# NOTE(vish): hmac can't handle unicode, so encode ensures that
|
||||
# secret isn't unicode
|
||||
@@ -294,7 +291,7 @@ class AuthManager(object):
|
||||
logging.debug('expected_signature: %s', expected_signature)
|
||||
logging.debug('signature: %s', signature)
|
||||
if signature != expected_signature:
|
||||
raise exception.NotAuthorized('Signature does not match')
|
||||
raise exception.NotAuthorized(_('Signature does not match'))
|
||||
return (user, project)
|
||||
|
||||
def get_access_key(self, user, project):
|
||||
@@ -364,7 +361,7 @@ class AuthManager(object):
|
||||
with self.driver() as drv:
|
||||
if role == 'projectmanager':
|
||||
if not project:
|
||||
raise exception.Error("Must specify project")
|
||||
raise exception.Error(_("Must specify project"))
|
||||
return self.is_project_manager(user, project)
|
||||
|
||||
global_role = drv.has_role(User.safe_id(user),
|
||||
@@ -398,9 +395,9 @@ class AuthManager(object):
|
||||
@param project: Project in which to add local role.
|
||||
"""
|
||||
if role not in FLAGS.allowed_roles:
|
||||
raise exception.NotFound("The %s role can not be found" % role)
|
||||
raise exception.NotFound(_("The %s role can not be found") % role)
|
||||
if project is not None and role in FLAGS.global_roles:
|
||||
raise exception.NotFound("The %s role is global only" % role)
|
||||
raise exception.NotFound(_("The %s role is global only") % role)
|
||||
with self.driver() as drv:
|
||||
drv.add_role(User.safe_id(user), role, Project.safe_id(project))
|
||||
|
||||
@@ -543,10 +540,10 @@ class AuthManager(object):
|
||||
"""
|
||||
|
||||
network_ref = db.project_get_network(context.get_admin_context(),
|
||||
Project.safe_id(project))
|
||||
Project.safe_id(project), False)
|
||||
|
||||
if not network_ref['vpn_public_port']:
|
||||
raise exception.NotFound('project network data has not been set')
|
||||
if not network_ref:
|
||||
return (None, None)
|
||||
return (network_ref['vpn_public_address'],
|
||||
network_ref['vpn_public_port'])
|
||||
|
||||
@@ -628,27 +625,37 @@ class AuthManager(object):
|
||||
def get_key_pairs(context):
|
||||
return db.key_pair_get_all_by_user(context.elevated(), context.user_id)
|
||||
|
||||
def get_credentials(self, user, project=None):
|
||||
def get_credentials(self, user, project=None, use_dmz=True):
|
||||
"""Get credential zip for user in project"""
|
||||
if not isinstance(user, User):
|
||||
user = self.get_user(user)
|
||||
if project is None:
|
||||
project = user.id
|
||||
pid = Project.safe_id(project)
|
||||
rc = self.__generate_rc(user.access, user.secret, pid)
|
||||
private_key, signed_cert = self._generate_x509_cert(user.id, pid)
|
||||
private_key, signed_cert = crypto.generate_x509_cert(user.id, pid)
|
||||
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
zf = os.path.join(tmpdir, "temp.zip")
|
||||
zippy = zipfile.ZipFile(zf, 'w')
|
||||
zippy.writestr(FLAGS.credential_rc_file, rc)
|
||||
if use_dmz and FLAGS.region_list:
|
||||
regions = {}
|
||||
for item in FLAGS.region_list:
|
||||
region, _sep, region_host = item.partition("=")
|
||||
regions[region] = region_host
|
||||
else:
|
||||
regions = {'nova': FLAGS.cc_host}
|
||||
for region, host in regions.iteritems():
|
||||
rc = self.__generate_rc(user.access,
|
||||
user.secret,
|
||||
pid,
|
||||
use_dmz,
|
||||
host)
|
||||
zippy.writestr(FLAGS.credential_rc_file % region, rc)
|
||||
|
||||
zippy.writestr(FLAGS.credential_key_file, private_key)
|
||||
zippy.writestr(FLAGS.credential_cert_file, signed_cert)
|
||||
|
||||
try:
|
||||
(vpn_ip, vpn_port) = self.get_project_vpn_data(project)
|
||||
except exception.NotFound:
|
||||
vpn_ip = None
|
||||
(vpn_ip, vpn_port) = self.get_project_vpn_data(project)
|
||||
if vpn_ip:
|
||||
configfile = open(FLAGS.vpn_client_template, "r")
|
||||
s = string.Template(configfile.read())
|
||||
@@ -659,10 +666,9 @@ class AuthManager(object):
|
||||
port=vpn_port)
|
||||
zippy.writestr(FLAGS.credential_vpn_file, config)
|
||||
else:
|
||||
logging.warn("No vpn data for project %s" %
|
||||
pid)
|
||||
logging.warn(_("No vpn data for project %s"), pid)
|
||||
|
||||
zippy.writestr(FLAGS.ca_file, crypto.fetch_ca(user.id))
|
||||
zippy.writestr(FLAGS.ca_file, crypto.fetch_ca(pid))
|
||||
zippy.close()
|
||||
with open(zf, 'rb') as f:
|
||||
read_buffer = f.read()
|
||||
@@ -670,38 +676,38 @@ class AuthManager(object):
|
||||
shutil.rmtree(tmpdir)
|
||||
return read_buffer
|
||||
|
||||
def get_environment_rc(self, user, project=None):
|
||||
def get_environment_rc(self, user, project=None, use_dmz=True):
|
||||
"""Get credential zip for user in project"""
|
||||
if not isinstance(user, User):
|
||||
user = self.get_user(user)
|
||||
if project is None:
|
||||
project = user.id
|
||||
pid = Project.safe_id(project)
|
||||
return self.__generate_rc(user.access, user.secret, pid)
|
||||
return self.__generate_rc(user.access, user.secret, pid, use_dmz)
|
||||
|
||||
@staticmethod
|
||||
def __generate_rc(access, secret, pid):
|
||||
def __generate_rc(access, secret, pid, use_dmz=True, host=None):
|
||||
"""Generate rc file for user"""
|
||||
if use_dmz:
|
||||
cc_host = FLAGS.cc_dmz
|
||||
else:
|
||||
cc_host = FLAGS.cc_host
|
||||
# NOTE(vish): Always use the dmz since it is used from inside the
|
||||
# instance
|
||||
s3_host = FLAGS.s3_dmz
|
||||
if host:
|
||||
s3_host = host
|
||||
cc_host = host
|
||||
rc = open(FLAGS.credentials_template).read()
|
||||
rc = rc % {'access': access,
|
||||
'project': pid,
|
||||
'secret': secret,
|
||||
'ec2': FLAGS.ec2_url,
|
||||
's3': 'http://%s:%s' % (FLAGS.s3_host, FLAGS.s3_port),
|
||||
'ec2': '%s://%s:%s%s' % (FLAGS.ec2_prefix,
|
||||
cc_host,
|
||||
FLAGS.cc_port,
|
||||
FLAGS.ec2_suffix),
|
||||
's3': 'http://%s:%s' % (s3_host, FLAGS.s3_port),
|
||||
'nova': FLAGS.ca_file,
|
||||
'cert': FLAGS.credential_cert_file,
|
||||
'key': FLAGS.credential_key_file}
|
||||
return rc
|
||||
|
||||
def _generate_x509_cert(self, uid, pid):
|
||||
"""Generate x509 cert for user"""
|
||||
(private_key, csr) = crypto.generate_x509_cert(
|
||||
self.__cert_subject(uid))
|
||||
# TODO(joshua): This should be async call back to the cloud controller
|
||||
signed_cert = crypto.sign_csr(csr, pid)
|
||||
return (private_key, signed_cert)
|
||||
|
||||
@staticmethod
|
||||
def __cert_subject(uid):
|
||||
"""Helper to generate cert subject"""
|
||||
return FLAGS.credential_cert_subject % (uid, utils.isotime())
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#
|
||||
# Person object for Nova
|
||||
# inetorgperson with extra attributes
|
||||
# Author: Vishvananda Ishaya <vishvananda@yahoo.com>
|
||||
# Schema version: 2
|
||||
# Authors: Vishvananda Ishaya <vishvananda@gmail.com>
|
||||
# Ryan Lane <rlane@wikimedia.org>
|
||||
#
|
||||
#
|
||||
|
||||
@@ -30,55 +32,19 @@ attributetype (
|
||||
SINGLE-VALUE
|
||||
)
|
||||
|
||||
attributetype (
|
||||
novaAttrs:3
|
||||
NAME 'keyFingerprint'
|
||||
DESC 'Fingerprint of private key'
|
||||
EQUALITY caseIgnoreMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
|
||||
SINGLE-VALUE
|
||||
)
|
||||
|
||||
attributetype (
|
||||
novaAttrs:4
|
||||
NAME 'isAdmin'
|
||||
DESC 'Is user an administrator?'
|
||||
NAME 'isNovaAdmin'
|
||||
DESC 'Is user an nova administrator?'
|
||||
EQUALITY booleanMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
|
||||
SINGLE-VALUE
|
||||
)
|
||||
|
||||
attributetype (
|
||||
novaAttrs:5
|
||||
NAME 'projectManager'
|
||||
DESC 'Project Managers of a project'
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
|
||||
)
|
||||
|
||||
objectClass (
|
||||
novaOCs:1
|
||||
NAME 'novaUser'
|
||||
DESC 'access and secret keys'
|
||||
AUXILIARY
|
||||
MUST ( uid )
|
||||
MAY ( accessKey $ secretKey $ isAdmin )
|
||||
)
|
||||
|
||||
objectClass (
|
||||
novaOCs:2
|
||||
NAME 'novaKeyPair'
|
||||
DESC 'Key pair for User'
|
||||
SUP top
|
||||
STRUCTURAL
|
||||
MUST ( cn $ sshPublicKey $ keyFingerprint )
|
||||
)
|
||||
|
||||
objectClass (
|
||||
novaOCs:3
|
||||
NAME 'novaProject'
|
||||
DESC 'Container for project'
|
||||
SUP groupOfNames
|
||||
STRUCTURAL
|
||||
MUST ( cn $ projectManager )
|
||||
MAY ( accessKey $ secretKey $ isNovaAdmin )
|
||||
)
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
#
|
||||
# Person object for Nova
|
||||
# inetorgperson with extra attributes
|
||||
# Author: Vishvananda Ishaya <vishvananda@yahoo.com>
|
||||
# Modified for strict RFC 4512 compatibility by: Ryan Lane <ryan@ryandlane.com>
|
||||
# Schema version: 2
|
||||
# Authors: Vishvananda Ishaya <vishvananda@gmail.com>
|
||||
# Ryan Lane <rlane@wikimedia.org>
|
||||
#
|
||||
# using internet experimental oid arc as per BP64 3.1
|
||||
dn: cn=schema
|
||||
attributeTypes: ( 1.3.6.1.3.1.666.666.3.1 NAME 'accessKey' DESC 'Key for accessing data' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
|
||||
attributeTypes: ( 1.3.6.1.3.1.666.666.3.2 NAME 'secretKey' DESC 'Secret key' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
|
||||
attributeTypes: ( 1.3.6.1.3.1.666.666.3.3 NAME 'keyFingerprint' DESC 'Fingerprint of private key' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE)
|
||||
attributeTypes: ( 1.3.6.1.3.1.666.666.3.4 NAME 'isAdmin' DESC 'Is user an administrator?' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
|
||||
attributeTypes: ( 1.3.6.1.3.1.666.666.3.5 NAME 'projectManager' DESC 'Project Managers of a project' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
|
||||
objectClasses: ( 1.3.6.1.3.1.666.666.4.1 NAME 'novaUser' DESC 'access and secret keys' SUP top AUXILIARY MUST ( uid ) MAY ( accessKey $ secretKey $ isAdmin ) )
|
||||
objectClasses: ( 1.3.6.1.3.1.666.666.4.2 NAME 'novaKeyPair' DESC 'Key pair for User' SUP top STRUCTURAL MUST ( cn $ sshPublicKey $ keyFingerprint ) )
|
||||
objectClasses: ( 1.3.6.1.3.1.666.666.4.3 NAME 'novaProject' DESC 'Container for project' SUP groupOfNames STRUCTURAL MUST ( cn $ projectManager ) )
|
||||
attributeTypes: ( 1.3.6.1.3.1.666.666.3.4 NAME 'isNovaAdmin' DESC 'Is user a nova administrator?' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
|
||||
objectClasses: ( 1.3.6.1.3.1.666.666.4.1 NAME 'novaUser' DESC 'access and secret keys' SUP top AUXILIARY MAY ( accessKey $ secretKey $ isNovaAdmin ) )
|
||||
|
||||
@@ -32,7 +32,6 @@ abspath=`dirname "$(cd "${0%/*}" 2>/dev/null; echo "$PWD"/"${0##*/}")"`
|
||||
schemapath='/var/opendj/instance/config/schema'
|
||||
cp $abspath/openssh-lpk_sun.schema $schemapath/97-openssh-lpk_sun.ldif
|
||||
cp $abspath/nova_sun.schema $schemapath/98-nova_sun.ldif
|
||||
chown opendj:opendj $schemapath/97-openssh-lpk_sun.ldif
|
||||
chown opendj:opendj $schemapath/98-nova_sun.ldif
|
||||
|
||||
cat >/etc/ldap/ldap.conf <<LDAP_CONF_EOF
|
||||
|
||||
@@ -22,7 +22,7 @@ apt-get install -y slapd ldap-utils python-ldap
|
||||
|
||||
abspath=`dirname "$(cd "${0%/*}" 2>/dev/null; echo "$PWD"/"${0##*/}")"`
|
||||
cp $abspath/openssh-lpk_openldap.schema /etc/ldap/schema/openssh-lpk_openldap.schema
|
||||
cp $abspath/nova_openldap.schema /etc/ldap/schema/nova_openldap.schema
|
||||
cp $abspath/nova_openldap.schema /etc/ldap/schema/nova.schema
|
||||
|
||||
mv /etc/ldap/slapd.conf /etc/ldap/slapd.conf.orig
|
||||
cat >/etc/ldap/slapd.conf <<SLAPD_CONF_EOF
|
||||
@@ -33,7 +33,6 @@ cat >/etc/ldap/slapd.conf <<SLAPD_CONF_EOF
|
||||
include /etc/ldap/schema/core.schema
|
||||
include /etc/ldap/schema/cosine.schema
|
||||
include /etc/ldap/schema/inetorgperson.schema
|
||||
include /etc/ldap/schema/openssh-lpk_openldap.schema
|
||||
include /etc/ldap/schema/nova.schema
|
||||
pidfile /var/run/slapd/slapd.pid
|
||||
argsfile /var/run/slapd/slapd.args
|
||||
|
||||
59
nova/fakememcache.py
Normal file
59
nova/fakememcache.py
Normal file
@@ -0,0 +1,59 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Super simple fake memcache client."""
|
||||
|
||||
import utils
|
||||
|
||||
|
||||
class Client(object):
|
||||
"""Replicates a tiny subset of memcached client interface."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Ignores the passed in args"""
|
||||
self.cache = {}
|
||||
|
||||
def get(self, key):
|
||||
"""Retrieves the value for a key or None."""
|
||||
(timeout, value) = self.cache.get(key, (0, None))
|
||||
if timeout == 0 or utils.utcnow_ts() < timeout:
|
||||
return value
|
||||
return None
|
||||
|
||||
def set(self, key, value, time=0, min_compress_len=0):
|
||||
"""Sets the value for a key."""
|
||||
timeout = 0
|
||||
if time != 0:
|
||||
timeout = utils.utcnow_ts() + time
|
||||
self.cache[key] = (timeout, value)
|
||||
return True
|
||||
|
||||
def add(self, key, value, time=0, min_compress_len=0):
|
||||
"""Sets the value for a key if it doesn't exist."""
|
||||
if not self.get(key) is None:
|
||||
return False
|
||||
return self.set(key, value, time, min_compress_len)
|
||||
|
||||
def incr(self, key, delta=1):
|
||||
"""Increments the value for a key."""
|
||||
value = self.get(key)
|
||||
if value is None:
|
||||
return None
|
||||
new_value = int(value) + delta
|
||||
self.cache[key] = (self.cache[key][0], str(new_value))
|
||||
return new_value
|
||||
@@ -25,6 +25,10 @@ from carrot.backends import base
|
||||
from eventlet import greenthread
|
||||
|
||||
|
||||
EXCHANGES = {}
|
||||
QUEUES = {}
|
||||
|
||||
|
||||
class Message(base.BaseMessage):
|
||||
pass
|
||||
|
||||
@@ -37,12 +41,12 @@ class Exchange(object):
|
||||
self._routes = {}
|
||||
|
||||
def publish(self, message, routing_key=None):
|
||||
logging.debug('(%s) publish (key: %s) %s',
|
||||
logging.debug(_('(%s) publish (key: %s) %s'),
|
||||
self.name, routing_key, message)
|
||||
routing_key = routing_key.split('.')[0]
|
||||
if routing_key in self._routes:
|
||||
for f in self._routes[routing_key]:
|
||||
logging.debug('Publishing to route %s', f)
|
||||
logging.debug(_('Publishing to route %s'), f)
|
||||
f(message, routing_key=routing_key)
|
||||
|
||||
def bind(self, callback, routing_key):
|
||||
@@ -68,81 +72,63 @@ class Queue(object):
|
||||
return self._queue.get()
|
||||
|
||||
|
||||
class Backend(object):
|
||||
""" Singleton backend for testing """
|
||||
class __impl(base.BaseBackend):
|
||||
def __init__(self, *args, **kwargs):
|
||||
#super(__impl, self).__init__(*args, **kwargs)
|
||||
self._exchanges = {}
|
||||
self._queues = {}
|
||||
class Backend(base.BaseBackend):
|
||||
def queue_declare(self, queue, **kwargs):
|
||||
global QUEUES
|
||||
if queue not in QUEUES:
|
||||
logging.debug(_('Declaring queue %s'), queue)
|
||||
QUEUES[queue] = Queue(queue)
|
||||
|
||||
def _reset_all(self):
|
||||
self._exchanges = {}
|
||||
self._queues = {}
|
||||
def exchange_declare(self, exchange, type, *args, **kwargs):
|
||||
global EXCHANGES
|
||||
if exchange not in EXCHANGES:
|
||||
logging.debug(_('Declaring exchange %s'), exchange)
|
||||
EXCHANGES[exchange] = Exchange(exchange, type)
|
||||
|
||||
def queue_declare(self, queue, **kwargs):
|
||||
if queue not in self._queues:
|
||||
logging.debug('Declaring queue %s', queue)
|
||||
self._queues[queue] = Queue(queue)
|
||||
def queue_bind(self, queue, exchange, routing_key, **kwargs):
|
||||
global EXCHANGES
|
||||
global QUEUES
|
||||
logging.debug(_('Binding %s to %s with key %s'),
|
||||
queue, exchange, routing_key)
|
||||
EXCHANGES[exchange].bind(QUEUES[queue].push, routing_key)
|
||||
|
||||
def exchange_declare(self, exchange, type, *args, **kwargs):
|
||||
if exchange not in self._exchanges:
|
||||
logging.debug('Declaring exchange %s', exchange)
|
||||
self._exchanges[exchange] = Exchange(exchange, type)
|
||||
def declare_consumer(self, queue, callback, *args, **kwargs):
|
||||
self.current_queue = queue
|
||||
self.current_callback = callback
|
||||
|
||||
def queue_bind(self, queue, exchange, routing_key, **kwargs):
|
||||
logging.debug('Binding %s to %s with key %s',
|
||||
queue, exchange, routing_key)
|
||||
self._exchanges[exchange].bind(self._queues[queue].push,
|
||||
routing_key)
|
||||
def consume(self, limit=None):
|
||||
while True:
|
||||
item = self.get(self.current_queue)
|
||||
if item:
|
||||
self.current_callback(item)
|
||||
raise StopIteration()
|
||||
greenthread.sleep(0)
|
||||
|
||||
def declare_consumer(self, queue, callback, *args, **kwargs):
|
||||
self.current_queue = queue
|
||||
self.current_callback = callback
|
||||
def get(self, queue, no_ack=False):
|
||||
global QUEUES
|
||||
if not queue in QUEUES or not QUEUES[queue].size():
|
||||
return None
|
||||
(message_data, content_type, content_encoding) = QUEUES[queue].pop()
|
||||
message = Message(backend=self, body=message_data,
|
||||
content_type=content_type,
|
||||
content_encoding=content_encoding)
|
||||
message.result = True
|
||||
logging.debug(_('Getting from %s: %s'), queue, message)
|
||||
return message
|
||||
|
||||
def consume(self, *args, **kwargs):
|
||||
while True:
|
||||
item = self.get(self.current_queue)
|
||||
if item:
|
||||
self.current_callback(item)
|
||||
raise StopIteration()
|
||||
greenthread.sleep(0)
|
||||
def prepare_message(self, message_data, delivery_mode,
|
||||
content_type, content_encoding, **kwargs):
|
||||
"""Prepare message for sending."""
|
||||
return (message_data, content_type, content_encoding)
|
||||
|
||||
def get(self, queue, no_ack=False):
|
||||
if not queue in self._queues or not self._queues[queue].size():
|
||||
return None
|
||||
(message_data, content_type, content_encoding) = \
|
||||
self._queues[queue].pop()
|
||||
message = Message(backend=self, body=message_data,
|
||||
content_type=content_type,
|
||||
content_encoding=content_encoding)
|
||||
message.result = True
|
||||
logging.debug('Getting from %s: %s', queue, message)
|
||||
return message
|
||||
|
||||
def prepare_message(self, message_data, delivery_mode,
|
||||
content_type, content_encoding, **kwargs):
|
||||
"""Prepare message for sending."""
|
||||
return (message_data, content_type, content_encoding)
|
||||
|
||||
def publish(self, message, exchange, routing_key, **kwargs):
|
||||
if exchange in self._exchanges:
|
||||
self._exchanges[exchange].publish(
|
||||
message, routing_key=routing_key)
|
||||
|
||||
__instance = None
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
if Backend.__instance is None:
|
||||
Backend.__instance = Backend.__impl(*args, **kwargs)
|
||||
self.__dict__['_Backend__instance'] = Backend.__instance
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self.__instance, attr)
|
||||
|
||||
def __setattr__(self, attr, value):
|
||||
return setattr(self.__instance, attr, value)
|
||||
def publish(self, message, exchange, routing_key, **kwargs):
|
||||
global EXCHANGES
|
||||
if exchange in EXCHANGES:
|
||||
EXCHANGES[exchange].publish(message, routing_key=routing_key)
|
||||
|
||||
|
||||
def reset_all():
|
||||
Backend()._reset_all()
|
||||
global EXCHANGES
|
||||
global QUEUES
|
||||
EXCHANGES = {}
|
||||
QUEUES = {}
|
||||
|
||||
@@ -29,6 +29,8 @@ import sys
|
||||
|
||||
import gflags
|
||||
|
||||
from nova import utils
|
||||
|
||||
|
||||
class FlagValues(gflags.FlagValues):
|
||||
"""Extension of gflags.FlagValues that allows undefined and runtime flags.
|
||||
@@ -159,6 +161,7 @@ class StrWrapper(object):
|
||||
return str(val)
|
||||
raise KeyError(name)
|
||||
|
||||
|
||||
FLAGS = FlagValues()
|
||||
gflags.FLAGS = FLAGS
|
||||
gflags.DEFINE_flag(gflags.HelpFlag(), FLAGS)
|
||||
@@ -183,6 +186,12 @@ DEFINE_list = _wrapper(gflags.DEFINE_list)
|
||||
DEFINE_spaceseplist = _wrapper(gflags.DEFINE_spaceseplist)
|
||||
DEFINE_multistring = _wrapper(gflags.DEFINE_multistring)
|
||||
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):
|
||||
@@ -203,8 +212,11 @@ DEFINE_list('region_list',
|
||||
DEFINE_string('connection_type', 'libvirt', 'libvirt, xenapi or fake')
|
||||
DEFINE_string('aws_access_key_id', 'admin', 'AWS Access ID')
|
||||
DEFINE_string('aws_secret_access_key', 'admin', 'AWS Access Key')
|
||||
DEFINE_integer('glance_port', 9292, 'glance port')
|
||||
DEFINE_string('glance_host', utils.get_my_ip(), 'glance host')
|
||||
DEFINE_integer('s3_port', 3333, 's3 port')
|
||||
DEFINE_string('s3_host', '127.0.0.1', 's3 host')
|
||||
DEFINE_string('s3_host', utils.get_my_ip(), 's3 host (for infrastructure)')
|
||||
DEFINE_string('s3_dmz', utils.get_my_ip(), 's3 dmz ip (for instances)')
|
||||
DEFINE_string('compute_topic', 'compute', 'the topic compute nodes listen on')
|
||||
DEFINE_string('scheduler_topic', 'scheduler',
|
||||
'the topic scheduler nodes listen on')
|
||||
@@ -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_max_retries', 12, 'rabbit connection attempts')
|
||||
DEFINE_string('control_exchange', 'nova', 'the main exchange to connect to')
|
||||
DEFINE_string('ec2_url', 'http://127.0.0.1:8773/services/Cloud',
|
||||
'Url to ec2 api server')
|
||||
DEFINE_string('ec2_prefix', 'http', 'prefix for ec2')
|
||||
DEFINE_string('cc_host', utils.get_my_ip(), 'ip of api server')
|
||||
DEFINE_string('cc_dmz', utils.get_my_ip(), 'internal ip of api server')
|
||||
DEFINE_integer('cc_port', 8773, 'cloud controller port')
|
||||
DEFINE_string('ec2_suffix', '/services/Cloud', 'suffix for ec2')
|
||||
|
||||
DEFINE_string('default_project', 'openstack', 'default project for openstack')
|
||||
DEFINE_string('default_image', 'ami-11111',
|
||||
'default image to use, testing only')
|
||||
DEFINE_string('default_kernel', 'aki-11111',
|
||||
'default kernel to use, testing only')
|
||||
DEFINE_string('default_ramdisk', 'ari-11111',
|
||||
'default ramdisk to use, testing only')
|
||||
DEFINE_string('default_instance_type', 'm1.small',
|
||||
'default instance type to use, testing only')
|
||||
DEFINE_string('null_kernel', 'nokernel',
|
||||
'kernel image that indicates not to use a kernel,'
|
||||
' but to use a raw disk image instead')
|
||||
|
||||
DEFINE_string('vpn_image_id', 'ami-CLOUDPIPE', 'AMI for cloudpipe vpn server')
|
||||
DEFINE_string('vpn_image_id', 'ami-cloudpipe', 'AMI for cloudpipe vpn server')
|
||||
DEFINE_string('vpn_key_suffix',
|
||||
'-key',
|
||||
'Suffix to add to project name for vpn key')
|
||||
'-vpn',
|
||||
'Suffix to add to project name for vpn key and secgroups')
|
||||
|
||||
DEFINE_integer('auth_token_ttl', 3600, 'Seconds for auth tokens to linger')
|
||||
|
||||
|
||||
209
nova/process.py
209
nova/process.py
@@ -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)
|
||||
119
nova/rpc.py
119
nova/rpc.py
@@ -25,18 +25,18 @@ import json
|
||||
import logging
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
import uuid
|
||||
|
||||
from carrot import connection as carrot_connection
|
||||
from carrot import messaging
|
||||
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 fakerabbit
|
||||
from nova import flags
|
||||
from nova import context
|
||||
from nova import utils
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
@@ -91,15 +91,15 @@ class Consumer(messaging.Consumer):
|
||||
self.failed_connection = False
|
||||
break
|
||||
except: # Catching all because carrot sucks
|
||||
logging.exception("AMQP server on %s:%d is unreachable." \
|
||||
" Trying again in %d seconds." % (
|
||||
logging.exception(_("AMQP server on %s:%d is unreachable."
|
||||
" Trying again in %d seconds.") % (
|
||||
FLAGS.rabbit_host,
|
||||
FLAGS.rabbit_port,
|
||||
FLAGS.rabbit_retry_interval))
|
||||
self.failed_connection = True
|
||||
if self.failed_connection:
|
||||
logging.exception("Unable to connect to AMQP server" \
|
||||
" after %d tries. Shutting down." % FLAGS.rabbit_max_retries)
|
||||
logging.exception(_("Unable to connect to AMQP server"
|
||||
" after %d tries. Shutting down.") % FLAGS.rabbit_max_retries)
|
||||
sys.exit(1)
|
||||
|
||||
def fetch(self, no_ack=None, auto_ack=None, enable_callbacks=False):
|
||||
@@ -116,29 +116,21 @@ class Consumer(messaging.Consumer):
|
||||
self.declare()
|
||||
super(Consumer, self).fetch(no_ack, auto_ack, enable_callbacks)
|
||||
if self.failed_connection:
|
||||
logging.error("Reconnected to queue")
|
||||
logging.error(_("Reconnected to queue"))
|
||||
self.failed_connection = False
|
||||
# NOTE(vish): This is catching all errors because we really don't
|
||||
# exceptions to be logged 10 times a second if some
|
||||
# persistent failure occurs.
|
||||
except Exception: # pylint: disable-msg=W0703
|
||||
if not self.failed_connection:
|
||||
logging.exception("Failed to fetch message from queue")
|
||||
logging.exception(_("Failed to fetch message from queue"))
|
||||
self.failed_connection = True
|
||||
|
||||
def attach_to_eventlet(self):
|
||||
"""Only needed for unit tests!"""
|
||||
def fetch_repeatedly():
|
||||
while True:
|
||||
self.fetch(enable_callbacks=True)
|
||||
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
|
||||
timer = utils.LoopingCall(self.fetch, enable_callbacks=True)
|
||||
timer.start(0.1)
|
||||
return timer
|
||||
|
||||
|
||||
class Publisher(messaging.Publisher):
|
||||
@@ -161,7 +153,7 @@ class TopicConsumer(Consumer):
|
||||
class AdapterConsumer(TopicConsumer):
|
||||
"""Calls methods on a proxy object based on method and args"""
|
||||
def __init__(self, connection=None, topic="broadcast", proxy=None):
|
||||
LOG.debug('Initing the Adapter Consumer for %s' % (topic))
|
||||
LOG.debug(_('Initing the Adapter Consumer for %s') % (topic))
|
||||
self.proxy = proxy
|
||||
super(AdapterConsumer, self).__init__(connection=connection,
|
||||
topic=topic)
|
||||
@@ -176,7 +168,7 @@ class AdapterConsumer(TopicConsumer):
|
||||
|
||||
Example: {'method': 'echo', 'args': {'value': 42}}
|
||||
"""
|
||||
LOG.debug('received %s' % (message_data))
|
||||
LOG.debug(_('received %s') % (message_data))
|
||||
msg_id = message_data.pop('_msg_id', None)
|
||||
|
||||
ctxt = _unpack_context(message_data)
|
||||
@@ -189,18 +181,20 @@ class AdapterConsumer(TopicConsumer):
|
||||
# messages stay in the queue indefinitely, so for now
|
||||
# we just log the message and send an error string
|
||||
# back to the caller
|
||||
LOG.warn('no method for message: %s' % (message_data))
|
||||
msg_reply(msg_id, 'No method for message: %s' % message_data)
|
||||
LOG.warn(_('no method for message: %s') % (message_data))
|
||||
msg_reply(msg_id, _('No method for message: %s') % message_data)
|
||||
return
|
||||
|
||||
node_func = getattr(self.proxy, str(method))
|
||||
node_args = dict((str(k), v) for k, v in args.iteritems())
|
||||
# NOTE(vish): magic is fun!
|
||||
# pylint: disable-msg=W0142
|
||||
d = defer.maybeDeferred(node_func, context=ctxt, **node_args)
|
||||
if msg_id:
|
||||
d.addCallback(lambda rval: msg_reply(msg_id, rval, None))
|
||||
d.addErrback(lambda e: msg_reply(msg_id, None, e))
|
||||
try:
|
||||
rval = node_func(context=ctxt, **node_args)
|
||||
if msg_id:
|
||||
msg_reply(msg_id, rval, None)
|
||||
except Exception as e:
|
||||
if msg_id:
|
||||
msg_reply(msg_id, None, sys.exc_info())
|
||||
return
|
||||
|
||||
|
||||
@@ -242,14 +236,16 @@ class DirectPublisher(Publisher):
|
||||
def msg_reply(msg_id, reply=None, failure=None):
|
||||
"""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:
|
||||
message = failure.getErrorMessage()
|
||||
traceback = failure.getTraceback()
|
||||
logging.error("Returning exception %s to caller", message)
|
||||
logging.error(traceback)
|
||||
failure = (failure.type.__name__, str(failure.value), traceback)
|
||||
conn = Connection.instance()
|
||||
message = str(failure[1])
|
||||
tb = traceback.format_exception(*failure)
|
||||
logging.error(_("Returning exception %s to caller"), message)
|
||||
logging.error(tb)
|
||||
failure = (failure[0].__name__, str(failure[1]), tb)
|
||||
conn = Connection.instance(True)
|
||||
publisher = DirectPublisher(connection=conn, msg_id=msg_id)
|
||||
try:
|
||||
publisher.send({'result': reply, 'failure': failure})
|
||||
@@ -287,7 +283,7 @@ def _unpack_context(msg):
|
||||
if key.startswith('_context_'):
|
||||
value = msg.pop(key)
|
||||
context_dict[key[9:]] = value
|
||||
LOG.debug('unpacked context: %s', context_dict)
|
||||
LOG.debug(_('unpacked context: %s'), context_dict)
|
||||
return context.RequestContext.from_dict(context_dict)
|
||||
|
||||
|
||||
@@ -306,14 +302,13 @@ def _pack_context(msg, context):
|
||||
|
||||
def call(context, topic, msg):
|
||||
"""Sends a message on a topic and wait for a response"""
|
||||
LOG.debug("Making asynchronous call...")
|
||||
LOG.debug(_("Making asynchronous call..."))
|
||||
msg_id = uuid.uuid4().hex
|
||||
msg.update({'_msg_id': msg_id})
|
||||
LOG.debug("MSG_ID is %s" % (msg_id))
|
||||
LOG.debug(_("MSG_ID is %s") % (msg_id))
|
||||
_pack_context(msg, context)
|
||||
|
||||
class WaitMessage(object):
|
||||
|
||||
def __call__(self, data, message):
|
||||
"""Acks message and sets result."""
|
||||
message.ack()
|
||||
@@ -337,41 +332,15 @@ def call(context, topic, msg):
|
||||
except StopIteration:
|
||||
pass
|
||||
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
|
||||
|
||||
|
||||
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):
|
||||
"""Sends a message on a topic without waiting for a response"""
|
||||
LOG.debug("Making asynchronous cast...")
|
||||
@@ -384,7 +353,7 @@ def cast(context, topic, msg):
|
||||
|
||||
def generic_response(message_data, message):
|
||||
"""Logs a result and exits"""
|
||||
LOG.debug('response %s', message_data)
|
||||
LOG.debug(_('response %s'), message_data)
|
||||
message.ack()
|
||||
sys.exit(0)
|
||||
|
||||
@@ -393,8 +362,8 @@ def send_message(topic, message, wait=True):
|
||||
"""Sends a message for testing"""
|
||||
msg_id = uuid.uuid4().hex
|
||||
message.update({'_msg_id': msg_id})
|
||||
LOG.debug('topic is %s', topic)
|
||||
LOG.debug('message %s', message)
|
||||
LOG.debug(_('topic is %s'), topic)
|
||||
LOG.debug(_('message %s'), message)
|
||||
|
||||
if wait:
|
||||
consumer = messaging.Consumer(connection=Connection.instance(),
|
||||
|
||||
@@ -47,7 +47,7 @@ class SimpleScheduler(chance.ChanceScheduler):
|
||||
for result in results:
|
||||
(service, instance_cores) = result
|
||||
if instance_cores + instance_ref['vcpus'] > FLAGS.max_cores:
|
||||
raise driver.NoValidHost("All hosts have too many cores")
|
||||
raise driver.NoValidHost(_("All hosts have too many cores"))
|
||||
if self.service_is_up(service):
|
||||
# NOTE(vish): this probably belongs in the manager, if we
|
||||
# can generalize this somehow
|
||||
@@ -57,7 +57,7 @@ class SimpleScheduler(chance.ChanceScheduler):
|
||||
{'host': service['host'],
|
||||
'scheduled_at': now})
|
||||
return service['host']
|
||||
raise driver.NoValidHost("No hosts found")
|
||||
raise driver.NoValidHost(_("No hosts found"))
|
||||
|
||||
def schedule_create_volume(self, context, volume_id, *_args, **_kwargs):
|
||||
"""Picks a host that is up and has the fewest volumes."""
|
||||
@@ -66,7 +66,8 @@ class SimpleScheduler(chance.ChanceScheduler):
|
||||
for result in results:
|
||||
(service, volume_gigabytes) = result
|
||||
if volume_gigabytes + volume_ref['size'] > FLAGS.max_gigabytes:
|
||||
raise driver.NoValidHost("All hosts have too many gigabytes")
|
||||
raise driver.NoValidHost(_("All hosts have too many "
|
||||
"gigabytes"))
|
||||
if self.service_is_up(service):
|
||||
# NOTE(vish): this probably belongs in the manager, if we
|
||||
# can generalize this somehow
|
||||
@@ -76,7 +77,7 @@ class SimpleScheduler(chance.ChanceScheduler):
|
||||
{'host': service['host'],
|
||||
'scheduled_at': now})
|
||||
return service['host']
|
||||
raise driver.NoValidHost("No hosts found")
|
||||
raise driver.NoValidHost(_("No hosts found"))
|
||||
|
||||
def schedule_set_network_host(self, context, *_args, **_kwargs):
|
||||
"""Picks a host that is up and has the fewest networks."""
|
||||
@@ -85,7 +86,7 @@ class SimpleScheduler(chance.ChanceScheduler):
|
||||
for result in results:
|
||||
(service, instance_count) = result
|
||||
if instance_count >= FLAGS.max_networks:
|
||||
raise driver.NoValidHost("All hosts have too many networks")
|
||||
raise driver.NoValidHost(_("All hosts have too many networks"))
|
||||
if self.service_is_up(service):
|
||||
return service['host']
|
||||
raise driver.NoValidHost("No hosts found")
|
||||
raise driver.NoValidHost(_("No hosts found"))
|
||||
|
||||
151
nova/server.py
151
nova/server.py
@@ -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)
|
||||
@@ -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')
|
||||
@@ -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)
|
||||
@@ -54,7 +54,7 @@ os.makedirs(os.path.join(OSS_TEMPDIR, 'images'))
|
||||
os.makedirs(os.path.join(OSS_TEMPDIR, 'buckets'))
|
||||
|
||||
|
||||
class ObjectStoreTestCase(test.TrialTestCase):
|
||||
class ObjectStoreTestCase(test.TestCase):
|
||||
"""Test objectstore API directly."""
|
||||
|
||||
def setUp(self):
|
||||
@@ -191,7 +191,7 @@ class TestSite(server.Site):
|
||||
protocol = TestHTTPChannel
|
||||
|
||||
|
||||
class S3APITestCase(test.TrialTestCase):
|
||||
class S3APITestCase(test.TestCase):
|
||||
"""Test objectstore through S3 API."""
|
||||
|
||||
def setUp(self):
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -35,7 +35,7 @@ class Context(object):
|
||||
pass
|
||||
|
||||
|
||||
class AccessTestCase(test.TrialTestCase):
|
||||
class AccessTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
super(AccessTestCase, self).setUp()
|
||||
um = manager.AuthManager()
|
||||
@@ -208,17 +208,13 @@ class AuthManagerTestCase(object):
|
||||
# so it probably belongs in crypto_unittest
|
||||
# but I'm leaving it where I found it.
|
||||
with user_and_project_generator(self.manager) as (user, project):
|
||||
# NOTE(todd): Should mention why we must setup controller first
|
||||
# (somebody please clue me in)
|
||||
cloud_controller = cloud.CloudController()
|
||||
cloud_controller.setup()
|
||||
_key, cert_str = self.manager._generate_x509_cert('test1',
|
||||
'testproj')
|
||||
# NOTE(vish): Setup runs genroot.sh if it hasn't been run
|
||||
cloud.CloudController().setup()
|
||||
_key, cert_str = crypto.generate_x509_cert(user.id, project.id)
|
||||
logging.debug(cert_str)
|
||||
|
||||
# Need to verify that it's signed by the right intermediate CA
|
||||
full_chain = crypto.fetch_ca(project_id='testproj', chain=True)
|
||||
int_cert = crypto.fetch_ca(project_id='testproj', chain=False)
|
||||
full_chain = crypto.fetch_ca(project_id=project.id, chain=True)
|
||||
int_cert = crypto.fetch_ca(project_id=project.id, chain=False)
|
||||
cloud_cert = crypto.fetch_ca()
|
||||
logging.debug("CA chain:\n\n =====\n%s\n\n=====" % full_chain)
|
||||
signed_cert = X509.load_cert_string(cert_str)
|
||||
@@ -227,7 +223,8 @@ class AuthManagerTestCase(object):
|
||||
cloud_cert = X509.load_cert_string(cloud_cert)
|
||||
self.assertTrue(signed_cert.verify(chain_cert.get_pubkey()))
|
||||
self.assertTrue(signed_cert.verify(int_cert.get_pubkey()))
|
||||
if not FLAGS.use_intermediate_ca:
|
||||
|
||||
if not FLAGS.use_project_ca:
|
||||
self.assertTrue(signed_cert.verify(cloud_cert.get_pubkey()))
|
||||
else:
|
||||
self.assertFalse(signed_cert.verify(cloud_cert.get_pubkey()))
|
||||
@@ -326,24 +323,20 @@ class AuthManagerTestCase(object):
|
||||
self.assertTrue(user.is_admin())
|
||||
|
||||
|
||||
class AuthManagerLdapTestCase(AuthManagerTestCase, test.TrialTestCase):
|
||||
class AuthManagerLdapTestCase(AuthManagerTestCase, test.TestCase):
|
||||
auth_driver = 'nova.auth.ldapdriver.FakeLdapDriver'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
AuthManagerTestCase.__init__(self)
|
||||
test.TrialTestCase.__init__(self, *args, **kwargs)
|
||||
test.TestCase.__init__(self, *args, **kwargs)
|
||||
import nova.auth.fakeldap as fakeldap
|
||||
FLAGS.redis_db = 8
|
||||
if FLAGS.flush_db:
|
||||
logging.info("Flushing redis datastore")
|
||||
try:
|
||||
r = fakeldap.Redis.instance()
|
||||
r.flushdb()
|
||||
except:
|
||||
self.skip = True
|
||||
logging.info("Flushing datastore")
|
||||
r = fakeldap.Store.instance()
|
||||
r.flushdb()
|
||||
|
||||
|
||||
class AuthManagerDbTestCase(AuthManagerTestCase, test.TrialTestCase):
|
||||
class AuthManagerDbTestCase(AuthManagerTestCase, test.TestCase):
|
||||
auth_driver = 'nova.auth.dbdriver.DbDriver'
|
||||
|
||||
|
||||
@@ -22,22 +22,18 @@ import logging
|
||||
from M2Crypto import BIO
|
||||
from M2Crypto import RSA
|
||||
import os
|
||||
import StringIO
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
from eventlet import greenthread
|
||||
from twisted.internet import defer
|
||||
import unittest
|
||||
from xml.etree import ElementTree
|
||||
|
||||
from nova import context
|
||||
from nova import crypto
|
||||
from nova import db
|
||||
from nova import flags
|
||||
from nova import rpc
|
||||
from nova import service
|
||||
from nova import test
|
||||
from nova import utils
|
||||
from nova.auth import manager
|
||||
from nova.compute import power_state
|
||||
from nova.api.ec2 import cloud
|
||||
@@ -53,10 +49,11 @@ IMAGES_PATH = os.path.join(OSS_TEMPDIR, 'images')
|
||||
os.makedirs(IMAGES_PATH)
|
||||
|
||||
|
||||
class CloudTestCase(test.TrialTestCase):
|
||||
class CloudTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
super(CloudTestCase, self).setUp()
|
||||
self.flags(connection_type='fake', images_path=IMAGES_PATH)
|
||||
self.flags(connection_type='fake',
|
||||
images_path=IMAGES_PATH)
|
||||
|
||||
self.conn = rpc.Connection.instance()
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
@@ -64,27 +61,23 @@ class CloudTestCase(test.TrialTestCase):
|
||||
# set up our cloud
|
||||
self.cloud = cloud.CloudController()
|
||||
|
||||
# set up a service
|
||||
self.compute = utils.import_object(FLAGS.compute_manager)
|
||||
self.compute_consumer = rpc.AdapterConsumer(connection=self.conn,
|
||||
topic=FLAGS.compute_topic,
|
||||
proxy=self.compute)
|
||||
self.compute_consumer.attach_to_eventlet()
|
||||
self.network = utils.import_object(FLAGS.network_manager)
|
||||
self.network_consumer = rpc.AdapterConsumer(connection=self.conn,
|
||||
topic=FLAGS.network_topic,
|
||||
proxy=self.network)
|
||||
self.network_consumer.attach_to_eventlet()
|
||||
# set up services
|
||||
self.compute = service.Service.create(binary='nova-compute')
|
||||
self.compute.start()
|
||||
self.network = service.Service.create(binary='nova-network')
|
||||
self.network.start()
|
||||
|
||||
self.manager = manager.AuthManager()
|
||||
self.user = self.manager.create_user('admin', 'admin', 'admin', True)
|
||||
self.project = self.manager.create_project('proj', 'admin', 'proj')
|
||||
self.context = context.RequestContext(user=self.user,
|
||||
project=self.project)
|
||||
project=self.project)
|
||||
|
||||
def tearDown(self):
|
||||
self.manager.delete_project(self.project)
|
||||
self.manager.delete_user(self.user)
|
||||
self.compute.kill()
|
||||
self.network.kill()
|
||||
super(CloudTestCase, self).tearDown()
|
||||
|
||||
def _create_key(self, name):
|
||||
@@ -111,12 +104,13 @@ class CloudTestCase(test.TrialTestCase):
|
||||
{'address': address,
|
||||
'host': FLAGS.host})
|
||||
self.cloud.allocate_address(self.context)
|
||||
inst = db.instance_create(self.context, {})
|
||||
inst = db.instance_create(self.context, {'host': FLAGS.host})
|
||||
fixed = self.network.allocate_fixed_ip(self.context, inst['id'])
|
||||
ec2_id = cloud.internal_id_to_ec2_id(inst['internal_id'])
|
||||
self.cloud.associate_address(self.context,
|
||||
instance_id=ec2_id,
|
||||
public_ip=address)
|
||||
greenthread.sleep(0.3)
|
||||
self.cloud.disassociate_address(self.context,
|
||||
public_ip=address)
|
||||
self.cloud.release_address(self.context,
|
||||
@@ -199,7 +193,7 @@ class CloudTestCase(test.TrialTestCase):
|
||||
logging.debug("Need to watch instance %s until it's running..." %
|
||||
instance['instance_id'])
|
||||
while True:
|
||||
rv = yield defer.succeed(time.sleep(1))
|
||||
greenthread.sleep(1)
|
||||
info = self.cloud._get_instance(instance['instance_id'])
|
||||
logging.debug(info['state'])
|
||||
if info['state'] == power_state.RUNNING:
|
||||
@@ -22,8 +22,6 @@ Tests For Compute
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
from twisted.internet import defer
|
||||
|
||||
from nova import context
|
||||
from nova import db
|
||||
from nova import exception
|
||||
@@ -33,15 +31,17 @@ from nova import utils
|
||||
from nova.auth import manager
|
||||
from nova.compute import api as compute_api
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
|
||||
class ComputeTestCase(test.TrialTestCase):
|
||||
class ComputeTestCase(test.TestCase):
|
||||
"""Test case for compute"""
|
||||
def setUp(self):
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
super(ComputeTestCase, self).setUp()
|
||||
self.flags(connection_type='fake',
|
||||
stub_network=True,
|
||||
network_manager='nova.network.manager.FlatManager')
|
||||
self.compute = utils.import_object(FLAGS.compute_manager)
|
||||
self.compute_api = compute_api.ComputeAPI()
|
||||
@@ -94,24 +94,22 @@ class ComputeTestCase(test.TrialTestCase):
|
||||
db.security_group_destroy(self.context, group['id'])
|
||||
db.instance_destroy(self.context, ref[0]['id'])
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_run_terminate(self):
|
||||
"""Make sure it is possible to run and terminate 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())
|
||||
logging.info("Running instances: %s", instances)
|
||||
logging.info(_("Running instances: %s"), instances)
|
||||
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())
|
||||
logging.info("After terminating instances: %s", instances)
|
||||
logging.info(_("After terminating instances: %s"), instances)
|
||||
self.assertEqual(len(instances), 0)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_run_terminate_timestamps(self):
|
||||
"""Make sure timestamps are set for launched and destroyed"""
|
||||
instance_id = self._create_instance()
|
||||
@@ -119,42 +117,64 @@ class ComputeTestCase(test.TrialTestCase):
|
||||
self.assertEqual(instance_ref['launched_at'], None)
|
||||
self.assertEqual(instance_ref['deleted_at'], None)
|
||||
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)
|
||||
self.assert_(instance_ref['launched_at'] > launch)
|
||||
self.assertEqual(instance_ref['deleted_at'], None)
|
||||
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)
|
||||
instance_ref = db.instance_get(self.context, instance_id)
|
||||
self.assert_(instance_ref['launched_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):
|
||||
"""Ensure instance can be rebooted"""
|
||||
instance_id = self._create_instance()
|
||||
yield self.compute.run_instance(self.context, instance_id)
|
||||
yield self.compute.reboot_instance(self.context, instance_id)
|
||||
yield self.compute.terminate_instance(self.context, instance_id)
|
||||
self.compute.run_instance(self.context, instance_id)
|
||||
self.compute.reboot_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):
|
||||
"""Make sure we can get console output from 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)
|
||||
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):
|
||||
"""Ensure failure when running an instance that already exists"""
|
||||
instance_id = self._create_instance()
|
||||
yield self.compute.run_instance(self.context, instance_id)
|
||||
self.assertFailure(self.compute.run_instance(self.context,
|
||||
instance_id),
|
||||
exception.Error)
|
||||
yield self.compute.terminate_instance(self.context, instance_id)
|
||||
self.compute.run_instance(self.context, instance_id)
|
||||
self.assertRaises(exception.Error,
|
||||
self.compute.run_instance,
|
||||
self.context,
|
||||
instance_id)
|
||||
self.compute.terminate_instance(self.context, instance_id)
|
||||
@@ -24,7 +24,7 @@ FLAGS = flags.FLAGS
|
||||
flags.DEFINE_string('flags_unittest', 'foo', 'for testing purposes only')
|
||||
|
||||
|
||||
class FlagsTestCase(test.TrialTestCase):
|
||||
class FlagsTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(FlagsTestCase, self).setUp()
|
||||
86
nova/tests/test_middleware.py
Normal file
86
nova/tests/test_middleware.py
Normal file
@@ -0,0 +1,86 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import datetime
|
||||
import webob
|
||||
import webob.dec
|
||||
import webob.exc
|
||||
|
||||
from nova.api import ec2
|
||||
from nova import flags
|
||||
from nova import test
|
||||
from nova import utils
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
|
||||
@webob.dec.wsgify
|
||||
def conditional_forbid(req):
|
||||
"""Helper wsgi app returns 403 if param 'die' is 1."""
|
||||
if 'die' in req.params and req.params['die'] == '1':
|
||||
raise webob.exc.HTTPForbidden()
|
||||
return 'OK'
|
||||
|
||||
|
||||
class LockoutTestCase(test.TrialTestCase):
|
||||
"""Test case for the Lockout middleware."""
|
||||
def setUp(self): # pylint: disable-msg=C0103
|
||||
super(LockoutTestCase, self).setUp()
|
||||
utils.set_time_override()
|
||||
self.lockout = ec2.Lockout(conditional_forbid)
|
||||
|
||||
def tearDown(self): # pylint: disable-msg=C0103
|
||||
utils.clear_time_override()
|
||||
super(LockoutTestCase, self).tearDown()
|
||||
|
||||
def _send_bad_attempts(self, access_key, num_attempts=1):
|
||||
"""Fail x."""
|
||||
for i in xrange(num_attempts):
|
||||
req = webob.Request.blank('/?AWSAccessKeyId=%s&die=1' % access_key)
|
||||
self.assertEqual(req.get_response(self.lockout).status_int, 403)
|
||||
|
||||
def _is_locked_out(self, access_key):
|
||||
"""Sends a test request to see if key is locked out."""
|
||||
req = webob.Request.blank('/?AWSAccessKeyId=%s' % access_key)
|
||||
return (req.get_response(self.lockout).status_int == 403)
|
||||
|
||||
def test_lockout(self):
|
||||
self._send_bad_attempts('test', FLAGS.lockout_attempts)
|
||||
self.assertTrue(self._is_locked_out('test'))
|
||||
|
||||
def test_timeout(self):
|
||||
self._send_bad_attempts('test', FLAGS.lockout_attempts)
|
||||
self.assertTrue(self._is_locked_out('test'))
|
||||
utils.advance_time_seconds(FLAGS.lockout_minutes * 60)
|
||||
self.assertFalse(self._is_locked_out('test'))
|
||||
|
||||
def test_multiple_keys(self):
|
||||
self._send_bad_attempts('test1', FLAGS.lockout_attempts)
|
||||
self.assertTrue(self._is_locked_out('test1'))
|
||||
self.assertFalse(self._is_locked_out('test2'))
|
||||
utils.advance_time_seconds(FLAGS.lockout_minutes * 60)
|
||||
self.assertFalse(self._is_locked_out('test1'))
|
||||
self.assertFalse(self._is_locked_out('test2'))
|
||||
|
||||
def test_window_timeout(self):
|
||||
self._send_bad_attempts('test', FLAGS.lockout_attempts - 1)
|
||||
self.assertFalse(self._is_locked_out('test'))
|
||||
utils.advance_time_seconds(FLAGS.lockout_window * 60)
|
||||
self._send_bad_attempts('test', FLAGS.lockout_attempts - 1)
|
||||
self.assertFalse(self._is_locked_out('test'))
|
||||
55
nova/tests/test_misc.py
Normal file
55
nova/tests/test_misc.py
Normal 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()
|
||||
@@ -26,6 +26,7 @@ from nova import context
|
||||
from nova import db
|
||||
from nova import exception
|
||||
from nova import flags
|
||||
from nova import service
|
||||
from nova import test
|
||||
from nova import utils
|
||||
from nova.auth import manager
|
||||
@@ -33,13 +34,14 @@ from nova.auth import manager
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
|
||||
class NetworkTestCase(test.TrialTestCase):
|
||||
class NetworkTestCase(test.TestCase):
|
||||
"""Test cases for network code"""
|
||||
def setUp(self):
|
||||
super(NetworkTestCase, self).setUp()
|
||||
# NOTE(vish): if you change these flags, make sure to change the
|
||||
# flags in the corresponding section in nova-dhcpbridge
|
||||
self.flags(connection_type='fake',
|
||||
fake_call=True,
|
||||
fake_network=True,
|
||||
network_size=16,
|
||||
num_networks=5)
|
||||
@@ -56,16 +58,13 @@ class NetworkTestCase(test.TrialTestCase):
|
||||
# create the necessary network data for the project
|
||||
user_context = context.RequestContext(project=self.projects[i],
|
||||
user=self.user)
|
||||
network_ref = self.network.get_network(user_context)
|
||||
self.network.set_network_host(context.get_admin_context(),
|
||||
network_ref['id'])
|
||||
host = self.network.get_network_host(user_context.elevated())
|
||||
instance_ref = self._create_instance(0)
|
||||
self.instance_id = instance_ref['id']
|
||||
instance_ref = self._create_instance(1)
|
||||
self.instance2_id = instance_ref['id']
|
||||
|
||||
def tearDown(self):
|
||||
super(NetworkTestCase, self).tearDown()
|
||||
# TODO(termie): this should really be instantiating clean datastores
|
||||
# in between runs, one failure kills all the tests
|
||||
db.instance_destroy(context.get_admin_context(), self.instance_id)
|
||||
@@ -73,6 +72,7 @@ class NetworkTestCase(test.TrialTestCase):
|
||||
for project in self.projects:
|
||||
self.manager.delete_project(project)
|
||||
self.manager.delete_user(self.user)
|
||||
super(NetworkTestCase, self).tearDown()
|
||||
|
||||
def _create_instance(self, project_num, mac=None):
|
||||
if not mac:
|
||||
@@ -20,8 +20,6 @@ Unit Tests for remote procedure calls using queue
|
||||
"""
|
||||
import logging
|
||||
|
||||
from twisted.internet import defer
|
||||
|
||||
from nova import context
|
||||
from nova import flags
|
||||
from nova import rpc
|
||||
@@ -31,32 +29,31 @@ from nova import test
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
|
||||
class RpcTestCase(test.TrialTestCase):
|
||||
class RpcTestCase(test.TestCase):
|
||||
"""Test cases for rpc"""
|
||||
def setUp(self):
|
||||
super(RpcTestCase, self).setUp()
|
||||
self.conn = rpc.Connection.instance()
|
||||
self.conn = rpc.Connection.instance(True)
|
||||
self.receiver = TestReceiver()
|
||||
self.consumer = rpc.AdapterConsumer(connection=self.conn,
|
||||
topic='test',
|
||||
proxy=self.receiver)
|
||||
self.consumer.attach_to_twisted()
|
||||
self.consumer.attach_to_eventlet()
|
||||
self.context = context.get_admin_context()
|
||||
|
||||
def test_call_succeed(self):
|
||||
"""Get a value through rpc call"""
|
||||
value = 42
|
||||
result = yield rpc.call_twisted(self.context,
|
||||
'test', {"method": "echo",
|
||||
result = rpc.call(self.context, 'test', {"method": "echo",
|
||||
"args": {"value": value}})
|
||||
self.assertEqual(value, result)
|
||||
|
||||
def test_context_passed(self):
|
||||
"""Makes sure a context is passed through rpc call"""
|
||||
value = 42
|
||||
result = yield rpc.call_twisted(self.context,
|
||||
'test', {"method": "context",
|
||||
"args": {"value": value}})
|
||||
result = rpc.call(self.context,
|
||||
'test', {"method": "context",
|
||||
"args": {"value": value}})
|
||||
self.assertEqual(self.context.to_dict(), result)
|
||||
|
||||
def test_call_exception(self):
|
||||
@@ -67,18 +64,48 @@ class RpcTestCase(test.TrialTestCase):
|
||||
to an int in the test.
|
||||
"""
|
||||
value = 42
|
||||
self.assertFailure(rpc.call_twisted(self.context, 'test',
|
||||
{"method": "fail",
|
||||
"args": {"value": value}}),
|
||||
rpc.RemoteError)
|
||||
self.assertRaises(rpc.RemoteError,
|
||||
rpc.call,
|
||||
self.context,
|
||||
'test',
|
||||
{"method": "fail",
|
||||
"args": {"value": value}})
|
||||
try:
|
||||
yield rpc.call_twisted(self.context,
|
||||
'test', {"method": "fail",
|
||||
"args": {"value": value}})
|
||||
rpc.call(self.context,
|
||||
'test',
|
||||
{"method": "fail",
|
||||
"args": {"value": value}})
|
||||
self.fail("should have thrown rpc.RemoteError")
|
||||
except rpc.RemoteError as exc:
|
||||
self.assertEqual(int(exc.value), value)
|
||||
|
||||
def test_nested_calls(self):
|
||||
"""Test that we can do an rpc.call inside another call"""
|
||||
class Nested(object):
|
||||
@staticmethod
|
||||
def echo(context, queue, value):
|
||||
"""Calls echo in the passed queue"""
|
||||
logging.debug("Nested received %s, %s", queue, value)
|
||||
ret = rpc.call(context,
|
||||
queue,
|
||||
{"method": "echo",
|
||||
"args": {"value": value}})
|
||||
logging.debug("Nested return %s", ret)
|
||||
return value
|
||||
|
||||
nested = Nested()
|
||||
conn = rpc.Connection.instance(True)
|
||||
consumer = rpc.AdapterConsumer(connection=conn,
|
||||
topic='nested',
|
||||
proxy=nested)
|
||||
consumer.attach_to_eventlet()
|
||||
value = 42
|
||||
result = rpc.call(self.context,
|
||||
'nested', {"method": "echo",
|
||||
"args": {"queue": "test",
|
||||
"value": value}})
|
||||
self.assertEqual(value, result)
|
||||
|
||||
|
||||
class TestReceiver(object):
|
||||
"""Simple Proxy class so the consumer has methods to call
|
||||
@@ -89,13 +116,13 @@ class TestReceiver(object):
|
||||
def echo(context, value):
|
||||
"""Simply returns whatever value is sent in"""
|
||||
logging.debug("Received %s", value)
|
||||
return defer.succeed(value)
|
||||
return value
|
||||
|
||||
@staticmethod
|
||||
def context(context, value):
|
||||
"""Returns dictionary version of context"""
|
||||
logging.debug("Received %s", context)
|
||||
return defer.succeed(context.to_dict())
|
||||
return context.to_dict()
|
||||
|
||||
@staticmethod
|
||||
def fail(context, value):
|
||||
@@ -33,6 +33,7 @@ from nova.scheduler import driver
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
flags.DECLARE('max_cores', 'nova.scheduler.simple')
|
||||
flags.DECLARE('stub_network', 'nova.compute.manager')
|
||||
|
||||
|
||||
class TestDriver(driver.Scheduler):
|
||||
@@ -44,11 +45,11 @@ class TestDriver(driver.Scheduler):
|
||||
return 'named_host'
|
||||
|
||||
|
||||
class SchedulerTestCase(test.TrialTestCase):
|
||||
class SchedulerTestCase(test.TestCase):
|
||||
"""Test case for scheduler"""
|
||||
def setUp(self):
|
||||
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):
|
||||
scheduler = manager.SchedulerManager()
|
||||
@@ -73,11 +74,12 @@ class SchedulerTestCase(test.TrialTestCase):
|
||||
scheduler.named_method(ctxt, 'topic', num=7)
|
||||
|
||||
|
||||
class SimpleDriverTestCase(test.TrialTestCase):
|
||||
class SimpleDriverTestCase(test.TestCase):
|
||||
"""Test case for simple driver"""
|
||||
def setUp(self):
|
||||
super(SimpleDriverTestCase, self).setUp()
|
||||
self.flags(connection_type='fake',
|
||||
stub_network=True,
|
||||
max_cores=4,
|
||||
max_gigabytes=4,
|
||||
network_manager='nova.network.manager.FlatManager',
|
||||
@@ -122,12 +124,12 @@ class SimpleDriverTestCase(test.TrialTestCase):
|
||||
'nova-compute',
|
||||
'compute',
|
||||
FLAGS.compute_manager)
|
||||
compute1.startService()
|
||||
compute1.start()
|
||||
compute2 = service.Service('host2',
|
||||
'nova-compute',
|
||||
'compute',
|
||||
FLAGS.compute_manager)
|
||||
compute2.startService()
|
||||
compute2.start()
|
||||
hosts = self.scheduler.driver.hosts_up(self.context, 'compute')
|
||||
self.assertEqual(len(hosts), 2)
|
||||
compute1.kill()
|
||||
@@ -139,12 +141,12 @@ class SimpleDriverTestCase(test.TrialTestCase):
|
||||
'nova-compute',
|
||||
'compute',
|
||||
FLAGS.compute_manager)
|
||||
compute1.startService()
|
||||
compute1.start()
|
||||
compute2 = service.Service('host2',
|
||||
'nova-compute',
|
||||
'compute',
|
||||
FLAGS.compute_manager)
|
||||
compute2.startService()
|
||||
compute2.start()
|
||||
instance_id1 = self._create_instance()
|
||||
compute1.run_instance(self.context, instance_id1)
|
||||
instance_id2 = self._create_instance()
|
||||
@@ -162,12 +164,12 @@ class SimpleDriverTestCase(test.TrialTestCase):
|
||||
'nova-compute',
|
||||
'compute',
|
||||
FLAGS.compute_manager)
|
||||
compute1.startService()
|
||||
compute1.start()
|
||||
compute2 = service.Service('host2',
|
||||
'nova-compute',
|
||||
'compute',
|
||||
FLAGS.compute_manager)
|
||||
compute2.startService()
|
||||
compute2.start()
|
||||
instance_ids1 = []
|
||||
instance_ids2 = []
|
||||
for index in xrange(FLAGS.max_cores):
|
||||
@@ -195,12 +197,12 @@ class SimpleDriverTestCase(test.TrialTestCase):
|
||||
'nova-volume',
|
||||
'volume',
|
||||
FLAGS.volume_manager)
|
||||
volume1.startService()
|
||||
volume1.start()
|
||||
volume2 = service.Service('host2',
|
||||
'nova-volume',
|
||||
'volume',
|
||||
FLAGS.volume_manager)
|
||||
volume2.startService()
|
||||
volume2.start()
|
||||
volume_id1 = self._create_volume()
|
||||
volume1.create_volume(self.context, volume_id1)
|
||||
volume_id2 = self._create_volume()
|
||||
@@ -218,12 +220,12 @@ class SimpleDriverTestCase(test.TrialTestCase):
|
||||
'nova-volume',
|
||||
'volume',
|
||||
FLAGS.volume_manager)
|
||||
volume1.startService()
|
||||
volume1.start()
|
||||
volume2 = service.Service('host2',
|
||||
'nova-volume',
|
||||
'volume',
|
||||
FLAGS.volume_manager)
|
||||
volume2.startService()
|
||||
volume2.start()
|
||||
volume_ids1 = []
|
||||
volume_ids2 = []
|
||||
for index in xrange(FLAGS.max_gigabytes):
|
||||
@@ -30,9 +30,11 @@ FLAGS = flags.FLAGS
|
||||
flags.DECLARE('instances_path', 'nova.compute.manager')
|
||||
|
||||
|
||||
class LibvirtConnTestCase(test.TrialTestCase):
|
||||
class LibvirtConnTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
super(LibvirtConnTestCase, self).setUp()
|
||||
libvirt_conn._late_load_cheetah()
|
||||
self.flags(fake_call=True)
|
||||
self.manager = manager.AuthManager()
|
||||
self.user = self.manager.create_user('fake', 'fake', 'fake',
|
||||
admin=True)
|
||||
@@ -40,33 +42,64 @@ class LibvirtConnTestCase(test.TrialTestCase):
|
||||
self.network = utils.import_object(FLAGS.network_manager)
|
||||
FLAGS.instances_path = ''
|
||||
|
||||
def test_get_uri_and_template(self):
|
||||
ip = '10.11.12.13'
|
||||
test_ip = '10.11.12.13'
|
||||
test_instance = {'memory_kb': '1024000',
|
||||
'basepath': '/some/path',
|
||||
'bridge_name': 'br100',
|
||||
'mac_address': '02:12:34:46:56:67',
|
||||
'vcpus': 2,
|
||||
'project_id': 'fake',
|
||||
'bridge': 'br101',
|
||||
'instance_type': 'm1.small'}
|
||||
|
||||
instance = {'internal_id': 1,
|
||||
'memory_kb': '1024000',
|
||||
'basepath': '/some/path',
|
||||
'bridge_name': 'br100',
|
||||
'mac_address': '02:12:34:46:56:67',
|
||||
'vcpus': 2,
|
||||
'project_id': 'fake',
|
||||
'bridge': 'br101',
|
||||
'instance_type': 'm1.small'}
|
||||
def test_xml_and_uri_no_ramdisk_no_kernel(self):
|
||||
instance_data = dict(self.test_instance)
|
||||
self._check_xml_and_uri(instance_data,
|
||||
expect_kernel=False, expect_ramdisk=False)
|
||||
|
||||
def test_xml_and_uri_no_ramdisk(self):
|
||||
instance_data = dict(self.test_instance)
|
||||
instance_data['kernel_id'] = 'aki-deadbeef'
|
||||
self._check_xml_and_uri(instance_data,
|
||||
expect_kernel=True, expect_ramdisk=False)
|
||||
|
||||
def test_xml_and_uri_no_kernel(self):
|
||||
instance_data = dict(self.test_instance)
|
||||
instance_data['ramdisk_id'] = 'ari-deadbeef'
|
||||
self._check_xml_and_uri(instance_data,
|
||||
expect_kernel=False, expect_ramdisk=False)
|
||||
|
||||
def test_xml_and_uri(self):
|
||||
instance_data = dict(self.test_instance)
|
||||
instance_data['ramdisk_id'] = 'ari-deadbeef'
|
||||
instance_data['kernel_id'] = 'aki-deadbeef'
|
||||
self._check_xml_and_uri(instance_data,
|
||||
expect_kernel=True, expect_ramdisk=True)
|
||||
|
||||
def test_xml_and_uri_rescue(self):
|
||||
instance_data = dict(self.test_instance)
|
||||
instance_data['ramdisk_id'] = 'ari-deadbeef'
|
||||
instance_data['kernel_id'] = 'aki-deadbeef'
|
||||
self._check_xml_and_uri(instance_data, expect_kernel=True,
|
||||
expect_ramdisk=True, rescue=True)
|
||||
|
||||
def _check_xml_and_uri(self, instance, expect_ramdisk, expect_kernel,
|
||||
rescue=False):
|
||||
user_context = context.RequestContext(project=self.project,
|
||||
user=self.user)
|
||||
instance_ref = db.instance_create(user_context, instance)
|
||||
network_ref = self.network.get_network(user_context)
|
||||
self.network.set_network_host(context.get_admin_context(),
|
||||
network_ref['id'])
|
||||
host = self.network.get_network_host(user_context.elevated())
|
||||
network_ref = db.project_get_network(context.get_admin_context(),
|
||||
self.project.id)
|
||||
|
||||
fixed_ip = {'address': ip,
|
||||
fixed_ip = {'address': self.test_ip,
|
||||
'network_id': network_ref['id']}
|
||||
|
||||
ctxt = context.get_admin_context()
|
||||
fixed_ip_ref = db.fixed_ip_create(ctxt, fixed_ip)
|
||||
db.fixed_ip_update(ctxt, ip, {'allocated': True,
|
||||
'instance_id': instance_ref['id']})
|
||||
db.fixed_ip_update(ctxt, self.test_ip,
|
||||
{'allocated': True,
|
||||
'instance_id': instance_ref['id']})
|
||||
|
||||
type_uri_map = {'qemu': ('qemu:///system',
|
||||
[(lambda t: t.find('.').get('type'), 'qemu'),
|
||||
@@ -78,23 +111,72 @@ class LibvirtConnTestCase(test.TrialTestCase):
|
||||
(lambda t: t.find('./devices/emulator'), None)]),
|
||||
'uml': ('uml:///system',
|
||||
[(lambda t: t.find('.').get('type'), 'uml'),
|
||||
(lambda t: t.find('./os/type').text, 'uml')])}
|
||||
(lambda t: t.find('./os/type').text, 'uml')]),
|
||||
'xen': ('xen:///',
|
||||
[(lambda t: t.find('.').get('type'), 'xen'),
|
||||
(lambda t: t.find('./os/type').text, 'linux')]),
|
||||
}
|
||||
|
||||
for hypervisor_type in ['qemu', 'kvm', 'xen']:
|
||||
check_list = type_uri_map[hypervisor_type][1]
|
||||
|
||||
if rescue:
|
||||
check = (lambda t: t.find('./os/kernel').text.split('/')[1],
|
||||
'rescue-kernel')
|
||||
check_list.append(check)
|
||||
check = (lambda t: t.find('./os/initrd').text.split('/')[1],
|
||||
'rescue-ramdisk')
|
||||
check_list.append(check)
|
||||
else:
|
||||
if expect_kernel:
|
||||
check = (lambda t: t.find('./os/kernel').text.split(
|
||||
'/')[1], 'kernel')
|
||||
else:
|
||||
check = (lambda t: t.find('./os/kernel'), None)
|
||||
check_list.append(check)
|
||||
|
||||
if expect_ramdisk:
|
||||
check = (lambda t: t.find('./os/initrd').text.split(
|
||||
'/')[1], 'ramdisk')
|
||||
else:
|
||||
check = (lambda t: t.find('./os/initrd'), None)
|
||||
check_list.append(check)
|
||||
|
||||
common_checks = [
|
||||
(lambda t: t.find('.').tag, 'domain'),
|
||||
(lambda t: t.find('./devices/interface/filterref/parameter').\
|
||||
get('name'), 'IP'),
|
||||
(lambda t: t.find('./devices/interface/filterref/parameter').\
|
||||
get('value'), '10.11.12.13')]
|
||||
(lambda t: t.find(
|
||||
'./devices/interface/filterref/parameter').get('name'), 'IP'),
|
||||
(lambda t: t.find(
|
||||
'./devices/interface/filterref/parameter').get(
|
||||
'value'), '10.11.12.13'),
|
||||
(lambda t: t.findall(
|
||||
'./devices/interface/filterref/parameter')[1].get(
|
||||
'name'), 'DHCPSERVER'),
|
||||
(lambda t: t.findall(
|
||||
'./devices/interface/filterref/parameter')[1].get(
|
||||
'value'), '10.0.0.1'),
|
||||
(lambda t: t.find('./devices/serial/source').get(
|
||||
'path').split('/')[1], 'console.log'),
|
||||
(lambda t: t.find('./memory').text, '2097152')]
|
||||
if rescue:
|
||||
common_checks += [
|
||||
(lambda t: t.findall('./devices/disk/source')[0].get(
|
||||
'file').split('/')[1], 'rescue-disk'),
|
||||
(lambda t: t.findall('./devices/disk/source')[1].get(
|
||||
'file').split('/')[1], 'disk')]
|
||||
else:
|
||||
common_checks += [(lambda t: t.findall(
|
||||
'./devices/disk/source')[0].get('file').split('/')[1],
|
||||
'disk')]
|
||||
|
||||
for (libvirt_type, (expected_uri, checks)) in type_uri_map.iteritems():
|
||||
FLAGS.libvirt_type = libvirt_type
|
||||
conn = libvirt_conn.LibvirtConnection(True)
|
||||
|
||||
uri, _template, _rescue = conn.get_uri_and_templates()
|
||||
uri = conn.get_uri()
|
||||
self.assertEquals(uri, expected_uri)
|
||||
|
||||
xml = conn.to_xml(instance_ref)
|
||||
xml = conn.to_xml(instance_ref, rescue)
|
||||
tree = xml_to_tree(xml)
|
||||
for i, (check, expected_result) in enumerate(checks):
|
||||
self.assertEqual(check(tree),
|
||||
@@ -106,6 +188,9 @@ class LibvirtConnTestCase(test.TrialTestCase):
|
||||
expected_result,
|
||||
'%s failed common check %d' % (xml, i))
|
||||
|
||||
# This test is supposed to make sure we don't override a specifically
|
||||
# set uri
|
||||
#
|
||||
# Deliberately not just assigning this string to FLAGS.libvirt_uri and
|
||||
# checking against that later on. This way we make sure the
|
||||
# implementation doesn't fiddle around with the FLAGS.
|
||||
@@ -114,7 +199,7 @@ class LibvirtConnTestCase(test.TrialTestCase):
|
||||
for (libvirt_type, (expected_uri, checks)) in type_uri_map.iteritems():
|
||||
FLAGS.libvirt_type = libvirt_type
|
||||
conn = libvirt_conn.LibvirtConnection(True)
|
||||
uri, _template, _rescue = conn.get_uri_and_templates()
|
||||
uri = conn.get_uri()
|
||||
self.assertEquals(uri, testuri)
|
||||
|
||||
def tearDown(self):
|
||||
@@ -123,7 +208,7 @@ class LibvirtConnTestCase(test.TrialTestCase):
|
||||
self.manager.delete_user(self.user)
|
||||
|
||||
|
||||
class NWFilterTestCase(test.TrialTestCase):
|
||||
class NWFilterTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(NWFilterTestCase, self).setUp()
|
||||
@@ -235,7 +320,7 @@ class NWFilterTestCase(test.TrialTestCase):
|
||||
'project_id': 'fake'})
|
||||
inst_id = instance_ref['id']
|
||||
|
||||
def _ensure_all_called(_):
|
||||
def _ensure_all_called():
|
||||
instance_filter = 'nova-instance-%s' % instance_ref['name']
|
||||
secgroup_filter = 'nova-secgroup-%s' % self.security_group['id']
|
||||
for required in [secgroup_filter, 'allow-dhcp-server',
|
||||
@@ -252,8 +337,7 @@ class NWFilterTestCase(test.TrialTestCase):
|
||||
self.security_group.id)
|
||||
instance = db.instance_get(self.context, inst_id)
|
||||
|
||||
d = self.fw.setup_nwfilters_for_instance(instance)
|
||||
d.addCallback(_ensure_all_called)
|
||||
d.addCallback(lambda _: self.teardown_security_group())
|
||||
|
||||
return d
|
||||
self.fw.setup_base_nwfilters()
|
||||
self.fw.setup_nwfilters_for_instance(instance)
|
||||
_ensure_all_called()
|
||||
self.teardown_security_group()
|
||||
@@ -21,8 +21,6 @@ Tests for Volume Code.
|
||||
"""
|
||||
import logging
|
||||
|
||||
from twisted.internet import defer
|
||||
|
||||
from nova import context
|
||||
from nova import exception
|
||||
from nova import db
|
||||
@@ -33,7 +31,7 @@ from nova import utils
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
|
||||
class VolumeTestCase(test.TrialTestCase):
|
||||
class VolumeTestCase(test.TestCase):
|
||||
"""Test Case for volumes."""
|
||||
|
||||
def setUp(self):
|
||||
@@ -56,51 +54,48 @@ class VolumeTestCase(test.TrialTestCase):
|
||||
vol['attach_status'] = "detached"
|
||||
return db.volume_create(context.get_admin_context(), vol)['id']
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_create_delete_volume(self):
|
||||
"""Test volume can be created and deleted."""
|
||||
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(),
|
||||
volume_id).id)
|
||||
|
||||
yield self.volume.delete_volume(self.context, volume_id)
|
||||
self.volume.delete_volume(self.context, volume_id)
|
||||
self.assertRaises(exception.NotFound,
|
||||
db.volume_get,
|
||||
self.context,
|
||||
volume_id)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_too_big_volume(self):
|
||||
"""Ensure failure if a too large of a volume is requested."""
|
||||
# FIXME(vish): validation needs to move into the data layer in
|
||||
# volume_create
|
||||
defer.returnValue(True)
|
||||
return True
|
||||
try:
|
||||
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")
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_too_many_volumes(self):
|
||||
"""Ensure that NoMoreTargets is raised when we run out of volumes."""
|
||||
vols = []
|
||||
total_slots = FLAGS.iscsi_num_targets
|
||||
for _index in xrange(total_slots):
|
||||
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)
|
||||
volume_id = self._create_volume()
|
||||
self.assertFailure(self.volume.create_volume(self.context,
|
||||
volume_id),
|
||||
db.NoMoreTargets)
|
||||
self.assertRaises(db.NoMoreTargets,
|
||||
self.volume.create_volume,
|
||||
self.context,
|
||||
volume_id)
|
||||
db.volume_destroy(context.get_admin_context(), volume_id)
|
||||
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):
|
||||
"""Make sure volume can be attached and detached from instance."""
|
||||
inst = {}
|
||||
@@ -115,15 +110,15 @@ class VolumeTestCase(test.TrialTestCase):
|
||||
instance_id = db.instance_create(self.context, inst)['id']
|
||||
mountpoint = "/dev/sdf"
|
||||
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:
|
||||
db.volume_attached(self.context, volume_id, instance_id,
|
||||
mountpoint)
|
||||
else:
|
||||
yield self.compute.attach_volume(self.context,
|
||||
instance_id,
|
||||
volume_id,
|
||||
mountpoint)
|
||||
self.compute.attach_volume(self.context,
|
||||
instance_id,
|
||||
volume_id,
|
||||
mountpoint)
|
||||
vol = db.volume_get(context.get_admin_context(), volume_id)
|
||||
self.assertEqual(vol['status'], "in-use")
|
||||
self.assertEqual(vol['attach_status'], "attached")
|
||||
@@ -131,25 +126,26 @@ class VolumeTestCase(test.TrialTestCase):
|
||||
instance_ref = db.volume_get_instance(self.context, volume_id)
|
||||
self.assertEqual(instance_ref['id'], instance_id)
|
||||
|
||||
self.assertFailure(self.volume.delete_volume(self.context, volume_id),
|
||||
exception.Error)
|
||||
self.assertRaises(exception.Error,
|
||||
self.volume.delete_volume,
|
||||
self.context,
|
||||
volume_id)
|
||||
if FLAGS.fake_tests:
|
||||
db.volume_detached(self.context, volume_id)
|
||||
else:
|
||||
yield self.compute.detach_volume(self.context,
|
||||
instance_id,
|
||||
volume_id)
|
||||
self.compute.detach_volume(self.context,
|
||||
instance_id,
|
||||
volume_id)
|
||||
vol = db.volume_get(self.context, volume_id)
|
||||
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,
|
||||
db.volume_get,
|
||||
self.context,
|
||||
volume_id)
|
||||
db.instance_destroy(self.context, instance_id)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_concurrent_volumes_get_different_targets(self):
|
||||
"""Ensure multiple concurrent volumes get different targets."""
|
||||
volume_ids = []
|
||||
@@ -164,15 +160,11 @@ class VolumeTestCase(test.TrialTestCase):
|
||||
self.assert_(iscsi_target not in targets)
|
||||
targets.append(iscsi_target)
|
||||
logging.debug("Target %s allocated", iscsi_target)
|
||||
deferreds = []
|
||||
total_slots = FLAGS.iscsi_num_targets
|
||||
for _index in xrange(total_slots):
|
||||
volume_id = self._create_volume()
|
||||
d = self.volume.create_volume(self.context, volume_id)
|
||||
d.addCallback(_check)
|
||||
d.addErrback(self.fail)
|
||||
deferreds.append(d)
|
||||
yield defer.DeferredList(deferreds)
|
||||
_check(d)
|
||||
for volume_id in volume_ids:
|
||||
self.volume.delete_volume(self.context, volume_id)
|
||||
|
||||
264
nova/tests/test_xenapi.py
Normal file
264
nova/tests/test_xenapi.py
Normal 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
|
||||
20
nova/tests/xenapi/__init__.py
Normal file
20
nova/tests/xenapi/__init__.py
Normal file
@@ -0,0 +1,20 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2010 Citrix Systems, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
:mod:`xenapi` -- Stubs for XenAPI
|
||||
=================================
|
||||
"""
|
||||
169
nova/tests/xenapi/stubs.py
Normal file
169
nova/tests/xenapi/stubs.py
Normal 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
|
||||
@@ -43,7 +43,7 @@ else:
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
flags.DEFINE_string('logdir', None, 'directory to keep log files in '
|
||||
flags.DEFINE_string('logdir', None, 'directory to keep log files in '
|
||||
'(will be prepended to $logfile)')
|
||||
|
||||
|
||||
@@ -208,7 +208,7 @@ def stop(pidfile):
|
||||
pid = None
|
||||
|
||||
if not pid:
|
||||
message = "pidfile %s does not exist. Daemon not running?\n"
|
||||
message = _("pidfile %s does not exist. Daemon not running?\n")
|
||||
sys.stderr.write(message % pidfile)
|
||||
# Not an error in a restart
|
||||
return
|
||||
@@ -229,7 +229,7 @@ def stop(pidfile):
|
||||
|
||||
|
||||
def serve(filename):
|
||||
logging.debug("Serving %s" % filename)
|
||||
logging.debug(_("Serving %s") % filename)
|
||||
name = os.path.basename(filename)
|
||||
OptionsClass = WrapTwistedOptions(TwistdServerOptions)
|
||||
options = OptionsClass()
|
||||
@@ -284,7 +284,7 @@ def serve(filename):
|
||||
else:
|
||||
logging.getLogger().setLevel(logging.WARNING)
|
||||
|
||||
logging.debug("Full set of FLAGS:")
|
||||
logging.debug(_("Full set of FLAGS:"))
|
||||
for flag in FLAGS:
|
||||
logging.debug("%s : %s" % (flag, FLAGS.get(flag, None)))
|
||||
|
||||
|
||||
@@ -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
|
||||
150
run_tests.py
150
run_tests.py
@@ -1,122 +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
|
||||
# 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
|
||||
# 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.
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
This 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 unittest
|
||||
import sys
|
||||
|
||||
from twisted.scripts import trial as trial_script
|
||||
|
||||
from nova import flags
|
||||
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 *
|
||||
from nose import config
|
||||
from nose import result
|
||||
from nose import core
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
flags.DEFINE_bool('flush_db', True,
|
||||
'Flush the database before running fake tests')
|
||||
flags.DEFINE_string('tests_stderr', 'run_tests.err.log',
|
||||
'Path to where to pipe STDERR during test runs.'
|
||||
' Default = "run_tests.err.log"')
|
||||
class NovaTestResult(result.TextTestResult):
|
||||
def __init__(self, *args, **kw):
|
||||
result.TextTestResult.__init__(self, *args, **kw)
|
||||
self._last_case = None
|
||||
|
||||
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__':
|
||||
OptionsClass = twistd.WrapTwistedOptions(trial_script.Options)
|
||||
config = OptionsClass()
|
||||
argv = config.parseOptions()
|
||||
c = config.Config(stream=sys.stdout,
|
||||
env=os.environ,
|
||||
verbosity=3)
|
||||
|
||||
FLAGS.verbose = True
|
||||
|
||||
# TODO(termie): these should make a call instead of doing work on import
|
||||
if FLAGS.fake_tests:
|
||||
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())
|
||||
runner = NovaTestRunner(stream=c.stream,
|
||||
verbosity=c.verbosity,
|
||||
config=c)
|
||||
sys.exit(not core.run(config=c, testRunner=runner))
|
||||
|
||||
Reference in New Issue
Block a user