
jsonutils provides multiple benefits in comparison to pure stdlib json (like using simplejson on Python 2.6). Similar patch was already merged before [1], but since it lacked hacking rule to enforce jsonutils usage, new occurrences of stdlib json module usage were introduced. This patch switches all the code to using jsonutils and adds a hacking rule to enforce the rule. The hacking rule requires that jsonutils module does not mimic as 'json' thru using import renames, so the code was updated not to rename the module when doing import. The hacking rule was shamelessly copied from the corresponding nova review [2]. [1]: https://review.openstack.org/#/c/99760/ [2]: https://review.openstack.org/111296/ Change-Id: Ie7a5bb76445e15cde9fbf9ff3d2101a014637b37
189 lines
6.5 KiB
Python
Executable File
189 lines
6.5 KiB
Python
Executable File
#!/usr/bin/env python
|
|
# 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 NeutronRestProxy tests
|
|
"""
|
|
from __future__ import print_function
|
|
|
|
import re
|
|
|
|
from six import moves
|
|
from wsgiref import simple_server
|
|
|
|
from neutron.openstack.common import jsonutils
|
|
|
|
|
|
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):
|
|
"""Add to the list of exptected inputs.
|
|
|
|
The incoming 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 request_handler(self, method, uri, body):
|
|
retstatus = self.default_status
|
|
retbody = self.default_response
|
|
for i in moves.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 = jsonutils.loads(request_data)
|
|
except Exception:
|
|
# OK for it not to be json! Ignore it
|
|
pass
|
|
|
|
if self.debug:
|
|
print('\n')
|
|
if self.debug_env:
|
|
print('environ:')
|
|
for (key, value) in sorted(environ.iteritems()):
|
|
print(' %16s : %s' % (key, value))
|
|
|
|
print('%s %s' % (method, uri))
|
|
if request_data:
|
|
print('%s' %
|
|
jsonutils.dumps(
|
|
request_data, sort_keys=True, indent=4))
|
|
|
|
status, body = self.request_handler(method, uri, None)
|
|
body_data = None
|
|
if body:
|
|
try:
|
|
body_data = jsonutils.loads(body)
|
|
except Exception:
|
|
# 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',
|
|
jsonutils.dumps(
|
|
body_data, sort_keys=True, indent=4)))
|
|
return body
|
|
return simple_server.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()
|