rpc.py 47.1 KB
Newer Older
Iustin Pop's avatar
Iustin Pop committed
1
#
Iustin Pop's avatar
Iustin Pop committed
2
3
#

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


22
"""Inter-node RPC library.
Iustin Pop's avatar
Iustin Pop committed
23
24
25

"""

Iustin Pop's avatar
Iustin Pop committed
26
27
28
29
30
31
# pylint: disable-msg=C0103,R0201,R0904
# C0103: Invalid name, since call_ are not valid
# R0201: Method could be a function, we keep all rpcs instance methods
# as not to change them back and forth between static/instance methods
# if they need to start using instance attributes
# R0904: Too many public methods
Iustin Pop's avatar
Iustin Pop committed
32
33

import os
34
import logging
35
36
import zlib
import base64
37
38
import pycurl
import threading
Iustin Pop's avatar
Iustin Pop committed
39
40
41

from ganeti import utils
from ganeti import objects
42
from ganeti import http
43
from ganeti import serializer
44
from ganeti import constants
45
from ganeti import errors
46
from ganeti import netutils
47
from ganeti import ssconf
48
from ganeti import runtime
Iustin Pop's avatar
Iustin Pop committed
49

Iustin Pop's avatar
Iustin Pop committed
50
51
# pylint has a bug here, doesn't see this import
import ganeti.http.client  # pylint: disable-msg=W0611
52

Iustin Pop's avatar
Iustin Pop committed
53

54
55
56
57
58
# Timeout for connecting to nodes (seconds)
_RPC_CONNECT_TIMEOUT = 5

_RPC_CLIENT_HEADERS = [
  "Content-type: %s" % http.HTTP_APP_JSON,
Iustin Pop's avatar
Iustin Pop committed
59
  "Expect:",
60
  ]
61

62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# Various time constants for the timeout table
_TMO_URGENT = 60 # one minute
_TMO_FAST = 5 * 60 # five minutes
_TMO_NORMAL = 15 * 60 # 15 minutes
_TMO_SLOW = 3600 # one hour
_TMO_4HRS = 4 * 3600
_TMO_1DAY = 86400

# Timeout table that will be built later by decorators
# Guidelines for choosing timeouts:
# - call used during watcher: timeout -> 1min, _TMO_URGENT
# - trivial (but be sure it is trivial) (e.g. reading a file): 5min, _TMO_FAST
# - other calls: 15 min, _TMO_NORMAL
# - special calls (instance add, etc.): either _TMO_SLOW (1h) or huge timeouts

_TIMEOUTS = {
}

80
81
82
83

def Init():
  """Initializes the module-global HTTP client manager.

84
85
  Must be called before using any RPC function and while exactly one thread is
  running.
86
87

  """
88
89
90
91
92
  # curl_global_init(3) and curl_global_cleanup(3) must be called with only
  # one thread running. This check is just a safety measure -- it doesn't
  # cover all cases.
  assert threading.activeCount() == 1, \
         "Found more than one active thread when initializing pycURL"
93

94
  logging.info("Using PycURL %s", pycurl.version)
95

96
  pycurl.global_init(pycurl.GLOBAL_ALL)
97
98
99
100
101


def Shutdown():
  """Stops the module-global HTTP client manager.

102
103
  Must be called before quitting the program and while exactly one thread is
  running.
104
105

  """
106
107
108
109
110
  pycurl.global_cleanup()


def _ConfigRpcCurl(curl):
  noded_cert = str(constants.NODED_CERT_FILE)
111

112
113
114
115
116
117
118
119
120
121
122
  curl.setopt(pycurl.FOLLOWLOCATION, False)
  curl.setopt(pycurl.CAINFO, noded_cert)
  curl.setopt(pycurl.SSL_VERIFYHOST, 0)
  curl.setopt(pycurl.SSL_VERIFYPEER, True)
  curl.setopt(pycurl.SSLCERTTYPE, "PEM")
  curl.setopt(pycurl.SSLCERT, noded_cert)
  curl.setopt(pycurl.SSLKEYTYPE, "PEM")
  curl.setopt(pycurl.SSLKEY, noded_cert)
  curl.setopt(pycurl.CONNECTTIMEOUT, _RPC_CONNECT_TIMEOUT)


123
124
125
126
127
128
# Aliasing this module avoids the following warning by epydoc: "Warning: No
# information available for ganeti.rpc._RpcThreadLocal's base threading.local"
_threading = threading


class _RpcThreadLocal(_threading.local):
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
  def GetHttpClientPool(self):
    """Returns a per-thread HTTP client pool.

    @rtype: L{http.client.HttpClientPool}

    """
    try:
      pool = self.hcp
    except AttributeError:
      pool = http.client.HttpClientPool(_ConfigRpcCurl)
      self.hcp = pool

    return pool


144
145
146
147
# Remove module alias (see above)
del _threading


148
_thread_local = _RpcThreadLocal()
149
150


151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
def _RpcTimeout(secs):
  """Timeout decorator.

  When applied to a rpc call_* function, it updates the global timeout
  table with the given function/timeout.

  """
  def decorator(f):
    name = f.__name__
    assert name.startswith("call_")
    _TIMEOUTS[name[len("call_"):]] = secs
    return f
  return decorator


