Statistics
| Branch: | Tag: | Revision:

root / lib / luxi.py @ 98dfcaff

History | View | Annotate | Download (15.7 kB)

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

24 8d5b316c Iustin Pop
This module implements the local unix socket protocol. You only need
25 c2a03789 Iustin Pop
this module and the opcodes module in the client program in order to
26 c2a03789 Iustin Pop
communicate with the master.
27 c2a03789 Iustin Pop

28 7577196d Guido Trotter
The module is also used by the master daemon.
29 c2a03789 Iustin Pop

30 c2a03789 Iustin Pop
"""
31 c2a03789 Iustin Pop
32 c2a03789 Iustin Pop
import socket
33 c2a03789 Iustin Pop
import collections
34 c2a03789 Iustin Pop
import time
35 03a8dbdc Iustin Pop
import errno
36 231db3a5 Michael Hanselmann
import logging
37 24d16f76 Michael Hanselmann
import warnings
38 c2a03789 Iustin Pop
39 fad50141 Michael Hanselmann
from ganeti import serializer
40 ceab32dd Iustin Pop
from ganeti import constants
41 6797ec29 Iustin Pop
from ganeti import errors
42 cb462b06 Michael Hanselmann
from ganeti import utils
43 28b71a76 Michael Hanselmann
from ganeti import objects
44 c2a03789 Iustin Pop
45 c2a03789 Iustin Pop
46 231db3a5 Michael Hanselmann
KEY_METHOD = "method"
47 231db3a5 Michael Hanselmann
KEY_ARGS = "args"
48 3d8548c4 Michael Hanselmann
KEY_SUCCESS = "success"
49 3d8548c4 Michael Hanselmann
KEY_RESULT = "result"
50 e986f20c Michael Hanselmann
KEY_VERSION = "version"
51 3d8548c4 Michael Hanselmann
52 0bbe448c Michael Hanselmann
REQ_SUBMIT_JOB = "SubmitJob"
53 2971c913 Iustin Pop
REQ_SUBMIT_MANY_JOBS = "SubmitManyJobs"
54 dfe57c22 Michael Hanselmann
REQ_WAIT_FOR_JOB_CHANGE = "WaitForJobChange"
55 0bbe448c Michael Hanselmann
REQ_CANCEL_JOB = "CancelJob"
56 0bbe448c Michael Hanselmann
REQ_ARCHIVE_JOB = "ArchiveJob"
57 07cd723a Iustin Pop
REQ_AUTOARCHIVE_JOBS = "AutoArchiveJobs"
58 28b71a76 Michael Hanselmann
REQ_QUERY = "Query"
59 28b71a76 Michael Hanselmann
REQ_QUERY_FIELDS = "QueryFields"
60 0bbe448c Michael Hanselmann
REQ_QUERY_JOBS = "QueryJobs"
61 ee6c7b94 Michael Hanselmann
REQ_QUERY_INSTANCES = "QueryInstances"
62 02f7fe54 Michael Hanselmann
REQ_QUERY_NODES = "QueryNodes"
63 a79ef2a5 Adeodato Simo
REQ_QUERY_GROUPS = "QueryGroups"
64 32f93223 Michael Hanselmann
REQ_QUERY_EXPORTS = "QueryExports"
65 ae5849b5 Michael Hanselmann
REQ_QUERY_CONFIG_VALUES = "QueryConfigValues"
66 66baeccc Iustin Pop
REQ_QUERY_CLUSTER_INFO = "QueryClusterInfo"
67 7699c3af Iustin Pop
REQ_QUERY_TAGS = "QueryTags"
68 19b9ba9a Michael Hanselmann
REQ_QUERY_LOCKS = "QueryLocks"
69 3ccafd0e Iustin Pop
REQ_QUEUE_SET_DRAIN_FLAG = "SetDrainFlag"
70 05e50653 Michael Hanselmann
REQ_SET_WATCHER_PAUSE = "SetWatcherPause"
71 c2a03789 Iustin Pop
72 c2a03789 Iustin Pop
DEF_CTMO = 10
73 c2a03789 Iustin Pop
DEF_RWTO = 60
74 c2a03789 Iustin Pop
75 793a8f7c Michael Hanselmann
# WaitForJobChange timeout
76 793a8f7c Michael Hanselmann
WFJC_TIMEOUT = (DEF_RWTO - 1) / 2
77 793a8f7c Michael Hanselmann
78 c2a03789 Iustin Pop
79 7a8bda3f Michael Hanselmann
class ProtocolError(errors.LuxiError):
80 5a1c22fe Iustin Pop
  """Denotes an error in the LUXI protocol."""
81 c2a03789 Iustin Pop
82 c2a03789 Iustin Pop
83 c2a03789 Iustin Pop
class ConnectionClosedError(ProtocolError):
84 5a1c22fe Iustin Pop
  """Connection closed error."""
85 c2a03789 Iustin Pop
86 c2a03789 Iustin Pop
87 c2a03789 Iustin Pop
class TimeoutError(ProtocolError):
88 5a1c22fe Iustin Pop
  """Operation timeout error."""
89 c2a03789 Iustin Pop
90 c2a03789 Iustin Pop
91 b77acb3e Iustin Pop
class RequestError(ProtocolError):
92 5a1c22fe Iustin Pop
  """Error on request.
