Statistics
| Branch: | Tag: | Revision:

root / lib / rpc / client.py @ 653bc0f1

History | View | Annotate | Download (6.1 kB)

1 912b2278 Petr Pudlak
#
2 912b2278 Petr Pudlak
#
3 912b2278 Petr Pudlak
4 912b2278 Petr Pudlak
# Copyright (C) 2013 Google Inc.
5 912b2278 Petr Pudlak
#
6 912b2278 Petr Pudlak
# This program is free software; you can redistribute it and/or modify
7 912b2278 Petr Pudlak
# it under the terms of the GNU General Public License as published by
8 912b2278 Petr Pudlak
# the Free Software Foundation; either version 2 of the License, or
9 912b2278 Petr Pudlak
# (at your option) any later version.
10 912b2278 Petr Pudlak
#
11 912b2278 Petr Pudlak
# This program is distributed in the hope that it will be useful, but
12 912b2278 Petr Pudlak
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 912b2278 Petr Pudlak
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 912b2278 Petr Pudlak
# General Public License for more details.
15 912b2278 Petr Pudlak
#
16 912b2278 Petr Pudlak
# You should have received a copy of the GNU General Public License
17 912b2278 Petr Pudlak
# along with this program; if not, write to the Free Software
18 912b2278 Petr Pudlak
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 912b2278 Petr Pudlak
# 02110-1301, USA.
20 912b2278 Petr Pudlak
21 912b2278 Petr Pudlak
22 912b2278 Petr Pudlak
"""Module for generic RPC clients.
23 912b2278 Petr Pudlak

24 912b2278 Petr Pudlak
"""
25 912b2278 Petr Pudlak
26 912b2278 Petr Pudlak
import logging
27 912b2278 Petr Pudlak
28 912b2278 Petr Pudlak
from ganeti import pathutils
29 912b2278 Petr Pudlak
import ganeti.rpc.transport as t
30 912b2278 Petr Pudlak
31 912b2278 Petr Pudlak
from ganeti import constants
32 912b2278 Petr Pudlak
from ganeti import errors
33 912b2278 Petr Pudlak
from ganeti.rpc.errors import (ProtocolError, RequestError, LuxiError)
34 912b2278 Petr Pudlak
from ganeti import serializer
35 912b2278 Petr Pudlak
36 912b2278 Petr Pudlak
KEY_METHOD = constants.LUXI_KEY_METHOD
37 912b2278 Petr Pudlak
KEY_ARGS = constants.LUXI_KEY_ARGS
38 912b2278 Petr Pudlak
KEY_SUCCESS = constants.LUXI_KEY_SUCCESS
39 912b2278 Petr Pudlak
KEY_RESULT = constants.LUXI_KEY_RESULT
40 912b2278 Petr Pudlak
KEY_VERSION = constants.LUXI_KEY_VERSION
41 912b2278 Petr Pudlak
42 912b2278 Petr Pudlak
43 912b2278 Petr Pudlak
def ParseRequest(msg):
44 912b2278 Petr Pudlak
  """Parses a request message.
45 912b2278 Petr Pudlak

46 912b2278 Petr Pudlak
  """
47 912b2278 Petr Pudlak
  try:
48 912b2278 Petr Pudlak
    request = serializer.LoadJson(msg)
49 912b2278 Petr Pudlak
  except ValueError, err:
50 24c09d5e Petr Pudlak
    raise ProtocolError("Invalid RPC request (parsing error): %s" % err)
51 912b2278 Petr Pudlak
52 24c09d5e Petr Pudlak
  logging.debug("RPC request: %s", request)
53 912b2278 Petr Pudlak
54 912b2278 Petr Pudlak
  if not isinstance(request, dict):
55 24c09d5e Petr Pudlak
    logging.error("RPC request not a dict: %r", msg)
56 24c09d5e Petr Pudlak
    raise ProtocolError("Invalid RPC request (not a dict)")
57 912b2278 Petr Pudlak
58 912b2278 Petr Pudlak
  method = request.get(KEY_METHOD, None) # pylint: disable=E1103
59 912b2278 Petr Pudlak
  args = request.get(KEY_ARGS, None) # pylint: disable=E1103
60 912b2278 Petr Pudlak
  version = request.get(KEY_VERSION, None) # pylint: disable=E1103
61 912b2278 Petr Pudlak
62 912b2278 Petr Pudlak
  if method is None or args is None:
63 24c09d5e Petr Pudlak
    logging.error("RPC request missing method or arguments: %r", msg)
64 24c09d5e Petr Pudlak
    raise ProtocolError(("Invalid RPC request (no method or arguments"
65 912b2278 Petr Pudlak
                         " in request): %r") % msg)
66 912b2278 Petr Pudlak
67 912b2278 Petr Pudlak
  return (method, args, version)
68 912b2278 Petr Pudlak
69 912b2278 Petr Pudlak
70 912b2278 Petr Pudlak
def ParseResponse(msg):
71 912b2278 Petr Pudlak
  """Parses a response message.
72 912b2278 Petr Pudlak

73 912b2278 Petr Pudlak
  """
