Statistics
| Branch: | Tag: | Revision:

root / lib / luxi.py @ 82599b3e

History | View | Annotate | Download (15.7 kB)

1 c2a03789 Iustin Pop
#
2 c2a03789 Iustin Pop
#
3 c2a03789 Iustin Pop
4 c2a03789 Iustin Pop
# Copyright (C) 2006, 2007 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 e7a25b08 Guido Trotter
  method = request.get(KEY_METHOD, None) # pylint: disable-msg=E1103
282 e7a25b08 Guido Trotter
  args = request.get(KEY_ARGS, None) # pylint: disable-msg=E1103
283 2317945a Guido Trotter
  version = request.get(KEY_VERSION, None) # pylint: disable-msg=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 231db3a5 Michael Hanselmann
  except Exception, err:
301 231db3a5 Michael Hanselmann
    raise ProtocolError("Error while deserializing response: %s" % str(err))
302 231db3a5 Michael Hanselmann
303 231db3a5 Michael Hanselmann
  # Validate response
304 231db3a5 Michael Hanselmann
  if not (isinstance(data, dict) and
305 231db3a5 Michael Hanselmann
          KEY_SUCCESS in data and
306 231db3a5 Michael Hanselmann
          KEY_RESULT in data):
307 231db3a5 Michael Hanselmann
    raise ProtocolError("Invalid response from server: %r" % data)
308 231db3a5 Michael Hanselmann
309 2317945a Guido Trotter
  return (data[KEY_SUCCESS], data[KEY_RESULT],
310 2317945a Guido Trotter
          data.get(KEY_VERSION, None)) # pylint: disable-msg=E1103
311 231db3a5 Michael Hanselmann
312 231db3a5 Michael Hanselmann
313 e986f20c Michael Hanselmann
def FormatResponse(success, result, version=None):
314 231db3a5 Michael Hanselmann
  """Formats a LUXI response message.
315 231db3a5 Michael Hanselmann

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

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

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

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

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

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

387 c2a03789 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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