166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
def RunWithRPC(fn):
  """RPC-wrapper decorator.

  When applied to a function, it runs it with the RPC system
  initialized, and it shutsdown the system afterwards. This means the
  function must be called without RPC being initialized.

  """
  def wrapper(*args, **kwargs):
    Init()
    try:
      return fn(*args, **kwargs)
    finally:
      Shutdown()
  return wrapper


183
184
185
186
187
188
189
class RpcResult(object):
  """RPC Result class.

  This class holds an RPC result. It is needed since in multi-node
  calls we can't raise an exception just because one one out of many
  failed, and therefore we use this class to encapsulate the result.

Michael Hanselmann's avatar
Michael Hanselmann committed
190
  @ivar data: the data payload, for successful results, or None
191
192
193
194
195
196
  @ivar call: the name of the RPC call
  @ivar node: the name of the node to which we made the call
  @ivar offline: whether the operation failed because the node was
      offline, as opposed to actual failure; offline=True will always
      imply failed=True, in order to allow simpler checking if
      the user doesn't care about the exact failure mode
197
  @ivar fail_msg: the error message if the call failed
198

199
  """
200
201
202
203
204
  def __init__(self, data=None, failed=False, offline=False,
               call=None, node=None):
    self.offline = offline
    self.call = call
    self.node = node
205

206
    if offline:
207
      self.fail_msg = "Node is marked offline"
208
      self.data = self.payload = None
209
    elif failed:
210
      self.fail_msg = self._EnsureErr(data)
211
      self.data = self.payload = None
212
213
    else:
      self.data = data
214
      if not isinstance(self.data, (tuple, list)):
215
216
        self.fail_msg = ("RPC layer error: invalid result type (%s)" %
                         type(self.data))
217
        self.payload = None
218
      elif len(data) != 2:
219
220
        self.fail_msg = ("RPC layer error: invalid result length (%d), "
                         "expected 2" % len(self.data))
221
        self.payload = None
222
      elif not self.data[0]:
223
        self.fail_msg = self._EnsureErr(self.data[1])
224
        self.payload = None
225
      else:
226
        # finally success
227
        self.fail_msg = None
228
229
        self.payload = data[1]

Iustin Pop's avatar
Iustin Pop committed
230
231
232
    for attr_name in ["call", "data", "fail_msg",
                      "node", "offline", "payload"]:
      assert hasattr(self, attr_name), "Missing attribute %s" % attr_name
233

234
235
236
237
238
239
240
  @staticmethod
  def _EnsureErr(val):
    """Helper to ensure we return a 'True' value for error."""
    if val:
      return val
    else:
      return "No error information"
241

242
  def Raise(self, msg, prereq=False, ecode=None):
243
244
245
246
247
248
    """If the result has failed, raise an OpExecError.

    This is used so that LU code doesn't have to check for each
    result, but instead can call this function.

    """
249
250
251
252
253
254
255
256
257
258
259
260
    if not self.fail_msg:
      return

    if not msg: # one could pass None for default message
      msg = ("Call '%s' to node '%s' has failed: %s" %
             (self.call, self.node, self.fail_msg))
    else:
      msg = "%s: %s" % (msg, self.fail_msg)
    if prereq:
      ec = errors.OpPrereqError
    else:
      ec = errors.OpExecError
261
    if ecode is not None:
Iustin Pop's avatar
Iustin Pop committed
262
      args = (msg, ecode)
263
264
    else:
      args = (msg, )
Iustin Pop's avatar
Iustin Pop committed
265
    raise ec(*args) # pylint: disable-msg=W0142
266
267


268
269
def _AddressLookup(node_list,
                   ssc=ssconf.SimpleStore,
270
                   nslookup_fn=netutils.Hostname.GetIP):
271
272
273
274
275
276
  """Return addresses for given node names.

  @type node_list: list
  @param node_list: List of node names
  @type ssc: class
  @param ssc: SimpleStore class that is used to obtain node->ip mappings
277
278
  @type nslookup_fn: callable
  @param nslookup_fn: function use to do NS lookup
279
280
281
282
  @rtype: list of addresses and/or None's
  @returns: List of corresponding addresses, if found

  """
Manuel Franceschini's avatar
Manuel Franceschini committed
283
284
285
  ss = ssc()
  iplist = ss.GetNodePrimaryIPList()
  family = ss.GetPrimaryIPFamily()
286
  addresses = []
287
288
289
290
  ipmap = dict(entry.split() for entry in iplist)
  for node in node_list:
    address = ipmap.get(node)
    if address is None:
Manuel Franceschini's avatar
Manuel Franceschini committed
291
      address = nslookup_fn(node, family=family)
292
    addresses.append(address)
293
294
295
296

  return addresses


