Statistics
| Branch: | Tag: | Revision:

root / lib / luxi.py @ 29e8788e

History | View | Annotate | Download (14.3 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 c2a03789 Iustin Pop
38 fad50141 Michael Hanselmann
from ganeti import serializer
39 ceab32dd Iustin Pop
from ganeti import constants
40 6797ec29 Iustin Pop
from ganeti import errors
41 cb462b06 Michael Hanselmann
from ganeti import utils
42 c2a03789 Iustin Pop
43 c2a03789 Iustin Pop
44 231db3a5 Michael Hanselmann
KEY_METHOD = "method"
45 231db3a5 Michael Hanselmann
KEY_ARGS = "args"
46 3d8548c4 Michael Hanselmann
KEY_SUCCESS = "success"
47 3d8548c4 Michael Hanselmann
KEY_RESULT = "result"
48 e986f20c Michael Hanselmann
KEY_VERSION = "version"
49 3d8548c4 Michael Hanselmann
50 0bbe448c Michael Hanselmann
REQ_SUBMIT_JOB = "SubmitJob"
51 2971c913 Iustin Pop
REQ_SUBMIT_MANY_JOBS = "SubmitManyJobs"
52 dfe57c22 Michael Hanselmann
REQ_WAIT_FOR_JOB_CHANGE = "WaitForJobChange"
53 0bbe448c Michael Hanselmann
REQ_CANCEL_JOB = "CancelJob"
54 0bbe448c Michael Hanselmann
REQ_ARCHIVE_JOB = "ArchiveJob"
55 07cd723a Iustin Pop
REQ_AUTOARCHIVE_JOBS = "AutoArchiveJobs"
56 0bbe448c Michael Hanselmann
REQ_QUERY_JOBS = "QueryJobs"
57 ee6c7b94 Michael Hanselmann
REQ_QUERY_INSTANCES = "QueryInstances"
58 02f7fe54 Michael Hanselmann
REQ_QUERY_NODES = "QueryNodes"
59 32f93223 Michael Hanselmann
REQ_QUERY_EXPORTS = "QueryExports"
60 ae5849b5 Michael Hanselmann
REQ_QUERY_CONFIG_VALUES = "QueryConfigValues"
61 66baeccc Iustin Pop
REQ_QUERY_CLUSTER_INFO = "QueryClusterInfo"
62 7699c3af Iustin Pop
REQ_QUERY_TAGS = "QueryTags"
63 19b9ba9a Michael Hanselmann
REQ_QUERY_LOCKS = "QueryLocks"
64 3ccafd0e Iustin Pop
REQ_QUEUE_SET_DRAIN_FLAG = "SetDrainFlag"
65 05e50653 Michael Hanselmann
REQ_SET_WATCHER_PAUSE = "SetWatcherPause"
66 c2a03789 Iustin Pop
67 c2a03789 Iustin Pop
DEF_CTMO = 10
68 c2a03789 Iustin Pop
DEF_RWTO = 60
69 c2a03789 Iustin Pop
70 793a8f7c Michael Hanselmann
# WaitForJobChange timeout
71 793a8f7c Michael Hanselmann
WFJC_TIMEOUT = (DEF_RWTO - 1) / 2
72 793a8f7c Michael Hanselmann
73 c2a03789 Iustin Pop
74 7a8bda3f Michael Hanselmann
class ProtocolError(errors.LuxiError):
75 5a1c22fe Iustin Pop
  """Denotes an error in the LUXI protocol."""
76 c2a03789 Iustin Pop
77 c2a03789 Iustin Pop
78 c2a03789 Iustin Pop
class ConnectionClosedError(ProtocolError):
79 5a1c22fe Iustin Pop
  """Connection closed error."""
80 c2a03789 Iustin Pop
81 c2a03789 Iustin Pop
82 c2a03789 Iustin Pop
class TimeoutError(ProtocolError):
83 5a1c22fe Iustin Pop
  """Operation timeout error."""
84 c2a03789 Iustin Pop
85 c2a03789 Iustin Pop
86 b77acb3e Iustin Pop
class RequestError(ProtocolError):
87 5a1c22fe Iustin Pop
  """Error on request.
88 b77acb3e Iustin Pop

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

92 b77acb3e Iustin Pop
  Some common conditions that can trigger this exception:
93 b77acb3e Iustin Pop
    - job submission failed because the job data was wrong
94 b77acb3e Iustin Pop
    - query failed because required fields were missing
95 b77acb3e Iustin Pop

96 b77acb3e Iustin Pop
  """
97 b77acb3e Iustin Pop
98 3d8548c4 Michael Hanselmann
99 03a8dbdc Iustin Pop
class NoMasterError(ProtocolError):
100 5a1c22fe Iustin Pop
  """The master cannot be reached.
101 03a8dbdc Iustin Pop

102 03a8dbdc Iustin Pop
  This means that the master daemon is not running or the socket has
103 03a8dbdc Iustin Pop
  been removed.
104 03a8dbdc Iustin Pop

105 03a8dbdc Iustin Pop
  """
106 03a8dbdc Iustin Pop
107 b77acb3e Iustin Pop
108 5a1c22fe Iustin Pop
class PermissionError(ProtocolError):
109 5a1c22fe Iustin Pop
  """Permission denied while connecting to the master socket.
110 5a1c22fe Iustin Pop

111 5a1c22fe Iustin Pop
  This means the user doesn't have the proper rights.
112 5a1c22fe Iustin Pop

113 5a1c22fe Iustin Pop
  """
114 5a1c22fe Iustin Pop
115 5a1c22fe Iustin Pop
116 c2a03789 Iustin Pop
class Transport:
117 c2a03789 Iustin Pop
  """Low-level transport class.
118 c2a03789 Iustin Pop

119 c2a03789 Iustin Pop
  This is used on the client side.
120 c2a03789 Iustin Pop

121 c2a03789 Iustin Pop
  This could be replace by any other class that provides the same
122 c2a03789 Iustin Pop
  semantics to the Client. This means:
123 c2a03789 Iustin Pop
    - can send messages and receive messages
124 c2a03789 Iustin Pop
    - safe for multithreading
125 c2a03789 Iustin Pop

126 c2a03789 Iustin Pop
  """
127 c2a03789 Iustin Pop
128 25942a6c Guido Trotter
  def __init__(self, address, timeouts=None):
129 c2a03789 Iustin Pop
    """Constructor for the Client class.
130 c2a03789 Iustin Pop

131 c2a03789 Iustin Pop
    Arguments:
132 c2a03789 Iustin Pop
      - address: a valid address the the used transport class
133 c2a03789 Iustin Pop
      - timeout: a list of timeouts, to be used on connect and read/write
134 c2a03789 Iustin Pop

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

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

140 c2a03789 Iustin Pop
    Note that on reading data, since the timeout applies to an
141 c2a03789 Iustin Pop
    invidual receive, it might be that the total duration is longer
142 c2a03789 Iustin Pop
    than timeout value passed (we make a hard limit at twice the read
143 c2a03789 Iustin Pop
    timeout).
144 c2a03789 Iustin Pop

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

194 c2a03789 Iustin Pop
    """
195 c2a03789 Iustin Pop
    if self.socket is None:
196 c2a03789 Iustin Pop
      raise ProtocolError("Connection is closed")
197 c2a03789 Iustin Pop
198 c2a03789 Iustin Pop
  def Send(self, msg):
199 c2a03789 Iustin Pop
    """Send a message.
200 c2a03789 Iustin Pop

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

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

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

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

248 c2a03789 Iustin Pop
    This is just a wrapper over Send and Recv.
249 c2a03789 Iustin Pop

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

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

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

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

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

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

370 c2a03789 Iustin Pop
  This uses a backing Transport-like class on top of which it
371 c2a03789 Iustin Pop
  implements data serialization/deserialization.
372 c2a03789 Iustin Pop

373 c2a03789 Iustin Pop
  """
374 ceab32dd Iustin Pop
  def __init__(self, address=None, timeouts=None, transport=Transport):
375 c2a03789 Iustin Pop
    """Constructor for the Client class.
376 c2a03789 Iustin Pop

377 c2a03789 Iustin Pop
    Arguments:
378 c2a03789 Iustin Pop
      - address: a valid address the the used transport class
379 c2a03789 Iustin Pop
      - timeout: a list of timeouts, to be used on connect and read/write
380 c2a03789 Iustin Pop
      - transport: a Transport-like class
381 c2a03789 Iustin Pop

382 c2a03789 Iustin Pop

383 c2a03789 Iustin Pop
    If timeout is not passed, the default timeouts of the transport
384 c2a03789 Iustin Pop
    class are used.
385 c2a03789 Iustin Pop

386 c2a03789 Iustin Pop
    """
387 ceab32dd Iustin Pop
    if address is None:
388 ceab32dd Iustin Pop
      address = constants.MASTER_SOCKET
389 8d5b316c Iustin Pop
    self.address = address
390 8d5b316c Iustin Pop
    self.timeouts = timeouts
391 8d5b316c Iustin Pop
    self.transport_class = transport
392 8d5b316c Iustin Pop
    self.transport = None
393 8d5b316c Iustin Pop
    self._InitTransport()
394 8d5b316c Iustin Pop
395 8d5b316c Iustin Pop
  def _InitTransport(self):
396 8d5b316c Iustin Pop
    """(Re)initialize the transport if needed.
397 8d5b316c Iustin Pop

398 8d5b316c Iustin Pop
    """
399 8d5b316c Iustin Pop
    if self.transport is None:
400 8d5b316c Iustin Pop
      self.transport = self.transport_class(self.address,
401 8d5b316c Iustin Pop
                                            timeouts=self.timeouts)
402 8d5b316c Iustin Pop
403 8d5b316c Iustin Pop
  def _CloseTransport(self):
404 8d5b316c Iustin Pop
    """Close the transport, ignoring errors.
405 8d5b316c Iustin Pop

406 8d5b316c Iustin Pop
    """
407 8d5b316c Iustin Pop
    if self.transport is None:
408 8d5b316c Iustin Pop
      return
409 8d5b316c Iustin Pop
    try:
410 8d5b316c Iustin Pop
      old_transp = self.transport
411 8d5b316c Iustin Pop
      self.transport = None
412 8d5b316c Iustin Pop
      old_transp.Close()
413 7260cfbe Iustin Pop
    except Exception: # pylint: disable-msg=W0703
414 8d5b316c Iustin Pop
      pass
415 c2a03789 Iustin Pop
416 231db3a5 Michael Hanselmann
  def _SendMethodCall(self, data):
417 3d8548c4 Michael Hanselmann
    # Send request and wait for response
418 8d5b316c Iustin Pop
    try:
419 8d5b316c Iustin Pop
      self._InitTransport()
420 231db3a5 Michael Hanselmann
      return self.transport.Call(data)
421 8d5b316c Iustin Pop
    except Exception:
422 8d5b316c Iustin Pop
      self._CloseTransport()
423 8d5b316c Iustin Pop
      raise
424 8d5b316c Iustin Pop
425 231db3a5 Michael Hanselmann
  def CallMethod(self, method, args):
426 231db3a5 Michael Hanselmann
    """Send a generic request and return the response.
427 3d8548c4 Michael Hanselmann

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

463 793a8f7c Michael Hanselmann
    @param job_id: Job ID
464 793a8f7c Michael Hanselmann
    @type fields: list
465 793a8f7c Michael Hanselmann
    @param fields: List of field names to be observed
466 793a8f7c Michael Hanselmann
    @type prev_job_info: None or list
467 793a8f7c Michael Hanselmann
    @param prev_job_info: Previously received job information
468 793a8f7c Michael Hanselmann
    @type prev_log_serial: None or int/long
469 793a8f7c Michael Hanselmann
    @param prev_log_serial: Highest log serial number previously received
470 793a8f7c Michael Hanselmann
    @type timeout: int/float
471 793a8f7c Michael Hanselmann
    @param timeout: Timeout in seconds (values larger than L{WFJC_TIMEOUT} will
472 793a8f7c Michael Hanselmann
                    be capped to that value)
473 793a8f7c Michael Hanselmann

474 793a8f7c Michael Hanselmann
    """
475 793a8f7c Michael Hanselmann
    assert timeout >= 0, "Timeout can not be negative"
476 f4484122 Michael Hanselmann
    return self.CallMethod(REQ_WAIT_FOR_JOB_CHANGE,
477 f4484122 Michael Hanselmann
                           (job_id, fields, prev_job_info,
478 793a8f7c Michael Hanselmann
                            prev_log_serial,
479 793a8f7c Michael Hanselmann
                            min(WFJC_TIMEOUT, timeout)))
480 f4484122 Michael Hanselmann
481 f4484122 Michael Hanselmann
  def WaitForJobChange(self, job_id, fields, prev_job_info, prev_log_serial):
482 5c735209 Iustin Pop
    while True:
483 f4484122 Michael Hanselmann
      result = self.WaitForJobChangeOnce(job_id, fields,
484 f4484122 Michael Hanselmann
                                         prev_job_info, prev_log_serial)
485 5c735209 Iustin Pop
      if result != constants.JOB_NOTCHANGED:
486 5c735209 Iustin Pop
        break
487 5c735209 Iustin Pop
    return result
488 dfe57c22 Michael Hanselmann
489 0bbe448c Michael Hanselmann
  def QueryJobs(self, job_ids, fields):
490 0bbe448c Michael Hanselmann
    return self.CallMethod(REQ_QUERY_JOBS, (job_ids, fields))
491 3d8548c4 Michael Hanselmann
492 ec79568d Iustin Pop
  def QueryInstances(self, names, fields, use_locking):
493 ec79568d Iustin Pop
    return self.CallMethod(REQ_QUERY_INSTANCES, (names, fields, use_locking))
494 ee6c7b94 Michael Hanselmann
495 ec79568d Iustin Pop
  def QueryNodes(self, names, fields, use_locking):
496 ec79568d Iustin Pop
    return self.CallMethod(REQ_QUERY_NODES, (names, fields, use_locking))
497 02f7fe54 Michael Hanselmann
498 ec79568d Iustin Pop
  def QueryExports(self, nodes, use_locking):
499 ec79568d Iustin Pop
    return self.CallMethod(REQ_QUERY_EXPORTS, (nodes, use_locking))
500 32f93223 Michael Hanselmann
501 66baeccc Iustin Pop
  def QueryClusterInfo(self):
502 66baeccc Iustin Pop
    return self.CallMethod(REQ_QUERY_CLUSTER_INFO, ())
503 66baeccc Iustin Pop
504 ae5849b5 Michael Hanselmann
  def QueryConfigValues(self, fields):
505 ae5849b5 Michael Hanselmann
    return self.CallMethod(REQ_QUERY_CONFIG_VALUES, fields)
506 ae5849b5 Michael Hanselmann
507 7699c3af Iustin Pop
  def QueryTags(self, kind, name):
508 7699c3af Iustin Pop
    return self.CallMethod(REQ_QUERY_TAGS, (kind, name))
509 19b9ba9a Michael Hanselmann
510 19b9ba9a Michael Hanselmann
  def QueryLocks(self, fields, sync):
511 19b9ba9a Michael Hanselmann
    return self.CallMethod(REQ_QUERY_LOCKS, (fields, sync))