93 b77acb3e Iustin Pop

94 b77acb3e Iustin Pop
  This signifies an error in the request format or request handling,
95 b77acb3e Iustin Pop
  but not (e.g.) an error in starting up an instance.
96 b77acb3e Iustin Pop

97 b77acb3e Iustin Pop
  Some common conditions that can trigger this exception:
98 b77acb3e Iustin Pop
    - job submission failed because the job data was wrong
99 b77acb3e Iustin Pop
    - query failed because required fields were missing
100 b77acb3e Iustin Pop

101 b77acb3e Iustin Pop
  """
102 b77acb3e Iustin Pop
103 3d8548c4 Michael Hanselmann
104 03a8dbdc Iustin Pop
class NoMasterError(ProtocolError):
105 5a1c22fe Iustin Pop
  """The master cannot be reached.
106 03a8dbdc Iustin Pop

107 03a8dbdc Iustin Pop
  This means that the master daemon is not running or the socket has
108 03a8dbdc Iustin Pop
  been removed.
109 03a8dbdc Iustin Pop

110 03a8dbdc Iustin Pop
  """
111 03a8dbdc Iustin Pop
112 b77acb3e Iustin Pop
113 5a1c22fe Iustin Pop
class PermissionError(ProtocolError):
114 5a1c22fe Iustin Pop
  """Permission denied while connecting to the master socket.
115 5a1c22fe Iustin Pop

116 5a1c22fe Iustin Pop
  This means the user doesn't have the proper rights.
117 5a1c22fe Iustin Pop

118 5a1c22fe Iustin Pop
  """
119 5a1c22fe Iustin Pop
120 5a1c22fe Iustin Pop
121 c2a03789 Iustin Pop
class Transport:
122 c2a03789 Iustin Pop
  """Low-level transport class.
123 c2a03789 Iustin Pop

124 c2a03789 Iustin Pop
  This is used on the client side.
125 c2a03789 Iustin Pop

126 c2a03789 Iustin Pop
  This could be replace by any other class that provides the same
127 c2a03789 Iustin Pop
  semantics to the Client. This means:
128 c2a03789 Iustin Pop
    - can send messages and receive messages
129 c2a03789 Iustin Pop
    - safe for multithreading
130 c2a03789 Iustin Pop

131 c2a03789 Iustin Pop
  """
132 c2a03789 Iustin Pop
133 25942a6c Guido Trotter
  def __init__(self, address, timeouts=None):
134 c2a03789 Iustin Pop
    """Constructor for the Client class.
135 c2a03789 Iustin Pop

136 c2a03789 Iustin Pop
    Arguments:
137 c2a03789 Iustin Pop
      - address: a valid address the the used transport class
138 c2a03789 Iustin Pop
      - timeout: a list of timeouts, to be used on connect and read/write
139 c2a03789 Iustin Pop

140 c2a03789 Iustin Pop
    There are two timeouts used since we might want to wait for a long
141 c2a03789 Iustin Pop
    time for a response, but the connect timeout should be lower.
142 c2a03789 Iustin Pop

143 c2a03789 Iustin Pop
    If not passed, we use a default of 10 and respectively 60 seconds.
144 c2a03789 Iustin Pop

145 c2a03789 Iustin Pop
    Note that on reading data, since the timeout applies to an
146 c2a03789 Iustin Pop
    invidual receive, it might be that the total duration is longer
147 c2a03789 Iustin Pop
    than timeout value passed (we make a hard limit at twice the read
148 c2a03789 Iustin Pop
    timeout).
149 c2a03789 Iustin Pop