Iustin Pop's avatar
Iustin Pop committed
297
298
299
class Client:
  """RPC Client class.

Alexander Schreiber's avatar
Alexander Schreiber committed
300
  This class, given a (remote) method name, a list of parameters and a
Iustin Pop's avatar
Iustin Pop committed
301
302
303
  list of nodes, will contact (in parallel) all nodes, and return a
  dict of results (key: node name, value: result).

Michael Hanselmann's avatar
Michael Hanselmann committed
304
  One current bug is that generic failure is still signaled by
Iustin Pop's avatar
Iustin Pop committed
305
306
307
308
  'False' result, which is not good. This overloading of values can
  cause bugs.

  """
309
  def __init__(self, procedure, body, port, address_lookup_fn=_AddressLookup):
310
311
    assert procedure in _TIMEOUTS, ("New RPC call not declared in the"
                                    " timeouts table")
Iustin Pop's avatar
Iustin Pop committed
312
    self.procedure = procedure
313
314
    self.body = body
    self.port = port
315
    self._request = {}
316
    self._address_lookup_fn = address_lookup_fn
317

318
  def ConnectList(self, node_list, address_list=None, read_timeout=None):
Iustin Pop's avatar
Iustin Pop committed
319
320
    """Add a list of nodes to the target nodes.

321
322
    @type node_list: list
    @param node_list: the list of node names to connect
323
324
325
    @type address_list: list or None
    @keyword address_list: either None or a list with node addresses,
        which must have the same length as the node list
326
    @type read_timeout: int
327
    @param read_timeout: overwrites default timeout for operation
328

Iustin Pop's avatar
Iustin Pop committed
329
    """
330
    if address_list is None:
331
332
333
334
335
336
      # Always use IP address instead of node name
      address_list = self._address_lookup_fn(node_list)

    assert len(node_list) == len(address_list), \
           "Name and address lists must have the same length"

337
    for node, address in zip(node_list, address_list):
338
      self.ConnectNode(node, address, read_timeout=read_timeout)
339

340
  def ConnectNode(self, name, address=None, read_timeout=None):
Iustin Pop's avatar
Iustin Pop committed
341
342
    """Add a node to the target list.

343
344
345
    @type name: str
    @param name: the node name
    @type address: str
346
347
348
    @param address: the node address, if known
    @type read_timeout: int
    @param read_timeout: overwrites default timeout for operation
349

Iustin Pop's avatar
Iustin Pop committed
350
    """
351
    if address is None:
352
353
354
355
      # Always use IP address instead of node name
      address = self._address_lookup_fn([name])[0]

    assert(address is not None)
356

357
358
359
    if read_timeout is None:
      read_timeout = _TIMEOUTS[self.procedure]

360
361
362
363
364
    self._request[name] = \
      http.client.HttpClientRequest(str(address), self.port,
                                    http.HTTP_PUT, str("/%s" % self.procedure),
                                    headers=_RPC_CLIENT_HEADERS,
                                    post_data=str(self.body),
365
                                    read_timeout=read_timeout)
Iustin Pop's avatar
Iustin Pop committed
366

367
  def GetResults(self, http_pool=None):
368
369
370
    """Call nodes and return results.

    @rtype: list
Iustin Pop's avatar
Iustin Pop committed
371
    @return: List of RPC results
Iustin Pop's avatar
Iustin Pop committed
372
373

    """
374
375
    if not http_pool:
      http_pool = _thread_local.GetHttpClientPool()
376

377
    http_pool.ProcessRequests(self._request.values())
Iustin Pop's avatar
Iustin Pop committed
378

379
    results = {}
Iustin Pop's avatar
Iustin Pop committed
380

381
    for name, req in self._request.iteritems():
382
      if req.success and req.resp_status_code == http.HTTP_OK:
383
384
        results[name] = RpcResult(data=serializer.LoadJson(req.resp_body),
                                  node=name, call=self.procedure)
385
        continue
Iustin Pop's avatar
Iustin Pop committed
386

387
      # TODO: Better error reporting
388
389
390
391
392
      if req.error:
        msg = req.error
      else:
        msg = req.resp_body

393
394
      logging.error("RPC error in %s from node %s: %s",
                    self.procedure, name, msg)
395
396
      results[name] = RpcResult(data=msg, failed=True, node=name,
                                call=self.procedure)
397
398

    return results
Iustin Pop's avatar
Iustin Pop committed
399
400


401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
def _EncodeImportExportIO(ieio, ieioargs):
  """Encodes import/export I/O information.

  """
  if ieio == constants.IEIO_RAW_DISK:
    assert len(ieioargs) == 1
    return (ieioargs[0].ToDict(), )

  if ieio == constants.IEIO_SCRIPT:
    assert len(ieioargs) == 2
    return (ieioargs[0].ToDict(), ieioargs[1])

  return ieioargs


Iustin Pop's avatar
Iustin Pop committed
416
417
class RpcRunner(object):
  """RPC runner class"""
Iustin Pop's avatar
Iustin Pop committed
418

Iustin Pop's avatar
Iustin Pop committed
419
420
  def __init__(self, cfg):
    """Initialized the rpc runner.
Iustin Pop's avatar
Iustin Pop committed
421

Iustin Pop's avatar
Iustin Pop committed
422
423
424
    @type cfg:  C{config.ConfigWriter}
    @param cfg: the configuration object that will be used to get data
                about the cluster
Iustin Pop's avatar
Iustin Pop committed
425

Iustin Pop's avatar
Iustin Pop committed
426
427
    """
    self._cfg = cfg