74 912b2278 Petr Pudlak
  # Parse the result
75 912b2278 Petr Pudlak
  try:
76 912b2278 Petr Pudlak
    data = serializer.LoadJson(msg)
77 912b2278 Petr Pudlak
  except KeyboardInterrupt:
78 912b2278 Petr Pudlak
    raise
79 912b2278 Petr Pudlak
  except Exception, err:
80 912b2278 Petr Pudlak
    raise ProtocolError("Error while deserializing response: %s" % str(err))
81 912b2278 Petr Pudlak
82 912b2278 Petr Pudlak
  # Validate response
83 912b2278 Petr Pudlak
  if not (isinstance(data, dict) and
84 912b2278 Petr Pudlak
          KEY_SUCCESS in data and
85 912b2278 Petr Pudlak
          KEY_RESULT in data):
86 912b2278 Petr Pudlak
    raise ProtocolError("Invalid response from server: %r" % data)
87 912b2278 Petr Pudlak
88 912b2278 Petr Pudlak
  return (data[KEY_SUCCESS], data[KEY_RESULT],
89 912b2278 Petr Pudlak
          data.get(KEY_VERSION, None)) # pylint: disable=E1103
90 912b2278 Petr Pudlak
91 912b2278 Petr Pudlak
92 912b2278 Petr Pudlak
def FormatResponse(success, result, version=None):
93 912b2278 Petr Pudlak
  """Formats a response message.
94 912b2278 Petr Pudlak

95 912b2278 Petr Pudlak
  """
96 912b2278 Petr Pudlak
  response = {
97 912b2278 Petr Pudlak
    KEY_SUCCESS: success,
98 912b2278 Petr Pudlak
    KEY_RESULT: result,
99 912b2278 Petr Pudlak
    }
100 912b2278 Petr Pudlak
101 912b2278 Petr Pudlak
  if version is not None:
102 912b2278 Petr Pudlak
    response[KEY_VERSION] = version
103 912b2278 Petr Pudlak
104 24c09d5e Petr Pudlak
  logging.debug("RPC response: %s", response)
105 912b2278 Petr Pudlak
106 912b2278 Petr Pudlak
  return serializer.DumpJson(response)
107 912b2278 Petr Pudlak
108 912b2278 Petr Pudlak
109 912b2278 Petr Pudlak
def FormatRequest(method, args, version=None):
110 912b2278 Petr Pudlak
  """Formats a request message.
111 912b2278 Petr Pudlak

112 912b2278 Petr Pudlak
  """
113 912b2278 Petr Pudlak
  # Build request
114 912b2278 Petr Pudlak
  request = {
115 912b2278 Petr Pudlak
    KEY_METHOD: method,
116 912b2278 Petr Pudlak
    KEY_ARGS: args,
117 912b2278 Petr Pudlak
    }
118 912b2278 Petr Pudlak
119 912b2278 Petr Pudlak
  if version is not None:
120 912b2278 Petr Pudlak
    request[KEY_VERSION] = version
121 912b2278 Petr Pudlak
122 912b2278 Petr Pudlak
  # Serialize the request
123 912b2278 Petr Pudlak
  return serializer.DumpJson(request)
124 912b2278 Petr Pudlak
125 912b2278 Petr Pudlak
126 24c09d5e Petr Pudlak
def CallRPCMethod(transport_cb, method, args, version=None):
127 24c09d5e Petr Pudlak
  """Send a RPC request via a transport and return the response.
128 912b2278 Petr Pudlak

129 912b2278 Petr Pudlak
  """
130 912b2278 Petr Pudlak
  assert callable(transport_cb)
131 912b2278 Petr Pudlak
132 912b2278 Petr Pudlak
  request_msg = FormatRequest(method, args, version=version)
133 912b2278 Petr Pudlak
134 912b2278 Petr Pudlak
  # Send request and wait for response
135 912b2278 Petr Pudlak
  response_msg = transport_cb(request_msg)
136 912b2278 Petr Pudlak
137 912b2278 Petr Pudlak
  (success, result, resp_version) = ParseResponse(response_msg)
138 912b2278 Petr Pudlak
139 912b2278 Petr Pudlak
  # Verify version if there was one in the response
140 912b2278 Petr Pudlak
  if resp_version is not None and resp_version != version:
141 24c09d5e Petr Pudlak
    raise LuxiError("RPC version mismatch, client %s, response %s" %
142 912b2278 Petr Pudlak
                    (version, resp_version))
143 912b2278 Petr Pudlak
144 912b2278 Petr Pudlak
  if success:
145 912b2278 Petr Pudlak
    return result
146 912b2278 Petr Pudlak
147 912b2278 Petr Pudlak
  errors.MaybeRaise(result)
148 912b2278 Petr Pudlak
  raise RequestError(result)
149 912b2278 Petr Pudlak
150 912b2278 Petr Pudlak
151 912b2278 Petr Pudlak
class AbstractClient(object):
152 912b2278 Petr Pudlak
  """High-level client abstraction.
153 912b2278 Petr Pudlak

154 912b2278 Petr Pudlak
  This uses a backing Transport-like class on top of which it
155 912b2278 Petr Pudlak
  implements data serialization/deserialization.
156 912b2278 Petr Pudlak

157 912b2278 Petr Pudlak
  """
