Statistics
| Branch: | Tag: | Revision:

root / lib / luxi.py @ 28b71a76

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

386 c2a03789 Iustin Pop

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

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

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

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

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

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

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

496 28b71a76 Michael Hanselmann
    @param what: One of L{constants.QR_OP_LUXI}
497 28b71a76 Michael Hanselmann
    @type fields: List of strings
498 28b71a76 Michael Hanselmann
    @param fields: List of requested fields
499 28b71a76 Michael Hanselmann
    @type filter_: None or list
500 28b71a76 Michael Hanselmann
    @param filter_: Query filter
501 28b71a76 Michael Hanselmann
    @rtype: L{objects.QueryResponse}
502 28b71a76 Michael Hanselmann

503 28b71a76 Michael Hanselmann
    """
504 28b71a76 Michael Hanselmann
    req = objects.QueryRequest(what=what, fields=fields, filter=filter_)
505 28b71a76 Michael Hanselmann
    result = self.CallMethod(REQ_QUERY, req.ToDict())
506 28b71a76 Michael Hanselmann
    return objects.QueryResponse.FromDict(result)
507 28b71a76 Michael Hanselmann
508 28b71a76 Michael Hanselmann
  def QueryFields(self, what, fields):
509 28b71a76 Michael Hanselmann
    """Query for available fields.
510 28b71a76 Michael Hanselmann

511 28b71a76 Michael Hanselmann
    @param what: One of L{constants.QR_OP_LUXI}
512 28b71a76 Michael Hanselmann
    @type fields: None or list of strings
513 28b71a76 Michael Hanselmann
    @param fields: List of requested fields
514 28b71a76 Michael Hanselmann
    @rtype: L{objects.QueryFieldsResponse}
515 28b71a76 Michael Hanselmann

516 28b71a76 Michael Hanselmann
    """
517 28b71a76 Michael Hanselmann
    req = objects.QueryFieldsRequest(what=what, fields=fields)
518 28b71a76 Michael Hanselmann
    result = self.CallMethod(REQ_QUERY_FIELDS, req.ToDict())
519 28b71a76 Michael Hanselmann
    return objects.QueryFieldsResponse.FromDict(result)
520 28b71a76 Michael Hanselmann
521 0bbe448c Michael Hanselmann
  def QueryJobs(self, job_ids, fields):
522 0bbe448c Michael Hanselmann
    return self.CallMethod(REQ_QUERY_JOBS, (job_ids, fields))
523 3d8548c4 Michael Hanselmann
524 ec79568d Iustin Pop
  def QueryInstances(self, names, fields, use_locking):
525 ec79568d Iustin Pop
    return self.CallMethod(REQ_QUERY_INSTANCES, (names, fields, use_locking))
526 ee6c7b94 Michael Hanselmann
527 ec79568d Iustin Pop
  def QueryNodes(self, names, fields, use_locking):
528 ec79568d Iustin Pop
    return self.CallMethod(REQ_QUERY_NODES, (names, fields, use_locking))
529 02f7fe54 Michael Hanselmann
530 a79ef2a5 Adeodato Simo
  def QueryGroups(self, names, fields, use_locking):
531 a79ef2a5 Adeodato Simo
    return self.CallMethod(REQ_QUERY_GROUPS, (names, fields, use_locking))
532 a79ef2a5 Adeodato Simo
533 ec79568d Iustin Pop
  def QueryExports(self, nodes, use_locking):
534 ec79568d Iustin Pop
    return self.CallMethod(REQ_QUERY_EXPORTS, (nodes, use_locking))
535 32f93223 Michael Hanselmann
536 66baeccc Iustin Pop
  def QueryClusterInfo(self):
537 66baeccc Iustin Pop
    return self.CallMethod(REQ_QUERY_CLUSTER_INFO, ())
538 66baeccc Iustin Pop
539 ae5849b5 Michael Hanselmann
  def QueryConfigValues(self, fields):
540 ae5849b5 Michael Hanselmann
    return self.CallMethod(REQ_QUERY_CONFIG_VALUES, fields)
541 ae5849b5 Michael Hanselmann
542 7699c3af Iustin Pop
  def QueryTags(self, kind, name):
543 7699c3af Iustin Pop
    return self.CallMethod(REQ_QUERY_TAGS, (kind, name))
544 19b9ba9a Michael Hanselmann
545 19b9ba9a Michael Hanselmann
  def QueryLocks(self, fields, sync):
546 19b9ba9a Michael Hanselmann
    return self.CallMethod(REQ_QUERY_LOCKS, (fields, sync))