rpc.py 47.3 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

"""

26
# pylint: disable=C0103,R0201,R0904
Iustin Pop's avatar
Iustin Pop committed
27
28
29
30
31
# 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
49
from ganeti import compat
Iustin Pop's avatar
Iustin Pop committed
50

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

Iustin Pop's avatar
Iustin Pop committed
54

55
56
57
58
59
# 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
60
  "Expect:",
61
  ]
62

63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# 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 = {
}

81
82
83
#: Special value to describe an offline host
_OFFLINE = object()

84
85
86
87

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

88
89
  Must be called before using any RPC function and while exactly one thread is
  running.
90
91

  """
92
93
94
95
96
  # 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"
97

98
  logging.info("Using PycURL %s", pycurl.version)
99

100
  pycurl.global_init(pycurl.GLOBAL_ALL)
101
102
103
104
105


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

106
107
  Must be called before quitting the program and while exactly one thread is
  running.
108
109

  """
110
111
112
113
114
  pycurl.global_cleanup()


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

116
117
118
119
120
121
122
123
124
125
126
  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)


127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
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


142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
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


159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
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)))


179
180
181
182
183
184
185
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
186
  @ivar data: the data payload, for successful results, or None
187
188
189
190
191
192
  @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
193
  @ivar fail_msg: the error message if the call failed
194

195
  """
196
197
198
199
200
  def __init__(self, data=None, failed=False, offline=False,
               call=None, node=None):
    self.offline = offline
    self.call = call
    self.node = node
201

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

Iustin Pop's avatar
Iustin Pop committed
226
227
228
    for attr_name in ["call", "data", "fail_msg",
                      "node", "offline", "payload"]:
      assert hasattr(self, attr_name), "Missing attribute %s" % attr_name
229

230
231
232
233
234
235
236
  @staticmethod
  def _EnsureErr(val):
    """Helper to ensure we return a 'True' value for error."""
    if val:
      return val
    else:
      return "No error information"
237

238
  def Raise(self, msg, prereq=False, ecode=None):
239
240
241
242
243
244
    """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.

    """
245
246
247
248
249
250
251
252
253
254
255
256
    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
257
    if ecode is not None:
Iustin Pop's avatar
Iustin Pop committed
258
      args = (msg, ecode)
259
260
    else:
      args = (msg, )
261
    raise ec(*args) # pylint: disable=W0142
262
263


264
265
266
def _SsconfResolver(node_list,
                    ssc=ssconf.SimpleStore,
                    nslookup_fn=netutils.Hostname.GetIP):
267
268
269
270
271
272
  """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
273
274
  @type nslookup_fn: callable
  @param nslookup_fn: function use to do NS lookup
275
276
  @rtype: list of tuple; (string, string)
  @return: List of tuples containing node name and IP address
277
278

  """
Manuel Franceschini's avatar
Manuel Franceschini committed
279
280
281
  ss = ssc()
  iplist = ss.GetNodePrimaryIPList()
  family = ss.GetPrimaryIPFamily()
282
  ipmap = dict(entry.split() for entry in iplist)
283
284

  result = []
285
  for node in node_list:
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
    ip = ipmap.get(node)
    if ip is None:
      ip = nslookup_fn(node, family=family)
    result.append((node, ip))

  return result


class _StaticResolver:
  def __init__(self, addresses):
    """Initializes this class.

    """
    self._addresses = addresses

  def __call__(self, hosts):
    """Returns static addresses for hosts.

    """
    assert len(hosts) == len(self._addresses)
    return zip(hosts, self._addresses)

308

309
310
def _CheckConfigNode(name, node):
  """Checks if a node is online.
311

312
313
314
315
  @type name: string
  @param name: Node name
  @type node: L{objects.Node} or None
  @param node: Node object
316

317
318
319
320
321
322
323
324
325
  """
  if node is None:
    # Depend on DNS for name resolution
    ip = name
  elif node.offline:
    ip = _OFFLINE
  else:
    ip = node.primary_ip
  return (name, ip)
Iustin Pop's avatar
Iustin Pop committed
326
327


328
329
def _NodeConfigResolver(single_node_fn, all_nodes_fn, hosts):
  """Calculate node addresses using configuration.
Iustin Pop's avatar
Iustin Pop committed
330
331

  """
