Remove nova Direct API

blueprint remove-nova-direct-api

Change-Id: I3229f8d7f37d66fcd6b978966f3a428a69e08bb1
This commit is contained in:
Joe Gordon 2012-04-09 14:16:14 -04:00
parent 9ef48ca920
commit 059b1495ba
4 changed files with 1 additions and 491 deletions

View File

@ -1,110 +0,0 @@
#!/usr/bin/env python
# pylint: disable=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.
"""Starter script for Nova Direct API."""
import eventlet
eventlet.monkey_patch()
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 compute
from nova import flags
from nova import log as logging
from nova import network
from nova.openstack.common import cfg
from nova import service
from nova import utils
from nova import volume
from nova import wsgi
from nova.api import direct
direct_api_opts = [
cfg.IntOpt('direct_port',
default=8001,
help='Direct API port'),
cfg.StrOpt('direct_host',
default='0.0.0.0',
help='Direct API host'),
]
FLAGS = flags.FLAGS
FLAGS.register_cli_opts(direct_api_opts)
# An example of an API that only exposes read-only methods.
# In this case we're just limiting which methods are exposed.
class ReadOnlyCompute(direct.Limited):
"""Read-only Compute API."""
_allowed = ['get', 'get_all', 'get_console_output']
# An example of an API that provides a backwards compatibility layer.
# In this case we're overwriting the implementation to ensure
# compatibility with an older version. In reality we would want the
# "description=None" to be part of the actual API so that code
# like this isn't even necessary, but this example shows what one can
# do if that isn't the situation.
class VolumeVersionOne(direct.Limited):
_allowed = ['create', 'delete', 'update', 'get']
def create(self, context, size, name):
self.proxy.create(context, size, name, description=None)
if __name__ == '__main__':
utils.default_flagfile()
FLAGS(sys.argv)
logging.setup()
direct.register_service('compute', compute.API())
direct.register_service('volume', volume.API())
direct.register_service('network', network.API())
direct.register_service('reflect', direct.Reflection())
# Here is how we could expose the code in the examples above.
#direct.register_service('compute-readonly',
# ReadOnlyCompute(compute.API()))
#direct.register_service('volume-v1', VolumeVersionOne(volume.API()))
router = direct.Router()
with_json = direct.JsonParamsMiddleware(router)
with_req = direct.PostParamsMiddleware(with_json)
with_auth = direct.DelegatedAuthMiddleware(with_req)
server = wsgi.Server("Direct API",
with_auth,
host=FLAGS.direct_host,
port=FLAGS.direct_port)
service.serve(server)
service.wait()

162
bin/stack
View File

