f7ae63c6f5
Config imports are needed for correct init quota_db is required to make sure quota database model at the proper timing Change-Id: I885ed1f673cfc4c6331818ad68e2a505f7d69188
189 lines
6.6 KiB
Python
Executable File
189 lines
6.6 KiB
Python
Executable File
#!/usr/bin/env python
|
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
|
|
# Copyright 2012, Big Switch Networks, Inc.
|
|
# 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.
|
|
#
|
|
# @author: Mandeep Dhami, Big Switch Networks, Inc.
|
|
|
|
"""
|
|
Test server mocking a REST based network ctrl. Used for QuantumRestProxy tests
|
|
"""
|
|
|
|
import json
|
|
import re
|
|
|
|
from wsgiref.simple_server import make_server
|
|
|
|
|
|
class TestNetworkCtrl(object):
|
|
def __init__(self, host='', port=8000,
|
|
default_status='404 Not Found',
|
|
default_response='404 Not Found',
|
|
debug=False):
|
|
self.host = host
|
|
self.port = port
|
|
self.default_status = default_status
|
|
self.default_response = default_response
|
|
self.debug = debug
|
|
self.debug_env = False
|
|
self.debug_resp = False
|
|
self.matches = []
|
|
|
|
def match(self, prior, method_regexp, uri_regexp, handler, data=None,
|
|
multi=True):
|
|
"""Adds to the list of exptected inputs. The incomming request is
|
|
matched in the order of priority. For same priority, match the
|
|
oldest match request first.
|
|
|
|
:param prior: intgere priority of this match (e.g. 100)
|
|
:param method_regexp: regexp to match method (e.g. 'PUT|POST')
|
|
:param uri_regexp: regexp to match uri (e.g. '/quantum/v?.?/')
|
|
:param handler: function with signature:
|
|
lambda(method, uri, body, **kwargs) : status, body
|
|
where
|
|
- method: HTTP method for this request
|
|
- uri: URI for this HTTP request
|
|
- body: body of this HTTP request
|
|
- kwargs are:
|
|
- data: data object that was in the match call
|
|
- node: TestNetworkCtrl object itself
|
|
- id: offset of the matching tuple
|
|
and return values is:
|
|
(status, body) where:
|
|
- status: HTTP resp status (e.g. '200 OK').
|
|
If None, use default_status
|
|
- body: HTTP resp body. If None, use ''
|
|
"""
|
|
assert int(prior) == prior, 'Priority should an integer be >= 0'
|
|
assert prior >= 0, 'Priority should an integer be >= 0'
|
|
|
|
lo, hi = 0, len(self.matches)
|
|
while lo < hi:
|
|
mid = (lo + hi) // 2
|
|
if prior < self.matches[mid]:
|
|
hi = mid
|
|
else:
|
|
lo = mid + 1
|
|
self.matches.insert(lo, (prior, method_regexp, uri_regexp, handler,
|
|
data, multi))
|
|
|
|
def remove_id(self, id_):
|
|
assert id_ >= 0, 'remove_id: id < 0'
|
|
assert id_ <= len(self.matches), 'remove_id: id > len()'
|
|
self.matches.pop(id_)
|
|
|
|
def remove_match(self, prior, method_regexp, uri_regexp):
|
|
for i in self.matches:
|
|
if (i[0], i[1], i[2]) == (method_regexp, uri_regexp, idstr):
|
|
self.remove_id(i)
|
|
break
|
|
|
|
def request_handler(self, method, uri, body):
|
|
retstatus = self.default_status
|
|
retbody = self.default_response
|
|
for i in xrange(len(self.matches)):
|
|
(prior, method_regexp, uri_regexp, handler, data, multi) = \
|
|
self.matches[i]
|
|
if re.match(method_regexp, method) and re.match(uri_regexp, uri):
|
|
kwargs = {
|
|
'data': data,
|
|
'node': self,
|
|
'id': i,
|
|
}
|
|
retstatus, retbody = handler(method, uri, body, **kwargs)
|
|
if multi is False:
|
|
self.remove_id(i)
|
|
break
|
|
if retbody is None:
|
|
retbody = ''
|
|
return (retstatus, retbody)
|
|
|
|
def server(self):
|
|
def app(environ, start_response):
|
|
uri = environ['PATH_INFO']
|
|
method = environ['REQUEST_METHOD']
|
|
headers = [('Content-type', 'text/json')]
|
|
content_len_str = environ['CONTENT_LENGTH']
|
|
|
|
content_len = 0
|
|
request_data = None
|
|
if content_len_str:
|
|
content_len = int(content_len_str)
|
|
request_data = environ.get('wsgi.input').read(content_len)
|
|
if request_data:
|
|
try:
|
|
request_data = json.loads(request_data)
|
|
except:
|
|
# OK for it not to be json! Ignore it
|
|
pass
|
|
|
|
if self.debug:
|
|
print '\n'
|
|
if self.debug_env:
|
|
print '%s:' % 'environ:'
|
|
for (key, value) in sorted(environ.iteritems()):
|
|
print ' %16s : %s' % (key, value)
|
|
|
|
print '%s %s' % (method, uri)
|
|
if request_data:
|
|
print '%s' % (
|
|
json.dumps(request_data, sort_keys=True, indent=4))
|
|
|
|
status, body = self.request_handler(method, uri, None)
|
|
body_data = None
|
|
if body:
|
|
try:
|
|
body_data = json.loads(body)
|
|
except:
|
|
# OK for it not to be json! Ignore it
|
|
pass
|
|
|
|
start_response(status, headers)
|
|
if self.debug:
|
|
if self.debug_env:
|
|
print '%s: %s' % ('Response',
|
|
json.dumps(body_data, sort_keys=True, indent=4))
|
|
return body
|
|
return make_server(self.host, self.port, app)
|
|
|
|
def run(self):
|
|
print "Serving on port %d ..." % self.port
|
|
try:
|
|
self.server().serve_forever()
|
|
except KeyboardInterrupt:
|
|
pass
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import sys
|
|
|
|
port = 8899
|
|
if len(sys.argv) > 1:
|
|
port = int(sys.argv[1])
|
|
|
|
debug = False
|
|
if len(sys.argv) > 2:
|
|
if sys.argv[2].lower() in ['debug', 'true']:
|
|
debug = True
|
|
|
|
ctrl = TestNetworkCtrl(port=port,
|
|
default_status='200 OK',
|
|
default_response='{"status":"200 OK"}',
|
|
debug=debug)
|
|
ctrl.match(100, 'GET', '/test',
|
|
lambda m, u, b, **k: ('200 OK', '["200 OK"]'))
|
|
ctrl.run()
|