332
333
334
335
336
337
338
339
340
341
342
  # Special case for single-host lookups
  if len(hosts) == 1:
    (name, ) = hosts
    return [_CheckConfigNode(name, single_node_fn(name))]
  else:
    all_nodes = all_nodes_fn()
    return [_CheckConfigNode(name, all_nodes.get(name, None))
            for name in hosts]


class _RpcProcessor:
343
  def __init__(self, resolver, port, lock_monitor_cb=None):
344
345
346
347
348
349
350
    """Initializes this class.

    @param resolver: callable accepting a list of hostnames, returning a list
      of tuples containing name and IP address (IP address can be the name or
      the special value L{_OFFLINE} to mark offline machines)
    @type port: int
    @param port: TCP port
351
    @param lock_monitor_cb: Callable for registering with lock monitor
352

Iustin Pop's avatar
Iustin Pop committed
353
    """
354
355
    self._resolver = resolver
    self._port = port
356
    self._lock_monitor_cb = lock_monitor_cb
357

358
359
360
  @staticmethod
  def _PrepareRequests(hosts, port, procedure, body, read_timeout):
    """Prepares requests by sorting offline hosts into separate list.
361

362
363
364
    """
    results = {}
    requests = {}
365

366
367
368
369
370
371
372
373
374
375
    for (name, ip) in hosts:
      if ip is _OFFLINE:
        # Node is marked as offline
        results[name] = RpcResult(node=name, offline=True, call=procedure)
      else:
        requests[name] = \
          http.client.HttpClientRequest(str(ip), port,
                                        http.HTTP_PUT, str("/%s" % procedure),
                                        headers=_RPC_CLIENT_HEADERS,
                                        post_data=body,
376
                                        read_timeout=read_timeout,
377
378
                                        nicename="%s/%s" % (name, procedure),
                                        curl_config_fn=_ConfigRpcCurl)
Iustin Pop's avatar
Iustin Pop committed
379

380
381
382
383
384
    return (results, requests)

  @staticmethod
  def _CombineResults(results, requests, procedure):
    """Combines pre-computed results for offline hosts with actual call results.
385

Iustin Pop's avatar
Iustin Pop committed
386
    """
387
388
389
390
391
392
393
394
395
396
    for name, req in requests.items():
      if req.success and req.resp_status_code == http.HTTP_OK:
        host_result = RpcResult(data=serializer.LoadJson(req.resp_body),
                                node=name, call=procedure)
      else:
        # TODO: Better error reporting
        if req.error:
          msg = req.error
        else:
          msg = req.resp_body
397

398
399
400
        logging.error("RPC error in %s on node %s: %s", procedure, name, msg)
        host_result = RpcResult(data=msg, failed=True, node=name,
                                call=procedure)
401

402
      results[name] = host_result
403

404
    return results
Iustin Pop's avatar
Iustin Pop committed
405

406
407
  def __call__(self, hosts, procedure, body, read_timeout=None,
               _req_process_fn=http.client.ProcessRequests):
408
    """Makes an RPC request to a number of nodes.
409

410
411
412
413
414
415
416
417
    @type hosts: sequence
    @param hosts: Hostnames
    @type procedure: string
    @param procedure: Request path
    @type body: string
    @param body: Request body
    @type read_timeout: int or None
    @param read_timeout: Read timeout for request
Iustin Pop's avatar
Iustin Pop committed
418
419

    """
420
421
422
423
    assert procedure in _TIMEOUTS, "RPC call not declared in the timeouts table"

    if read_timeout is None:
      read_timeout = _TIMEOUTS[procedure]
Iustin Pop's avatar
Iustin Pop committed
424

425
426
427
    (results, requests) = \
      self._PrepareRequests(self._resolver(hosts), self._port, procedure,
                            str(body), read_timeout)
Iustin Pop's avatar
Iustin Pop committed
428

429
    _req_process_fn(requests.values(), lock_monitor_cb=self._lock_monitor_cb)
Iustin Pop's avatar
Iustin Pop committed
430

431
    assert not frozenset(results).intersection(requests)
432

433
    return self._CombineResults(results, requests, procedure)
Iustin Pop's avatar
Iustin Pop committed
434
435