158 912b2278 Petr Pudlak
159 912b2278 Petr Pudlak
  def __init__(self, address=None, timeouts=None,
160 912b2278 Petr Pudlak
               transport=t.Transport):
161 912b2278 Petr Pudlak
    """Constructor for the Client class.
162 912b2278 Petr Pudlak

163 912b2278 Petr Pudlak
    Arguments:
164 912b2278 Petr Pudlak
      - address: a valid address the the used transport class
165 912b2278 Petr Pudlak
      - timeout: a list of timeouts, to be used on connect and read/write
166 912b2278 Petr Pudlak
      - transport: a Transport-like class
167 912b2278 Petr Pudlak

168 912b2278 Petr Pudlak

169 912b2278 Petr Pudlak
    If timeout is not passed, the default timeouts of the transport
170 912b2278 Petr Pudlak
    class are used.
171 912b2278 Petr Pudlak

172 912b2278 Petr Pudlak
    """
173 912b2278 Petr Pudlak
    if address is None:
174 912b2278 Petr Pudlak
      address = pathutils.MASTER_SOCKET
175 912b2278 Petr Pudlak
    self.address = address
176 912b2278 Petr Pudlak
    self.timeouts = timeouts
177 912b2278 Petr Pudlak
    self.transport_class = transport
178 912b2278 Petr Pudlak
    self.transport = None
179 912b2278 Petr Pudlak
    self._InitTransport()
180 cda215a9 Petr Pudlak
    # The version used in RPC communication, by default unused:
181 cda215a9 Petr Pudlak
    self.version = None
182 912b2278 Petr Pudlak
183 912b2278 Petr Pudlak
  def _InitTransport(self):
184 912b2278 Petr Pudlak
    """(Re)initialize the transport if needed.
185 912b2278 Petr Pudlak

186 912b2278 Petr Pudlak
    """
187 912b2278 Petr Pudlak
    if self.transport is None:
188 912b2278 Petr Pudlak
      self.transport = self.transport_class(self.address,
189 912b2278 Petr Pudlak
                                            timeouts=self.timeouts)
190 912b2278 Petr Pudlak
191 912b2278 Petr Pudlak
  def _CloseTransport(self):
192 912b2278 Petr Pudlak
    """Close the transport, ignoring errors.
193 912b2278 Petr Pudlak

194 912b2278 Petr Pudlak
    """
195 912b2278 Petr Pudlak
    if self.transport is None:
196 912b2278 Petr Pudlak
      return
197 912b2278 Petr Pudlak
    try:
198 912b2278 Petr Pudlak
      old_transp = self.transport
199 912b2278 Petr Pudlak
      self.transport = None
200 912b2278 Petr Pudlak
      old_transp.Close()
201 912b2278 Petr Pudlak
    except Exception: # pylint: disable=W0703
202 912b2278 Petr Pudlak
      pass
203 912b2278 Petr Pudlak
204 912b2278 Petr Pudlak
  def _SendMethodCall(self, data):
205 912b2278 Petr Pudlak
    # Send request and wait for response
206 912b2278 Petr Pudlak
    try:
207 912b2278 Petr Pudlak
      self._InitTransport()
208 912b2278 Petr Pudlak
      return self.transport.Call(data)
209 912b2278 Petr Pudlak
    except Exception:
210 912b2278 Petr Pudlak
      self._CloseTransport()
211 912b2278 Petr Pudlak
      raise
212 912b2278 Petr Pudlak
213 912b2278 Petr Pudlak
  def Close(self):
214 912b2278 Petr Pudlak
    """Close the underlying connection.
215 912b2278 Petr Pudlak

216 912b2278 Petr Pudlak
    """
217 912b2278 Petr Pudlak
    self._CloseTransport()
218 912b2278 Petr Pudlak
219 912b2278 Petr Pudlak
  def close(self):
220 912b2278 Petr Pudlak
    """Same as L{Close}, to be used with contextlib.closing(...).
221 912b2278 Petr Pudlak

222 912b2278 Petr Pudlak
    """
223 912b2278 Petr Pudlak
    self.Close()
224 912b2278 Petr Pudlak
225 912b2278 Petr Pudlak
  def CallMethod(self, method, args):
226 912b2278 Petr Pudlak
    """Send a generic request and return the response.
227 912b2278 Petr Pudlak

228 912b2278 Petr Pudlak
    """
229 912b2278 Petr Pudlak
    if not isinstance(args, (list, tuple)):
230 912b2278 Petr Pudlak
      raise errors.ProgrammerError("Invalid parameter passed to CallMethod:"
231 912b2278 Petr Pudlak
                                   " expected list, got %s" % type(args))
232 24c09d5e Petr Pudlak
    return CallRPCMethod(self._SendMethodCall, method, args,
233 cda215a9 Petr Pudlak
                         version=self.version)