428
    self.port = netutils.GetDaemonPort(constants.NODED)
Iustin Pop's avatar
Iustin Pop committed
429

430
  def _InstDict(self, instance, hvp=None, bep=None, osp=None):
431
432
433
434
435
436
437
    """Convert the given instance to a dict.

    This is done via the instance's ToDict() method and additionally
    we fill the hvparams with the cluster defaults.

    @type instance: L{objects.Instance}
    @param instance: an Instance object
438
    @type hvp: dict or None
Michael Hanselmann's avatar
Michael Hanselmann committed
439
    @param hvp: a dictionary with overridden hypervisor parameters
440
    @type bep: dict or None
Michael Hanselmann's avatar
Michael Hanselmann committed
441
    @param bep: a dictionary with overridden backend parameters
442
    @type osp: dict or None
443
    @param osp: a dictionary with overridden os parameters
444
445
446
447
448
449
    @rtype: dict
    @return: the instance dict, with the hvparams filled with the
        cluster defaults

    """
    idict = instance.ToDict()
450
451
    cluster = self._cfg.GetClusterInfo()
    idict["hvparams"] = cluster.FillHV(instance)
452
453
    if hvp is not None:
      idict["hvparams"].update(hvp)
454
    idict["beparams"] = cluster.FillBE(instance)
455
456
    if bep is not None:
      idict["beparams"].update(bep)
457
458
459
    idict["osparams"] = cluster.SimpleFillOS(instance.os, instance.osparams)
    if osp is not None:
      idict["osparams"].update(osp)
460
461
462
463
    for nic in idict["nics"]:
      nic['nicparams'] = objects.FillDict(
        cluster.nicparams[constants.PP_DEFAULT],
        nic['nicparams'])
464
465
    return idict

466
  def _ConnectList(self, client, node_list, call, read_timeout=None):
467
468
    """Helper for computing node addresses.

Iustin Pop's avatar
Iustin Pop committed
469
    @type client: L{ganeti.rpc.Client}
470
471
472
    @param client: a C{Client} instance
    @type node_list: list
    @param node_list: the node list we should connect
473
474
475
    @type call: string
    @param call: the name of the remote procedure call, for filling in
        correctly any eventual offline nodes' results
476
477
478
    @type read_timeout: int
    @param read_timeout: overwrites the default read timeout for the
        given operation
479
480
481

    """
    all_nodes = self._cfg.GetAllNodesInfo()
482
    name_list = []
483
    addr_list = []
484
    skip_dict = {}
485
486
    for node in node_list:
      if node in all_nodes:
487
        if all_nodes[node].offline:
488
          skip_dict[node] = RpcResult(node=node, offline=True, call=call)
489
          continue
490
491
492
493
        val = all_nodes[node].primary_ip
      else:
        val = None
      addr_list.append(val)
494
495
      name_list.append(node)
    if name_list:
496
497
      client.ConnectList(name_list, address_list=addr_list,
                         read_timeout=read_timeout)
498
    return skip_dict
499

500
  def _ConnectNode(self, client, node, call, read_timeout=None):
501
502
    """Helper for computing one node's address.

Iustin Pop's avatar
Iustin Pop committed
503
    @type client: L{ganeti.rpc.Client}
504
505
506
    @param client: a C{Client} instance
    @type node: str
    @param node: the node we should connect
507
508
509
    @type call: string
    @param call: the name of the remote procedure call, for filling in
        correctly any eventual offline nodes' results
510
511
512
    @type read_timeout: int
    @param read_timeout: overwrites the default read timeout for the
        given operation
513
514
515
516

    """
    node_info = self._cfg.GetNodeInfo(node)
    if node_info is not None:
517
      if node_info.offline:
518
        return RpcResult(node=node, offline=True, call=call)
519
520
521
      addr = node_info.primary_ip
    else:
      addr = None
522
    client.ConnectNode(node, address=addr, read_timeout=read_timeout)
523

524
  def _MultiNodeCall(self, node_list, procedure, args, read_timeout=None):
525
526
527
528
529
    """Helper for making a multi-node call

    """
    body = serializer.DumpJson(args, indent=False)
    c = Client(procedure, body, self.port)
530
531
    skip_dict = self._ConnectList(c, node_list, procedure,
                                  read_timeout=read_timeout)
532
533
    skip_dict.update(c.GetResults())
    return skip_dict
534
535
536

  @classmethod
  def _StaticMultiNodeCall(cls, node_list, procedure, args,
537
                           address_list=None, read_timeout=None):
538
539
540
541
    """Helper for making a multi-node static call

    """
    body = serializer.DumpJson(args, indent=False)
542
    c = Client(procedure, body, netutils.GetDaemonPort(constants.NODED))
543
544
    c.ConnectList(node_list, address_list=address_list,
                  read_timeout=read_timeout)
545
546
    return c.GetResults()

547
  def _SingleNodeCall(self, node, procedure, args, read_timeout=None):
548
    """Helper for making a single-node call
549
550

    """