436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
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
451
class RpcRunner(object):
452
  """RPC runner class.
Iustin Pop's avatar
Iustin Pop committed
453

454
455
456
  """
  def __init__(self, context):
    """Initialized the RPC runner.
Iustin Pop's avatar
Iustin Pop committed
457

458
459
    @type context: C{masterd.GanetiContext}
    @param context: Ganeti context
Iustin Pop's avatar
Iustin Pop committed
460

Iustin Pop's avatar
Iustin Pop committed
461
    """
462
    self._cfg = context.cfg
463
464
465
    self._proc = _RpcProcessor(compat.partial(_NodeConfigResolver,
                                              self._cfg.GetNodeInfo,
                                              self._cfg.GetAllNodesInfo),
466
467
                               netutils.GetDaemonPort(constants.NODED),
                               lock_monitor_cb=context.glm.AddToLockMonitor)
Iustin Pop's avatar
Iustin Pop committed
468

469
  def _InstDict(self, instance, hvp=None, bep=None, osp=None):
470
471
472
473
474
475
476
    """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
477
    @type hvp: dict or None
Michael Hanselmann's avatar
Michael Hanselmann committed
478
    @param hvp: a dictionary with overridden hypervisor parameters
479
    @type bep: dict or None
Michael Hanselmann's avatar
Michael Hanselmann committed
480
    @param bep: a dictionary with overridden backend parameters
481
    @type osp: dict or None
482
    @param osp: a dictionary with overridden os parameters
483
484
485
486
487
488
    @rtype: dict
    @return: the instance dict, with the hvparams filled with the
        cluster defaults

    """
    idict = instance.ToDict()
489
490
    cluster = self._cfg.GetClusterInfo()
    idict["hvparams"] = cluster.FillHV(instance)
491
492
    if hvp is not None:
      idict["hvparams"].update(hvp)
493
    idict["beparams"] = cluster.FillBE(instance)
494
495
    if bep is not None:
      idict["beparams"].update(bep)
496
497
498
    idict["osparams"] = cluster.SimpleFillOS(instance.os, instance.osparams)
    if osp is not None:
      idict["osparams"].update(osp)
499
500
501
502
    for nic in idict["nics"]:
      nic['nicparams'] = objects.FillDict(
        cluster.nicparams[constants.PP_DEFAULT],
        nic['nicparams'])
503
504
    return idict

505
  def _MultiNodeCall(self, node_list, procedure, args, read_timeout=None):
506
507
508
509
    """Helper for making a multi-node call

    """
    body = serializer.DumpJson(args, indent=False)
510
    return self._proc(node_list, procedure, body, read_timeout=read_timeout)
511

512
513
  @staticmethod
  def _StaticMultiNodeCall(node_list, procedure, args,
514
                           address_list=None, read_timeout=None):
515
516
517
518
    """Helper for making a multi-node static call

    """
    body = serializer.DumpJson(args, indent=False)
519
520
521
522
523
524
525
526
527
528

    if address_list is None:
      resolver = _SsconfResolver
    else:
      # Caller provided an address list
      resolver = _StaticResolver(address_list)

    proc = _RpcProcessor(resolver,
                         netutils.GetDaemonPort(constants.NODED))
    return proc(node_list, procedure, body, read_timeout=read_timeout)
529

530
  def _SingleNodeCall(self, node, procedure, args, read_timeout=None):
531
    """Helper for making a single-node call
532
533

    """
534
    body = serializer.DumpJson(args, indent=False)
535
    return self._proc([node], procedure, body, read_timeout=read_timeout)[node]
536
537

  @classmethod
538
  def _StaticSingleNodeCall(cls, node, procedure, args, read_timeout=None):
539
    """Helper for making a single-node static call
540
541

    """
542
    body = serializer.DumpJson(args, indent=False)
543
544
545
    proc = _RpcProcessor(_SsconfResolver,
                         netutils.GetDaemonPort(constants.NODED))
    return proc([node], procedure, body, read_timeout=read_timeout)[node]
546

547
548
549
550
  #
  # Begin RPC calls
  #

551
552
553
554
555
556
557
558
559
  @_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])

560
  @_RpcTimeout(_TMO_URGENT)
561
  def call_lv_list(self, node_list, vg_name):
Iustin Pop's avatar
Iustin Pop committed
562
    """Gets the logical volumes present in a given volume group.
Iustin Pop's avatar
Iustin Pop committed
563

Iustin Pop's avatar
Iustin Pop committed
564
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
565

Iustin Pop's avatar
Iustin Pop committed
566
    """
