merged trunk and fixed merge errors

This commit is contained in:
Vishvananda Ishaya
2010-08-23 14:36:14 -07:00
32 changed files with 400 additions and 507 deletions

View File

@@ -18,17 +18,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Daemon for the Rackspace API endpoint.
Nova API daemon.
"""
from nova import api
from nova import flags
from nova import utils
from nova import wsgi
from nova.endpoint import newapi
FLAGS = flags.FLAGS
flags.DEFINE_integer('cc_port', 8773, 'cloud controller port')
flags.DEFINE_integer('api_port', 8773, 'API port')
if __name__ == '__main__':
utils.default_flagfile()
wsgi.run_server(newapi.APIVersionRouter(), FLAGS.cc_port)
wsgi.run_server(api.API(), FLAGS.api_port)

View File

@@ -29,4 +29,4 @@ if __name__ == '__main__':
twistd.serve(__file__)
if __name__ == '__builtin__':
application = service.ComputeService.create()
application = service.ComputeService.create() # pylint: disable-msg=C0103

View File

@@ -41,7 +41,7 @@ from nova.auth import manager # for auth flags
FLAGS = flags.FLAGS
def add_lease(_mac, ip, _hostname, _interface):
def add_lease(_mac, ip_address, _hostname, _interface):
"""Set the IP that was assigned by the DHCP server."""
if FLAGS.fake_rabbit:
logging.debug("leasing ip")
@@ -49,27 +49,27 @@ def add_lease(_mac, ip, _hostname, _interface):
print models.FixedIp.count()
print models.Network.count()
print FLAGS.sql_connection
service.VlanNetworkService().lease_fixed_ip(ip)
service.VlanNetworkService().lease_fixed_ip(ip_address)
else:
rpc.cast("%s.%s" % (FLAGS.network_topic, FLAGS.node_name),
{"method": "lease_fixed_ip",
"args": {"address": ip}})
"args": {"address": ip_address}})
def old_lease(_mac, _ip, _hostname, _interface):
def old_lease(_mac, _ip_address, _hostname, _interface):
"""Do nothing, just an old lease update."""
logging.debug("Adopted old lease or got a change of mac/hostname")
def del_lease(_mac, ip, _hostname, _interface):
def del_lease(_mac, ip_address, _hostname, _interface):
"""Called when a lease expires."""
if FLAGS.fake_rabbit:
logging.debug("releasing ip")
service.VlanNetworkService().release_fixed_ip(ip)
service.VlanNetworkService().release_fixed_ip(ip_address)
else:
rpc.cast("%s.%s" % (FLAGS.network_topic, FLAGS.node_name),
{"method": "release_fixed_ip",
"args": {"address": ip}})
"args": {"fixed_ip": ip_address}})
def init_leases(interface):

View File

@@ -35,12 +35,12 @@ from nova.objectstore import image
FLAGS = flags.FLAGS
api_url = 'https://imagestore.canonical.com/api/dashboard'
API_URL = 'https://imagestore.canonical.com/api/dashboard'
def get_images():
"""Get a list of the images from the imagestore URL."""
images = json.load(urllib2.urlopen(api_url))['images']
images = json.load(urllib2.urlopen(API_URL))['images']
images = [img for img in images if img['title'].find('amd64') > -1]
return images
@@ -56,21 +56,21 @@ def download(img):
for f in img['files']:
if f['kind'] == 'kernel':
dest = os.path.join(tempdir, 'kernel')
subprocess.call(['curl', f['url'], '-o', dest])
subprocess.call(['curl', '--fail', f['url'], '-o', dest])
kernel_id = image.Image.add(dest,
description='kernel/' + img['title'], kernel=True)
for f in img['files']:
if f['kind'] == 'ramdisk':
dest = os.path.join(tempdir, 'ramdisk')
subprocess.call(['curl', f['url'], '-o', dest])
subprocess.call(['curl', '--fail', f['url'], '-o', dest])
ramdisk_id = image.Image.add(dest,
description='ramdisk/' + img['title'], ramdisk=True)
for f in img['files']:
if f['kind'] == 'image':
dest = os.path.join(tempdir, 'image')
subprocess.call(['curl', f['url'], '-o', dest])
subprocess.call(['curl', '--fail', f['url'], '-o', dest])
ramdisk_id = image.Image.add(dest,
description=img['title'], kernel=kernel_id, ramdisk=ramdisk_id)

View File

@@ -35,9 +35,10 @@ if __name__ == '__main__':
if __name__ == '__builtin__':
logging.warn('Starting instance monitor')
m = monitor.InstanceMonitor()
# pylint: disable-msg=C0103
monitor = monitor.InstanceMonitor()
# This is the parent service that twistd will be looking for when it
# parses this file, return it so that we can get it into globals below
application = service.Application('nova-instancemonitor')
m.setServiceParent(application)
monitor.setServiceParent(application)

View File

@@ -56,7 +56,8 @@ class VpnCommands(object):
vpn = self._vpn_for(project.id)
if vpn:
command = "ping -c1 -w1 %s > /dev/null; echo $?"
out, _err = utils.execute(command % vpn['private_dns_name'])
out, _err = utils.execute( command % vpn['private_dns_name'],
check_exit_code=False)
if out.strip() == '0':
net = 'up'
else:
@@ -211,7 +212,7 @@ class ProjectCommands(object):
f.write(zip_file)
categories = [
CATEGORIES = [
('user', UserCommands),
('project', ProjectCommands),
('role', RoleCommands),
@@ -258,11 +259,11 @@ def main():
if len(argv) < 1:
print script_name + " category action [<args>]"
print "Available categories:"
for k, _ in categories:
for k, _ in CATEGORIES:
print "\t%s" % k
sys.exit(2)
category = argv.pop(0)
matches = lazy_match(category, categories)
matches = lazy_match(category, CATEGORIES)
# instantiate the command group object
category, fn = matches[0]
command_object = fn()

View File

@@ -33,4 +33,5 @@ if __name__ == '__main__':
twistd.serve(__file__)
if __name__ == '__builtin__':
# pylint: disable-msg=C0103
application = service.type_to_class(FLAGS.network_type).create()

View File

@@ -35,4 +35,4 @@ if __name__ == '__main__':
if __name__ == '__builtin__':
utils.default_flagfile()
application = handler.get_application()
application = handler.get_application() # pylint: disable-msg=C0103

View File

@@ -29,4 +29,4 @@ if __name__ == '__main__':
twistd.serve(__file__)
if __name__ == '__builtin__':
application = service.VolumeService.create()
application = service.VolumeService.create() # pylint: disable-msg=C0103

View File

@@ -30,20 +30,23 @@ from nova import datastore
SCOPE_BASE = 0
SCOPE_ONELEVEL = 1 # not implemented
SCOPE_SUBTREE = 2
SCOPE_SUBTREE = 2
MOD_ADD = 0
MOD_DELETE = 1
class NO_SUCH_OBJECT(Exception):
class NO_SUCH_OBJECT(Exception): # pylint: disable-msg=C0103
"""Duplicate exception class from real LDAP module."""
pass
class OBJECT_CLASS_VIOLATION(Exception):
class OBJECT_CLASS_VIOLATION(Exception): # pylint: disable-msg=C0103
"""Duplicate exception class from real LDAP module."""
pass
def initialize(uri):
def initialize(_uri):
"""Opens a fake connection with an LDAP server."""
return FakeLDAP()
@@ -68,7 +71,7 @@ def _match_query(query, attrs):
# cut off the ! and the nested parentheses
return not _match_query(query[2:-1], attrs)
(k, sep, v) = inner.partition('=')
(k, _sep, v) = inner.partition('=')
return _match(k, v, attrs)
@@ -85,20 +88,20 @@ def _paren_groups(source):
if source[pos] == ')':
count -= 1
if count == 0:
result.append(source[start:pos+1])
result.append(source[start:pos + 1])
return result
def _match(k, v, attrs):
def _match(key, value, attrs):
"""Match a given key and value against an attribute list."""
if k not in attrs:
if key not in attrs:
return False
if k != "objectclass":
return v in attrs[k]
if key != "objectclass":
return value in attrs[key]
# it is an objectclass check, so check subclasses
values = _subs(v)
for value in values:
if value in attrs[k]:
values = _subs(value)
for v in values:
if v in attrs[key]:
return True
return False
@@ -145,6 +148,7 @@ 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):
"""This method is ignored, but provided for compatibility."""
@@ -207,6 +211,7 @@ class FakeLDAP(object):
# get the attributes from redis
attrs = redis.hgetall(key)
# turn the values from redis into lists
# pylint: disable-msg=E1103
attrs = dict([(k, _from_json(v))
for k, v in attrs.iteritems()])
# filter the objects by query
@@ -215,12 +220,12 @@ class FakeLDAP(object):
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))
# pylint: enable-msg=E1103
if objects == []:
raise NO_SUCH_OBJECT()
return objects
@property
def __redis_prefix(self):
def __redis_prefix(self): # pylint: disable-msg=R0201
"""Get the prefix to use for all redis keys."""
return 'ldap:'

View File

@@ -34,7 +34,7 @@ from nova import flags
FLAGS = flags.FLAGS
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_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_unit', 'Users', 'OID for Users')
@@ -63,14 +63,18 @@ flags.DEFINE_string('ldap_developer',
# to define a set interface for AuthDrivers. I'm delaying
# creating this now because I'm expecting an auth refactor
# in which we may want to change the interface a bit more.
class LdapDriver(object):
"""Ldap Auth driver
Defines enter and exit and therefore supports the with/as syntax.
"""
def __init__(self):
"""Imports the LDAP module"""
self.ldap = __import__('ldap')
self.conn = None
def __enter__(self):
"""Creates the connection to LDAP"""
@@ -78,7 +82,7 @@ class LdapDriver(object):
self.conn.simple_bind_s(FLAGS.ldap_user_dn, FLAGS.ldap_password)
return self
def __exit__(self, type, value, traceback):
def __exit__(self, exc_type, exc_value, traceback):
"""Destroys the connection to LDAP"""
self.conn.unbind_s()
return False
@@ -123,11 +127,11 @@ class LdapDriver(object):
def get_projects(self, uid=None):
"""Retrieve list of projects"""
filter = '(objectclass=novaProject)'
pattern = '(objectclass=novaProject)'
if uid:
filter = "(&%s(member=%s))" % (filter, self.__uid_to_dn(uid))
pattern = "(&%s(member=%s))" % (pattern, self.__uid_to_dn(uid))
attrs = self.__find_objects(FLAGS.ldap_project_subtree,
filter)
pattern)
return [self.__to_project(attr) for attr in attrs]
def create_user(self, name, access_key, secret_key, is_admin):
@@ -194,8 +198,7 @@ class LdapDriver(object):
('cn', [name]),
('description', [description]),
('projectManager', [manager_dn]),
('member', members)
]
('member', members)]
self.conn.add_s('cn=%s,%s' % (name, FLAGS.ldap_project_subtree), attr)
return self.__to_project(dict(attr))
@@ -287,7 +290,6 @@ class LdapDriver(object):
def __key_pair_exists(self, uid, key_name):
"""Check if key pair exists"""
return self.get_user(uid) != None
return self.get_key_pair(uid, key_name) != None
def __project_exists(self, project_id):
@@ -310,7 +312,7 @@ class LdapDriver(object):
except self.ldap.NO_SUCH_OBJECT:
return []
# just return the DNs
return [dn for dn, attributes in res]
return [dn for dn, _attributes in res]
def __find_objects(self, dn, query=None, scope=None):
"""Find objects by query"""
@@ -346,7 +348,8 @@ class LdapDriver(object):
for key in keys:
self.delete_key_pair(uid, key['name'])
def __role_to_dn(self, role, project_id=None):
@staticmethod
def __role_to_dn(role, project_id=None):
"""Convert role to corresponding dn"""
if project_id == None:
return FLAGS.__getitem__("ldap_%s" % role).value
@@ -356,7 +359,7 @@ class LdapDriver(object):
FLAGS.ldap_project_subtree)
def __create_group(self, group_dn, name, uid,
description, member_uids = None):
description, member_uids=None):
"""Create a group"""
if self.__group_exists(group_dn):
raise exception.Duplicate("Group can't be created because "
@@ -375,8 +378,7 @@ class LdapDriver(object):
('objectclass', ['groupOfNames']),
('cn', [name]),
('description', [description]),
('member', members)
]
('member', members)]
self.conn.add_s(group_dn, attr)
def __is_in_group(self, uid, group_dn):
@@ -402,9 +404,7 @@ class LdapDriver(object):
if self.__is_in_group(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))
]
attr = [(self.ldap.MOD_ADD, 'member', self.__uid_to_dn(uid))]
self.conn.modify_s(group_dn, attr)
def __remove_from_group(self, uid, group_dn):
@@ -432,7 +432,7 @@ class LdapDriver(object):
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 )
"Deleting the group at %s instead.", group_dn)
self.__delete_group(group_dn)
def __remove_from_all(self, uid):
@@ -440,7 +440,6 @@ class LdapDriver(object):
if not self.__user_exists(uid):
raise exception.NotFound("User %s can't be removed from all "
"because the user doesn't exist" % (uid,))
dn = self.__uid_to_dn(uid)
role_dns = self.__find_group_dns_with_member(
FLAGS.role_project_subtree, uid)
for role_dn in role_dns:
@@ -448,7 +447,7 @@ class LdapDriver(object):
project_dns = self.__find_group_dns_with_member(
FLAGS.ldap_project_subtree, uid)
for project_dn in project_dns:
self.__safe_remove_from_group(uid, role_dn)
self.__safe_remove_from_group(uid, project_dn)
def __delete_group(self, group_dn):
"""Delete Group"""
@@ -461,7 +460,8 @@ class LdapDriver(object):
for role_dn in self.__find_role_dns(project_dn):
self.__delete_group(role_dn)
def __to_user(self, attr):
@staticmethod
def __to_user(attr):
"""Convert ldap attributes to User object"""
if attr == None:
return None
@@ -470,10 +470,10 @@ class LdapDriver(object):
'name': attr['cn'][0],
'access': attr['accessKey'][0],
'secret': attr['secretKey'][0],
'admin': (attr['isAdmin'][0] == 'TRUE')
}
'admin': (attr['isAdmin'][0] == 'TRUE')}
def __to_key_pair(self, owner, attr):
@staticmethod
def __to_key_pair(owner, attr):
"""Convert ldap attributes to KeyPair object"""
if attr == None:
return None
@@ -482,8 +482,7 @@ class LdapDriver(object):
'name': attr['cn'][0],
'owner_id': owner,
'public_key': attr['sshPublicKey'][0],
'fingerprint': attr['keyFingerprint'][0],
}
'fingerprint': attr['keyFingerprint'][0]}
def __to_project(self, attr):
"""Convert ldap attributes to Project object"""
@@ -495,21 +494,22 @@ class LdapDriver(object):
'name': attr['cn'][0],
'project_manager_id': self.__dn_to_uid(attr['projectManager'][0]),
'description': attr.get('description', [None])[0],
'member_ids': [self.__dn_to_uid(x) for x in member_dns]
}
'member_ids': [self.__dn_to_uid(x) for x in member_dns]}
def __dn_to_uid(self, dn):
@staticmethod
def __dn_to_uid(dn):
"""Convert user dn to uid"""
return dn.split(',')[0].split('=')[1]
def __uid_to_dn(self, dn):
@staticmethod
def __uid_to_dn(dn):
"""Convert uid to dn"""
return 'uid=%s,%s' % (dn, FLAGS.ldap_user_subtree)
class FakeLdapDriver(LdapDriver):
"""Fake Ldap Auth driver"""
def __init__(self):
def __init__(self): # pylint: disable-msg=W0231
__import__('nova.auth.fakeldap')
self.ldap = sys.modules['nova.auth.fakeldap']

View File

@@ -23,7 +23,7 @@ Nova authentication management
import logging
import os
import shutil
import string
import string # pylint: disable-msg=W0402
import tempfile
import uuid
import zipfile
@@ -32,7 +32,6 @@ from nova import crypto
from nova import db
from nova import exception
from nova import flags
from nova import models
from nova import utils
from nova.auth import signer
@@ -195,12 +194,12 @@ class Project(AuthBase):
@property
def vpn_ip(self):
ip, port = AuthManager().get_project_vpn_data(self)
ip, _port = AuthManager().get_project_vpn_data(self)
return ip
@property
def vpn_port(self):
ip, port = AuthManager().get_project_vpn_data(self)
_ip, port = AuthManager().get_project_vpn_data(self)
return port
def has_manager(self, user):
@@ -222,11 +221,9 @@ class Project(AuthBase):
return AuthManager().get_credentials(user, self)
def __repr__(self):
return "Project('%s', '%s', '%s', '%s', %s)" % (self.id,
self.name,
self.project_manager_id,
self.description,
self.member_ids)
return "Project('%s', '%s', '%s', '%s', %s)" % \
(self.id, self.name, self.project_manager_id, self.description,
self.member_ids)
class AuthManager(object):
@@ -298,7 +295,7 @@ class AuthManager(object):
@return: User and project that the request represents.
"""
# TODO(vish): check for valid timestamp
(access_key, sep, project_id) = access.partition(':')
(access_key, _sep, project_id) = access.partition(':')
logging.info('Looking up user: %r', access_key)
user = self.get_user_from_access_key(access_key)
@@ -321,7 +318,8 @@ class AuthManager(object):
raise exception.NotFound('User %s is not a member of project %s' %
(user.id, project.id))
if check_type == 's3':
expected_signature = signer.Signer(user.secret.encode()).s3_authorization(headers, verb, path)
sign = signer.Signer(user.secret.encode())
expected_signature = sign.s3_authorization(headers, verb, path)
logging.debug('user.secret: %s', user.secret)
logging.debug('expected_signature: %s', expected_signature)
logging.debug('signature: %s', signature)
@@ -466,7 +464,8 @@ class AuthManager(object):
with self.driver() as drv:
drv.remove_role(User.safe_id(user), role, Project.safe_id(project))
def get_roles(self, project_roles=True):
@staticmethod
def get_roles(project_roles=True):
"""Get list of allowed roles"""
if project_roles:
return list(set(FLAGS.allowed_roles) - set(FLAGS.global_roles))
@@ -519,10 +518,10 @@ class AuthManager(object):
if member_users:
member_users = [User.safe_id(u) for u in member_users]
with self.driver() as drv:
project_dict = drv.create_project(name,
User.safe_id(manager_user),
description,
member_users)
project_dict = drv.create_project(name,
User.safe_id(manager_user),
description,
member_users)
if project_dict:
project = Project(**project_dict)
# FIXME(ja): EVIL HACK
@@ -553,7 +552,8 @@ class AuthManager(object):
return drv.remove_from_project(User.safe_id(user),
Project.safe_id(project))
def get_project_vpn_data(self, project, context=None):
@staticmethod
def get_project_vpn_data(project, context=None):
"""Gets vpn ip and port for project
@type project: Project or project_id
@@ -563,11 +563,9 @@ class AuthManager(object):
@return: A tuple containing (ip, port) or None, None if vpn has
not been allocated for user.
"""
# FIXME(vish): this shouldn't be messing with the datamodel directly
if not isinstance(project, Project):
project = self.get_project(project)
network_ref = db.project_get_network(context, project.id)
network_ref = db.project_get_network(context,
Project.safe_id(project))
if not network_ref['vpn_public_port']:
raise exception.NotFound('project network data has not been set')
@@ -577,9 +575,8 @@ class AuthManager(object):
def delete_project(self, project, context=None):
"""Deletes a project"""
# FIXME(ja): EVIL HACK
if not isinstance(project, Project):
project = self.get_project(project)
network_ref = db.project_get_network(context, project.id)
network_ref = db.project_get_network(context,
Project.safe_id(project))
try:
db.network_destroy(context, network_ref['id'])
except:
@@ -632,8 +629,10 @@ class AuthManager(object):
@rtype: User
@return: The new user.
"""
if access == None: access = str(uuid.uuid4())
if secret == None: secret = str(uuid.uuid4())
if access == None:
access = str(uuid.uuid4())
if secret == None:
secret = str(uuid.uuid4())
with self.driver() as drv:
user_dict = drv.create_user(name, access, secret, admin)
if user_dict:
@@ -675,10 +674,10 @@ class AuthManager(object):
def create_key_pair(self, user, key_name, public_key, fingerprint):
"""Creates a key pair for user"""
with self.driver() as drv:
kp_dict = drv.create_key_pair(User.safe_id(user),
key_name,
public_key,
fingerprint)
kp_dict = drv.create_key_pair(User.safe_id(user),
key_name,
public_key,
fingerprint)
if kp_dict:
return KeyPair(**kp_dict)
@@ -721,7 +720,7 @@ class AuthManager(object):
(vpn_ip, vpn_port) = self.get_project_vpn_data(project)
if vpn_ip:
configfile = open(FLAGS.vpn_client_template,"r")
configfile = open(FLAGS.vpn_client_template, "r")
s = string.Template(configfile.read())
configfile.close()
config = s.substitute(keyfile=FLAGS.credential_key_file,
@@ -736,10 +735,10 @@ class AuthManager(object):
zippy.writestr(FLAGS.ca_file, crypto.fetch_ca(user.id))
zippy.close()
with open(zf, 'rb') as f:
buffer = f.read()
read_buffer = f.read()
shutil.rmtree(tmpdir)
return buffer
return read_buffer
def get_environment_rc(self, user, project=None):
"""Get credential zip for user in project"""
@@ -750,18 +749,18 @@ class AuthManager(object):
pid = Project.safe_id(project)
return self.__generate_rc(user.access, user.secret, pid)
def __generate_rc(self, access, secret, pid):
@staticmethod
def __generate_rc(access, secret, pid):
"""Generate rc file for user"""
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),
'nova': FLAGS.ca_file,
'cert': FLAGS.credential_cert_file,
'key': FLAGS.credential_key_file,
}
rc = rc % {'access': access,
'project': pid,
'secret': secret,
'ec2': FLAGS.ec2_url,
's3': 'http://%s:%s' % (FLAGS.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):
@@ -772,6 +771,7 @@ class AuthManager(object):
signed_cert = crypto.sign_csr(csr, pid)
return (private_key, signed_cert)
def __cert_subject(self, uid):
@staticmethod
def __cert_subject(uid):
"""Helper to generate cert subject"""
return FLAGS.credential_cert_subject % (uid, utils.isotime())

View File

@@ -16,40 +16,54 @@
# License for the specific language governing permissions and limitations
# under the License.
"""Role-based access control decorators to use fpr wrapping other
methods with."""
from nova import exception
from nova.auth import manager
def allow(*roles):
def wrap(f):
def wrapped_f(self, context, *args, **kwargs):
"""Allow the given roles access the wrapped function."""
def wrap(func): # pylint: disable-msg=C0111
def wrapped_func(self, context, *args,
**kwargs): # pylint: disable-msg=C0111
if context.user.is_superuser():
return f(self, context, *args, **kwargs)
return func(self, context, *args, **kwargs)
for role in roles:
if __matches_role(context, role):
return f(self, context, *args, **kwargs)
return func(self, context, *args, **kwargs)
raise exception.NotAuthorized()
return wrapped_f
return wrapped_func
return wrap
def deny(*roles):
def wrap(f):
def wrapped_f(self, context, *args, **kwargs):
"""Deny the given roles access the wrapped function."""
def wrap(func): # pylint: disable-msg=C0111
def wrapped_func(self, context, *args,
**kwargs): # pylint: disable-msg=C0111
if context.user.is_superuser():
return f(self, context, *args, **kwargs)
return func(self, context, *args, **kwargs)
for role in roles:
if __matches_role(context, role):
raise exception.NotAuthorized()
return f(self, context, *args, **kwargs)
return wrapped_f
return func(self, context, *args, **kwargs)
return wrapped_func
return wrap
def __matches_role(context, role):
"""Check if a role is allowed."""
if role == 'all':
return True
if role == 'none':
return False
return context.project.has_role(context.user.id, role)

View File

@@ -50,15 +50,15 @@ import logging
import urllib
# NOTE(vish): for new boto
import boto
import boto
# NOTE(vish): for old boto
import boto.utils
import boto.utils
from nova.exception import Error
class Signer(object):
""" hacked up code from boto/connection.py """
"""Hacked up code from boto/connection.py"""
def __init__(self, secret_key):
self.hmac = hmac.new(secret_key, digestmod=hashlib.sha1)
@@ -66,22 +66,27 @@ class Signer(object):
self.hmac_256 = hmac.new(secret_key, digestmod=hashlib.sha256)
def s3_authorization(self, headers, verb, path):
"""Generate S3 authorization string."""
c_string = boto.utils.canonical_string(verb, path, headers)
hmac = self.hmac.copy()
hmac.update(c_string)
b64_hmac = base64.encodestring(hmac.digest()).strip()
hmac_copy = self.hmac.copy()
hmac_copy.update(c_string)
b64_hmac = base64.encodestring(hmac_copy.digest()).strip()
return b64_hmac
def generate(self, params, verb, server_string, path):
"""Generate auth string according to what SignatureVersion is given."""
if params['SignatureVersion'] == '0':
return self._calc_signature_0(params)
if params['SignatureVersion'] == '1':
return self._calc_signature_1(params)
if params['SignatureVersion'] == '2':
return self._calc_signature_2(params, verb, server_string, path)
raise Error('Unknown Signature Version: %s' % self.SignatureVersion)
raise Error('Unknown Signature Version: %s' %
params['SignatureVersion'])
def _get_utf8_value(self, value):
@staticmethod
def _get_utf8_value(value):
"""Get the UTF8-encoded version of a value."""
if not isinstance(value, str) and not isinstance(value, unicode):
value = str(value)
if isinstance(value, unicode):
@@ -90,10 +95,11 @@ class Signer(object):
return value
def _calc_signature_0(self, params):
"""Generate AWS signature version 0 string."""
s = params['Action'] + params['Timestamp']
self.hmac.update(s)
keys = params.keys()
keys.sort(cmp = lambda x, y: cmp(x.lower(), y.lower()))
keys.sort(cmp=lambda x, y: cmp(x.lower(), y.lower()))
pairs = []
for key in keys:
val = self._get_utf8_value(params[key])
@@ -101,8 +107,9 @@ class Signer(object):
return base64.b64encode(self.hmac.digest())
def _calc_signature_1(self, params):
"""Generate AWS signature version 1 string."""
keys = params.keys()
keys.sort(cmp = lambda x, y: cmp(x.lower(), y.lower()))
keys.sort(cmp=lambda x, y: cmp(x.lower(), y.lower()))
pairs = []
for key in keys:
self.hmac.update(key)
@@ -112,30 +119,34 @@ class Signer(object):
return base64.b64encode(self.hmac.digest())
def _calc_signature_2(self, params, verb, server_string, path):
"""Generate AWS signature version 2 string."""
logging.debug('using _calc_signature_2')
string_to_sign = '%s\n%s\n%s\n' % (verb, server_string, path)
if self.hmac_256:
hmac = self.hmac_256
current_hmac = self.hmac_256
params['SignatureMethod'] = 'HmacSHA256'
else:
hmac = self.hmac
current_hmac = self.hmac
params['SignatureMethod'] = 'HmacSHA1'
keys = params.keys()
keys.sort()
pairs = []
for key in keys:
val = self._get_utf8_value(params[key])
pairs.append(urllib.quote(key, safe='') + '=' + urllib.quote(val, safe='-_~'))
val = urllib.quote(val, safe='-_~')
pairs.append(urllib.quote(key, safe='') + '=' + val)
qs = '&'.join(pairs)
logging.debug('query string: %s' % qs)
logging.debug('query string: %s', qs)
string_to_sign += qs
logging.debug('string_to_sign: %s' % string_to_sign)
hmac.update(string_to_sign)
b64 = base64.b64encode(hmac.digest())
logging.debug('len(b64)=%d' % len(b64))
logging.debug('base64 encoded digest: %s' % b64)
logging.debug('string_to_sign: %s', string_to_sign)
current_hmac.update(string_to_sign)
b64 = base64.b64encode(current_hmac.digest())
logging.debug('len(b64)=%d', len(b64))
logging.debug('base64 encoded digest: %s', b64)
return b64
if __name__ == '__main__':
print Signer('foo').generate({"SignatureMethod": 'HmacSHA256', 'SignatureVersion': '2'}, "get", "server", "/foo")
print Signer('foo').generate({'SignatureMethod': 'HmacSHA256',
'SignatureVersion': '2'},
'get', 'server', '/foo')

View File

@@ -1,22 +0,0 @@
import routes
import webob.dec
from nova import wsgi
# TODO(gundlach): temp
class API(wsgi.Router):
"""WSGI entry point for all AWS API requests."""
def __init__(self):
mapper = routes.Mapper()
mapper.connect(None, "{all:.*}", controller=self.dummy)
super(API, self).__init__(mapper)
@webob.dec.wsgify
def dummy(self, req):
#TODO(gundlach)
msg = "dummy response -- please hook up __init__() to cloud.py instead"
return repr({ 'dummy': msg,
'kwargs': repr(req.environ['wsgiorg.routing_args'][1]) })

View File

@@ -1,51 +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.
"""
:mod:`nova.endpoint` -- Main NOVA Api endpoints
=====================================================
.. automodule:: nova.endpoint
:platform: Unix
:synopsis: REST APIs for all nova functions
.. moduleauthor:: Jesse Andrews <jesse@ansolabs.com>
.. moduleauthor:: Devin Carlen <devin.carlen@gmail.com>
.. moduleauthor:: Vishvananda Ishaya <vishvananda@yahoo.com>
.. moduleauthor:: Joshua McKenty <joshua@cognition.ca>
.. moduleauthor:: Manish Singh <yosh@gimp.org>
.. moduleauthor:: Andy Smith <andy@anarkystic.com>
"""
from nova import wsgi
import routes
from nova.endpoint import rackspace
from nova.endpoint import aws
class APIVersionRouter(wsgi.Router):
"""Routes top-level requests to the appropriate API."""
def __init__(self):
mapper = routes.Mapper()
rsapi = rackspace.API()
mapper.connect(None, "/v1.0/{path_info:.*}", controller=rsapi)
mapper.connect(None, "/ec2/{path_info:.*}", controller=aws.API())
super(APIVersionRouter, self).__init__(mapper)

View File

@@ -1,83 +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.
"""
Rackspace API Endpoint
"""
import json
import time
import webob.dec
import webob.exc
import routes
from nova import flags
from nova import wsgi
from nova.auth import manager
from nova.endpoint.rackspace import controllers
class API(wsgi.Middleware):
"""WSGI entry point for all Rackspace API requests."""
def __init__(self):
app = AuthMiddleware(APIRouter())
super(API, self).__init__(app)
class AuthMiddleware(wsgi.Middleware):
"""Authorize the rackspace API request or return an HTTP Forbidden."""
#TODO(gundlach): isn't this the old Nova API's auth? Should it be replaced
#with correct RS API auth?
@webob.dec.wsgify
def __call__(self, req):
context = {}
if "HTTP_X_AUTH_TOKEN" in req.environ:
context['user'] = manager.AuthManager().get_user_from_access_key(
req.environ['HTTP_X_AUTH_TOKEN'])
if context['user']:
context['project'] = manager.AuthManager().get_project(
context['user'].name)
if "user" not in context:
return webob.exc.HTTPForbidden()
req.environ['nova.context'] = context
return self.application
class APIRouter(wsgi.Router):
"""
Routes requests on the Rackspace API to the appropriate controller
and method.
"""
def __init__(self):
mapper = routes.Mapper()
mapper.resource("server", "servers",
controller=controllers.ServersController())
mapper.resource("image", "images",
controller=controllers.ImagesController())
mapper.resource("flavor", "flavors",
controller=controllers.FlavorsController())
mapper.resource("sharedipgroup", "sharedipgroups",
controller=controllers.SharedIpGroupsController())
super(APIRouter, self).__init__(mapper)

View File

@@ -1,5 +0,0 @@
from nova.endpoint.rackspace.controllers.images import ImagesController
from nova.endpoint.rackspace.controllers.flavors import FlavorsController
from nova.endpoint.rackspace.controllers.servers import ServersController
from nova.endpoint.rackspace.controllers.sharedipgroups import \
SharedIpGroupsController

View File

@@ -1,9 +0,0 @@
from nova import wsgi
class BaseController(wsgi.Controller):
@classmethod
def render(cls, instance):
if isinstance(instance, list):
return { cls.entity_name : cls.render(instance) }
else:
return { "TODO": "TODO" }

View File

@@ -1 +0,0 @@
class FlavorsController(object): pass

View File

@@ -1 +0,0 @@
class ImagesController(object): pass

View File

@@ -1,63 +0,0 @@
from nova import rpc
from nova.compute import model as compute
from nova.endpoint.rackspace.controllers.base import BaseController
class ServersController(BaseController):
entity_name = 'servers'
def index(self, **kwargs):
return [instance_details(inst) for inst in compute.InstanceDirectory().all]
def show(self, **kwargs):
instance_id = kwargs['id']
return compute.InstanceDirectory().get(instance_id)
def delete(self, **kwargs):
instance_id = kwargs['id']
instance = compute.InstanceDirectory().get(instance_id)
if not instance:
raise ServerNotFound("The requested server was not found")
instance.destroy()
return True
def create(self, **kwargs):
inst = self.build_server_instance(kwargs['server'])
rpc.cast(
FLAGS.compute_topic, {
"method": "run_instance",
"args": {"instance_id": inst.instance_id}})
def update(self, **kwargs):
instance_id = kwargs['id']
instance = compute.InstanceDirectory().get(instance_id)
if not instance:
raise ServerNotFound("The requested server was not found")
instance.update(kwargs['server'])
instance.save()
def build_server_instance(self, env):
"""Build instance data structure and save it to the data store."""
reservation = utils.generate_uid('r')
ltime = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
inst = self.instdir.new()
inst['name'] = env['server']['name']
inst['image_id'] = env['server']['imageId']
inst['instance_type'] = env['server']['flavorId']
inst['user_id'] = env['user']['id']
inst['project_id'] = env['project']['id']
inst['reservation_id'] = reservation
inst['launch_time'] = ltime
inst['mac_address'] = utils.generate_mac()
address = self.network.allocate_ip(
inst['user_id'],
inst['project_id'],
mac=inst['mac_address'])
inst['private_dns_name'] = str(address)
inst['bridge_name'] = network.BridgedNetwork.get_network_for_project(
inst['user_id'],
inst['project_id'],
'default')['bridge_name']
# key_data, key_name, ami_launch_index
# TODO(todd): key data or root password
inst.save()
return inst

View File

@@ -1 +0,0 @@
class SharedIpGroupsController(object): pass

View File

@@ -2,6 +2,7 @@
# 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
@@ -20,17 +21,12 @@
Process pool, still buggy right now.
"""
import logging
import multiprocessing
import StringIO
from twisted.internet import defer
from twisted.internet import error
from twisted.internet import process
from twisted.internet import protocol
from twisted.internet import reactor
from twisted.internet import threads
from twisted.python import failure
from nova import flags
@@ -55,111 +51,100 @@ class UnexpectedErrorOutput(IOError):
IOError.__init__(self, "got stdout: %r\nstderr: %r" % (stdout, stderr))
# NOTE(termie): this too
class _BackRelay(protocol.ProcessProtocol):
# 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, if C{errortoo} is true, all of stderr as well (mixed together in
one string). If C{errortoo} is false and any bytes are received over
stderr, this will fire with an L{_UnexpectedErrorOutput} instance and
the attribute will be set to C{None}.
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{_UnexpectedErrorOutput} instance and the attribute will be set to
C{None}.
@ivar onProcessEnded: If C{errortoo} 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{_UnexpectedErrorOutput} 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.
@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{_UnexpectedErrorOutput} 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, errortoo=0):
def __init__(self, deferred, started_deferred=None,
terminate_on_stderr=False, check_exit_code=True,
process_input=None):
self.deferred = deferred
self.s = StringIO.StringIO()
if errortoo:
self.errReceived = self.errReceivedIsGood
else:
self.errReceived = self.errReceivedIsBad
def errReceivedIsBad(self, text):
if self.deferred is not None:
self.onProcessEnded = defer.Deferred()
err = UnexpectedErrorOutput(text, self.onProcessEnded)
self.deferred.errback(failure.Failure(err))
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 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(UnexpectedErrorOutput(
stdout=self.stdout.getvalue(),
stderr=self.stderr.getvalue()))
self.deferred = None
self.transport.loseConnection()
def errReceivedIsGood(self, text):
self.s.write(text)
def outReceived(self, text):
self.s.write(text)
self.stdout.write(text)
def processEnded(self, reason):
if self.deferred is not None:
self.deferred.callback(self.s.getvalue())
elif self.onProcessEnded is not None:
self.onProcessEnded.errback(reason)
class BackRelayWithInput(_BackRelay):
def __init__(self, deferred, startedDeferred=None, error_ok=0,
input=None):
# Twisted doesn't use new-style classes in most places :(
_BackRelay.__init__(self, deferred, errortoo=error_ok)
self.error_ok = error_ok
self.input = input
self.stderr = StringIO.StringIO()
self.startedDeferred = startedDeferred
def errReceivedIsBad(self, text):
self.stderr.write(text)
self.transport.loseConnection()
def errReceivedIsGood(self, text):
self.stderr.write(text)
def connectionMade(self):
if self.startedDeferred:
self.startedDeferred.callback(self)
if self.input:
self.transport.write(self.input)
self.transport.closeStdin()
def processEnded(self, reason):
if self.deferred is not None:
stdout, stderr = self.s.getvalue(), self.stderr.getvalue()
stdout, stderr = self.stdout.getvalue(), self.stderr.getvalue()
try:
# NOTE(termie): current behavior means if error_ok is True
# we won't throw an error even if the process
# exited with a non-0 status, so you can't be
# okay with stderr output and not with bad exit
# codes.
if not self.error_ok:
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...
self.deferred.errback(UnexpectedErrorOutput(stdout, stderr))
elif self.on_process_ended is not None:
self.on_process_ended.errback(reason)
def getProcessOutput(executable, args=None, env=None, path=None, reactor=None,
error_ok=0, input=None, startedDeferred=None):
if reactor is None:
from twisted.internet import reactor
def connectionMade(self):
if self.started_deferred:
self.started_deferred.callback(self)
if self.process_input:
self.transport.write(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 {}
d = defer.Deferred()
p = BackRelayWithInput(
d, startedDeferred=startedDeferred, error_ok=error_ok, input=input)
deferred = defer.Deferred()
process_handler = BackRelayWithInput(
deferred,
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]
reactor.spawnProcess(p, executable, (executable,)+tuple(args), env, path)
return d
process_reactor.spawnProcess( process_handler, executable,
(executable,)+tuple(args), env, path)
return deferred
class ProcessPool(object):
@@ -185,26 +170,26 @@ class ProcessPool(object):
return self.execute(executable, args, **kw)
def execute(self, *args, **kw):
d = self._pool.acquire()
deferred = self._pool.acquire()
def _associateProcess(proto):
d.process = proto.transport
def _associate_process(proto):
deferred.process = proto.transport
return proto.transport
started = defer.Deferred()
started.addCallback(_associateProcess)
kw.setdefault('startedDeferred', started)
started.addCallback(_associate_process)
kw.setdefault('started_deferred', started)
d.process = None
d.started = started
deferred.process = None
deferred.started = started
d.addCallback(lambda _: getProcessOutput(*args, **kw))
d.addBoth(self._release)
return d
deferred.addCallback(lambda _: get_process_output(*args, **kw))
deferred.addBoth(self._release)
return deferred
def _release(self, rv=None):
def _release(self, retval=None):
self._pool.release()
return rv
return retval
class SharedPool(object):

View File

@@ -59,7 +59,7 @@ class Connection(carrot_connection.BrokerConnection):
params['backend_cls'] = fakerabbit.Backend
# NOTE(vish): magic is fun!
# pylint: disable=W0142
# pylint: disable-msg=W0142
cls._instance = cls(**params)
return cls._instance
@@ -104,7 +104,7 @@ class Consumer(messaging.Consumer):
if self.failed_connection:
# NOTE(vish): conn is defined in the parent class, we can
# recreate it as long as we create the backend too
# pylint: disable=W0201
# pylint: disable-msg=W0201
self.conn = Connection.recreate()
self.backend = self.conn.create_backend()
super(Consumer, self).fetch(no_ack, auto_ack, enable_callbacks)
@@ -114,7 +114,7 @@ class Consumer(messaging.Consumer):
# 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=W0703
except Exception: # pylint: disable-msg=W0703
if not self.failed_connection:
logging.exception("Failed to fetch message from queue")
self.failed_connection = True
@@ -178,7 +178,7 @@ class AdapterConsumer(TopicConsumer):
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=W0142
# pylint: disable-msg=W0142
d = defer.maybeDeferred(node_func, **node_args)
if msg_id:
d.addCallback(lambda rval: msg_reply(msg_id, rval, None))

View File

@@ -36,7 +36,7 @@ FLAGS = flags.FLAGS
class NetworkTestCase(test.TrialTestCase):
"""Test cases for network code"""
def setUp(self): # pylint: disable=C0103
def setUp(self): # pylint: disable-msg=C0103
super(NetworkTestCase, self).setUp()
# NOTE(vish): if you change these flags, make sure to change the
# flags in the corresponding section in nova-dhcpbridge
@@ -65,7 +65,7 @@ class NetworkTestCase(test.TrialTestCase):
{'mac_address': utils.generate_mac()})
self.instance2_id = instance_id
def tearDown(self): # pylint: disable=C0103
def tearDown(self): # pylint: disable-msg=C0103
super(NetworkTestCase, self).tearDown()
# TODO(termie): this should really be instantiating clean datastores
# in between runs, one failure kills all the tests
@@ -140,7 +140,6 @@ class NetworkTestCase(test.TrialTestCase):
self.assertTrue(is_allocated_in_project(address2, self.projects[1].id))
self.service.deallocate_fixed_ip(address2)
issue_ip(address2, net.bridge)
release_ip(address2, net2.bridge)
self.assertFalse(is_allocated_in_project(address2, self.projects[1].id))

