Statistics
| Branch: | Tag: | Revision:

root / lib / rpc / client.py @ 560ef132

History | View | Annotate | Download (6.2 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 560ef132 Santi Raffa
  return serializer.DumpJson(request,
124 560ef132 Santi Raffa
                             private_encoder=serializer.EncodeWithPrivateFields)
125 912b2278 Petr Pudlak
126 912b2278 Petr Pudlak
127 24c09d5e Petr Pudlak
def CallRPCMethod(transport_cb, method, args, version=None):
128 24c09d5e Petr Pudlak
  """Send a RPC request via a transport and return the response.
129 912b2278 Petr Pudlak

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

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

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

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

169 912b2278 Petr Pudlak

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

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

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

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

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

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

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