551
552
    body = serializer.DumpJson(args, indent=False)
    c = Client(procedure, body, self.port)
553
    result = self._ConnectNode(c, node, procedure, read_timeout=read_timeout)
554
555
556
557
    if result is None:
      # we did connect, node is not offline
      result = c.GetResults()[node]
    return result
558
559

  @classmethod
560
  def _StaticSingleNodeCall(cls, node, procedure, args, read_timeout=None):
561
    """Helper for making a single-node static call
562
563

    """
564
    body = serializer.DumpJson(args, indent=False)
565
    c = Client(procedure, body, netutils.GetDaemonPort(constants.NODED))
566
    c.ConnectNode(node, read_timeout=read_timeout)
567
    return c.GetResults()[node]
568

569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
  @staticmethod
  def _Compress(data):
    """Compresses a string for transport over RPC.

    Small amounts of data are not compressed.

    @type data: str
    @param data: Data
    @rtype: tuple
    @return: Encoded data to send

    """
    # Small amounts of data are not compressed
    if len(data) < 512:
      return (constants.RPC_ENCODING_NONE, data)

    # Compress with zlib and encode in base64
    return (constants.RPC_ENCODING_ZLIB_BASE64,
            base64.b64encode(zlib.compress(data, 3)))

589
590
591
592
  #
  # Begin RPC calls
  #

593
594
595
596
597
598
599
600
601
  @_RpcTimeout(_TMO_URGENT)
  def call_bdev_sizes(self, node_list, devices):
    """Gets the sizes of requested block devices present on a node

    This is a multi-node call.

    """
    return self._MultiNodeCall(node_list, "bdev_sizes", [devices])

602
  @_RpcTimeout(_TMO_URGENT)
603
  def call_lv_list(self, node_list, vg_name):
Iustin Pop's avatar
Iustin Pop committed
604
    """Gets the logical volumes present in a given volume group.
Iustin Pop's avatar
Iustin Pop committed
605

Iustin Pop's avatar
Iustin Pop committed
606
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
607

Iustin Pop's avatar
Iustin Pop committed
608
    """
609
    return self._MultiNodeCall(node_list, "lv_list", [vg_name])
Iustin Pop's avatar
Iustin Pop committed
610

611
  @_RpcTimeout(_TMO_URGENT)
Iustin Pop's avatar
Iustin Pop committed
612
613
  def call_vg_list(self, node_list):
    """Gets the volume group list.
Iustin Pop's avatar
Iustin Pop committed
614

Iustin Pop's avatar
Iustin Pop committed
615
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
616

Iustin Pop's avatar
Iustin Pop committed
617
    """
618
    return self._MultiNodeCall(node_list, "vg_list", [])
Iustin Pop's avatar
Iustin Pop committed
619

620
  @_RpcTimeout(_TMO_NORMAL)
621
  def call_storage_list(self, node_list, su_name, su_args, name, fields):
622
    """Get list of storage units.
623
624
625
626
627
628
629

    This is a multi-node call.

    """
    return self._MultiNodeCall(node_list, "storage_list",
                               [su_name, su_args, name, fields])

630
  @_RpcTimeout(_TMO_NORMAL)
631
632
633
634
635
636
637
638
639
  def call_storage_modify(self, node, su_name, su_args, name, changes):
    """Modify a storage unit.

    This is a single-node call.

    """
    return self._SingleNodeCall(node, "storage_modify",
                                [su_name, su_args, name, changes])

640
  @_RpcTimeout(_TMO_NORMAL)
641
642
643
644
645
646
647
648
649
  def call_storage_execute(self, node, su_name, su_args, name, op):
    """Executes an operation on a storage unit.

    This is a single-node call.

    """
    return self._SingleNodeCall(node, "storage_execute",
                                [su_name, su_args, name, op])

650
  @_RpcTimeout(_TMO_URGENT)
Iustin Pop's avatar
Iustin Pop committed
651
652
  def call_bridges_exist(self, node, bridges_list):
    """Checks if a node has all the bridges given.
Iustin Pop's avatar
Iustin Pop committed
653

Iustin Pop's avatar
Iustin Pop committed
654
655
656
    This method checks if all bridges given in the bridges_list are
    present on the remote node, so that an instance that uses interfaces
    on those bridges can be started.
Iustin Pop's avatar
Iustin Pop committed
657

Iustin Pop's avatar
Iustin Pop committed
658
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
659

Iustin Pop's avatar
Iustin Pop committed
660
    """
661
    return self._SingleNodeCall(node, "bridges_exist", [bridges_list])
Iustin Pop's avatar
Iustin Pop committed
662

663
  @_RpcTimeout(_TMO_NORMAL)
664
  def call_instance_start(self, node, instance, hvp, bep, startup_paused):
Iustin Pop's avatar
Iustin Pop committed
665
    """Starts an instance.
Iustin Pop's avatar
Iustin Pop committed
666

Iustin Pop's avatar
Iustin Pop committed
667
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
668

Iustin Pop's avatar
Iustin Pop committed
669
    """
670
    idict = self._InstDict(instance, hvp=hvp, bep=bep)
671
    return self._SingleNodeCall(node, "instance_start", [idict, startup_paused])
