~ubuntu-branches/ubuntu/vivid/neutron/vivid-updates

« back to all changes in this revision

Viewing changes to neutron/plugins/vmware/api_client/eventlet_request.py

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2015-03-30 11:17:19 UTC
  • mfrom: (1.1.21)
  • Revision ID: package-import@ubuntu.com-20150330111719-h0gx7233p4jkkgfh
Tags: 1:2015.1~b3-0ubuntu1
* New upstream milestone release:
  - d/control: Align version requirements with upstream.
  - d/control: Add new dependency on oslo-log.
  - d/p/*: Rebase.
  - d/control,d/neutron-plugin-hyperv*: Dropped, decomposed into
    separate project upstream.
  - d/control,d/neutron-plugin-openflow*: Dropped, decomposed into
    separate project upstream.
  - d/neutron-common.install: Add neutron-rootwrap-daemon and 
    neutron-keepalived-state-change binaries.
  - d/rules: Ignore neutron-hyperv-agent when installing; only for Windows.
  - d/neutron-plugin-cisco.install: Drop neutron-cisco-cfg-agent as
    decomposed into separate project upstream.
  - d/neutron-plugin-vmware.install: Drop neutron-check-nsx-config and
    neutron-nsx-manage as decomposed into separate project upstream.
  - d/control: Add dependency on python-neutron-fwaas to neutron-l3-agent.
* d/pydist-overrides: Add overrides for oslo packages.
* d/control: Fixup type in package description (LP: #1263539).
* d/p/fixup-driver-test-execution.patch: Cherry pick fix from upstream VCS
  to support unit test exection in out-of-tree vendor drivers.
* d/neutron-common.postinst: Allow general access to /etc/neutron but limit
  access to root/neutron to /etc/neutron/neutron.conf to support execution
  of unit tests in decomposed vendor drivers.
* d/control: Add dependency on python-neutron-fwaas to neutron-l3-agent
  package.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright 2012 VMware, Inc.
2
 
#
3
 
# All Rights Reserved
4
 
#
5
 
# Licensed under the Apache License, Version 2.0 (the "License"); you may
6
 
# not use this file except in compliance with the License. You may obtain
7
 
# a copy of the License at
8
 
#
9
 
# http://www.apache.org/licenses/LICENSE-2.0
10
 
#
11
 
# Unless required by applicable law or agreed to in writing, software
12
 
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
 
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
 
# License for the specific language governing permissions and limitations
15
 
# under the License.
16
 
 
17
 
import httplib
18
 
import urllib
19
 
 
20
 
import eventlet
21
 
from oslo.serialization import jsonutils
22
 
 
23
 
from neutron.i18n import _LI, _LW
24
 
from neutron.openstack.common import log as logging
25
 
from neutron.plugins.vmware.api_client import request
26
 
 
27
 
LOG = logging.getLogger(__name__)
28
 
USER_AGENT = "Neutron eventlet client/2.0"
29
 
 
30
 
 
31
 
class EventletApiRequest(request.ApiRequest):
32
 
    '''Eventlet-based ApiRequest class.
33
 
 
34
 
    This class will form the basis for eventlet-based ApiRequest classes
35
 
    '''
36
 
 
37
 
    # Maximum number of green threads present in the system at one time.
38
 
    API_REQUEST_POOL_SIZE = request.DEFAULT_API_REQUEST_POOL_SIZE
39
 
 
40
 
    # Pool of green threads. One green thread is allocated per incoming
41
 
    # request. Incoming requests will block when the pool is empty.
42
 
    API_REQUEST_POOL = eventlet.GreenPool(API_REQUEST_POOL_SIZE)
43
 
 
44
 
    # A unique id is assigned to each incoming request. When the current
45
 
    # request id reaches MAXIMUM_REQUEST_ID it wraps around back to 0.
46
 
    MAXIMUM_REQUEST_ID = request.DEFAULT_MAXIMUM_REQUEST_ID
47
 
 
48
 
    # The request id for the next incoming request.
49
 
    CURRENT_REQUEST_ID = 0
50
 
 
51
 
    def __init__(self, client_obj, url, method="GET", body=None,
52
 
                 headers=None,
53
 
                 retries=request.DEFAULT_RETRIES,
54
 
                 auto_login=True,
55
 
                 redirects=request.DEFAULT_REDIRECTS,
56
 
                 http_timeout=request.DEFAULT_HTTP_TIMEOUT, client_conn=None):
57
 
        '''Constructor.'''
58
 
        self._api_client = client_obj
59
 
        self._url = url
60
 
        self._method = method
61
 
        self._body = body
62
 
        self._headers = headers or {}
63
 
        self._request_timeout = http_timeout * retries
64
 
        self._retries = retries
65
 
        self._auto_login = auto_login
66
 
        self._redirects = redirects
67
 
        self._http_timeout = http_timeout
68
 
        self._client_conn = client_conn
69
 
        self._abort = False
70
 
 
71
 
        self._request_error = None
72
 
 
73
 
        if "User-Agent" not in self._headers:
74
 
            self._headers["User-Agent"] = USER_AGENT
75
 
 
76
 
        self._green_thread = None
77
 
        # Retrieve and store this instance's unique request id.
78
 
        self._request_id = EventletApiRequest.CURRENT_REQUEST_ID
79
 
        # Update the class variable that tracks request id.
80
 
        # Request IDs wrap around at MAXIMUM_REQUEST_ID
81
 
        next_request_id = self._request_id + 1
82
 
        next_request_id %= self.MAXIMUM_REQUEST_ID
83
 
        EventletApiRequest.CURRENT_REQUEST_ID = next_request_id
84
 
 
85
 
    @classmethod
86
 
    def _spawn(cls, func, *args, **kwargs):
87
 
        '''Allocate a green thread from the class pool.'''
88
 
        return cls.API_REQUEST_POOL.spawn(func, *args, **kwargs)
89
 
 
90
 
    def spawn(self, func, *args, **kwargs):
91
 
        '''Spawn a new green thread with the supplied function and args.'''
92
 
        return self.__class__._spawn(func, *args, **kwargs)
93
 
 
94
 
    @classmethod
95
 
    def joinall(cls):
96
 
        '''Wait for all outstanding requests to complete.'''
97
 
        return cls.API_REQUEST_POOL.waitall()
98
 
 
99
 
    def join(self):
100
 
        '''Wait for instance green thread to complete.'''
101
 
        if self._green_thread is not None:
102
 
            return self._green_thread.wait()
103
 
        return Exception(_('Joining an invalid green thread'))
104
 
 
105
 
    def start(self):
106
 
        '''Start request processing.'''
107
 
        self._green_thread = self.spawn(self._run)
108
 
 
109
 
    def copy(self):
110
 
        '''Return a copy of this request instance.'''
111
 
        return EventletApiRequest(
112
 
            self._api_client, self._url, self._method, self._body,
113
 
            self._headers, self._retries,
114
 
            self._auto_login, self._redirects, self._http_timeout)
115
 
 
116
 
    def _run(self):
117
 
        '''Method executed within green thread.'''
118
 
        if self._request_timeout:
119
 
            # No timeout exception escapes the with block.
120
 
            with eventlet.timeout.Timeout(self._request_timeout, False):
121
 
                return self._handle_request()
122
 
 
123
 
            LOG.info(_LI('[%d] Request timeout.'), self._rid())
124
 
            self._request_error = Exception(_('Request timeout'))
125
 
            return None
126
 
        else:
127
 
            return self._handle_request()
128
 
 
129
 
    def _handle_request(self):
130
 
        '''First level request handling.'''
131
 
        attempt = 0
132
 
        timeout = 0
133
 
        response = None
134
 
        while response is None and attempt <= self._retries:
135
 
            eventlet.greenthread.sleep(timeout)
136
 
            attempt += 1
137
 
 
138
 
            req = self._issue_request()
139
 
            # automatically raises any exceptions returned.
140
 
            if isinstance(req, httplib.HTTPResponse):
141
 
                timeout = 0
142
 
                if attempt <= self._retries and not self._abort:
143
 
                    if req.status in (httplib.UNAUTHORIZED, httplib.FORBIDDEN):
144
 
                        continue
145
 
                    elif req.status == httplib.SERVICE_UNAVAILABLE:
146
 
                        timeout = 0.5
147
 
                        continue
148
 
                    # else fall through to return the error code
149
 
 
150
 
                LOG.debug("[%(rid)d] Completed request '%(method)s %(url)s'"
151
 
                          ": %(status)s",
152
 
                          {'rid': self._rid(), 'method': self._method,
153
 
                           'url': self._url, 'status': req.status})
154
 
                self._request_error = None
155
 
                response = req
156
 
            else:
157
 
                LOG.info(_LI('[%(rid)d] Error while handling request: '
158
 
                             '%(req)s'),
159
 
                         {'rid': self._rid(), 'req': req})
160
 
                self._request_error = req
161
 
                response = None
162
 
        return response
163
 
 
164
 
 
165
 
class LoginRequestEventlet(EventletApiRequest):
166
 
    '''Process a login request.'''
167
 
 
168
 
    def __init__(self, client_obj, user, password, client_conn=None,
169
 
                 headers=None):
170
 
        if headers is None:
171
 
            headers = {}
172
 
        headers.update({"Content-Type": "application/x-www-form-urlencoded"})
173
 
        body = urllib.urlencode({"username": user, "password": password})
174
 
        super(LoginRequestEventlet, self).__init__(
175
 
            client_obj, "/ws.v1/login", "POST", body, headers,
176
 
            auto_login=False, client_conn=client_conn)
177
 
 
178
 
    def session_cookie(self):
179
 
        if self.successful():
180
 
            return self.value.getheader("Set-Cookie")
181
 
        return None
182
 
 
183
 
 
184
 
class GetApiProvidersRequestEventlet(EventletApiRequest):
185
 
    '''Get a list of API providers.'''
186
 
 
187
 
    def __init__(self, client_obj):
188
 
        url = "/ws.v1/control-cluster/node?fields=roles"
189
 
        super(GetApiProvidersRequestEventlet, self).__init__(
190
 
            client_obj, url, "GET", auto_login=True)
191
 
 
192
 
    def api_providers(self):
193
 
        """Parse api_providers from response.
194
 
 
195
 
        Returns: api_providers in [(host, port, is_ssl), ...] format
196
 
        """
197
 
        def _provider_from_listen_addr(addr):
198
 
            # (pssl|ptcp):<ip>:<port> => (host, port, is_ssl)
199
 
            parts = addr.split(':')
200
 
            return (parts[1], int(parts[2]), parts[0] == 'pssl')
201
 
 
202
 
        try:
203
 
            if self.successful():
204
 
                ret = []
205
 
                body = jsonutils.loads(self.value.body)
206
 
                for node in body.get('results', []):
207
 
                    for role in node.get('roles', []):
208
 
                        if role.get('role') == 'api_provider':
209
 
                            addr = role.get('listen_addr')
210
 
                            if addr:
211
 
                                ret.append(_provider_from_listen_addr(addr))
212
 
                return ret
213
 
        except Exception as e:
214
 
            LOG.warn(_LW("[%(rid)d] Failed to parse API provider: %(e)s"),
215
 
                     {'rid': self._rid(), 'e': e})
216
 
            # intentionally fall through
217
 
        return None
218
 
 
219
 
 
220
 
class GenericRequestEventlet(EventletApiRequest):
221
 
    '''Handle a generic request.'''
222
 
 
223
 
    def __init__(self, client_obj, method, url, body, content_type,
224
 
                 auto_login=False,
225
 
                 http_timeout=request.DEFAULT_HTTP_TIMEOUT,
226
 
                 retries=request.DEFAULT_RETRIES,
227
 
                 redirects=request.DEFAULT_REDIRECTS):
228
 
        headers = {"Content-Type": content_type}
229
 
        super(GenericRequestEventlet, self).__init__(
230
 
            client_obj, url, method, body, headers,
231
 
            retries=retries,
232
 
            auto_login=auto_login, redirects=redirects,
233
 
            http_timeout=http_timeout)
234
 
 
235
 
    def session_cookie(self):
236
 
        if self.successful():
237
 
            return self.value.getheader("Set-Cookie")
238
 
        return None
239
 
 
240
 
 
241
 
request.ApiRequest.register(EventletApiRequest)