Statistics
| Branch: | Tag: | Revision:

root / lib / luxi.py @ 0dbaa9ca

History | View | Annotate | Download (14.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 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 a79ef2a5 Adeodato Simo
REQ_QUERY_GROUPS = "QueryGroups"
60 32f93223 Michael Hanselmann
REQ_QUERY_EXPORTS = "QueryExports"
61 ae5849b5 Michael Hanselmann
REQ_QUERY_CONFIG_VALUES = "QueryConfigValues"
62 66baeccc Iustin Pop
REQ_QUERY_CLUSTER_INFO = "QueryClusterInfo"
63 7699c3af Iustin Pop
REQ_QUERY_TAGS = "QueryTags"
64 19b9ba9a Michael Hanselmann
REQ_QUERY_LOCKS = "QueryLocks"
65 3ccafd0e Iustin Pop
REQ_QUEUE_SET_DRAIN_FLAG = "SetDrainFlag"
66 05e50653 Michael Hanselmann
REQ_SET_WATCHER_PAUSE = "SetWatcherPause"
67 c2a03789 Iustin Pop
68 c2a03789 Iustin Pop
DEF_CTMO = 10
69 c2a03789 Iustin Pop
DEF_RWTO = 60
70 c2a03789 Iustin Pop
71 793a8f7c Michael Hanselmann
# WaitForJobChange timeout
72 793a8f7c Michael Hanselmann
WFJC_TIMEOUT = (DEF_RWTO - 1) / 2
73 793a8f7c Michael Hanselmann
74 c2a03789 Iustin Pop
75 7a8bda3f Michael Hanselmann
class ProtocolError(errors.LuxiError):
76 5a1c22fe Iustin Pop
  """Denotes an error in the LUXI protocol."""
77 c2a03789 Iustin Pop
78 c2a03789 Iustin Pop
79 c2a03789 Iustin Pop
class ConnectionClosedError(ProtocolError):
80 5a1c22fe Iustin Pop
  """Connection closed error."""
81 c2a03789 Iustin Pop
82 c2a03789 Iustin Pop
83 c2a03789 Iustin Pop
class TimeoutError(ProtocolError):
84 5a1c22fe Iustin Pop
  """Operation timeout error."""
85 c2a03789 Iustin Pop
86 c2a03789 Iustin Pop
87 b77acb3e Iustin Pop
class RequestError(ProtocolError):
88 5a1c22fe Iustin Pop
  """Error on request.
89 b77acb3e Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

383 c2a03789 Iustin Pop

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

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

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

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

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

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

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