150 c2a03789 Iustin Pop
    """
151 c2a03789 Iustin Pop
    self.address = address
152 c2a03789 Iustin Pop
    if timeouts is None:
153 c2a03789 Iustin Pop
      self._ctimeout, self._rwtimeout = DEF_CTMO, DEF_RWTO
154 c2a03789 Iustin Pop
    else:
155 c2a03789 Iustin Pop
      self._ctimeout, self._rwtimeout = timeouts
156 c2a03789 Iustin Pop
157 c2a03789 Iustin Pop
    self.socket = None
158 c2a03789 Iustin Pop
    self._buffer = ""
159 c2a03789 Iustin Pop
    self._msgs = collections.deque()
160 c2a03789 Iustin Pop
161 c2a03789 Iustin Pop
    try:
162 c2a03789 Iustin Pop
      self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
163 cb462b06 Michael Hanselmann
164 cb462b06 Michael Hanselmann
      # Try to connect
165 c2a03789 Iustin Pop
      try:
166 cb462b06 Michael Hanselmann
        utils.Retry(self._Connect, 1.0, self._ctimeout,
167 cb462b06 Michael Hanselmann
                    args=(self.socket, address, self._ctimeout))
168 cb462b06 Michael Hanselmann
      except utils.RetryTimeout:
169 cb462b06 Michael Hanselmann
        raise TimeoutError("Connect timed out")
170 cb462b06 Michael Hanselmann
171 c2a03789 Iustin Pop
      self.socket.settimeout(self._rwtimeout)
172 03a8dbdc Iustin Pop
    except (socket.error, NoMasterError):
173 c2a03789 Iustin Pop
      if self.socket is not None:
174 c2a03789 Iustin Pop
        self.socket.close()
175 c2a03789 Iustin Pop
      self.socket = None
176 c2a03789 Iustin Pop
      raise
177 c2a03789 Iustin Pop
178 cb462b06 Michael Hanselmann
  @staticmethod
179 cb462b06 Michael Hanselmann
  def _Connect(sock, address, timeout):
180 cb462b06 Michael Hanselmann
    sock.settimeout(timeout)
181 cb462b06 Michael Hanselmann
    try:
182 cb462b06 Michael Hanselmann
      sock.connect(address)
183 cb462b06 Michael Hanselmann
    except socket.timeout, err:
184 cb462b06 Michael Hanselmann
      raise TimeoutError("Connect timed out: %s" % str(err))
185 cb462b06 Michael Hanselmann
    except socket.error, err:
186 5a1c22fe Iustin Pop
      error_code = err.args[0]
187 5a1c22fe Iustin Pop
      if error_code in (errno.ENOENT, errno.ECONNREFUSED):
188 cb462b06 Michael Hanselmann
        raise NoMasterError(address)
189 5a1c22fe Iustin Pop
      elif error_code in (errno.EPERM, errno.EACCES):
190 5a1c22fe Iustin Pop
        raise PermissionError(address)
191 5a1c22fe Iustin Pop
      elif error_code == errno.EAGAIN:
192 cb462b06 Michael Hanselmann
        # Server's socket backlog is full at the moment
193 cb462b06 Michael Hanselmann
        raise utils.RetryAgain()
194 cb462b06 Michael Hanselmann
      raise
195 cb462b06 Michael Hanselmann
196 c2a03789 Iustin Pop
  def _CheckSocket(self):
197 c2a03789 Iustin Pop
    """Make sure we are connected.
198 c2a03789 Iustin Pop

199 c2a03789 Iustin Pop
    """
200 c2a03789 Iustin Pop
    if self.socket is None:
201 c2a03789 Iustin Pop
      raise ProtocolError("Connection is closed")
202 c2a03789 Iustin Pop
203 c2a03789 Iustin Pop
  def Send(self, msg):
204 c2a03789 Iustin Pop
    """Send a message.
205 c2a03789 Iustin Pop

206 c2a03789 Iustin Pop
    This just sends a message and doesn't wait for the response.
207 c2a03789 Iustin Pop

208 c2a03789 Iustin Pop
    """
209 25942a6c Guido Trotter
    if constants.LUXI_EOM in msg:
210 797506fc Michael Hanselmann
      raise ProtocolError("Message terminator found in payload")
211 797506fc Michael Hanselmann
212 c2a03789 Iustin Pop
    self._CheckSocket()
213 c2a03789 Iustin Pop
    try:
214 6096ee13 Michael Hanselmann
      # TODO: sendall is not guaranteed to send everything
215 25942a6c Guido Trotter
      self.socket.sendall(msg + constants.LUXI_EOM)
216 c2a03789 Iustin Pop
    except socket.timeout, err:
217 c2a03789 Iustin Pop
      raise TimeoutError("Sending timeout: %s" % str(err))
218 c2a03789 Iustin Pop
219 c2a03789 Iustin Pop
  def Recv(self):
220 5bbd3f7f Michael Hanselmann
    """Try to receive a message from the socket.
221 c2a03789 Iustin Pop

222 c2a03789 Iustin Pop
    In case we already have messages queued, we just return from the
223 c2a03789 Iustin Pop
    queue. Otherwise, we try to read data with a _rwtimeout network
224 c2a03789 Iustin Pop
    timeout, and making sure we don't go over 2x_rwtimeout as a global
225 c2a03789 Iustin Pop
    limit.
226 c2a03789 Iustin Pop