Iustin Pop's avatar
Iustin Pop committed
672

673
  @_RpcTimeout(_TMO_NORMAL)
674
  def call_instance_shutdown(self, node, instance, timeout):
Iustin Pop's avatar
Iustin Pop committed
675
    """Stops an instance.
Iustin Pop's avatar
Iustin Pop committed
676

Iustin Pop's avatar
Iustin Pop committed
677
    This is a single-node call.
678

Iustin Pop's avatar
Iustin Pop committed
679
    """
680
    return self._SingleNodeCall(node, "instance_shutdown",
681
                                [self._InstDict(instance), timeout])
682

683
  @_RpcTimeout(_TMO_NORMAL)
684
685
686
687
688
689
690
691
692
693
694
695
696
697
  def call_migration_info(self, node, instance):
    """Gather the information necessary to prepare an instance migration.

    This is a single-node call.

    @type node: string
    @param node: the node on which the instance is currently running
    @type instance: C{objects.Instance}
    @param instance: the instance definition

    """
    return self._SingleNodeCall(node, "migration_info",
                                [self._InstDict(instance)])

698
  @_RpcTimeout(_TMO_NORMAL)
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
  def call_accept_instance(self, node, instance, info, target):
    """Prepare a node to accept an instance.

    This is a single-node call.

    @type node: string
    @param node: the target node for the migration
    @type instance: C{objects.Instance}
    @param instance: the instance definition
    @type info: opaque/hypervisor specific (string/data)
    @param info: result for the call_migration_info call
    @type target: string
    @param target: target hostname (usually ip address) (on the node itself)

    """
    return self._SingleNodeCall(node, "accept_instance",
                                [self._InstDict(instance), info, target])

717
  @_RpcTimeout(_TMO_NORMAL)
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
  def call_finalize_migration(self, node, instance, info, success):
    """Finalize any target-node migration specific operation.

    This is called both in case of a successful migration and in case of error
    (in which case it should abort the migration).

    This is a single-node call.

    @type node: string
    @param node: the target node for the migration
    @type instance: C{objects.Instance}
    @param instance: the instance definition
    @type info: opaque/hypervisor specific (string/data)
    @param info: result for the call_migration_info call
    @type success: boolean
    @param success: whether the migration was a success or a failure

    """
    return self._SingleNodeCall(node, "finalize_migration",
                                [self._InstDict(instance), info, success])

739
  @_RpcTimeout(_TMO_SLOW)
Iustin Pop's avatar
Iustin Pop committed
740
741
  def call_instance_migrate(self, node, instance, target, live):
    """Migrate an instance.
742

Iustin Pop's avatar
Iustin Pop committed
743
    This is a single-node call.
744

Iustin Pop's avatar
Iustin Pop committed
745
746
747
748
749
750
751
752
753
    @type node: string
    @param node: the node on which the instance is currently running
    @type instance: C{objects.Instance}
    @param instance: the instance definition
    @type target: string
    @param target: the target node name
    @type live: boolean
    @param live: whether the migration should be done live or not (the
        interpretation of this parameter is left to the hypervisor)
754

Iustin Pop's avatar
Iustin Pop committed
755
    """
756
757
    return self._SingleNodeCall(node, "instance_migrate",
                                [self._InstDict(instance), target, live])
758

759
  @_RpcTimeout(_TMO_NORMAL)
760
  def call_instance_reboot(self, node, inst, reboot_type, shutdown_timeout):
Iustin Pop's avatar
Iustin Pop committed
761
    """Reboots an instance.
762

Iustin Pop's avatar
Iustin Pop committed
763
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
764

Iustin Pop's avatar
Iustin Pop committed
765
    """
766
    return self._SingleNodeCall(node, "instance_reboot",
767
768
                                [self._InstDict(inst), reboot_type,
                                 shutdown_timeout])
Iustin Pop's avatar
Iustin Pop committed
769

770
  @_RpcTimeout(_TMO_1DAY)
771
  def call_instance_os_add(self, node, inst, reinstall, debug, osparams=None):
Iustin Pop's avatar
Iustin Pop committed
772
    """Installs an OS on the given instance.
Iustin Pop's avatar
Iustin Pop committed
773

Iustin Pop's avatar
Iustin Pop committed
774
    This is a single-node call.
775

Iustin Pop's avatar
Iustin Pop committed
776
    """
777
    return self._SingleNodeCall(node, "instance_os_add",
778
779
                                [self._InstDict(inst, osp=osparams),
                                 reinstall, debug])
780

781
  @_RpcTimeout(_TMO_SLOW)
782
  def call_instance_run_rename(self, node, inst, old_name, debug):
Iustin Pop's avatar
Iustin Pop committed
783
    """Run the OS rename script for an instance.
784

Iustin Pop's avatar
Iustin Pop committed
785
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
786

Iustin Pop's avatar
Iustin Pop committed
787
    """
788
    return self._SingleNodeCall(node, "instance_run_rename",
789
                                [self._InstDict(inst), old_name, debug])
Iustin Pop's avatar
Iustin Pop committed
790

791
  @_RpcTimeout(_TMO_URGENT)