567
    return self._MultiNodeCall(node_list, "lv_list", [vg_name])
Iustin Pop's avatar
Iustin Pop committed
568

569
  @_RpcTimeout(_TMO_URGENT)
Iustin Pop's avatar
Iustin Pop committed
570
571
  def call_vg_list(self, node_list):
    """Gets the volume group list.
Iustin Pop's avatar
Iustin Pop committed
572

Iustin Pop's avatar
Iustin Pop committed
573
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
574

Iustin Pop's avatar
Iustin Pop committed
575
    """
576
    return self._MultiNodeCall(node_list, "vg_list", [])
Iustin Pop's avatar
Iustin Pop committed
577

578
  @_RpcTimeout(_TMO_NORMAL)
579
  def call_storage_list(self, node_list, su_name, su_args, name, fields):
580
    """Get list of storage units.
581
582
583
584
585
586
587

    This is a multi-node call.

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

588
  @_RpcTimeout(_TMO_NORMAL)
589
590
591
592
593
594
595
596
597
  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])

598
  @_RpcTimeout(_TMO_NORMAL)
599
600
601
602
603
604
605
606
607
  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])

608
  @_RpcTimeout(_TMO_URGENT)
Iustin Pop's avatar
Iustin Pop committed
609
610
  def call_bridges_exist(self, node, bridges_list):
    """Checks if a node has all the bridges given.
Iustin Pop's avatar
Iustin Pop committed
611

Iustin Pop's avatar
Iustin Pop committed
612
613
614
    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
615

Iustin Pop's avatar
Iustin Pop committed
616
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
617

Iustin Pop's avatar
Iustin Pop committed
618
    """
619
    return self._SingleNodeCall(node, "bridges_exist", [bridges_list])
Iustin Pop's avatar
Iustin Pop committed
620

621
  @_RpcTimeout(_TMO_NORMAL)
622
  def call_instance_start(self, node, instance, hvp, bep, startup_paused):
Iustin Pop's avatar
Iustin Pop committed
623
    """Starts an instance.
Iustin Pop's avatar
Iustin Pop committed
624

Iustin Pop's avatar
Iustin Pop committed
625
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
626

Iustin Pop's avatar
Iustin Pop committed
627
    """
628
    idict = self._InstDict(instance, hvp=hvp, bep=bep)
629
    return self._SingleNodeCall(node, "instance_start", [idict, startup_paused])
Iustin Pop's avatar
Iustin Pop committed
630

631
  @_RpcTimeout(_TMO_NORMAL)
632
  def call_instance_shutdown(self, node, instance, timeout):
Iustin Pop's avatar
Iustin Pop committed
633
    """Stops an instance.
Iustin Pop's avatar
Iustin Pop committed
634

Iustin Pop's avatar
Iustin Pop committed
635
    This is a single-node call.
636

Iustin Pop's avatar
Iustin Pop committed
637
    """
638
    return self._SingleNodeCall(node, "instance_shutdown",
639
                                [self._InstDict(instance), timeout])
640

641
  @_RpcTimeout(_TMO_NORMAL)
642
643
644
645
646
647
648
649
650
651
652
653
654
655
  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)])

656
  @_RpcTimeout(_TMO_NORMAL)
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
  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])

675
  @_RpcTimeout(_TMO_NORMAL)
676
  def call_instance_finalize_migration_dst(self, node, instance, info, success):
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
    """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

    """
694
    return self._SingleNodeCall(node, "instance_finalize_migration_dst",
695
696
                                [self._InstDict(instance), info, success])

697
  @_RpcTimeout(_TMO_SLOW)
Iustin Pop's avatar
Iustin Pop committed
698
699
  def call_instance_migrate(self, node, instance, target, live):
    """Migrate an instance.
700

Iustin Pop's avatar
Iustin Pop committed
701
    This is a single-node call.
702

Iustin Pop's avatar
Iustin Pop committed
703
704
705
706
707
708
709
710
711
    @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)
712