227 c2a03789 Iustin Pop
    """
228 c2a03789 Iustin Pop
    self._CheckSocket()
229 c2a03789 Iustin Pop
    etime = time.time() + self._rwtimeout
230 c2a03789 Iustin Pop
    while not self._msgs:
231 c2a03789 Iustin Pop
      if time.time() > etime:
232 c2a03789 Iustin Pop
        raise TimeoutError("Extended receive timeout")
233 6096ee13 Michael Hanselmann
      while True:
234 6096ee13 Michael Hanselmann
        try:
235 6096ee13 Michael Hanselmann
          data = self.socket.recv(4096)
236 28e3e216 Michael Hanselmann
        except socket.timeout, err:
237 28e3e216 Michael Hanselmann
          raise TimeoutError("Receive timeout: %s" % str(err))
238 6096ee13 Michael Hanselmann
        except socket.error, err:
239 6096ee13 Michael Hanselmann
          if err.args and err.args[0] == errno.EAGAIN:
240 6096ee13 Michael Hanselmann
            continue
241 6096ee13 Michael Hanselmann
          raise
242 6096ee13 Michael Hanselmann
        break
243 c2a03789 Iustin Pop
      if not data:
244 c2a03789 Iustin Pop
        raise ConnectionClosedError("Connection closed while reading")
245 25942a6c Guido Trotter
      new_msgs = (self._buffer + data).split(constants.LUXI_EOM)
246 c2a03789 Iustin Pop
      self._buffer = new_msgs.pop()
247 c2a03789 Iustin Pop
      self._msgs.extend(new_msgs)
248 c2a03789 Iustin Pop
    return self._msgs.popleft()
249 c2a03789 Iustin Pop
250 c2a03789 Iustin Pop
  def Call(self, msg):
251 c2a03789 Iustin Pop
    """Send a message and wait for the response.
252 c2a03789 Iustin Pop

253 c2a03789 Iustin Pop
    This is just a wrapper over Send and Recv.
254 c2a03789 Iustin Pop

255 c2a03789 Iustin Pop
    """
256 c2a03789 Iustin Pop
    self.Send(msg)
257 c2a03789 Iustin Pop
    return self.Recv()
258 c2a03789 Iustin Pop
259 c2a03789 Iustin Pop
  def Close(self):
260 c2a03789 Iustin Pop
    """Close the socket"""
261 c2a03789 Iustin Pop
    if self.socket is not None:
262 c2a03789 Iustin Pop
      self.socket.close()
263 c2a03789 Iustin Pop
      self.socket = None
264 c2a03789 Iustin Pop
265 c2a03789 Iustin Pop
266 231db3a5 Michael Hanselmann
def ParseRequest(msg):
267 231db3a5 Michael Hanselmann
  """Parses a LUXI request message.
268 231db3a5 Michael Hanselmann

269 231db3a5 Michael Hanselmann
  """
270 231db3a5 Michael Hanselmann
  try:
271 231db3a5 Michael Hanselmann
    request = serializer.LoadJson(msg)
272 231db3a5 Michael Hanselmann
  except ValueError, err:
273 231db3a5 Michael Hanselmann
    raise ProtocolError("Invalid LUXI request (parsing error): %s" % err)
274 231db3a5 Michael Hanselmann
275 231db3a5 Michael Hanselmann
  logging.debug("LUXI request: %s", request)
276 231db3a5 Michael Hanselmann
277 231db3a5 Michael Hanselmann
  if not isinstance(request, dict):
278 231db3a5 Michael Hanselmann
    logging.error("LUXI request not a dict: %r", msg)
279 231db3a5 Michael Hanselmann
    raise ProtocolError("Invalid LUXI request (not a dict)")
280 231db3a5 Michael Hanselmann
281 b459a848 Andrea Spadaccini
  method = request.get(KEY_METHOD, None) # pylint: disable=E1103
282 b459a848 Andrea Spadaccini
  args = request.get(KEY_ARGS, None) # pylint: disable=E1103
283 b459a848 Andrea Spadaccini
  version = request.get(KEY_VERSION, None) # pylint: disable=E1103
284 e7a25b08 Guido Trotter
285 231db3a5 Michael Hanselmann
  if method is None or args is None:
286 231db3a5 Michael Hanselmann
    logging.error("LUXI request missing method or arguments: %r", msg)
287 231db3a5 Michael Hanselmann
    raise ProtocolError(("Invalid LUXI request (no method or arguments"
288 231db3a5 Michael Hanselmann
                         " in request): %r") % msg)
289 231db3a5 Michael Hanselmann
290 e986f20c Michael Hanselmann
  return (method, args, version)
291 231db3a5 Michael Hanselmann
292 231db3a5 Michael Hanselmann
293 231db3a5 Michael Hanselmann
def ParseResponse(msg):
294 231db3a5 Michael Hanselmann
  """Parses a LUXI response message.
295 231db3a5 Michael Hanselmann

