Reworked WSGI helper module and converted rackspace API endpoint to use it.
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
#!/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
|
||||||
@@ -17,31 +18,17 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
"""
|
"""
|
||||||
WSGI daemon for the main API endpoint.
|
Daemon for the Rackspace API endpoint.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
|
||||||
from tornado import ioloop
|
|
||||||
from wsgiref import simple_server
|
|
||||||
|
|
||||||
from nova import flags
|
from nova import flags
|
||||||
from nova import rpc
|
|
||||||
from nova import server
|
|
||||||
from nova import utils
|
from nova import utils
|
||||||
from nova.auth import manager
|
from nova import wsgi
|
||||||
from nova.endpoint import rackspace
|
from nova.endpoint import rackspace
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
FLAGS = flags.FLAGS
|
||||||
flags.DEFINE_integer('cc_port', 8773, 'cloud controller port')
|
flags.DEFINE_integer('cc_port', 8773, 'cloud controller port')
|
||||||
|
|
||||||
def main(_argv):
|
|
||||||
api_instance = rackspace.Api()
|
|
||||||
http_server = simple_server.WSGIServer(('0.0.0.0', FLAGS.cc_port), simple_server.WSGIRequestHandler)
|
|
||||||
http_server.set_app(api_instance.handler)
|
|
||||||
logging.debug('Started HTTP server on port %i' % FLAGS.cc_port)
|
|
||||||
while True:
|
|
||||||
http_server.handle_request()
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
utils.default_flagfile()
|
utils.default_flagfile()
|
||||||
server.serve('nova-rsapi', main)
|
wsgi.run_server(rackspace.API(), FLAGS.cc_port)
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
import eventlet
|
|
||||||
import eventlet.wsgi
|
|
||||||
eventlet.patcher.monkey_patch(all=False, socket=True)
|
|
||||||
|
|
||||||
def serve(app, port):
|
|
||||||
sock = eventlet.listen(('0.0.0.0', port))
|
|
||||||
eventlet.wsgi.server(sock, app)
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
import eventletserver
|
|
||||||
import carrot.connection
|
|
||||||
import carrot.messaging
|
|
||||||
import itertools
|
|
||||||
import routes
|
|
||||||
|
|
||||||
|
|
||||||
# See http://pythonpaste.org/webob/ for usage
|
|
||||||
from webob.dec import wsgify
|
|
||||||
from webob import exc, Request, Response
|
|
||||||
|
|
||||||
class WSGILayer(object):
|
|
||||||
def __init__(self, application=None):
|
|
||||||
self.application = application
|
|
||||||
|
|
||||||
def __call__(self, environ, start_response):
|
|
||||||
# Subclasses will probably want to implement __call__ like this:
|
|
||||||
#
|
|
||||||
# @wsgify
|
|
||||||
# def __call__(self, req):
|
|
||||||
# # Any of the following objects work as responses:
|
|
||||||
#
|
|
||||||
# # Option 1: simple string
|
|
||||||
# resp = 'message\n'
|
|
||||||
#
|
|
||||||
# # Option 2: a nicely formatted HTTP exception page
|
|
||||||
# resp = exc.HTTPForbidden(detail='Nice try')
|
|
||||||
#
|
|
||||||
# # Option 3: a webob Response object (in case you need to play with
|
|
||||||
# # headers, or you want to be treated like an iterable, or or or)
|
|
||||||
# resp = Response(); resp.app_iter = open('somefile')
|
|
||||||
#
|
|
||||||
# # Option 4: any wsgi app to be run next
|
|
||||||
# resp = self.application
|
|
||||||
#
|
|
||||||
# # Option 5: you can get a Response object for a wsgi app, too, to
|
|
||||||
# # play with headers etc
|
|
||||||
# resp = req.get_response(self.application)
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# # You can then just return your response...
|
|
||||||
# return resp # option 1
|
|
||||||
# # ... or set req.response and return None.
|
|
||||||
# req.response = resp # option 2
|
|
||||||
#
|
|
||||||
# See the end of http://pythonpaste.org/webob/modules/dec.html
|
|
||||||
# for more info.
|
|
||||||
raise NotImplementedError("You must implement __call__")
|
|
||||||
|
|
||||||
|
|
||||||
class WsgiStack(WSGILayer):
|
|
||||||
def __init__(self, wsgi_layers):
|
|
||||||
bottom_up = list(reversed(wsgi_layers))
|
|
||||||
app, remaining = bottom_up[0], bottom_up[1:]
|
|
||||||
for layer in remaining:
|
|
||||||
layer.application = app
|
|
||||||
app = layer
|
|
||||||
super(WsgiStack, self).__init__(app)
|
|
||||||
|
|
||||||
@wsgify
|
|
||||||
def __call__(self, req):
|
|
||||||
return self.application
|
|
||||||
|
|
||||||
class Debug(WSGILayer):
|
|
||||||
@wsgify
|
|
||||||
def __call__(self, req):
|
|
||||||
for k, v in req.environ.items():
|
|
||||||
print k, "=", v
|
|
||||||
return self.application
|
|
||||||
|
|
||||||
class Auth(WSGILayer):
|
|
||||||
@wsgify
|
|
||||||
def __call__(self, req):
|
|
||||||
if not 'openstack.auth.token' in req.environ:
|
|
||||||
# Check auth params here
|
|
||||||
if True:
|
|
||||||
req.environ['openstack.auth.token'] = '12345'
|
|
||||||
else:
|
|
||||||
return exc.HTTPForbidden(detail="Go away")
|
|
||||||
|
|
||||||
response = req.get_response(self.application)
|
|
||||||
response.headers['X-Openstack-Auth'] = 'Success'
|
|
||||||
return response
|
|
||||||
|
|
||||||
class Router(WSGILayer):
|
|
||||||
def __init__(self, application=None):
|
|
||||||
super(Router, self).__init__(application)
|
|
||||||
self.map = routes.Mapper()
|
|
||||||
self._connect()
|
|
||||||
|
|
||||||
@wsgify
|
|
||||||
def __call__(self, req):
|
|
||||||
match = self.map.match(req.path_info)
|
|
||||||
if match is None:
|
|
||||||
return self.application
|
|
||||||
req.environ['openstack.match'] = match
|
|
||||||
return match['controller']
|
|
||||||
|
|
||||||
def _connect(self):
|
|
||||||
raise NotImplementedError("You must implement _connect")
|
|
||||||
|
|
||||||
class FileRouter(Router):
|
|
||||||
def _connect(self):
|
|
||||||
self.map.connect(None, '/files/{file}', controller=File())
|
|
||||||
self.map.connect(None, '/rfiles/{file}', controller=Reverse(File()))
|
|
||||||
|
|
||||||
class Message(WSGILayer):
|
|
||||||
@wsgify
|
|
||||||
def __call__(self, req):
|
|
||||||
return 'message\n'
|
|
||||||
|
|
||||||
class Reverse(WSGILayer):
|
|
||||||
@wsgify
|
|
||||||
def __call__(self, req):
|
|
||||||
inner_resp = req.get_response(self.application)
|
|
||||||
resp = Response()
|
|
||||||
resp.app_iter = itertools.imap(lambda x: x[::-1], inner_resp.app_iter)
|
|
||||||
return resp
|
|
||||||
|
|
||||||
class File(WSGILayer):
|
|
||||||
@wsgify
|
|
||||||
def __call__(self, req):
|
|
||||||
try:
|
|
||||||
myfile = open(req.environ['openstack.match']['file'])
|
|
||||||
except IOError, e:
|
|
||||||
raise exc.HTTPNotFound()
|
|
||||||
req.response = Response()
|
|
||||||
req.response.app_iter = myfile
|
|
||||||
|
|
||||||
wsgi_layers = [
|
|
||||||
Auth(),
|
|
||||||
Debug(),
|
|
||||||
FileRouter(),
|
|
||||||
Message(),
|
|
||||||
]
|
|
||||||
eventletserver.serve(app=WsgiStack(wsgi_layers), port=12345)
|
|
||||||
@@ -17,206 +17,95 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Rackspace API
|
Rackspace API Endpoint
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import base64
|
|
||||||
import json
|
import json
|
||||||
import logging
|
|
||||||
import multiprocessing
|
|
||||||
import os
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from nova import datastore
|
import webob.dec
|
||||||
from nova import exception
|
import webob.exc
|
||||||
|
|
||||||
from nova import flags
|
from nova import flags
|
||||||
from nova import rpc
|
from nova import rpc
|
||||||
from nova import utils
|
from nova import utils
|
||||||
|
from nova import wsgi
|
||||||
from nova.auth import manager
|
from nova.auth import manager
|
||||||
from nova.compute import model
|
from nova.compute import model as compute
|
||||||
from nova.network import model as network
|
from nova.network import model as network
|
||||||
from nova.endpoint import images
|
|
||||||
from nova.endpoint import wsgi
|
|
||||||
|
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
FLAGS = flags.FLAGS
|
||||||
flags.DEFINE_string('cloud_topic', 'cloud', 'the topic clouds listen on')
|
flags.DEFINE_string('cloud_topic', 'cloud', 'the topic clouds listen on')
|
||||||
|
|
||||||
|
|
||||||
class Unauthorized(Exception):
|
class API(wsgi.Middleware):
|
||||||
pass
|
"""Entry point for all requests."""
|
||||||
|
|
||||||
class NotFound(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Api(object):
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""build endpoints here"""
|
super(API, self).__init__(Router(webob.exc.HTTPNotFound()))
|
||||||
self.controllers = {
|
|
||||||
"v1.0": RackspaceAuthenticationApi(),
|
|
||||||
"servers": RackspaceCloudServerApi()
|
|
||||||
}
|
|
||||||
|
|
||||||
def handler(self, environ, responder):
|
def __call__(self, environ, start_response):
|
||||||
"""
|
context = {}
|
||||||
This is the entrypoint from wsgi. Read PEP 333 and wsgi.org for
|
if "HTTP_X_AUTH_TOKEN" in environ:
|
||||||
more intormation. The key points are responder is a callback that
|
context['user'] = manager.AuthManager().get_user_from_access_key(
|
||||||
needs to run before you return, and takes two arguments, response
|
environ['HTTP_X_AUTH_TOKEN'])
|
||||||
code string ("200 OK") and headers (["X-How-Cool-Am-I: Ultra-Suede"])
|
if context['user']:
|
||||||
and the return value is the body of the response.
|
context['project'] = manager.AuthManager().get_project(
|
||||||
"""
|
context['user'].name)
|
||||||
environ['nova.context'] = self.build_context(environ)
|
if "user" not in context:
|
||||||
controller, path = wsgi.Util.route(
|
return webob.exc.HTTPForbidden()(environ, start_response)
|
||||||
environ['PATH_INFO'],
|
environ['nova.context'] = context
|
||||||
self.controllers
|
return self.application(environ, start_response)
|
||||||
)
|
|
||||||
logging.debug("Route %s to %s", str(path), str(controller))
|
|
||||||
if not controller:
|
|
||||||
responder("404 Not Found", [])
|
|
||||||
return ""
|
|
||||||
try:
|
|
||||||
rv = controller.process(path, environ)
|
|
||||||
if type(rv) is tuple:
|
|
||||||
responder(rv[0], rv[1])
|
|
||||||
rv = rv[2]
|
|
||||||
else:
|
|
||||||
responder("200 OK", [])
|
|
||||||
return rv
|
|
||||||
except Unauthorized:
|
|
||||||
responder("401 Unauthorized", [])
|
|
||||||
return ""
|
|
||||||
except NotFound:
|
|
||||||
responder("404 Not Found", [])
|
|
||||||
return ""
|
|
||||||
|
|
||||||
|
|
||||||
def build_context(self, env):
|
class Router(wsgi.Router):
|
||||||
rv = {}
|
"""Route requests to the next WSGI application."""
|
||||||
if env.has_key("HTTP_X_AUTH_TOKEN"):
|
|
||||||
rv['user'] = manager.AuthManager().get_user_from_access_key(
|
def _build_map(self):
|
||||||
env['HTTP_X_AUTH_TOKEN']
|
"""Build routing map for authentication and cloud."""
|
||||||
)
|
self._connect("/v1.0", controller=AuthenticationAPI())
|
||||||
if rv['user']:
|
cloud = CloudServerAPI()
|
||||||
rv['project'] = manager.AuthManager().get_project(
|
self._connect("/servers", controller=cloud.launch_server,
|
||||||
rv['user'].name
|
conditions={"method": ["POST"]})
|
||||||
)
|
self._connect("/servers/{server_id}", controller=cloud.delete_server,
|
||||||
return rv
|
conditions={'method': ["DELETE"]})
|
||||||
|
self._connect("/servers", controller=cloud)
|
||||||
|
|
||||||
|
|
||||||
class RackspaceApiEndpoint(object):
|
class AuthenticationAPI(wsgi.Application):
|
||||||
def process(self, path, env):
|
"""Handle all authorization requests through WSGI applications."""
|
||||||
"""
|
|
||||||
Main entrypoint for all controllers (what gets run by the wsgi handler).
|
|
||||||
Check authentication based on key, raise Unauthorized if invalid.
|
|
||||||
|
|
||||||
Select the most appropriate action based on request type GET, POST, etc,
|
@webob.dec.wsgify
|
||||||
then pass it through to the implementing controller. Defalut to GET if
|
def __call__(self, req): # pylint: disable-msg=W0221
|
||||||
the implementing child doesn't respond to a particular type.
|
# TODO(todd): make a actual session with a unique token
|
||||||
"""
|
# just pass the auth key back through for now
|
||||||
if not self.check_authentication(env):
|
res = webob.Response()
|
||||||
raise Unauthorized("Unable to authenticate")
|
res.status = '204 No Content'
|
||||||
|
res.headers.add('X-Server-Management-Url', req.host_url)
|
||||||
method = env['REQUEST_METHOD'].lower()
|
res.headers.add('X-Storage-Url', req.host_url)
|
||||||
callback = getattr(self, method, None)
|
res.headers.add('X-CDN-Managment-Url', req.host_url)
|
||||||
if not callback:
|
res.headers.add('X-Auth-Token', req.headers['X-Auth-Key'])
|
||||||
callback = getattr(self, "get")
|
return res
|
||||||
logging.debug("%s processing %s with %s", self, method, callback)
|
|
||||||
return callback(path, env)
|
|
||||||
|
|
||||||
def get(self, path, env):
|
|
||||||
"""
|
|
||||||
The default GET will look at the path and call an appropriate
|
|
||||||
action within this controller based on the the structure of the path.
|
|
||||||
|
|
||||||
Given the following path lengths (with the first part stripped of by
|
|
||||||
router, as it is the controller name):
|
|
||||||
= 0 -> index
|
|
||||||
= 1 -> first component (/servers/details -> details)
|
|
||||||
>= 2 -> second path component (/servers/ID/ips/* -> ips)
|
|
||||||
|
|
||||||
This should return
|
|
||||||
A String if 200 OK and no additional headers
|
|
||||||
(CODE, HEADERS, BODY) for custom response code and headers
|
|
||||||
"""
|
|
||||||
if len(path) == 0 and hasattr(self, "index"):
|
|
||||||
logging.debug("%s running index", self)
|
|
||||||
return self.index(env)
|
|
||||||
if len(path) >= 2:
|
|
||||||
action = path[1]
|
|
||||||
else:
|
|
||||||
action = path.pop(0)
|
|
||||||
|
|
||||||
logging.debug("%s running action %s", self, action)
|
|
||||||
if hasattr(self, action):
|
|
||||||
method = getattr(self, action)
|
|
||||||
return method(path, env)
|
|
||||||
else:
|
|
||||||
raise NotFound("Missing method %s" % path[0])
|
|
||||||
|
|
||||||
def check_authentication(self, env):
|
|
||||||
if not env['nova.context']['user']:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class RackspaceAuthenticationApi(object):
|
class CloudServerAPI(wsgi.Application):
|
||||||
|
"""Handle all server requests through WSGI applications."""
|
||||||
def process(self, path, env):
|
|
||||||
return self.index(path, env)
|
|
||||||
|
|
||||||
# TODO(todd): make a actual session with a unique token
|
|
||||||
# just pass the auth key back through for now
|
|
||||||
def index(self, _path, env):
|
|
||||||
response = '204 No Content'
|
|
||||||
headers = [
|
|
||||||
('X-Server-Management-Url', 'http://%s' % env['HTTP_HOST']),
|
|
||||||
('X-Storage-Url', 'http://%s' % env['HTTP_HOST']),
|
|
||||||
('X-CDN-Managment-Url', 'http://%s' % env['HTTP_HOST']),
|
|
||||||
('X-Auth-Token', env['HTTP_X_AUTH_KEY'])
|
|
||||||
]
|
|
||||||
body = ""
|
|
||||||
return (response, headers, body)
|
|
||||||
|
|
||||||
|
|
||||||
class RackspaceCloudServerApi(RackspaceApiEndpoint):
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.instdir = model.InstanceDirectory()
|
super(CloudServerAPI, self).__init__()
|
||||||
|
self.instdir = compute.InstanceDirectory()
|
||||||
self.network = network.PublicNetworkController()
|
self.network = network.PublicNetworkController()
|
||||||
|
|
||||||
def post(self, path, env):
|
@webob.dec.wsgify
|
||||||
if len(path) == 0:
|
def __call__(self, req): # pylint: disable-msg=W0221
|
||||||
return self.launch_server(env)
|
|
||||||
|
|
||||||
def delete(self, path_parts, env):
|
|
||||||
if self.delete_server(path_parts[0]):
|
|
||||||
return ("202 Accepted", [], "")
|
|
||||||
else:
|
|
||||||
return ("404 Not Found", [],
|
|
||||||
"Did not find image, or it was not in a running state")
|
|
||||||
|
|
||||||
|
|
||||||
def index(self, env):
|
|
||||||
return self.detail(env)
|
|
||||||
|
|
||||||
def detail(self, args, env):
|
|
||||||
value = {"servers": []}
|
value = {"servers": []}
|
||||||
for inst in self.instdir.all:
|
for inst in self.instdir.all:
|
||||||
value["servers"].append(self.instance_details(inst))
|
value["servers"].append(self.instance_details(inst))
|
||||||
return json.dumps(value)
|
return json.dumps(value)
|
||||||
|
|
||||||
##
|
def instance_details(self, inst): # pylint: disable-msg=R0201
|
||||||
##
|
"Build the data structure to represent details for an instance."
|
||||||
|
|
||||||
def launch_server(self, env):
|
|
||||||
data = json.loads(env['wsgi.input'].read(int(env['CONTENT_LENGTH'])))
|
|
||||||
inst = self.build_server_instance(data, env['nova.context'])
|
|
||||||
self.schedule_launch_of_instance(inst)
|
|
||||||
return json.dumps({"server": self.instance_details(inst)})
|
|
||||||
|
|
||||||
def instance_details(self, inst):
|
|
||||||
return {
|
return {
|
||||||
"id": inst.get("instance_id", None),
|
"id": inst.get("instance_id", None),
|
||||||
"imageId": inst.get("image_id", None),
|
"imageId": inst.get("image_id", None),
|
||||||
@@ -224,11 +113,9 @@ class RackspaceCloudServerApi(RackspaceApiEndpoint):
|
|||||||
"hostId": inst.get("node_name", None),
|
"hostId": inst.get("node_name", None),
|
||||||
"status": inst.get("state", "pending"),
|
"status": inst.get("state", "pending"),
|
||||||
"addresses": {
|
"addresses": {
|
||||||
"public": [self.network.get_public_ip_for_instance(
|
"public": [network.get_public_ip_for_instance(
|
||||||
inst.get("instance_id", None)
|
inst.get("instance_id", None))],
|
||||||
)],
|
"private": [inst.get("private_dns_name", None)]},
|
||||||
"private": [inst.get("private_dns_name", None)]
|
|
||||||
},
|
|
||||||
|
|
||||||
# implemented only by Rackspace, not AWS
|
# implemented only by Rackspace, not AWS
|
||||||
"name": inst.get("name", "Not-Specified"),
|
"name": inst.get("name", "Not-Specified"),
|
||||||
@@ -237,11 +124,22 @@ class RackspaceCloudServerApi(RackspaceApiEndpoint):
|
|||||||
"progress": "Not-Supported",
|
"progress": "Not-Supported",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"Server Label": "Not-Supported",
|
"Server Label": "Not-Supported",
|
||||||
"Image Version": "Not-Supported"
|
"Image Version": "Not-Supported"}}
|
||||||
}
|
|
||||||
}
|
@webob.dec.wsgify
|
||||||
|
def launch_server(self, req):
|
||||||
|
"""Launch a new instance."""
|
||||||
|
data = json.loads(req.body)
|
||||||
|
inst = self.build_server_instance(data, req.environ['nova.context'])
|
||||||
|
rpc.cast(
|
||||||
|
FLAGS.compute_topic, {
|
||||||
|
"method": "run_instance",
|
||||||
|
"args": {"instance_id": inst.instance_id}})
|
||||||
|
|
||||||
|
return json.dumps({"server": self.instance_details(inst)})
|
||||||
|
|
||||||
def build_server_instance(self, env, context):
|
def build_server_instance(self, env, context):
|
||||||
|
"""Build instance data structure and save it to the data store."""
|
||||||
reservation = utils.generate_uid('r')
|
reservation = utils.generate_uid('r')
|
||||||
ltime = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
|
ltime = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
|
||||||
inst = self.instdir.new()
|
inst = self.instdir.new()
|
||||||
@@ -253,45 +151,33 @@ class RackspaceCloudServerApi(RackspaceApiEndpoint):
|
|||||||
inst['reservation_id'] = reservation
|
inst['reservation_id'] = reservation
|
||||||
inst['launch_time'] = ltime
|
inst['launch_time'] = ltime
|
||||||
inst['mac_address'] = utils.generate_mac()
|
inst['mac_address'] = utils.generate_mac()
|
||||||
address = network.allocate_ip(
|
address = self.network.allocate_ip(
|
||||||
inst['user_id'],
|
inst['user_id'],
|
||||||
inst['project_id'],
|
inst['project_id'],
|
||||||
mac=inst['mac_address']
|
mac=inst['mac_address'])
|
||||||
)
|
|
||||||
inst['private_dns_name'] = str(address)
|
inst['private_dns_name'] = str(address)
|
||||||
inst['bridge_name'] = network.BridgedNetwork.get_network_for_project(
|
inst['bridge_name'] = network.BridgedNetwork.get_network_for_project(
|
||||||
inst['user_id'],
|
inst['user_id'],
|
||||||
inst['project_id'],
|
inst['project_id'],
|
||||||
'default' # security group
|
'default')['bridge_name']
|
||||||
)['bridge_name']
|
|
||||||
# key_data, key_name, ami_launch_index
|
# key_data, key_name, ami_launch_index
|
||||||
# TODO(todd): key data or root password
|
# TODO(todd): key data or root password
|
||||||
inst.save()
|
inst.save()
|
||||||
return inst
|
return inst
|
||||||
|
|
||||||
def schedule_launch_of_instance(self, inst):
|
@webob.dec.wsgify
|
||||||
rpc.cast(
|
@wsgi.route_args
|
||||||
FLAGS.compute_topic,
|
def delete_server(self, req, route_args): # pylint: disable-msg=R0201
|
||||||
{
|
"""Delete an instance."""
|
||||||
"method": "run_instance",
|
owner_hostname = None
|
||||||
"args": {"instance_id": inst.instance_id}
|
instance = compute.Instance.lookup(route_args['server_id'])
|
||||||
}
|
if instance:
|
||||||
)
|
owner_hostname = instance["node_name"]
|
||||||
|
|
||||||
def delete_server(self, instance_id):
|
|
||||||
owner_hostname = self.host_for_instance(instance_id)
|
|
||||||
# it isn't launched?
|
|
||||||
if not owner_hostname:
|
if not owner_hostname:
|
||||||
return None
|
return webob.exc.HTTPNotFound("Did not find image, or it was "
|
||||||
|
"not in a running state.")
|
||||||
rpc_transport = "%s:%s" % (FLAGS.compute_topic, owner_hostname)
|
rpc_transport = "%s:%s" % (FLAGS.compute_topic, owner_hostname)
|
||||||
rpc.cast(rpc_transport,
|
rpc.cast(rpc_transport,
|
||||||
{"method": "reboot_instance",
|
{"method": "reboot_instance",
|
||||||
"args": {"instance_id": instance_id}})
|
"args": {"instance_id": route_args['server_id']}})
|
||||||
return True
|
req.status = "202 Accepted"
|
||||||
|
|
||||||
def host_for_instance(self, instance_id):
|
|
||||||
instance = model.Instance.lookup(instance_id)
|
|
||||||
if not instance:
|
|
||||||
return None
|
|
||||||
return instance["node_name"]
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,40 +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.
|
|
||||||
|
|
||||||
'''
|
|
||||||
Utility methods for working with WSGI servers
|
|
||||||
'''
|
|
||||||
|
|
||||||
class Util(object):
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def route(reqstr, controllers):
|
|
||||||
if len(reqstr) == 0:
|
|
||||||
return Util.select_root_controller(controllers), []
|
|
||||||
parts = [x for x in reqstr.split("/") if len(x) > 0]
|
|
||||||
if len(parts) == 0:
|
|
||||||
return Util.select_root_controller(controllers), []
|
|
||||||
return controllers[parts[0]], parts[1:]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def select_root_controller(controllers):
|
|
||||||
if '' in controllers:
|
|
||||||
return controllers['']
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user