Iustin Pop's avatar
Iustin Pop committed
792
793
  def call_instance_info(self, node, instance, hname):
    """Returns information about a single instance.
Iustin Pop's avatar
Iustin Pop committed
794

Iustin Pop's avatar
Iustin Pop committed
795
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
796

797
798
    @type node: list
    @param node: the list of nodes to query
Iustin Pop's avatar
Iustin Pop committed
799
800
801
802
    @type instance: string
    @param instance: the instance name
    @type hname: string
    @param hname: the hypervisor type of the instance
Iustin Pop's avatar
Iustin Pop committed
803

Iustin Pop's avatar
Iustin Pop committed
804
    """
805
    return self._SingleNodeCall(node, "instance_info", [instance, hname])
806

807
  @_RpcTimeout(_TMO_NORMAL)
808
809
810
811
812
813
814
815
816
817
818
819
820
821
  def call_instance_migratable(self, node, instance):
    """Checks whether the given instance can be migrated.

    This is a single-node call.

    @param node: the node to query
    @type instance: L{objects.Instance}
    @param instance: the instance to check


    """
    return self._SingleNodeCall(node, "instance_migratable",
                                [self._InstDict(instance)])

822
  @_RpcTimeout(_TMO_URGENT)
Iustin Pop's avatar
Iustin Pop committed
823
824
  def call_all_instances_info(self, node_list, hypervisor_list):
    """Returns information about all instances on the given nodes.
Iustin Pop's avatar
Iustin Pop committed
825

Iustin Pop's avatar
Iustin Pop committed
826
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
827

Iustin Pop's avatar
Iustin Pop committed
828
829
830
831
    @type node_list: list
    @param node_list: the list of nodes to query
    @type hypervisor_list: list
    @param hypervisor_list: the hypervisors to query for instances
Iustin Pop's avatar
Iustin Pop committed
832

Iustin Pop's avatar
Iustin Pop committed
833
    """
834
835
    return self._MultiNodeCall(node_list, "all_instances_info",
                               [hypervisor_list])
836

837
  @_RpcTimeout(_TMO_URGENT)
Iustin Pop's avatar
Iustin Pop committed
838
839
  def call_instance_list(self, node_list, hypervisor_list):
    """Returns the list of running instances on a given node.
Iustin Pop's avatar
Iustin Pop committed
840

Iustin Pop's avatar
Iustin Pop committed
841
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
842

Iustin Pop's avatar
Iustin Pop committed
843
844
845
846
    @type node_list: list
    @param node_list: the list of nodes to query
    @type hypervisor_list: list
    @param hypervisor_list: the hypervisors to query for instances
847

Iustin Pop's avatar
Iustin Pop committed
848
    """
849
    return self._MultiNodeCall(node_list, "instance_list", [hypervisor_list])
850

851
  @_RpcTimeout(_TMO_FAST)
Iustin Pop's avatar
Iustin Pop committed
852
853
854
  def call_node_tcp_ping(self, node, source, target, port, timeout,
                         live_port_needed):
    """Do a TcpPing on the remote node
Iustin Pop's avatar
Iustin Pop committed
855

Iustin Pop's avatar
Iustin Pop committed
856
    This is a single-node call.
857

Iustin Pop's avatar
Iustin Pop committed
858
    """
859
860
    return self._SingleNodeCall(node, "node_tcp_ping",
                                [source, target, port, timeout,
Iustin Pop's avatar
Iustin Pop committed
861
                                 live_port_needed])
Iustin Pop's avatar
Iustin Pop committed
862

863
  @_RpcTimeout(_TMO_FAST)
864
865
866
867
868
869
  def call_node_has_ip_address(self, node, address):
    """Checks if a node has the given IP address.

    This is a single-node call.

    """
870
    return self._SingleNodeCall(node, "node_has_ip_address", [address])
Iustin Pop's avatar
Iustin Pop committed
871

872
  @_RpcTimeout(_TMO_URGENT)
Iustin Pop's avatar
Iustin Pop committed
873
874
  def call_node_info(self, node_list, vg_name, hypervisor_type):
    """Return node information.
875

Iustin Pop's avatar
Iustin Pop committed
876
877
    This will return memory information and volume group size and free
    space.
Iustin Pop's avatar
Iustin Pop committed
878

Iustin Pop's avatar
Iustin Pop committed
879
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
880

Iustin Pop's avatar
Iustin Pop committed
881
882
    @type node_list: list
    @param node_list: the list of nodes to query
Iustin Pop's avatar
Iustin Pop committed
883
884
    @type vg_name: C{string}
    @param vg_name: the name of the volume group to ask for disk space
Iustin Pop's avatar
Iustin Pop committed
885
886
887
888
        information
    @type hypervisor_type: C{str}
    @param hypervisor_type: the name of the hypervisor to ask for
        memory information
Iustin Pop's avatar
Iustin Pop committed
889

Iustin Pop's avatar
Iustin Pop committed
890
    """
891
892
    return self._MultiNodeCall(node_list, "node_info",
                               [vg_name, hypervisor_type])
Iustin Pop's avatar
Iustin Pop committed
893