296 231db3a5 Michael Hanselmann
  """
297 231db3a5 Michael Hanselmann
  # Parse the result
298 231db3a5 Michael Hanselmann
  try:
299 231db3a5 Michael Hanselmann
    data = serializer.LoadJson(msg)
300 d143f2c6 Iustin Pop
  except KeyboardInterrupt:
301 d143f2c6 Iustin Pop
    raise
302 231db3a5 Michael Hanselmann
  except Exception, err:
303 231db3a5 Michael Hanselmann
    raise ProtocolError("Error while deserializing response: %s" % str(err))
304 231db3a5 Michael Hanselmann
305 231db3a5 Michael Hanselmann
  # Validate response
306 231db3a5 Michael Hanselmann
  if not (isinstance(data, dict) and
307 231db3a5 Michael Hanselmann
          KEY_SUCCESS in data and
308 231db3a5 Michael Hanselmann
          KEY_RESULT in data):
309 231db3a5 Michael Hanselmann
    raise ProtocolError("Invalid response from server: %r" % data)
310 231db3a5 Michael Hanselmann
311 2317945a Guido Trotter
  return (data[KEY_SUCCESS], data[KEY_RESULT],
312 b459a848 Andrea Spadaccini
          data.get(KEY_VERSION, None)) # pylint: disable=E1103
313 231db3a5 Michael Hanselmann
314 231db3a5 Michael Hanselmann
315 e986f20c Michael Hanselmann
def FormatResponse(success, result, version=None):
316 231db3a5 Michael Hanselmann
  """Formats a LUXI response message.
317 231db3a5 Michael Hanselmann

318 231db3a5 Michael Hanselmann
  """
319 231db3a5 Michael Hanselmann
  response = {
320 231db3a5 Michael Hanselmann
    KEY_SUCCESS: success,
321 231db3a5 Michael Hanselmann
    KEY_RESULT: result,
322 231db3a5 Michael Hanselmann
    }
323 231db3a5 Michael Hanselmann
324 e986f20c Michael Hanselmann
  if version is not None:
325 e986f20c Michael Hanselmann
    response[KEY_VERSION] = version
326 e986f20c Michael Hanselmann
327 231db3a5 Michael Hanselmann
  logging.debug("LUXI response: %s", response)
328 231db3a5 Michael Hanselmann
329 231db3a5 Michael Hanselmann
  return serializer.DumpJson(response)
330 231db3a5 Michael Hanselmann
331 231db3a5 Michael Hanselmann
332 e986f20c Michael Hanselmann
def FormatRequest(method, args, version=None):
333 231db3a5 Michael Hanselmann
  """Formats a LUXI request message.
334 231db3a5 Michael Hanselmann

335 231db3a5 Michael Hanselmann
  """
336 231db3a5 Michael Hanselmann
  # Build request
337 231db3a5 Michael Hanselmann
  request = {
338 231db3a5 Michael Hanselmann
    KEY_METHOD: method,
339 231db3a5 Michael Hanselmann
    KEY_ARGS: args,
340 231db3a5 Michael Hanselmann
    }
341 231db3a5 Michael Hanselmann
342 e986f20c Michael Hanselmann
  if version is not None:
343 e986f20c Michael Hanselmann
    request[KEY_VERSION] = version
344 e986f20c Michael Hanselmann
345 231db3a5 Michael Hanselmann
  # Serialize the request
346 231db3a5 Michael Hanselmann
  return serializer.DumpJson(request, indent=False)
347 231db3a5 Michael Hanselmann
348 231db3a5 Michael Hanselmann
349 e986f20c Michael Hanselmann
def CallLuxiMethod(transport_cb, method, args, version=None):
350 231db3a5 Michael Hanselmann
  """Send a LUXI request via a transport and return the response.
351 231db3a5 Michael Hanselmann

352 231db3a5 Michael Hanselmann
  """
353 231db3a5 Michael Hanselmann
  assert callable(transport_cb)
354 231db3a5 Michael Hanselmann
355 e986f20c Michael Hanselmann
  request_msg = FormatRequest(method, args, version=version)
356 231db3a5 Michael Hanselmann
357 231db3a5 Michael Hanselmann
  # Send request and wait for response
358 231db3a5 Michael Hanselmann
  response_msg = transport_cb(request_msg)
359 231db3a5 Michael Hanselmann
360 e986f20c Michael Hanselmann
  (success, result, resp_version) = ParseResponse(response_msg)
361 e986f20c Michael Hanselmann
362 e986f20c Michael Hanselmann
  # Verify version if there was one in the response
363 e986f20c Michael Hanselmann
  if resp_version is not None and resp_version != version:
364 e986f20c Michael Hanselmann
    raise errors.LuxiError("LUXI version mismatch, client %s, response %s" %
365 e986f20c Michael Hanselmann
                           (version, resp_version))
366 231db3a5 Michael Hanselmann
367 231db3a5 Michael Hanselmann
  if success:
368 231db3a5 Michael Hanselmann
    return result
369 231db3a5 Michael Hanselmann
370 231db3a5 Michael Hanselmann
  errors.MaybeRaise(result)
371 231db3a5 Michael Hanselmann
  raise RequestError(result)
372 231db3a5 Michael Hanselmann
373 231db3a5 Michael Hanselmann
374 c2a03789 Iustin Pop
class Client(object):
375 c2a03789 Iustin Pop
  """High-level client implementation.
