300486546b
The current version of the nvp_api client does not load balance requests across multiple controllers. Instead, it just sends all the requests to one controller and if there is a controller failure it will failover to use another controller. This blueprint implements the ablility to utilize all controllers at once. blueprint nvp-api-client-loadbalance-request Change-Id: I331be2a23ae360a95786152d5f116359f690d9f3
340 lines
13 KiB
Python
340 lines
13 KiB
Python
# Copyright (C) 2009-2012 Nicira 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.
|
|
|
|
import httplib
|
|
import logging
|
|
import new
|
|
import random
|
|
import unittest
|
|
|
|
import eventlet
|
|
from eventlet.green import urllib2
|
|
from mock import Mock
|
|
from mock import patch
|
|
|
|
from quantum.plugins.nicira.nicira_nvp_plugin.api_client import (
|
|
client_eventlet as nace,
|
|
request_eventlet as nare,
|
|
)
|
|
|
|
|
|
logging.basicConfig(level=logging.DEBUG)
|
|
LOG = logging.getLogger("test_nvp_api_request_eventlet")
|
|
|
|
|
|
REQUEST_TIMEOUT = 1
|
|
|
|
|
|
def fetch(url):
|
|
return urllib2.urlopen(url).read()
|
|
|
|
|
|
class NvpApiRequestEventletTest(unittest.TestCase):
|
|
|
|
def setUp(self):
|
|
|
|
self.client = nace.NvpApiClientEventlet(
|
|
[("127.0.0.1", 4401, True)], "admin", "admin")
|
|
self.url = "/ws.v1/_debug"
|
|
self.req = nare.NvpApiRequestEventlet(self.client, self.url)
|
|
|
|
def tearDown(self):
|
|
self.client = None
|
|
self.req = None
|
|
|
|
def test_construct_eventlet_api_request(self):
|
|
e = nare.NvpApiRequestEventlet(self.client, self.url)
|
|
self.assertTrue(e is not None)
|
|
|
|
def test_apirequest_spawn(self):
|
|
def x(id):
|
|
eventlet.greenthread.sleep(random.random())
|
|
LOG.info('spawned: %d' % id)
|
|
|
|
for i in range(10):
|
|
nare.NvpApiRequestEventlet._spawn(x, i)
|
|
|
|
def test_apirequest_start(self):
|
|
for i in range(10):
|
|
a = nare.NvpApiRequestEventlet(
|
|
self.client, self.url, request_timeout=0.1)
|
|
a._handle_request = Mock()
|
|
a.start()
|
|
eventlet.greenthread.sleep(0.1)
|
|
logging.info('_handle_request called: %s' %
|
|
a._handle_request.called)
|
|
nare.NvpApiRequestEventlet.joinall()
|
|
|
|
def test_join_with_handle_request(self):
|
|
self.req._handle_request = Mock()
|
|
self.req.start()
|
|
self.req.join()
|
|
self.assertTrue(self.req._handle_request.called)
|
|
|
|
def test_join_without_handle_request(self):
|
|
self.req._handle_request = Mock()
|
|
self.req.join()
|
|
self.assertFalse(self.req._handle_request.called)
|
|
|
|
def test_copy(self):
|
|
req = self.req.copy()
|
|
for att in [
|
|
'_api_client', '_url', '_method', '_body', '_headers',
|
|
'_http_timeout', '_request_timeout', '_retries',
|
|
'_redirects', '_auto_login']:
|
|
self.assertTrue(getattr(req, att) is getattr(self.req, att))
|
|
|
|
def test_request_error(self):
|
|
self.assertTrue(self.req.request_error is None)
|
|
|
|
def test_run_and_handle_request(self):
|
|
self.req._request_timeout = None
|
|
self.req._handle_request = Mock()
|
|
self.req.start()
|
|
self.req.join()
|
|
self.assertTrue(self.req._handle_request.called)
|
|
|
|
def test_run_and_timeout(self):
|
|
def my_handle_request(self):
|
|
LOG.info('my_handle_request() self: %s' % self)
|
|
LOG.info('my_handle_request() dir(self): %s' % dir(self))
|
|
eventlet.greenthread.sleep(REQUEST_TIMEOUT * 2)
|
|
|
|
self.req._request_timeout = REQUEST_TIMEOUT
|
|
self.req._handle_request = new.instancemethod(
|
|
my_handle_request, self.req, nare.NvpApiRequestEventlet)
|
|
self.req.start()
|
|
self.assertTrue(self.req.join() is None)
|
|
|
|
def prep_issue_request(self):
|
|
mysock = Mock()
|
|
mysock.gettimeout.return_value = 4242
|
|
|
|
myresponse = Mock()
|
|
myresponse.read.return_value = 'body'
|
|
myresponse.getheaders.return_value = 'headers'
|
|
myresponse.status = httplib.MOVED_PERMANENTLY
|
|
|
|
myconn = Mock()
|
|
myconn.request.return_value = None
|
|
myconn.sock = mysock
|
|
myconn.getresponse.return_value = myresponse
|
|
myconn.__str__ = Mock()
|
|
myconn.__str__.return_value = 'myconn string'
|
|
|
|
req = self.req
|
|
req._redirect_params = Mock()
|
|
req._redirect_params.return_value = (myconn, 'url')
|
|
req._request_str = Mock()
|
|
req._request_str.return_value = 'http://cool/cool'
|
|
|
|
client = self.client
|
|
client.need_login = False
|
|
client._auto_login = False
|
|
client._auth_cookie = False
|
|
client.acquire_connection = Mock()
|
|
client.acquire_connection.return_value = myconn
|
|
client.release_connection = Mock()
|
|
|
|
return (mysock, myresponse, myconn)
|
|
|
|
def test_issue_request_trigger_exception(self):
|
|
(mysock, myresponse, myconn) = self.prep_issue_request()
|
|
self.client.acquire_connection.return_value = None
|
|
|
|
self.req._issue_request()
|
|
LOG.info('request_error: %s' % self.req._request_error)
|
|
self.assertTrue(isinstance(self.req._request_error, Exception))
|
|
self.assertTrue(self.client.acquire_connection.called)
|
|
|
|
def test_issue_request_handle_none_sock(self):
|
|
(mysock, myresponse, myconn) = self.prep_issue_request()
|
|
myconn.sock = None
|
|
self.req.start()
|
|
self.assertTrue(self.req.join() is None)
|
|
self.assertTrue(self.client.acquire_connection.called)
|
|
|
|
def test_issue_request_exceed_maximum_retries(self):
|
|
(mysock, myresponse, myconn) = self.prep_issue_request()
|
|
self.req.start()
|
|
self.assertTrue(self.req.join() is None)
|
|
self.assertTrue(self.client.acquire_connection.called)
|
|
|
|
def test_issue_request_trigger_non_redirect(self):
|
|
(mysock, myresponse, myconn) = self.prep_issue_request()
|
|
myresponse.status = httplib.OK
|
|
self.req.start()
|
|
self.assertTrue(self.req.join() is None)
|
|
self.assertTrue(self.client.acquire_connection.called)
|
|
|
|
def test_issue_request_trigger_internal_server_error(self):
|
|
(mysock, myresponse, myconn) = self.prep_issue_request()
|
|
self.req._redirect_params.return_value = (myconn, None)
|
|
self.req.start()
|
|
self.assertTrue(self.req.join() is None)
|
|
self.assertTrue(self.client.acquire_connection.called)
|
|
|
|
def test_redirect_params_break_on_location(self):
|
|
myconn = Mock()
|
|
(conn, retval) = self.req._redirect_params(
|
|
myconn, [('location', None)])
|
|
self.assertTrue(retval is None)
|
|
|
|
def test_redirect_params_parse_a_url(self):
|
|
myconn = Mock()
|
|
(conn, retval) = self.req._redirect_params(
|
|
myconn, [('location', '/path/a/b/c')])
|
|
self.assertTrue(retval is not None)
|
|
|
|
def test_redirect_params_invalid_redirect_location(self):
|
|
myconn = Mock()
|
|
(conn, retval) = self.req._redirect_params(
|
|
myconn, [('location', '+path/a/b/c')])
|
|
self.assertTrue(retval is None)
|
|
|
|
def test_redirect_params_invalid_scheme(self):
|
|
myconn = Mock()
|
|
(conn, retval) = self.req._redirect_params(
|
|
myconn, [('location', 'invalidscheme://hostname:1/path')])
|
|
self.assertTrue(retval is None)
|
|
|
|
def test_redirect_params_setup_https_with_cooki(self):
|
|
with patch('quantum.plugins.nicira.nicira_nvp_plugin.api_client.'
|
|
'client_eventlet.NvpApiClientEventlet') as mock:
|
|
api_client = mock.return_value
|
|
self.req._api_client = api_client
|
|
myconn = Mock()
|
|
(conn, retval) = self.req._redirect_params(
|
|
myconn, [('location', 'https://host:1/path')])
|
|
|
|
self.assertTrue(retval is not None)
|
|
self.assertTrue(api_client.acquire_redirect_connection.called)
|
|
|
|
def test_redirect_params_setup_htttps_and_query(self):
|
|
with patch('quantum.plugins.nicira.nicira_nvp_plugin.api_client.'
|
|
'client_eventlet.NvpApiClientEventlet') as mock:
|
|
api_client = mock.return_value
|
|
self.req._api_client = api_client
|
|
myconn = Mock()
|
|
(conn, retval) = self.req._redirect_params(myconn, [
|
|
('location', 'https://host:1/path?q=1')])
|
|
|
|
self.assertTrue(retval is not None)
|
|
self.assertTrue(api_client.acquire_redirect_connection.called)
|
|
|
|
def test_redirect_params_setup_https_connection_no_cookie(self):
|
|
with patch('quantum.plugins.nicira.nicira_nvp_plugin.api_client.'
|
|
'client_eventlet.NvpApiClientEventlet') as mock:
|
|
api_client = mock.return_value
|
|
self.req._api_client = api_client
|
|
myconn = Mock()
|
|
(conn, retval) = self.req._redirect_params(myconn, [
|
|
('location', 'https://host:1/path')])
|
|
|
|
self.assertTrue(retval is not None)
|
|
self.assertTrue(api_client.acquire_redirect_connection.called)
|
|
|
|
def test_redirect_params_setup_https_and_query_no_cookie(self):
|
|
with patch('quantum.plugins.nicira.nicira_nvp_plugin.api_client.'
|
|
'client_eventlet.NvpApiClientEventlet') as mock:
|
|
api_client = mock.return_value
|
|
self.req._api_client = api_client
|
|
myconn = Mock()
|
|
(conn, retval) = self.req._redirect_params(
|
|
myconn, [('location', 'https://host:1/path?q=1')])
|
|
self.assertTrue(retval is not None)
|
|
self.assertTrue(api_client.acquire_redirect_connection.called)
|
|
|
|
def test_redirect_params_path_only_with_query(self):
|
|
with patch('quantum.plugins.nicira.nicira_nvp_plugin.api_client.'
|
|
'client_eventlet.NvpApiClientEventlet') as mock:
|
|
api_client = mock.return_value
|
|
api_client.wait_for_login.return_value = None
|
|
api_client.auth_cookie = None
|
|
api_client.acquire_connection.return_value = True
|
|
myconn = Mock()
|
|
(conn, retval) = self.req._redirect_params(myconn, [
|
|
('location', '/path?q=1')])
|
|
self.assertTrue(retval is not None)
|
|
|
|
def test_handle_request_auto_login(self):
|
|
self.req._auto_login = True
|
|
self.req._api_client = Mock()
|
|
self.req._api_client.need_login = True
|
|
self.req._request_str = Mock()
|
|
self.req._request_str.return_value = 'http://cool/cool'
|
|
self.req.spawn = Mock()
|
|
self.req._handle_request()
|
|
|
|
def test_handle_request_auto_login_unauth(self):
|
|
self.req._auto_login = True
|
|
self.req._api_client = Mock()
|
|
self.req._api_client.need_login = True
|
|
self.req._request_str = Mock()
|
|
self.req._request_str.return_value = 'http://cool/cool'
|
|
|
|
import socket
|
|
resp = httplib.HTTPResponse(socket.socket())
|
|
resp.status = httplib.UNAUTHORIZED
|
|
mywaiter = Mock()
|
|
mywaiter.wait = Mock()
|
|
mywaiter.wait.return_value = resp
|
|
self.req.spawn = Mock(return_value=mywaiter)
|
|
self.req._handle_request()
|
|
|
|
# NvpLoginRequestEventlet tests.
|
|
def test_construct_eventlet_login_request(self):
|
|
r = nare.NvpLoginRequestEventlet(self.client, 'user', 'password')
|
|
self.assertTrue(r is not None)
|
|
|
|
def test_session_cookie_session_cookie_retrieval(self):
|
|
r = nare.NvpLoginRequestEventlet(self.client, 'user', 'password')
|
|
r.successful = Mock()
|
|
r.successful.return_value = True
|
|
r.value = Mock()
|
|
r.value.get_header = Mock()
|
|
r.value.get_header.return_value = 'cool'
|
|
self.assertTrue(r.session_cookie() is not None)
|
|
|
|
def test_session_cookie_not_retrieved(self):
|
|
r = nare.NvpLoginRequestEventlet(self.client, 'user', 'password')
|
|
r.successful = Mock()
|
|
r.successful.return_value = False
|
|
r.value = Mock()
|
|
r.value.get_header = Mock()
|
|
r.value.get_header.return_value = 'cool'
|
|
self.assertTrue(r.session_cookie() is None)
|
|
|
|
# NvpGetApiProvidersRequestEventlet tests.
|
|
def test_construct_eventlet_get_api_providers_request(self):
|
|
r = nare.NvpGetApiProvidersRequestEventlet(self.client)
|
|
self.assertTrue(r is not None)
|
|
|
|
def test_api_providers_none_api_providers(self):
|
|
r = nare.NvpGetApiProvidersRequestEventlet(self.client)
|
|
r.successful = Mock(return_value=False)
|
|
self.assertTrue(r.api_providers() is None)
|
|
|
|
def test_api_providers_non_none_api_providers(self):
|
|
r = nare.NvpGetApiProvidersRequestEventlet(self.client)
|
|
r.value = Mock()
|
|
r.value.body = """{
|
|
"results": [
|
|
{ "roles": [
|
|
{ "role": "api_provider",
|
|
"listen_addr": "pssl:1.1.1.1:1" }]}]}"""
|
|
r.successful = Mock(return_value=True)
|
|
LOG.info('%s' % r.api_providers())
|
|
self.assertTrue(r.api_providers() is not None)
|