Merge trunk

This commit is contained in:
Soren Hansen
2010-09-24 10:25:29 +02:00
9 changed files with 98 additions and 172 deletions

View File

@@ -1,31 +1,28 @@
#!/usr/bin/env python #!/usr/bin/env python
# pylint: disable-msg=C0103
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the # Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration. # Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License");
# not use this file except in compliance with the License. You may obtain # you may not use this file except in compliance with the License.
# a copy of the License at # You may obtain a copy of the License at
# #
# http://www.apache.org/licenses/LICENSE-2.0 # http://www.apache.org/licenses/LICENSE-2.0
# #
# Unless required by applicable law or agreed to in writing, software # Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # distributed under the License is distributed on an "AS IS" BASIS,
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# License for the specific language governing permissions and limitations # See the License for the specific language governing permissions and
# under the License. # limitations under the License.
""" """
Tornado daemon for the main API endpoint. Nova API daemon.
""" """
import logging
import os import os
import sys import sys
from tornado import httpserver
from tornado import ioloop
# If ../nova/__init__.py exists, add ../ to Python search path, so that # 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... # it will override what happens to be installed in /usr/(local/)lib/python...
@@ -36,28 +33,16 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
sys.path.insert(0, possible_topdir) sys.path.insert(0, possible_topdir)
from nova import flags from nova import flags
from nova import server
from nova import utils from nova import utils
from nova.endpoint import admin from nova import server
from nova.endpoint import api
from nova.endpoint import cloud
FLAGS = flags.FLAGS FLAGS = flags.FLAGS
flags.DEFINE_integer('api_port', 8773, 'API port')
def main(_args):
def main(_argv): from nova import api
"""Load the controllers and start the tornado I/O loop.""" from nova import wsgi
controllers = { wsgi.run_server(api.API(), FLAGS.api_port)
'Cloud': cloud.CloudController(),
'Admin': admin.AdminController()}
_app = api.APIServerApplication(controllers)
io_inst = ioloop.IOLoop.instance()
http_server = httpserver.HTTPServer(_app)
http_server.listen(FLAGS.cc_port)
logging.debug('Started HTTP server on %s', FLAGS.cc_port)
io_inst.start()
if __name__ == '__main__': if __name__ == '__main__':
utils.default_flagfile() utils.default_flagfile()

View File

@@ -1,45 +0,0 @@
#!/usr/bin/env python
# pylint: disable-msg=C0103
# 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.
"""
Nova API daemon.
"""
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)
from nova import api
from nova import flags
from nova import utils
from nova import wsgi
FLAGS = flags.FLAGS
flags.DEFINE_integer('api_port', 8773, 'API port')
if __name__ == '__main__':
utils.default_flagfile()
wsgi.run_server(api.API(), FLAGS.api_port)

View File

@@ -73,7 +73,6 @@ from nova import quota
from nova import utils from nova import utils
from nova.auth import manager from nova.auth import manager
from nova.cloudpipe import pipelib from nova.cloudpipe import pipelib
from nova.endpoint import cloud
FLAGS = flags.FLAGS FLAGS = flags.FLAGS
@@ -84,7 +83,7 @@ class VpnCommands(object):
def __init__(self): def __init__(self):
self.manager = manager.AuthManager() self.manager = manager.AuthManager()
self.pipe = pipelib.CloudPipe(cloud.CloudController()) self.pipe = pipelib.CloudPipe()
def list(self): def list(self):
"""Print a listing of the VPNs for all projects.""" """Print a listing of the VPNs for all projects."""

View File