Iustin Pop's avatar
Iustin Pop committed
713
    """
714
715
    return self._SingleNodeCall(node, "instance_migrate",
                                [self._InstDict(instance), target, live])
716

717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
  @_RpcTimeout(_TMO_SLOW)
  def call_instance_finalize_migration_src(self, node, instance, success, live):
    """Finalize the instance migration on the source node.

    This is a single-node call.

    @type instance: L{objects.Instance}
    @param instance: the instance that was migrated
    @type success: bool
    @param success: whether the migration succeeded or not
    @type live: bool
    @param live: whether the user requested a live migration or not

    """
    return self._SingleNodeCall(node, "instance_finalize_migration_src",
                                [self._InstDict(instance), success, live])

  @_RpcTimeout(_TMO_SLOW)
  def call_instance_get_migration_status(self, node, instance):
    """Report migration status.

    This is a single-node call that must be executed on the source node.

    @type instance: L{objects.Instance}
    @param instance: the instance that is being migrated
    @rtype: L{objects.MigrationStatus}
    @return: the status of the current migration (one of
             L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
             progress info that can be retrieved from the hypervisor

    """
    result = self._SingleNodeCall(node, "instance_get_migration_status",
                                  [self._InstDict(instance)])
    if not result.fail_msg and result.payload is not None:
      result.payload = objects.MigrationStatus.FromDict(result.payload)
    return result

754
  @_RpcTimeout(_TMO_NORMAL)
755
  def call_instance_reboot(self, node, inst, reboot_type, shutdown_timeout):
Iustin Pop's avatar
Iustin Pop committed
756
    """Reboots an instance.
757

Iustin Pop's avatar
Iustin Pop committed
758
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
759

Iustin Pop's avatar
Iustin Pop committed
760
    """
761
    return self._SingleNodeCall(node, "instance_reboot",
762
763
                                [self._InstDict(inst), reboot_type,
                                 shutdown_timeout])
Iustin Pop's avatar
Iustin Pop committed
764

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

Iustin Pop's avatar
Iustin Pop committed
769
    This is a single-node call.
770

Iustin Pop's avatar
Iustin Pop committed
771
    """
772
    return self._SingleNodeCall(node, "instance_os_add",
773
774
                                [self._InstDict(inst, osp=osparams),
                                 reinstall, debug])
775

776
  @_RpcTimeout(_TMO_SLOW)
777
  def call_instance_run_rename(self, node, inst, old_name, debug):
Iustin Pop's avatar
Iustin Pop committed
778
    """Run the OS rename script for an instance.
779

Iustin Pop's avatar
Iustin Pop committed
780
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
781

Iustin Pop's avatar
Iustin Pop committed
782
    """
783
    return self._SingleNodeCall(node, "instance_run_rename",
784
                                [self._InstDict(inst), old_name, debug])
Iustin Pop's avatar
Iustin Pop committed
785

786
  @_RpcTimeout(_TMO_URGENT)
Iustin Pop's avatar
Iustin Pop committed
787
788
  def call_instance_info(self, node, instance, hname):
    """Returns information about a single instance.
Iustin Pop's avatar
Iustin Pop committed
789

Iustin Pop's avatar
Iustin Pop committed
790
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
791

792
793
    @type node: list
    @param node: the list of nodes to query
Iustin Pop's avatar
Iustin Pop committed
794
795
796
797
    @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
798

Iustin Pop's avatar
Iustin Pop committed
799
    """
800
    return self._SingleNodeCall(node, "instance_info", [instance, hname])
801

802
  @_RpcTimeout(_TMO_NORMAL)
803
804
805
806
807
808
809
810
811
812
813
814
815
816
  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)])

817
  @_RpcTimeout(_TMO_URGENT)
Iustin Pop's avatar
Iustin Pop committed
818
819
  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
820

Iustin Pop's avatar
Iustin Pop committed
821
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
822

Iustin Pop's avatar
Iustin Pop committed
823
824
825
826
    @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
827

Iustin Pop's avatar
Iustin Pop committed
828
    """
829
830
    return self._MultiNodeCall(node_list, "all_instances_info",
                               [hypervisor_list])
831

832
  @_RpcTimeout(_TMO_URGENT)
Iustin Pop's avatar
Iustin Pop committed
833
834
  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
835

Iustin Pop's avatar
Iustin Pop committed
836
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
837

Iustin Pop's avatar
Iustin Pop committed
838
839
840
841
    @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
842

Iustin Pop's avatar
Iustin Pop committed
843
    """
844
    return self._MultiNodeCall(node_list, "instance_list", [hypervisor_list])
845

846
  @_RpcTimeout(_TMO_FAST)
