在前面的三篇博文中,介紹了restful和SWGI的實現。結合restful和WSGI配置就能夠簡單的實現nova服務模型的最簡單的操作。
如下的內容是借鑒網上博文,因為寫的很巧妙,將nova管理虛擬機的過程刻畫十分清楚,所以想自己實現一遍,加深印象。
在上一篇博文中寫的URL的對應處理函數,結果是十分簡單的return 一個網頁。

在openstack當中肯定不會這么簡單的處理,畢竟前面的工作只是將路修好,真正的功能還未實現呢!
所以對於nova操作的模擬,要實現的是對虛擬機的增,刪,改,查等功能。而這些功能不是在一個return中就能實現的。
nova功能模擬操作的RESTFUl:
| URL | 方法 | 描述 |
| /instances | GET | 列出集合所有的虛擬機記錄 |
| /instances | POST | 添加一條虛擬機記錄 |
| /instances/instance_id | GET | 獲取一條虛擬機記錄的信息 |
| /instances/instance_id | PUT | 更新一條虛擬機的記錄 |
| /instances/instancd_id | DELETE | 刪除一條虛擬機九路 |
在博文中定義的文件如下:

配置WSGI服務器:
1 配置 configure.ini
[pipeline:main]
pipeline = auth instance
[app:instance]
paste.app_factory = routers:app_factory
[filter:auth]
paste.filter_factory = auth:filter_factory
首先解析配置文件,pipeline是管道,管道流是由“過濾器認證模塊”auth 和 app 應用程序 instance組成。
app 應用程序的位置是routers文件的app_factory方法
filter 過濾器實現的認證模塊,位置是auth文件的filter_factory方法。
下面的文件分別實現了router.py和auth.py
2 服務器端
server.py
from wsgiref.simple_server import make_server
from paste import httpserver
from paste.deploy import loadapp
import os
if __name__ == '__main__':
configfile = 'configure.ini'
appname = 'main'
wsgi_app = loadapp('config:%s' % os.path.abspath(configfile), appname)
server = make_server('localhost', 8000, wsgi_app)
server.serve_forever()
3 filter auth實現程序
auth.py
from webob.dec import *
from webob import Request,Response
from webob import exc
class Auth(object):
def __init__(self, app):
self.app = app
@wsgify
def __call__(self, req):
print 'Auth class'
resp = self.process_request(req)
if resp:
return resp
return self.app(req)
@wsgify
def process_request(self, req):
if req.headers.get('X-Auth-Token') != 'open-sesame':
return exc.HTTPForbidden()
@wsgify.middleware
def filter_factory(app, global_config, **local_config):
return Auth(app)
過濾器的實現,其實現方法是filter_factory,參數有一個app,然后調用Auth方法。Auth方法是類的__call__方法,其實現是:
def __call__(self, req):
print 'Auth class' 打印了一個字符串
認證具體實現的地方,調用了一個函數。 具體實現就是取出請求報文頭中的'X-Auth-Token',判斷是不是'open-sesame'
resp = self.process_request(req)
if resp:
return resp
如果判斷通過了,則調用傳入的app,具體到本例中就是instance
return self.app(req)
4 app instance實現程序
routers.py
from webob.dec import *
from webob import Request,Response
import webob.exc
from routes import Mapper,middleware
import controllers
class Router(object):
def __init__(self):
self.mapper = Mapper()
self.add_routes()
self.router = middleware.RoutesMiddleware(self._dispatch, self.mapper)
@wsgify
def __call__(self, req):
return self.router
def add_routes(self):
controller = controllers.Controller()
self.mapper.connect('/instances', controller = controller,action = 'create', conditions = {'method' : ['POST']})
self.mapper.connect('/instances', controller = controller,action = 'index', conditions = {'method' : ['GET']})
self.mapper.connect('/instances/{instance_id}', controller = controller,action = 'show', conditions = {'method' : ['GET']})
self.mapper.connect('/instances/{instance_id}', controller = controller,action = 'update', conditions = {'method' : ['PUT']})
self.mapper.connect('/instances/{instance_id}', controller = controller,action = 'delete', conditions = {'method':['DELETE']})
@staticmethod
@wsgify
def _dispatch(req):
match = req.environ['wsgiorg.routing_args'][1]
if not match:
return webob.exc.HTTPNotFound()
app = match['controller']
return app
def app_factory(global_config, **local_config):
return Router()
router.py是instance的實現文件。app_factory為處理方法,其調用了Router(),Router()是類Router的__call__()方法。
__call__方法直接返回了對象router,router在初始化的時候實現了函數
middleware.RoutesMiddleware(self._dispatch, self.mapper)
該函數有兩個參數,一個是mapper,一個是_dispatch。傳入參數
mapper的作用是在該函數中負責解析URL,根據URL查找到對應的處理函數的類。
_dispath函數就是負責接收mapper解析出來的類,然后調用該類。
可以看到_dispatch從接收到的數據中匹配出app app = match['controller'] ,然后返回了該app,而這個app就是實現nova具體功能的類。對應的類在下面的文件controller中實現。
mapper成員出了作為函數的傳入參數之后,還做了一件事情,就是注冊了URL的請求和對應方法。簡單來說就是注冊URL和對應處理函數。
例如:create方法:
controller = controllers.Controller()
self.mapper.connect('/instances', controller = controller,action = 'create', conditions = {'method' : ['POST']})
第一行是說變量controller對應的是文件controller.py 中的Controller類。
第二行進行綁定,URL路徑是/instances,請求類型是POST,對應的處理方法是 類 Controller中的'create'函數。
5 實際處理函數
controllers.py
import uuid
from webob import Request,Response
import simplejson
from webob.dec import wsgify
class Controller(object):
def __init__(self):
self.instances = {}
for i in range(3):
inst_id = str(uuid.uuid4())
self.instances[inst_id] = {'id' : inst_id, 'name' : 'inst-' + str(i)}
#print self.instances
@wsgify
def create(self, req):
print req.params
name = req.params['name']
if name:
inst_id = str(uuid.uuid4())
inst = {'id' : inst_id, 'name' : name}
self.instances[inst_id] = inst
return {'instance' : inst}
@wsgify
def show(self, req, instance_id):
inst = self.instances.get(instance_id)
return {'instance ' : inst}
@wsgify
def index(self, req):
return {'instances': self.instances.values()}
@wsgify
def delete(self, req, instance_id):
if self.instances.get(instance_id):
self.instances.pop(instance_id)
@wsgify
def update(self, req, instance_id):
inst = self.instances.get(instance_id)
name = req.params['name']
if inst and name:
inst['name'] = name
return {'instance': inst}
@wsgify
def __call__(self, req):
arg_dict = req.environ['wsgiorg.routing_args'][1]
action = arg_dict.pop('action')
del arg_dict['controller']
method = getattr(self, action)
result = method(req, **arg_dict)
if result is None:
return Response(body='',status='204 Not Found',headerlist=[('Content-Type','application/json')])
else:
if not isinstance(result, str):
result = simplejson.dumps(result)
return result
終於到了功能具體實現的地方了。當router.py中的_dispatch方法返回了一個app之后,就是調用了該app。這個app就是類名字,同時也是類中的__call__方法。
__call__方法中實現的功能如下:
@wsgify
def __call__(self, req):
arg_dict = req.environ['wsgiorg.routing_args'][1] #獲取URL解析結果
action = arg_dict.pop('action') #獲取處理的方法
del arg_dict['controller'] #刪除 controller項,剩下的都是參數列表
method = getattr(self, action) #搜索controller類中定義的方法
result = method(req, **arg_dict) #調用方法,處理HTTP請求
if result is None: #無返回值
return Response(body='',status='204 Not Found',headerlist=[('Content-Type','application/json')])
else:#有返回值
if not isinstance(result, str):
result = simplejson.dumps(result) #將返回值轉化為字符串
return result

運行WSGI服務器:

使用請求:
GET http://127.0.0.1:8080/instances


以查看虛擬機情況的流程為例,分析過程:
- 客戶端使用命令:curl - H 'X-Auth-Token:open-sesame ' 127.0.0.1:8080/instances,請求虛擬機信息
- server.py中的WSGI服務器一直監聽在127.0.0.1的8080端口,收到客戶端的請求。請求的具體內容就是上圖的"req"打印內容。
- WSGI服務通過配置的configure.ini文件,分析出/instancesURL處理的類是COntroller類。
- router.py中的app_factory被調用,分析URL和添加對應關系之后,調用Controller類。
- Controller類的__call__方法根據 GET方法 + /instance路徑分析出對應的處理函數是index。
- 調用index函數,返回結果,判斷返回值,將信息返回給客戶端。
添加虛擬機:
POST http://127.0.0.1:8080/instances
data : name=demo_one