@@ -44,7 +44,7 @@ flags.DEFINE_list('allowed_roles',
# NOTE(vish): a user with one of these roles will be a superuser and # NOTE(vish): a user with one of these roles will be a superuser and
# have access to all api commands # have access to all api commands
flags.DEFINE_list('superuser_roles', ['cloudadmin'], flags.DEFINE_list('superuser_roles', ['cloudadmin'],
'Roles that ignore rbac checking completely') 'Roles that ignore authorization checking completely')
# NOTE(vish): a user with one of these roles will have it for every # NOTE(vish): a user with one of these roles will have it for every
# project, even if he or she is not a member of the project # project, even if he or she is not a member of the project
@@ -266,7 +266,7 @@ class AuthManager(object):
# NOTE(vish): if we stop using project name as id we need better # NOTE(vish): if we stop using project name as id we need better
# logic to find a default project for user # logic to find a default project for user
if project_id is '': if project_id == '':
project_id = user.name project_id = user.name
project = self.get_project(project_id) project = self.get_project(project_id)
@@ -304,7 +304,7 @@ class AuthManager(object):
return "%s:%s" % (user.access, Project.safe_id(project)) return "%s:%s" % (user.access, Project.safe_id(project))
def is_superuser(self, user): def is_superuser(self, user):
"""Checks for superuser status, allowing user to bypass rbac """Checks for superuser status, allowing user to bypass authorization
@type user: User or uid @type user: User or uid
@param user: User to check. @param user: User to check.

View File

@@ -46,9 +46,9 @@ LOG.setLevel(logging.DEBUG)
class Connection(carrot_connection.BrokerConnection): class Connection(carrot_connection.BrokerConnection):
"""Connection instance object""" """Connection instance object"""
@classmethod @classmethod
def instance(cls): def instance(cls, new=False):
"""Returns the instance""" """Returns the instance"""
if not hasattr(cls, '_instance'): if new or not hasattr(cls, '_instance'):
params = dict(hostname=FLAGS.rabbit_host, params = dict(hostname=FLAGS.rabbit_host,
port=FLAGS.rabbit_port, port=FLAGS.rabbit_port,
userid=FLAGS.rabbit_userid, userid=FLAGS.rabbit_userid,
@@ -60,7 +60,10 @@ class Connection(carrot_connection.BrokerConnection):
# NOTE(vish): magic is fun! # NOTE(vish): magic is fun!
# pylint: disable-msg=W0142 # pylint: disable-msg=W0142
cls._instance = cls(**params) if new:
return cls(**params)
else:
cls._instance = cls(**params)
return cls._instance return cls._instance
@classmethod @classmethod
@@ -94,8 +97,6 @@ class Consumer(messaging.Consumer):
injected.start() injected.start()
return injected return injected
attachToTornado = attach_to_tornado
def fetch(self, no_ack=None, auto_ack=None, enable_callbacks=False): def fetch(self, no_ack=None, auto_ack=None, enable_callbacks=False):
"""Wraps the parent fetch with some logic for failed connections""" """Wraps the parent fetch with some logic for failed connections"""
# TODO(vish): the logic for failed connections and logging should be # TODO(vish): the logic for failed connections and logging should be
@@ -265,28 +266,32 @@ def call(topic, msg):
msg.update({'_msg_id': msg_id}) msg.update({'_msg_id': msg_id})
LOG.debug("MSG_ID is %s" % (msg_id)) LOG.debug("MSG_ID is %s" % (msg_id))
conn = Connection.instance() class WaitMessage(object):
d = defer.Deferred()
def __call__(self, data, message):
"""Acks message and sets result."""
message.ack()
if data['failure']:
self.result = RemoteError(*data['failure'])
else:
self.result = data['result']
wait_msg = WaitMessage()
conn = Connection.instance(True)
consumer = DirectConsumer(connection=conn, msg_id=msg_id) consumer = DirectConsumer(connection=conn, msg_id=msg_id)
consumer.register_callback(wait_msg)
def deferred_receive(data, message): conn = Connection.instance()
"""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_tornado()
# 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 = TopicPublisher(connection=conn, topic=topic)
publisher.send(msg) publisher.send(msg)
publisher.close() publisher.close()
return d
try:
consumer.wait(limit=1)
except StopIteration:
pass
consumer.close()
return wait_msg.result
def cast(topic, msg): def cast(topic, msg):

View File

@@ -18,12 +18,13 @@
import unittest import unittest
import logging import logging
import webob
from nova import exception from nova import exception
from nova import flags from nova import flags
from nova import test from nova import test
from nova.api import ec2
from nova.auth import manager from nova.auth import manager
from nova.auth import rbac
FLAGS = flags.FLAGS FLAGS = flags.FLAGS
@@ -72,9 +73,17 @@ class AccessTestCase(test.BaseTestCase):
try: try:
self.project.add_role(self.testsys, 'sysadmin') self.project.add_role(self.testsys, 'sysadmin')
except: pass except: pass
self.context = Context()
self.context.project = self.project
#user is set in each test #user is set in each test
def noopWSGIApp(environ, start_response):
start_response('200 OK', [])
return ['']
self.mw = ec2.Authorizer(noopWSGIApp)
self.mw.action_roles = {'str': {
'_allow_all': ['all'],
'_allow_none': [],
'_allow_project_manager': ['projectmanager'],
'_allow_sys_and_net': ['sysadmin', 'netadmin'],
'_allow_sysadmin': ['sysadmin']}}
def tearDown(self): def tearDown(self):
um = manager.AuthManager() um = manager.AuthManager()
@@ -87,76 +96,46 @@ class AccessTestCase(test.BaseTestCase):
um.delete_user('testsys') um.delete_user('testsys')
super(AccessTestCase, self).tearDown() super(AccessTestCase, self).tearDown()
def response_status(self, user, methodName):
context = Context()
context.project = self.project
context.user = user
environ = {'ec2.context' : context,
'ec2.controller': 'some string',
'ec2.action': methodName}
req = webob.Request.blank('/', environ)
resp = req.get_response(self.mw)
return resp.status_int
def shouldAllow(self, user, methodName):
self.assertEqual(200, self.response_status(user, methodName))
def shouldDeny(self, user, methodName):
self.assertEqual(401, self.response_status(user, methodName))
def test_001_allow_all(self): def test_001_allow_all(self):
self.context.user = self.testadmin users = [self.testadmin, self.testpmsys, self.testnet, self.testsys]
self.assertTrue(self._allow_all(self.context)) for user in users:
self.context.user = self.testpmsys self.shouldAllow(user, '_allow_all')
self.assertTrue(self._allow_all(self.context))
self.context.user = self.testnet
self.assertTrue(self._allow_all(self.context))
self.context.user = self.testsys
self.assertTrue(self._allow_all(self.context))
def test_002_allow_none(self): def test_002_allow_none(self):
self.context.user = self.testadmin self.shouldAllow(self.testadmin, '_allow_none')
self.assertTrue(self._allow_none(self.context)) users = [self.testpmsys, self.testnet, self.testsys]
self.context.user = self.testpmsys for user in users:
self.assertRaises(exception.NotAuthorized, self._allow_none, self.context) self.shouldDeny(user, '_allow_none')
self.context.user = self.testnet
self.assertRaises(exception.NotAuthorized, self._allow_none, self.context)
self.context.user = self.testsys
self.assertRaises(exception.NotAuthorized, self._allow_none, self.context)
def test_003_allow_project_manager(self): def test_003_allow_project_manager(self):
self.context.user = self.testadmin for user in [self.testadmin, self.testpmsys]:
self.assertTrue(self._allow_project_manager(self.context)) self.shouldAllow(user, '_allow_project_manager')
self.context.user = self.testpmsys for user in [self.testnet, self.testsys]:
self.assertTrue(self._allow_project_manager(self.context)) self.shouldDeny(user, '_allow_project_manager')
self.context.user = self.testnet
self.assertRaises(exception.NotAuthorized, self._allow_project_manager, self.context)
self.context.user = self.testsys
self.assertRaises(exception.NotAuthorized, self._allow_project_manager, self.context)
def test_004_allow_sys_and_net(self): def test_004_allow_sys_and_net(self):
self.context.user = self.testadmin for user in [self.testadmin, self.testnet, self.testsys]:
self.assertTrue(self._allow_sys_and_net(self.context)) self.shouldAllow(user, '_allow_sys_and_net')
self.context.user = self.testpmsys # doesn't have the per project sysadmin # denied because it doesn't have the per project sysadmin
self.assertRaises(exception.NotAuthorized, self._allow_sys_and_net, self.context) for user in [self.testpmsys]:
self.context.user = self.testnet self.shouldDeny(user, '_allow_sys_and_net')
self.assertTrue(self._allow_sys_and_net(self.context))
self.context.user = self.testsys
self.assertTrue(self._allow_sys_and_net(self.context))
def test_005_allow_sys_no_pm(self):
self.context.user = self.testadmin
self.assertTrue(self._allow_sys_no_pm(self.context))
self.context.user = self.testpmsys
self.assertRaises(exception.NotAuthorized, self._allow_sys_no_pm, self.context)
self.context.user = self.testnet
self.assertRaises(exception.NotAuthorized, self._allow_sys_no_pm, self.context)
self.context.user = self.testsys
self.assertTrue(self._allow_sys_no_pm(self.context))
@rbac.allow('all')
def _allow_all(self, context):
return True
@rbac.allow('none')
def _allow_none(self, context):
return True
@rbac.allow('projectmanager')
def _allow_project_manager(self, context):
return True
@rbac.allow('sysadmin', 'netadmin')
def _allow_sys_and_net(self, context):
return True
@rbac.allow('sysadmin')
@rbac.deny('projectmanager')
def _allow_sys_no_pm(self, context):
return True
if __name__ == "__main__": if __name__ == "__main__":
# TODO: Implement use_fake as an option # TODO: Implement use_fake as an option

View File

@@ -25,12 +25,17 @@ import random
import StringIO import StringIO
import webob import webob
from nova import flags
from nova import test from nova import test
from nova import api from nova import api
from nova.api.ec2 import cloud from nova.api.ec2 import cloud
from nova.auth import manager from nova.auth import manager
FLAGS = flags.FLAGS
FLAGS.FAKE_subdomain = 'ec2'
class FakeHttplibSocket(object): class FakeHttplibSocket(object):
"""a fake socket implementation for httplib.HTTPResponse, trivial""" """a fake socket implementation for httplib.HTTPResponse, trivial"""
def __init__(self, response_string): def __init__(self, response_string):

View File

@@ -22,7 +22,6 @@ from M2Crypto import RSA
import StringIO import StringIO
import time import time
from tornado import ioloop
from twisted.internet import defer from twisted.internet import defer
import unittest import unittest
from xml.etree import ElementTree from xml.etree import ElementTree

View File

@@ -49,8 +49,7 @@ from nova import datastore
from nova import flags from nova import flags
from nova import twistd from nova import twistd
#TODO(gundlach): rewrite and readd this after merge from nova.tests.access_unittest import *
#from nova.tests.access_unittest import *
from nova.tests.auth_unittest import * from nova.tests.auth_unittest import *
from nova.tests.api_unittest import * from nova.tests.api_unittest import *
from nova.tests.cloud_unittest import * from nova.tests.cloud_unittest import *