376 c2a03789 Iustin Pop

377 c2a03789 Iustin Pop
  This uses a backing Transport-like class on top of which it
378 c2a03789 Iustin Pop
  implements data serialization/deserialization.
379 c2a03789 Iustin Pop

380 c2a03789 Iustin Pop
  """
381 ceab32dd Iustin Pop
  def __init__(self, address=None, timeouts=None, transport=Transport):
382 c2a03789 Iustin Pop
    """Constructor for the Client class.
383 c2a03789 Iustin Pop

384 c2a03789 Iustin Pop
    Arguments:
385 c2a03789 Iustin Pop
      - address: a valid address the the used transport class
386 c2a03789 Iustin Pop
      - timeout: a list of timeouts, to be used on connect and read/write
387 c2a03789 Iustin Pop
      - transport: a Transport-like class
388 c2a03789 Iustin Pop

389 c2a03789 Iustin Pop

390 c2a03789 Iustin Pop
    If timeout is not passed, the default timeouts of the transport
391 c2a03789 Iustin Pop
    class are used.
392 c2a03789 Iustin Pop

393 c2a03789 Iustin Pop
    """
394 ceab32dd Iustin Pop
    if address is None:
395 ceab32dd Iustin Pop
      address = constants.MASTER_SOCKET
396 8d5b316c Iustin Pop
    self.address = address
397 8d5b316c Iustin Pop
    self.timeouts = timeouts
398 8d5b316c Iustin Pop
    self.transport_class = transport
399 8d5b316c Iustin Pop
    self.transport = None
400 8d5b316c Iustin Pop
    self._InitTransport()
401 8d5b316c Iustin Pop
402 8d5b316c Iustin Pop
  def _InitTransport(self):
403 8d5b316c Iustin Pop
    """(Re)initialize the transport if needed.
404 8d5b316c Iustin Pop

405 8d5b316c Iustin Pop
    """
406 8d5b316c Iustin Pop
    if self.transport is None:
407 8d5b316c Iustin Pop
      self.transport = self.transport_class(self.address,
408 8d5b316c Iustin Pop
                                            timeouts=self.timeouts)
409 8d5b316c Iustin Pop
410 8d5b316c Iustin Pop
  def _CloseTransport(self):
411 8d5b316c Iustin Pop
    """Close the transport, ignoring errors.
412 8d5b316c Iustin Pop

413 8d5b316c Iustin Pop
    """
414 8d5b316c Iustin Pop
    if self.transport is None:
415 8d5b316c Iustin Pop
      return
416 8d5b316c Iustin Pop
    try:
417 8d5b316c Iustin Pop
      old_transp = self.transport
418 8d5b316c Iustin Pop
      self.transport = None
419 8d5b316c Iustin Pop
      old_transp.Close()
420 b459a848 Andrea Spadaccini
    except Exception: # pylint: disable=W0703
421 8d5b316c Iustin Pop
      pass
422 c2a03789 Iustin Pop
423 231db3a5 Michael Hanselmann
  def _SendMethodCall(self, data):
424 3d8548c4 Michael Hanselmann
    # Send request and wait for response
425 8d5b316c Iustin Pop
    try:
426 8d5b316c Iustin Pop
      self._InitTransport()
427 231db3a5 Michael Hanselmann
      return self.transport.Call(data)
428 8d5b316c Iustin Pop
    except Exception:
429 8d5b316c Iustin Pop
      self._CloseTransport()
430 8d5b316c Iustin Pop
      raise
431 8d5b316c Iustin Pop
432 2a917701 Michael Hanselmann
  def Close(self):
433 2a917701 Michael Hanselmann
    """Close the underlying connection.
434 2a917701 Michael Hanselmann

435 2a917701 Michael Hanselmann
    """
436 2a917701 Michael Hanselmann
    self._CloseTransport()
437 2a917701 Michael Hanselmann
438 231db3a5 Michael Hanselmann
  def CallMethod(self, method, args):
439 231db3a5 Michael Hanselmann
    """Send a generic request and return the response.
440 3d8548c4 Michael Hanselmann