View File

@@ -48,7 +48,7 @@ class ProcessTestCase(test.TrialTestCase):
def test_execute_stderr(self):
pool = process.ProcessPool(2)
d = pool.simple_execute('cat BAD_FILE', error_ok=1)
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])

View File

@@ -32,7 +32,7 @@ FLAGS = flags.FLAGS
class RpcTestCase(test.BaseTestCase):
"""Test cases for rpc"""
def setUp(self): # pylint: disable=C0103
def setUp(self): # pylint: disable-msg=C0103
super(RpcTestCase, self).setUp()
self.conn = rpc.Connection.instance()
self.receiver = TestReceiver()

View File

@@ -56,23 +56,25 @@ def fetchfile(url, target):
# c.perform()
# c.close()
# fp.close()
execute("curl %s -o %s" % (url, target))
execute("curl --fail %s -o %s" % (url, target))
def execute(cmd, input=None, addl_env=None):
def execute(cmd, process_input=None, addl_env=None, check_exit_code=True):
env = os.environ.copy()
if addl_env:
env.update(addl_env)
obj = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
result = None
if input != None:
result = obj.communicate(input)
if process_input != None:
result = obj.communicate(process_input)
else:
result = obj.communicate()
obj.stdin.close()
if obj.returncode:
logging.debug("Result was %s" % (obj.returncode))
if check_exit_code and obj.returncode <> 0:
raise Exception( "Unexpected exit code: %s. result=%s"
% (obj.returncode, result))
return result
@@ -98,9 +100,13 @@ def debug(arg):
return arg
def runthis(prompt, cmd):
def runthis(prompt, cmd, check_exit_code = True):
logging.debug("Running %s" % (cmd))
logging.debug(prompt % (subprocess.call(cmd.split(" "))))
exit_code = subprocess.call(cmd.split(" "))
logging.debug(prompt % (exit_code))
if check_exit_code and exit_code <> 0:
raise Exception( "Unexpected exit code: %s from cmd: %s"
% (exit_code, cmd))
def generate_uid(topic, size=8):