@ -1,162 +0,0 @@
#!/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.
"""CLI for the Direct API."""
import eventlet
eventlet.monkey_patch()
import json
import os
import pprint
import sys
import textwrap
import urllib
import urllib2
# 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)
import gflags
FLAGS = gflags.FLAGS
gflags.DEFINE_string('host', '127.0.0.1', 'Direct API host')
gflags.DEFINE_integer('port', 8001, 'Direct API host')
gflags.DEFINE_string('user', 'user1', 'Direct API username')
gflags.DEFINE_string('project', 'proj1', 'Direct API project')
USAGE = """usage: stack [options] <controller> <method> [arg1=value arg2=value]
`stack help` should output the list of available controllers
`stack <controller>` should output the available methods for that controller
`stack help <controller>` should do the same
`stack help <controller> <method>` should output info for a method
"""
def format_help(d):
"""Format help text, keys are labels and values are descriptions."""
MAX_INDENT = 30
indent = max([len(k) for k in d])
if indent > MAX_INDENT:
indent = MAX_INDENT - 6
out = []
for k, v in sorted(d.iteritems()):
if (len(k) + 6) > MAX_INDENT:
out.extend([' %s' % k])
initial_indent = ' ' * (indent + 6)
else:
initial_indent = ' %s ' % k.ljust(indent)
subsequent_indent = ' ' * (indent + 6)
t = textwrap.TextWrapper(initial_indent=initial_indent,
subsequent_indent=subsequent_indent)
out.extend(t.wrap(v))
return out
def help_all():
rv = do_request('reflect', 'get_controllers')
out = format_help(rv)
return (USAGE + str(FLAGS.MainModuleHelp()) +
'\n\nAvailable controllers:\n' +
'\n'.join(out) + '\n')
def help_controller(controller):
rv = do_request('reflect', 'get_methods')
methods = dict([(k.split('/')[2], v) for k, v in rv.iteritems()
if k.startswith('/%s' % controller)])
return ('Available methods for %s:\n' % controller +
'\n'.join(format_help(methods)))
def help_method(controller, method):
rv = do_request('reflect',
'get_method_info',
{'method': '/%s/%s' % (controller, method)})
sig = '%s(%s):' % (method, ', '.join(['='.join(x) for x in rv['args']]))
out = textwrap.wrap(sig, subsequent_indent=' ' * len('%s(' % method))
out.append('\n' + rv['doc'])
return '\n'.join(out)
def do_request(controller, method, params=None):
if params:
data = urllib.urlencode(params)
else:
data = None
url = 'http://%s:%s/%s/%s' % (FLAGS.host, FLAGS.port, controller, method)
headers = {'X-OpenStack-User': FLAGS.user,
'X-OpenStack-Project': FLAGS.project}
req = urllib2.Request(url, data, headers)
try:
resp = urllib2.urlopen(req)
except urllib2.HTTPError, e:
print e.read()
sys.exit(1)
except urllib2.URLError, e:
print 'Failed to connect to %s: %s' % (url, e.reason)
sys.exit(1)
return json.loads(resp.read())
if __name__ == '__main__':
args = FLAGS(sys.argv)
cmd = args.pop(0)
if not args:
print help_all()
sys.exit()
first = args.pop(0)
if first == 'help':
action = help_all
params = []
if args:
params.append(args.pop(0))
action = help_controller
if args:
params.append(args.pop(0))
action = help_method
print action(*params)
sys.exit(0)
controller = first
if not args:
print help_controller(controller)
sys.exit()
method = args.pop(0)
params = {}
for x in args:
key, value = x.split('=', 1)
params[key] = value
pprint.pprint(do_request(controller, method, params))

View File