894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
  @_RpcTimeout(_TMO_NORMAL)
  def call_etc_hosts_modify(self, node, mode, name, ip):
    """Modify hosts file with name

    @type node: string
    @param node: The node to call
    @type mode: string
    @param mode: The mode to operate. Currently "add" or "remove"
    @type name: string
    @param name: The host name to be modified
    @type ip: string
    @param ip: The ip of the entry (just valid if mode is "add")

    """
    return self._SingleNodeCall(node, "etc_hosts_modify", [mode, name, ip])

910
  @_RpcTimeout(_TMO_NORMAL)
Iustin Pop's avatar
Iustin Pop committed
911
912
  def call_node_verify(self, node_list, checkdict, cluster_name):
    """Request verification of given parameters.
Iustin Pop's avatar
Iustin Pop committed
913

Iustin Pop's avatar
Iustin Pop committed
914
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
915

Iustin Pop's avatar
Iustin Pop committed
916
    """
917
918
    return self._MultiNodeCall(node_list, "node_verify",
                               [checkdict, cluster_name])
Iustin Pop's avatar
Iustin Pop committed
919

920
  @classmethod
921
  @_RpcTimeout(_TMO_FAST)
922
  def call_node_start_master(cls, node, start_daemons, no_voting):
Iustin Pop's avatar
Iustin Pop committed
923
    """Tells a node to activate itself as a master.
Iustin Pop's avatar
Iustin Pop committed
924

Iustin Pop's avatar
Iustin Pop committed
925
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
926

Iustin Pop's avatar
Iustin Pop committed
927
    """
928
    return cls._StaticSingleNodeCall(node, "node_start_master",
929
                                     [start_daemons, no_voting])
Iustin Pop's avatar
Iustin Pop committed
930

931
  @classmethod
932
  @_RpcTimeout(_TMO_FAST)
933
  def call_node_stop_master(cls, node, stop_daemons):
Iustin Pop's avatar
Iustin Pop committed
934
    """Tells a node to demote itself from master status.
Iustin Pop's avatar
Iustin Pop committed
935

Iustin Pop's avatar
Iustin Pop committed
936
    This is a single-node call.
937

Iustin Pop's avatar
Iustin Pop committed
938
    """
939
    return cls._StaticSingleNodeCall(node, "node_stop_master", [stop_daemons])
940

941
  @classmethod
942
  @_RpcTimeout(_TMO_URGENT)
943
  def call_master_info(cls, node_list):
Iustin Pop's avatar
Iustin Pop committed
944
    """Query master info.
945

Iustin Pop's avatar
Iustin Pop committed
946
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
947

Iustin Pop's avatar
Iustin Pop committed
948
949
    """
    # TODO: should this method query down nodes?
950
    return cls._StaticMultiNodeCall(node_list, "master_info", [])
Iustin Pop's avatar
Iustin Pop committed
951

952
  @classmethod
953
  @_RpcTimeout(_TMO_URGENT)
954
  def call_version(cls, node_list):
Iustin Pop's avatar
Iustin Pop committed
955
    """Query node version.
Iustin Pop's avatar
Iustin Pop committed
956

Iustin Pop's avatar
Iustin Pop committed
957
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
958

Iustin Pop's avatar
Iustin Pop committed
959
    """
960
    return cls._StaticMultiNodeCall(node_list, "version", [])
Iustin Pop's avatar
Iustin Pop committed
961

962
  @_RpcTimeout(_TMO_NORMAL)
Iustin Pop's avatar
Iustin Pop committed
963
964
  def call_blockdev_create(self, node, bdev, size, owner, on_primary, info):
    """Request creation of a given block device.
Iustin Pop's avatar
Iustin Pop committed
965

Iustin Pop's avatar
Iustin Pop committed
966
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
967

Iustin Pop's avatar
Iustin Pop committed
968
    """
969
970
    return self._SingleNodeCall(node, "blockdev_create",
                                [bdev.ToDict(), size, owner, on_primary, info])
Iustin Pop's avatar
Iustin Pop committed
971

972
973
974
975
976
977
978
979
980
981
  @_RpcTimeout(_TMO_SLOW)
  def call_blockdev_wipe(self, node, bdev, offset, size):
    """Request wipe at given offset with given size of a block device.

    This is a single-node call.

    """
    return self._SingleNodeCall(node, "blockdev_wipe",
                                [bdev.ToDict(), offset, size])

982
  @_RpcTimeout(_TMO_NORMAL)
Iustin Pop's avatar
Iustin Pop committed
983
984
  def call_blockdev_remove(self, node, bdev):
    """Request removal of a given block device.
Iustin Pop's avatar
Iustin Pop committed
985

Iustin Pop's avatar
Iustin Pop committed
986
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
987

Iustin Pop's avatar
Iustin Pop committed
988
    """
989
    return self._SingleNodeCall(node, "blockdev_remove", [bdev.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
990

991
  @_RpcTimeout(_TMO_NORMAL)
Iustin Pop's avatar
Iustin Pop committed
992
993
  def call_blockdev_rename(self, node, devlist):
    """Request rename of the given block devices.
Iustin Pop's avatar
Iustin Pop committed
994