441 231db3a5 Michael Hanselmann
    """
442 e986f20c Michael Hanselmann
    return CallLuxiMethod(self._SendMethodCall, method, args,
443 e986f20c Michael Hanselmann
                          version=constants.LUXI_VERSION)
444 c2a03789 Iustin Pop
445 3ccafd0e Iustin Pop
  def SetQueueDrainFlag(self, drain_flag):
446 3ccafd0e Iustin Pop
    return self.CallMethod(REQ_QUEUE_SET_DRAIN_FLAG, drain_flag)
447 3ccafd0e Iustin Pop
448 05e50653 Michael Hanselmann
  def SetWatcherPause(self, until):
449 05e50653 Michael Hanselmann
    return self.CallMethod(REQ_SET_WATCHER_PAUSE, [until])
450 05e50653 Michael Hanselmann
451 0bbe448c Michael Hanselmann
  def SubmitJob(self, ops):
452 0bbe448c Michael Hanselmann
    ops_state = map(lambda op: op.__getstate__(), ops)
453 0bbe448c Michael Hanselmann
    return self.CallMethod(REQ_SUBMIT_JOB, ops_state)
454 0bbe448c Michael Hanselmann
455 2971c913 Iustin Pop
  def SubmitManyJobs(self, jobs):
456 2971c913 Iustin Pop
    jobs_state = []
457 2971c913 Iustin Pop
    for ops in jobs:
458 2971c913 Iustin Pop
      jobs_state.append([op.__getstate__() for op in ops])
459 2971c913 Iustin Pop
    return self.CallMethod(REQ_SUBMIT_MANY_JOBS, jobs_state)
460 2971c913 Iustin Pop
461 0bbe448c Michael Hanselmann
  def CancelJob(self, job_id):
462 0bbe448c Michael Hanselmann
    return self.CallMethod(REQ_CANCEL_JOB, job_id)
463 0bbe448c Michael Hanselmann
464 0bbe448c Michael Hanselmann
  def ArchiveJob(self, job_id):
465 0bbe448c Michael Hanselmann
    return self.CallMethod(REQ_ARCHIVE_JOB, job_id)
466 0bbe448c Michael Hanselmann
467 07cd723a Iustin Pop
  def AutoArchiveJobs(self, age):
468 f8ad5591 Michael Hanselmann
    timeout = (DEF_RWTO - 1) / 2
469 f8ad5591 Michael Hanselmann
    return self.CallMethod(REQ_AUTOARCHIVE_JOBS, (age, timeout))
470 07cd723a Iustin Pop
471 f4484122 Michael Hanselmann
  def WaitForJobChangeOnce(self, job_id, fields,
472 793a8f7c Michael Hanselmann
                           prev_job_info, prev_log_serial,
473 793a8f7c Michael Hanselmann
                           timeout=WFJC_TIMEOUT):
474 793a8f7c Michael Hanselmann
    """Waits for changes on a job.
475 793a8f7c Michael Hanselmann

476 793a8f7c Michael Hanselmann
    @param job_id: Job ID
477 793a8f7c Michael Hanselmann
    @type fields: list
478 793a8f7c Michael Hanselmann
    @param fields: List of field names to be observed
479 793a8f7c Michael Hanselmann
    @type prev_job_info: None or list
480 793a8f7c Michael Hanselmann
    @param prev_job_info: Previously received job information
481 793a8f7c Michael Hanselmann
    @type prev_log_serial: None or int/long
482 793a8f7c Michael Hanselmann
    @param prev_log_serial: Highest log serial number previously received
483 793a8f7c Michael Hanselmann
    @type timeout: int/float
484 793a8f7c Michael Hanselmann
    @param timeout: Timeout in seconds (values larger than L{WFJC_TIMEOUT} will
485 793a8f7c Michael Hanselmann
                    be capped to that value)
486 793a8f7c Michael Hanselmann

487 793a8f7c Michael Hanselmann
    """
488 793a8f7c Michael Hanselmann
    assert timeout >= 0, "Timeout can not be negative"
489 f4484122 Michael Hanselmann
    return self.CallMethod(REQ_WAIT_FOR_JOB_CHANGE,
490 f4484122 Michael Hanselmann
                           (job_id, fields, prev_job_info,
491 793a8f7c Michael Hanselmann
                            prev_log_serial,
492 793a8f7c Michael Hanselmann
                            min(WFJC_TIMEOUT, timeout)))
493 f4484122 Michael Hanselmann
494 f4484122 Michael Hanselmann
  def WaitForJobChange(self, job_id, fields, prev_job_info, prev_log_serial):
495 5c735209 Iustin Pop
    while True:
496 f4484122 Michael Hanselmann
      result = self.WaitForJobChangeOnce(job_id, fields,
497 f4484122 Michael Hanselmann
                                         prev_job_info, prev_log_serial)
498 5c735209 Iustin Pop
      if result != constants.JOB_NOTCHANGED:
499 5c735209 Iustin Pop
        break
500 5c735209 Iustin Pop
    return result
501 dfe57c22 Michael Hanselmann
502 28b71a76 Michael Hanselmann
  def Query(self, what, fields, filter_):
503 28b71a76 Michael Hanselmann
    """Query for resources/items.