@ -1,216 +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.
"""Tests for Direct API."""
import json
import webob
from nova import context
from nova import exception
from nova import test
from nova.api import direct
class ArbitraryObject(object):
pass
class FakeService(object):
def echo(self, context, data):
return {'data': data}
def context(self, context):
return {'user': context.user_id,
'project': context.project_id}
def echo_data_directly(self, context, data):
return data
def invalid_return(self, context):
return ArbitraryObject()
class MyLimited(direct.Limited):
_allowed = ['var1', 'func1']
class MyProxy(object):
var1 = var2 = True
def func1(self):
return True
def func2(self):
return True
class DirectTestCase(test.TestCase):
def setUp(self):
super(DirectTestCase, self).setUp()
direct.register_service('fake', FakeService())
self.router = direct.PostParamsMiddleware(
direct.JsonParamsMiddleware(
direct.Router()))
self.auth_router = direct.DelegatedAuthMiddleware(self.router)
self.context = context.RequestContext('user1', 'proj1')
def tearDown(self):
direct.ROUTES = {}
super(DirectTestCase, self).tearDown()
def test_delegated_auth(self):
req = webob.Request.blank('/fake/context')
req.headers['X-OpenStack-User'] = 'user1'
req.headers['X-OpenStack-Project'] = 'proj1'
resp = req.get_response(self.auth_router)
self.assertEqual(resp.status_int, 200)
data = json.loads(resp.body)
self.assertEqual(data['user'], 'user1')
self.assertEqual(data['project'], 'proj1')
def test_json_params(self):
req = webob.Request.blank('/fake/echo')
req.environ['openstack.context'] = self.context
req.method = 'POST'
req.body = 'json=%s' % json.dumps({'data': 'foo'})
resp = req.get_response(self.router)
self.assertEqual(resp.status_int, 200)
resp_parsed = json.loads(resp.body)
self.assertEqual(resp_parsed['data'], 'foo')
def test_filter_json_params(self):
req = webob.Request.blank('/fake/echo')
req.environ['openstack.context'] = self.context
req.method = 'POST'
req.body = 'json=%s' % json.dumps({'data': 'foo',
'_underscored': 'ignoreMe',
'self': 'ignoreMe',
'context': 'ignoreMe'})
resp = req.get_response(self.router)
self.assertEqual(resp.status_int, 200)
resp_parsed = json.loads(resp.body)
self.assertEqual(resp_parsed['data'], 'foo')
self.assertNotIn('_underscored', resp_parsed)
self.assertNotIn('self', resp_parsed)
self.assertNotIn('context', resp_parsed)
def test_post_params(self):
req = webob.Request.blank('/fake/echo')
req.environ['openstack.context'] = self.context
req.method = 'POST'
req.body = 'data=foo'
resp = req.get_response(self.router)
self.assertEqual(resp.status_int, 200)
resp_parsed = json.loads(resp.body)
self.assertEqual(resp_parsed['data'], 'foo')
def test_filter_post_params(self):
req = webob.Request.blank('/fake/echo')
req.environ['openstack.context'] = self.context
req.method = 'POST'
req.body = ('data=foo&_underscored=ignoreMe&self=ignoreMe&context='
'ignoreMe')
resp = req.get_response(self.router)
self.assertEqual(resp.status_int, 200)
resp_parsed = json.loads(resp.body)
self.assertEqual(resp_parsed['data'], 'foo')
self.assertNotIn('_underscored', resp_parsed)
self.assertNotIn('self', resp_parsed)
self.assertNotIn('context', resp_parsed)
def test_string_resp(self):
req = webob.Request.blank('/fake/echo_data_directly')
req.environ['openstack.context'] = self.context
req.method = 'POST'
req.body = 'data=foo'
resp = req.get_response(self.router)
self.assertEqual(resp.status_int, 200)
self.assertEqual(resp.body, 'foo')
def test_invalid(self):
req = webob.Request.blank('/fake/invalid_return')
req.environ['openstack.context'] = self.context
req.method = 'POST'
self.assertRaises(exception.Error, req.get_response, self.router)
def test_proxy(self):
proxy = direct.Proxy(self.router)
rv = proxy.fake.echo(self.context, data='baz')
self.assertEqual(rv['data'], 'baz')
class LimitedTestCase(test.TestCase):
def test_limited_class_getattr(self):
limited = MyLimited(MyProxy())
# Allowed are still visible
self.assertTrue(limited.func1())
self.assertTrue(limited.var1)
# Non-allowed are no longer visible
self.assertRaises(AttributeError, getattr, limited, 'func2')
self.assertRaises(AttributeError, getattr, limited, 'var2')
def test_limited_class_dir(self):
limited = MyLimited(MyProxy())
# Allowed are still visible
self.assertIn('func1', dir(limited))
self.assertIn('var1', dir(limited))
# Non-allowed are no longer visible
self.assertNotIn('func2', dir(limited))
self.assertNotIn('var2', dir(limited))
def test_limited_class_no_allowed(self):
# New MyLimited class with no _allowed variable
class MyLimited(direct.Limited):
pass
limited = MyLimited(MyProxy())
# Nothing in MyProxy object visible now
self.assertNotIn('func1', dir(limited))
self.assertNotIn('var1', dir(limited))
# NOTE(jkoelker): This fails using the EC2 api
#class DirectCloudTestCase(test_cloud.CloudTestCase):
# def setUp(self):
# super(DirectCloudTestCase, self).setUp()
# compute_handle = compute.API(image_service=self.cloud.image_service)
# volume_handle = volume.API()
# network_handle = network.API()
# direct.register_service('compute', compute_handle)
# direct.register_service('volume', volume_handle)
# direct.register_service('network', network_handle)
#
# self.router = direct.JsonParamsMiddleware(direct.Router())
# proxy = direct.Proxy(self.router)
# self.cloud.compute_api = proxy.compute
# self.cloud.volume_api = proxy.volume
# self.cloud.network_api = proxy.network
# compute_handle.volume_api = proxy.volume
# compute_handle.network_api = proxy.network
#
# def tearDown(self):
# super(DirectCloudTestCase, self).tearDown()
# direct.ROUTES = {}

View File

@ -76,13 +76,11 @@ setuptools.setup(name='nova',
'bin/nova-console',
'bin/nova-consoleauth',
'bin/nova-dhcpbridge',
'bin/nova-direct-api',
'bin/nova-manage',
'bin/nova-network',
'bin/nova-objectstore',
'bin/nova-rootwrap',
'bin/nova-scheduler',
'bin/nova-volume',
'bin/nova-xvpvncproxy',
'bin/stack'],
'bin/nova-xvpvncproxy'],
py_modules=[])