847
848
849
850
851
852
  def call_node_has_ip_address(self, node, address):
    """Checks if a node has the given IP address.

    This is a single-node call.

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

855
  @_RpcTimeout(_TMO_URGENT)
Iustin Pop's avatar
Iustin Pop committed
856
857
  def call_node_info(self, node_list, vg_name, hypervisor_type):
    """Return node information.
858

Iustin Pop's avatar
Iustin Pop committed
859
860
    This will return memory information and volume group size and free
    space.
Iustin Pop's avatar
Iustin Pop committed
861

Iustin Pop's avatar
Iustin Pop committed
862
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
863

Iustin Pop's avatar
Iustin Pop committed
864
865
    @type node_list: list
    @param node_list: the list of nodes to query
Iustin Pop's avatar
Iustin Pop committed
866
867
    @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
868
869
870
871
        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
872

Iustin Pop's avatar
Iustin Pop committed
873
    """
874
875
    return self._MultiNodeCall(node_list, "node_info",
                               [vg_name, hypervisor_type])
Iustin Pop's avatar
Iustin Pop committed
876

877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
  @_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])

893
  @_RpcTimeout(_TMO_NORMAL)
Iustin Pop's avatar
Iustin Pop committed
894
895
  def call_node_verify(self, node_list, checkdict, cluster_name):
    """Request verification of given parameters.
Iustin Pop's avatar
Iustin Pop committed
896

Iustin Pop's avatar
Iustin Pop committed
897
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
898

Iustin Pop's avatar
Iustin Pop committed
899
    """
900
901
    return self._MultiNodeCall(node_list, "node_verify",
                               [checkdict, cluster_name])
Iustin Pop's avatar
Iustin Pop committed
902

903
  @classmethod
904
  @_RpcTimeout(_TMO_FAST)
905
906
  def call_node_start_master_daemons(cls, node, no_voting):
    """Starts master daemons on a node.
Iustin Pop's avatar
Iustin Pop committed
907

Iustin Pop's avatar
Iustin Pop committed
908
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
909

Iustin Pop's avatar
Iustin Pop committed
910
    """
911
912
    return cls._StaticSingleNodeCall(node, "node_start_master_daemons",
                                     [no_voting])
Iustin Pop's avatar
Iustin Pop committed
913

914
  @classmethod
915
  @_RpcTimeout(_TMO_FAST)
916
917
  def call_node_activate_master_ip(cls, node):
    """Activates master IP on a node.
Iustin Pop's avatar
Iustin Pop committed
918

Iustin Pop's avatar
Iustin Pop committed
919
    This is a single-node call.
920

Iustin Pop's avatar
Iustin Pop committed
921
    """
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
    return cls._StaticSingleNodeCall(node, "node_activate_master_ip", [])

  @classmethod
  @_RpcTimeout(_TMO_FAST)
  def call_node_stop_master(cls, node):
    """Deactivates master IP and stops master daemons on a node.

    This is a single-node call.

    """
    return cls._StaticSingleNodeCall(node, "node_stop_master", [])

  @classmethod
  @_RpcTimeout(_TMO_FAST)
  def call_node_deactivate_master_ip(cls, node):
    """Deactivates master IP on a node.

    This is a single-node call.

    """
    return cls._StaticSingleNodeCall(node, "node_deactivate_master_ip", [])
943

944
945
946
947
948
949
950
951
952
953
954
  @classmethod
  @_RpcTimeout(_TMO_FAST)
  def call_node_change_master_netmask(cls, node, netmask):
    """Change master IP netmask.

    This is a single-node call.

    """
    return cls._StaticSingleNodeCall(node, "node_change_master_netmask",
                  [netmask])

955
  @classmethod
956
  @_RpcTimeout(_TMO_URGENT)
957
  def call_master_info(cls, node_list):
Iustin Pop's avatar
Iustin Pop committed
958
    """Query master info.
959

Iustin Pop's avatar
Iustin Pop committed
960
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
961

Iustin Pop's avatar
Iustin Pop committed
962
963
    """
    # TODO: should this method query down nodes?
964
    return cls._StaticMultiNodeCall(node_list, "master_info", [])
Iustin Pop's avatar
Iustin Pop committed
965

966
  @classmethod
967
  @_RpcTimeout(_TMO_URGENT)
968
  def call_version(cls, node_list):
Iustin Pop's avatar
Iustin Pop committed
969
    """Query node version.
Iustin Pop's avatar
Iustin Pop committed
970