504 28b71a76 Michael Hanselmann

505 abd66bf8 Michael Hanselmann
    @param what: One of L{constants.QR_VIA_LUXI}
506 28b71a76 Michael Hanselmann
    @type fields: List of strings
507 28b71a76 Michael Hanselmann
    @param fields: List of requested fields
508 28b71a76 Michael Hanselmann
    @type filter_: None or list
509 28b71a76 Michael Hanselmann
    @param filter_: Query filter
510 28b71a76 Michael Hanselmann
    @rtype: L{objects.QueryResponse}
511 28b71a76 Michael Hanselmann

512 28b71a76 Michael Hanselmann
    """
513 28b71a76 Michael Hanselmann
    req = objects.QueryRequest(what=what, fields=fields, filter=filter_)
514 28b71a76 Michael Hanselmann
    result = self.CallMethod(REQ_QUERY, req.ToDict())
515 28b71a76 Michael Hanselmann
    return objects.QueryResponse.FromDict(result)
516 28b71a76 Michael Hanselmann
517 28b71a76 Michael Hanselmann
  def QueryFields(self, what, fields):
518 28b71a76 Michael Hanselmann
    """Query for available fields.
519 28b71a76 Michael Hanselmann

520 abd66bf8 Michael Hanselmann
    @param what: One of L{constants.QR_VIA_LUXI}
521 28b71a76 Michael Hanselmann
    @type fields: None or list of strings
522 28b71a76 Michael Hanselmann
    @param fields: List of requested fields
523 28b71a76 Michael Hanselmann
    @rtype: L{objects.QueryFieldsResponse}
524 28b71a76 Michael Hanselmann

525 28b71a76 Michael Hanselmann
    """
526 28b71a76 Michael Hanselmann
    req = objects.QueryFieldsRequest(what=what, fields=fields)
527 28b71a76 Michael Hanselmann
    result = self.CallMethod(REQ_QUERY_FIELDS, req.ToDict())
528 28b71a76 Michael Hanselmann
    return objects.QueryFieldsResponse.FromDict(result)
529 28b71a76 Michael Hanselmann
530 0bbe448c Michael Hanselmann
  def QueryJobs(self, job_ids, fields):
531 0bbe448c Michael Hanselmann
    return self.CallMethod(REQ_QUERY_JOBS, (job_ids, fields))
532 3d8548c4 Michael Hanselmann
533 ec79568d Iustin Pop
  def QueryInstances(self, names, fields, use_locking):
534 ec79568d Iustin Pop
    return self.CallMethod(REQ_QUERY_INSTANCES, (names, fields, use_locking))
535 ee6c7b94 Michael Hanselmann
536 ec79568d Iustin Pop
  def QueryNodes(self, names, fields, use_locking):
537 ec79568d Iustin Pop
    return self.CallMethod(REQ_QUERY_NODES, (names, fields, use_locking))
538 02f7fe54 Michael Hanselmann
539 a79ef2a5 Adeodato Simo
  def QueryGroups(self, names, fields, use_locking):
540 a79ef2a5 Adeodato Simo
    return self.CallMethod(REQ_QUERY_GROUPS, (names, fields, use_locking))
541 a79ef2a5 Adeodato Simo
542 ec79568d Iustin Pop
  def QueryExports(self, nodes, use_locking):
543 ec79568d Iustin Pop
    return self.CallMethod(REQ_QUERY_EXPORTS, (nodes, use_locking))
544 32f93223 Michael Hanselmann
545 66baeccc Iustin Pop
  def QueryClusterInfo(self):
546 66baeccc Iustin Pop
    return self.CallMethod(REQ_QUERY_CLUSTER_INFO, ())
547 66baeccc Iustin Pop
548 ae5849b5 Michael Hanselmann
  def QueryConfigValues(self, fields):
549 ae5849b5 Michael Hanselmann
    return self.CallMethod(REQ_QUERY_CONFIG_VALUES, fields)
550 ae5849b5 Michael Hanselmann
551 7699c3af Iustin Pop
  def QueryTags(self, kind, name):
552 7699c3af Iustin Pop
    return self.CallMethod(REQ_QUERY_TAGS, (kind, name))
553 19b9ba9a Michael Hanselmann
554 19b9ba9a Michael Hanselmann
  def QueryLocks(self, fields, sync):
555 24d16f76 Michael Hanselmann
    warnings.warn("This LUXI call is deprecated and will be removed, use"
556 24d16f76 Michael Hanselmann
                  " Query(\"%s\", ...) instead" % constants.QR_LOCK)
557 19b9ba9a Michael Hanselmann
    return self.CallMethod(REQ_QUERY_LOCKS, (fields, sync))