View File

@@ -83,7 +83,7 @@ class Application(object):
raise NotImplementedError("You must implement __call__")
class Middleware(Application): # pylint: disable=W0223
class Middleware(Application):
"""
Base WSGI middleware wrapper. These classes require an application to be
initialized that will be called next. By default the middleware will
@@ -91,11 +91,11 @@ class Middleware(Application): # pylint: disable=W0223
behavior.
"""
def __init__(self, application): # pylint: disable=W0231
def __init__(self, application): # pylint: disable-msg=W0231
self.application = application
@webob.dec.wsgify
def __call__(self, req):
def __call__(self, req): # pylint: disable-msg=W0221
"""Override to implement middleware behavior."""
return self.application
@@ -113,7 +113,7 @@ class Debug(Middleware):
resp = req.get_response(self.application)
print ("*" * 40) + " RESPONSE HEADERS"
for (key, value) in resp.headers:
for (key, value) in resp.headers.iteritems():
print key, "=", value
print
@@ -127,7 +127,7 @@ class Debug(Middleware):
Iterator that prints the contents of a wrapper string iterator
when iterated.
"""
print ("*" * 40) + "BODY"
print ("*" * 40) + " BODY"
for part in app_iter:
sys.stdout.write(part)
sys.stdout.flush()
@@ -176,8 +176,9 @@ class Router(object):
"""
return self._router
@staticmethod
@webob.dec.wsgify
def _dispatch(self, req):
def _dispatch(req):
"""
Called by self._router after matching the incoming request to a route
and putting the information into req.environ. Either returns 404
@@ -197,6 +198,7 @@ class Controller(object):
must, in addition to their normal parameters, accept a 'req' argument
which is the incoming webob.Request.
"""
@webob.dec.wsgify
def __call__(self, req):
"""
@@ -249,6 +251,7 @@ class Serializer(object):
return repr(data)
def _to_xml_node(self, doc, metadata, nodename, data):
"""Recursive method to convert data members to XML nodes."""
result = doc.createElement(nodename)
if type(data) is list:
singular = metadata.get('plurals', {}).get(nodename, None)
@@ -262,7 +265,7 @@ class Serializer(object):
result.appendChild(node)
elif type(data) is dict:
attrs = metadata.get('attributes', {}).get(nodename, {})
for k,v in data.items():
for k, v in data.items():
if k in attrs:
result.setAttribute(k, str(v))
else:

96
nova/wsgi_test.py Normal file
View File

@@ -0,0 +1,96 @@
# 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 OpenStack LLC.
# 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.
"""
Test WSGI basics and provide some helper functions for other WSGI tests.
"""
import unittest
import routes
import webob
from nova import wsgi
class Test(unittest.TestCase):
def test_debug(self):
class Application(wsgi.Application):
"""Dummy application to test debug."""
def __call__(self, environ, start_response):
start_response("200", [("X-Test", "checking")])
return ['Test result']
application = wsgi.Debug(Application())
result = webob.Request.blank('/').get_response(application)
self.assertEqual(result.body, "Test result")
def test_router(self):
class Application(wsgi.Application):
"""Test application to call from router."""
def __call__(self, environ, start_response):
start_response("200", [])
return ['Router result']
class Router(wsgi.Router):
"""Test router."""
def __init__(self):
mapper = routes.Mapper()
mapper.connect("/test", controller=Application())
super(Router, self).__init__(mapper)
result = webob.Request.blank('/test').get_response(Router())
self.assertEqual(result.body, "Router result")
result = webob.Request.blank('/bad').get_response(Router())
self.assertNotEqual(result.body, "Router result")
def test_controller(self):
class Controller(wsgi.Controller):
"""Test controller to call from router."""
test = self
def show(self, req, id): # pylint: disable-msg=W0622,C0103
"""Default action called for requests with an ID."""
self.test.assertEqual(req.path_info, '/tests/123')
self.test.assertEqual(id, '123')
return id
class Router(wsgi.Router):
"""Test router."""
def __init__(self):
mapper = routes.Mapper()
mapper.resource("test", "tests", controller=Controller())
super(Router, self).__init__(mapper)
result = webob.Request.blank('/tests/123').get_response(Router())
self.assertEqual(result.body, "123")
result = webob.Request.blank('/test/123').get_response(Router())
self.assertNotEqual(result.body, "123")
def test_serializer(self):
# TODO(eday): Placeholder for serializer testing.
pass

View File

@@ -1,19 +1,26 @@
[Messages Control]
disable=C0103
# TODOs in code comments are fine...
disable=W0511
# *args and **kwargs are fine
disable=W0142
# W0511: TODOs in code comments are fine.
# W0142: *args and **kwargs are fine.
disable-msg=W0511,W0142
[Basic]
# Variables can be 1 to 31 characters long, with
# lowercase and underscores
# Variable names can be 1 to 31 characters long, with lowercase and underscores
variable-rgx=[a-z_][a-z0-9_]{0,30}$
# Argument names can be 2 to 31 characters long, with lowercase and underscores
argument-rgx=[a-z_][a-z0-9_]{1,30}$
# Method names should be at least 3 characters long
# and be lowecased with underscores
method-rgx=[a-z_][a-z0-9_]{2,50}$
# Module names matching nova-* are ok (files in bin/)
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+)|(nova-[a-z0-9_-]+))$
# Don't require docstrings on tests.
no-docstring-rgx=((__.*__)|([tT]est.*)|setUp|tearDown)$
[Design]
max-public-methods=100
min-public-methods=0
max-args=6