Iustin Pop's avatar
Iustin Pop committed
971
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
972

Iustin Pop's avatar
Iustin Pop committed
973
    """
974
    return cls._StaticMultiNodeCall(node_list, "version", [])
Iustin Pop's avatar
Iustin Pop committed
975

976
  @_RpcTimeout(_TMO_NORMAL)
Iustin Pop's avatar
Iustin Pop committed
977
978
  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
979

Iustin Pop's avatar
Iustin Pop committed
980
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
981

Iustin Pop's avatar
Iustin Pop committed
982
    """
983
984
    return self._SingleNodeCall(node, "blockdev_create",
                                [bdev.ToDict(), size, owner, on_primary, info])
Iustin Pop's avatar
Iustin Pop committed
985

986
987
988
989
990
991
992
993
994
995
  @_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])

996
  @_RpcTimeout(_TMO_NORMAL)
Iustin Pop's avatar
Iustin Pop committed
997
998
  def call_blockdev_remove(self, node, bdev):
    """Request removal of a given block device.
Iustin Pop's avatar
Iustin Pop committed
999

Iustin Pop's avatar
Iustin Pop committed
1000
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
1001

Iustin Pop's avatar
Iustin Pop committed
1002
    """
1003
    return self._SingleNodeCall(node, "blockdev_remove", [bdev.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
1004

1005
  @_RpcTimeout(_TMO_NORMAL)
Iustin Pop's avatar
Iustin Pop committed
1006
1007
  def call_blockdev_rename(self, node, devlist):
    """Request rename of the given block devices.
Iustin Pop's avatar
Iustin Pop committed
1008

Iustin Pop's avatar
Iustin Pop committed
1009
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
1010

Iustin Pop's avatar
Iustin Pop committed
1011
    """
1012
    return self._SingleNodeCall(node, "blockdev_rename",
1013
                                [[(d.ToDict(), uid) for d, uid in devlist]])
Iustin Pop's avatar
Iustin Pop committed
1014

1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
  @_RpcTimeout(_TMO_NORMAL)
  def call_blockdev_pause_resume_sync(self, node, disks, pause):
    """Request a pause/resume of given block device.

    This is a single-node call.

    """
    return self._SingleNodeCall(node, "blockdev_pause_resume_sync",
                                [[bdev.ToDict() for bdev in disks], pause])

1025
  @_RpcTimeout(_TMO_NORMAL)
1026
  def call_blockdev_assemble(self, node, disk, owner, on_primary, idx):
Iustin Pop's avatar
Iustin Pop committed
1027
    """Request assembling of a given block device.
Iustin Pop's avatar
Iustin Pop committed
1028

Iustin Pop's avatar
Iustin Pop committed
1029
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
1030

Iustin Pop's avatar
Iustin Pop committed
1031
    """
1032
    return self._SingleNodeCall(node, "blockdev_assemble",
1033
                                [disk.ToDict(), owner, on_primary, idx])
Iustin Pop's avatar
Iustin Pop committed
1034

1035
  @_RpcTimeout(_TMO_NORMAL)
Iustin Pop's avatar
Iustin Pop committed
1036
1037
  def call_blockdev_shutdown(self, node, disk):
    """Request shutdown of a given block device.
Iustin Pop's avatar
Iustin Pop committed
1038

Iustin Pop's avatar
Iustin Pop committed
1039
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
1040

Iustin Pop's avatar
Iustin Pop committed
1041
    """
1042
    return self._SingleNodeCall(node, "blockdev_shutdown", [disk.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
1043

1044
  @_RpcTimeout(_TMO_NORMAL)
Iustin Pop's avatar
Iustin Pop committed
1045
1046
  def call_blockdev_addchildren(self, node, bdev, ndevs):
    """Request adding a list of children to a (mirroring) device.
Iustin Pop's avatar
Iustin Pop committed
1047

Iustin Pop's avatar
Iustin Pop committed
1048
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
1049

Iustin Pop's avatar
Iustin Pop committed
1050
    """
1051
1052
1053
    return self._SingleNodeCall(node, "blockdev_addchildren",
                                [bdev.ToDict(),
                                 [disk.ToDict() for disk in ndevs]])
Iustin Pop's avatar
Iustin Pop committed
1054

1055
  @_RpcTimeout(_TMO_NORMAL)
Iustin Pop's avatar
Iustin Pop committed
1056