rpc.py 33.5 KB
Newer Older
Iustin Pop's avatar
Iustin Pop committed
1
#
Iustin Pop's avatar
Iustin Pop committed
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#

# Copyright (C) 2006, 2007 Google Inc.
#
# 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
Iustin Pop's avatar
Iustin Pop committed
37
38
39

from ganeti import utils
from ganeti import objects
40
from ganeti import http
41
from ganeti import serializer
42
from ganeti import constants
43
from ganeti import errors
Iustin Pop's avatar
Iustin Pop committed
44

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

Iustin Pop's avatar
Iustin Pop committed
48

49
50
51
52
53
54
55
56
57
58
# Module level variable
_http_manager = None


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

  Must be called before using any RPC function.

  """
Iustin Pop's avatar
Iustin Pop committed
59
  global _http_manager # pylint: disable-msg=W0603
60
61
62

  assert not _http_manager, "RPC module initialized more than once"

63
  _http_manager = http.client.HttpClientManager()
64
65
66
67
68
69
70
71


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

  Must be called before quitting the program.

  """
Iustin Pop's avatar
Iustin Pop committed
72
  global _http_manager # pylint: disable-msg=W0603
73
74
75
76
77
78

  if _http_manager:
    _http_manager.Shutdown()
    _http_manager = None


79
80
81
82
83
84
85
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
86
  @ivar data: the data payload, for successful results, or None
87
88
89
90
91
92
93
94
95
96
  @type failed: boolean
  @ivar failed: whether the operation failed at RPC level (not
      application level on the remote node)
  @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

97
  """
98
99
  def __init__(self, data=None, failed=False, offline=False,
               call=None, node=None):
100
    self.failed = failed
101
102
103
104
105
106
    self.offline = offline
    self.call = call
    self.node = node
    if offline:
      self.failed = True
      self.error = "Node is marked offline"
107
      self.data = self.payload = None
108
    elif failed:
109
      self.error = data
110
      self.data = self.payload = None
111
112
113
    else:
      self.data = data
      self.error = None
114
115
116
117
      if isinstance(data, (tuple, list)) and len(data) == 2:
        self.payload = data[1]
      else:
        self.payload = None
118
119
120
121
122
123
124
125
126
127
128
129

  def Raise(self):
    """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.

    """
    if self.failed:
      raise errors.OpExecError("Call '%s' to node '%s' has failed: %s" %
                               (self.call, self.node, self.error))

130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
  def RemoteFailMsg(self):
    """Check if the remote procedure failed.

    This is valid only for RPC calls which return result of the form
    (status, data | error_msg).

    @return: empty string for succcess, otherwise an error message

    """
    def _EnsureErr(val):
      """Helper to ensure we return a 'True' value for error."""
      if val:
        return val
      else:
        return "No error information"

    if self.failed:
      return _EnsureErr(self.error)
    if not isinstance(self.data, (tuple, list)):
      return "Invalid result type (%s)" % type(self.data)
    if len(self.data) != 2:
      return "Invalid result length (%d), expected 2" % len(self.data)
    if not self.data[0]:
      return _EnsureErr(self.data[1])
    return ""

156

Iustin Pop's avatar
Iustin Pop committed
157
158
159
class Client:
  """RPC Client class.

Alexander Schreiber's avatar
Alexander Schreiber committed
160
  This class, given a (remote) method name, a list of parameters and a
Iustin Pop's avatar
Iustin Pop committed
161
162
163
  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
164
  One current bug is that generic failure is still signaled by
Iustin Pop's avatar
Iustin Pop committed
165
166
167
168
  'False' result, which is not good. This overloading of values can
  cause bugs.

  """
169
  def __init__(self, procedure, body, port):
Iustin Pop's avatar
Iustin Pop committed
170
    self.procedure = procedure
171
172
    self.body = body
    self.port = port
173
    self.nc = {}
Iustin Pop's avatar
Iustin Pop committed
174

175
176
177
178
    self._ssl_params = \
      http.HttpSslParams(ssl_key_path=constants.SSL_CERT_FILE,
                         ssl_cert_path=constants.SSL_CERT_FILE)

179
  def ConnectList(self, node_list, address_list=None):
Iustin Pop's avatar
Iustin Pop committed
180
181
    """Add a list of nodes to the target nodes.

182
183
    @type node_list: list
    @param node_list: the list of node names to connect
184
185
186
    @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
187

Iustin Pop's avatar
Iustin Pop committed
188
    """
189
190
191
192
193
194
195
196
197
    if address_list is None:
      address_list = [None for _ in node_list]
    else:
      assert len(node_list) == len(address_list), \
             "Name and address lists should have the same length"
    for node, address in zip(node_list, address_list):
      self.ConnectNode(node, address)

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

200
201
202
203
204
    @type name: str
    @param name: the node name
    @type address: str
    @keyword address: the node address, if known

Iustin Pop's avatar
Iustin Pop committed
205
    """
206
207
208
    if address is None:
      address = name

209
210
211
212
213
214
    self.nc[name] = \
      http.client.HttpClientRequest(address, self.port, http.HTTP_PUT,
                                    "/%s" % self.procedure,
                                    post_data=self.body,
                                    ssl_params=self._ssl_params,
                                    ssl_verify_peer=True)
Iustin Pop's avatar
Iustin Pop committed
215

216
  def GetResults(self):
217
218
219
    """Call nodes and return results.

    @rtype: list
Iustin Pop's avatar
Iustin Pop committed
220
    @return: List of RPC results
Iustin Pop's avatar
Iustin Pop committed
221
222

    """
Michael Hanselmann's avatar
Michael Hanselmann committed
223
    assert _http_manager, "RPC module not initialized"
224
225

    _http_manager.ExecRequests(self.nc.values())
Iustin Pop's avatar
Iustin Pop committed
226

227
    results = {}
Iustin Pop's avatar
Iustin Pop committed
228

229
    for name, req in self.nc.iteritems():
230
      if req.success and req.resp_status_code == http.HTTP_OK:
231
232
        results[name] = RpcResult(data=serializer.LoadJson(req.resp_body),
                                  node=name, call=self.procedure)
233
        continue
Iustin Pop's avatar
Iustin Pop committed
234

235
      # TODO: Better error reporting
236
237
238
239
240
      if req.error:
        msg = req.error
      else:
        msg = req.resp_body

241
242
      logging.error("RPC error in %s from node %s: %s",
                    self.procedure, name, msg)
243
244
      results[name] = RpcResult(data=msg, failed=True, node=name,
                                call=self.procedure)
245
246

    return results
Iustin Pop's avatar
Iustin Pop committed
247
248


Iustin Pop's avatar
Iustin Pop committed
249
250
class RpcRunner(object):
  """RPC runner class"""
Iustin Pop's avatar
Iustin Pop committed
251

Iustin Pop's avatar
Iustin Pop committed
252
253
  def __init__(self, cfg):
    """Initialized the rpc runner.
Iustin Pop's avatar
Iustin Pop committed
254

Iustin Pop's avatar
Iustin Pop committed
255
256
257
    @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
258

Iustin Pop's avatar
Iustin Pop committed
259
260
    """
    self._cfg = cfg
261
    self.port = utils.GetNodeDaemonPort()
Iustin Pop's avatar
Iustin Pop committed
262

263
  def _InstDict(self, instance, hvp=None, bep=None):
264
265
266
267
268
269
270
    """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
271
    @type hvp: dict or None
Michael Hanselmann's avatar
Michael Hanselmann committed
272
    @param hvp: a dictionary with overridden hypervisor parameters
273
    @type bep: dict or None
Michael Hanselmann's avatar
Michael Hanselmann committed
274
    @param bep: a dictionary with overridden backend parameters
275
276
277
278
279
280
    @rtype: dict
    @return: the instance dict, with the hvparams filled with the
        cluster defaults

    """
    idict = instance.ToDict()
281
282
    cluster = self._cfg.GetClusterInfo()
    idict["hvparams"] = cluster.FillHV(instance)
283
284
    if hvp is not None:
      idict["hvparams"].update(hvp)
285
    idict["beparams"] = cluster.FillBE(instance)
286
287
    if bep is not None:
      idict["beparams"].update(bep)
288
289
    return idict

290
  def _ConnectList(self, client, node_list, call):
291
292
    """Helper for computing node addresses.

Iustin Pop's avatar
Iustin Pop committed
293
    @type client: L{ganeti.rpc.Client}
294
295
296
    @param client: a C{Client} instance
    @type node_list: list
    @param node_list: the node list we should connect
297
298
299
    @type call: string
    @param call: the name of the remote procedure call, for filling in
        correctly any eventual offline nodes' results
300
301
302

    """
    all_nodes = self._cfg.GetAllNodesInfo()
303
    name_list = []
304
    addr_list = []
305
    skip_dict = {}
306
307
    for node in node_list:
      if node in all_nodes:
308
        if all_nodes[node].offline:
309
          skip_dict[node] = RpcResult(node=node, offline=True, call=call)
310
          continue
311
312
313
314
        val = all_nodes[node].primary_ip
      else:
        val = None
      addr_list.append(val)
315
316
317
318
      name_list.append(node)
    if name_list:
      client.ConnectList(name_list, address_list=addr_list)
    return skip_dict
319

320
  def _ConnectNode(self, client, node, call):
321
322
    """Helper for computing one node's address.

Iustin Pop's avatar
Iustin Pop committed
323
    @type client: L{ganeti.rpc.Client}
324
325
326
    @param client: a C{Client} instance
    @type node: str
    @param node: the node we should connect
327
328
329
    @type call: string
    @param call: the name of the remote procedure call, for filling in
        correctly any eventual offline nodes' results
330
331
332
333

    """
    node_info = self._cfg.GetNodeInfo(node)
    if node_info is not None:
334
      if node_info.offline:
335
        return RpcResult(node=node, offline=True, call=call)
336
337
338
339
340
      addr = node_info.primary_ip
    else:
      addr = None
    client.ConnectNode(node, address=addr)

341
  def _MultiNodeCall(self, node_list, procedure, args):
342
343
344
345
346
    """Helper for making a multi-node call

    """
    body = serializer.DumpJson(args, indent=False)
    c = Client(procedure, body, self.port)
347
    skip_dict = self._ConnectList(c, node_list, procedure)
348
349
    skip_dict.update(c.GetResults())
    return skip_dict
350
351
352
353

  @classmethod
  def _StaticMultiNodeCall(cls, node_list, procedure, args,
                           address_list=None):
354
355
356
357
358
    """Helper for making a multi-node static call

    """
    body = serializer.DumpJson(args, indent=False)
    c = Client(procedure, body, utils.GetNodeDaemonPort())
359
360
361
362
    c.ConnectList(node_list, address_list=address_list)
    return c.GetResults()

  def _SingleNodeCall(self, node, procedure, args):
363
    """Helper for making a single-node call
364
365

    """
366
367
    body = serializer.DumpJson(args, indent=False)
    c = Client(procedure, body, self.port)
368
    result = self._ConnectNode(c, node, procedure)
369
370
371
372
    if result is None:
      # we did connect, node is not offline
      result = c.GetResults()[node]
    return result
373
374
375

  @classmethod
  def _StaticSingleNodeCall(cls, node, procedure, args):
376
    """Helper for making a single-node static call
377
378

    """
379
380
    body = serializer.DumpJson(args, indent=False)
    c = Client(procedure, body, utils.GetNodeDaemonPort())
381
    c.ConnectNode(node)
382
    return c.GetResults()[node]
383

384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
  @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)))

404
405
406
407
  #
  # Begin RPC calls
  #

Iustin Pop's avatar
Iustin Pop committed
408
409
  def call_volume_list(self, node_list, vg_name):
    """Gets the logical volumes present in a given volume group.
Iustin Pop's avatar
Iustin Pop committed
410

Iustin Pop's avatar
Iustin Pop committed
411
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
412

Iustin Pop's avatar
Iustin Pop committed
413
    """
414
    return self._MultiNodeCall(node_list, "volume_list", [vg_name])
Iustin Pop's avatar
Iustin Pop committed
415

Iustin Pop's avatar
Iustin Pop committed
416
417
  def call_vg_list(self, node_list):
    """Gets the volume group list.
Iustin Pop's avatar
Iustin Pop committed
418

Iustin Pop's avatar
Iustin Pop committed
419
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
420

Iustin Pop's avatar
Iustin Pop committed
421
    """
422
    return self._MultiNodeCall(node_list, "vg_list", [])
Iustin Pop's avatar
Iustin Pop committed
423

Iustin Pop's avatar
Iustin Pop committed
424
425
  def call_bridges_exist(self, node, bridges_list):
    """Checks if a node has all the bridges given.
Iustin Pop's avatar
Iustin Pop committed
426

Iustin Pop's avatar
Iustin Pop committed
427
428
429
    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
430

Iustin Pop's avatar
Iustin Pop committed
431
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
432

Iustin Pop's avatar
Iustin Pop committed
433
    """
434
    return self._SingleNodeCall(node, "bridges_exist", [bridges_list])
Iustin Pop's avatar
Iustin Pop committed
435

436
  def call_instance_start(self, node, instance, hvp, bep):
Iustin Pop's avatar
Iustin Pop committed
437
    """Starts an instance.
Iustin Pop's avatar
Iustin Pop committed
438

Iustin Pop's avatar
Iustin Pop committed
439
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
440

Iustin Pop's avatar
Iustin Pop committed
441
    """
442
443
    idict = self._InstDict(instance, hvp=hvp, bep=bep)
    return self._SingleNodeCall(node, "instance_start", [idict])
Iustin Pop's avatar
Iustin Pop committed
444

Iustin Pop's avatar
Iustin Pop committed
445
446
  def call_instance_shutdown(self, node, instance):
    """Stops an instance.
Iustin Pop's avatar
Iustin Pop committed
447

Iustin Pop's avatar
Iustin Pop committed
448
    This is a single-node call.
449

Iustin Pop's avatar
Iustin Pop committed
450
    """
451
452
    return self._SingleNodeCall(node, "instance_shutdown",
                                [self._InstDict(instance)])
453

454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
  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)])

  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])

  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])

Iustin Pop's avatar
Iustin Pop committed
507
508
  def call_instance_migrate(self, node, instance, target, live):
    """Migrate an instance.
509

Iustin Pop's avatar
Iustin Pop committed
510
    This is a single-node call.
511

Iustin Pop's avatar
Iustin Pop committed
512
513
514
515
516
517
518
519
520
    @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)
521

Iustin Pop's avatar
Iustin Pop committed
522
    """
523
524
    return self._SingleNodeCall(node, "instance_migrate",
                                [self._InstDict(instance), target, live])
525

526
  def call_instance_reboot(self, node, instance, reboot_type):
Iustin Pop's avatar
Iustin Pop committed
527
    """Reboots an instance.
528

Iustin Pop's avatar
Iustin Pop committed
529
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
530

Iustin Pop's avatar
Iustin Pop committed
531
    """
532
    return self._SingleNodeCall(node, "instance_reboot",
533
                                [self._InstDict(instance), reboot_type])
Iustin Pop's avatar
Iustin Pop committed
534

535
  def call_instance_os_add(self, node, inst):
Iustin Pop's avatar
Iustin Pop committed
536
    """Installs an OS on the given instance.
Iustin Pop's avatar
Iustin Pop committed
537

Iustin Pop's avatar
Iustin Pop committed
538
    This is a single-node call.
539

Iustin Pop's avatar
Iustin Pop committed
540
    """
541
542
    return self._SingleNodeCall(node, "instance_os_add",
                                [self._InstDict(inst)])
543

544
  def call_instance_run_rename(self, node, inst, old_name):
Iustin Pop's avatar
Iustin Pop committed
545
    """Run the OS rename script for an instance.
546

Iustin Pop's avatar
Iustin Pop committed
547
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
548

Iustin Pop's avatar
Iustin Pop committed
549
    """
550
551
    return self._SingleNodeCall(node, "instance_run_rename",
                                [self._InstDict(inst), old_name])
Iustin Pop's avatar
Iustin Pop committed
552

Iustin Pop's avatar
Iustin Pop committed
553
554
  def call_instance_info(self, node, instance, hname):
    """Returns information about a single instance.
Iustin Pop's avatar
Iustin Pop committed
555

Iustin Pop's avatar
Iustin Pop committed
556
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
557

558
559
    @type node: list
    @param node: the list of nodes to query
Iustin Pop's avatar
Iustin Pop committed
560
561
562
563
    @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
564

Iustin Pop's avatar
Iustin Pop committed
565
    """
566
    return self._SingleNodeCall(node, "instance_info", [instance, hname])
567

568
569
570
571
572
573
574
575
576
577
578
579
580
581
  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)])

Iustin Pop's avatar
Iustin Pop committed
582
583
  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
584

Iustin Pop's avatar
Iustin Pop committed
585
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
586

Iustin Pop's avatar
Iustin Pop committed
587
588
589
590
    @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
591

Iustin Pop's avatar
Iustin Pop committed
592
    """
593
594
    return self._MultiNodeCall(node_list, "all_instances_info",
                               [hypervisor_list])
595

Iustin Pop's avatar
Iustin Pop committed
596
597
  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
598

Iustin Pop's avatar
Iustin Pop committed
599
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
600

Iustin Pop's avatar
Iustin Pop committed
601
602
603
604
    @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
605

Iustin Pop's avatar
Iustin Pop committed
606
    """
607
    return self._MultiNodeCall(node_list, "instance_list", [hypervisor_list])
608

Iustin Pop's avatar
Iustin Pop committed
609
610
611
  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
612

Iustin Pop's avatar
Iustin Pop committed
613
    This is a single-node call.
614

Iustin Pop's avatar
Iustin Pop committed
615
    """
616
617
    return self._SingleNodeCall(node, "node_tcp_ping",
                                [source, target, port, timeout,
Iustin Pop's avatar
Iustin Pop committed
618
                                 live_port_needed])
Iustin Pop's avatar
Iustin Pop committed
619

620
621
622
623
624
625
  def call_node_has_ip_address(self, node, address):
    """Checks if a node has the given IP address.

    This is a single-node call.

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

Iustin Pop's avatar
Iustin Pop committed
628
629
  def call_node_info(self, node_list, vg_name, hypervisor_type):
    """Return node information.
630

Iustin Pop's avatar
Iustin Pop committed
631
632
    This will return memory information and volume group size and free
    space.
Iustin Pop's avatar
Iustin Pop committed
633

Iustin Pop's avatar
Iustin Pop committed
634
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
635

Iustin Pop's avatar
Iustin Pop committed
636
637
    @type node_list: list
    @param node_list: the list of nodes to query
Iustin Pop's avatar
Iustin Pop committed
638
639
    @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
640
641
642
643
        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
644

Iustin Pop's avatar
Iustin Pop committed
645
    """
646
647
    retux = self._MultiNodeCall(node_list, "node_info",
                                [vg_name, hypervisor_type])
Iustin Pop's avatar
Iustin Pop committed
648

649
650
651
    for result in retux.itervalues():
      if result.failed or not isinstance(result.data, dict):
        result.data = {}
652
653
654
655
      if result.offline:
        log_name = None
      else:
        log_name = "call_node_info"
656
657
658
659
660
661
662

      utils.CheckDict(result.data, {
        'memory_total' : '-',
        'memory_dom0' : '-',
        'memory_free' : '-',
        'vg_size' : 'node_unreachable',
        'vg_free' : '-',
663
        }, log_name)
Iustin Pop's avatar
Iustin Pop committed
664
    return retux
Iustin Pop's avatar
Iustin Pop committed
665

Iustin Pop's avatar
Iustin Pop committed
666
667
  def call_node_add(self, node, dsa, dsapub, rsa, rsapub, ssh, sshpub):
    """Add a node to the cluster.
Iustin Pop's avatar
Iustin Pop committed
668

Iustin Pop's avatar
Iustin Pop committed
669
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
670

Iustin Pop's avatar
Iustin Pop committed
671
    """
672
673
    return self._SingleNodeCall(node, "node_add",
                                [dsa, dsapub, rsa, rsapub, ssh, sshpub])
Iustin Pop's avatar
Iustin Pop committed
674

Iustin Pop's avatar
Iustin Pop committed
675
676
  def call_node_verify(self, node_list, checkdict, cluster_name):
    """Request verification of given parameters.
Iustin Pop's avatar
Iustin Pop committed
677

Iustin Pop's avatar
Iustin Pop committed
678
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
679

Iustin Pop's avatar
Iustin Pop committed
680
    """
681
682
    return self._MultiNodeCall(node_list, "node_verify",
                               [checkdict, cluster_name])
Iustin Pop's avatar
Iustin Pop committed
683

684
  @classmethod
685
  def call_node_start_master(cls, node, start_daemons, no_voting):
Iustin Pop's avatar
Iustin Pop committed
686
    """Tells a node to activate itself as a master.
Iustin Pop's avatar
Iustin Pop committed
687

Iustin Pop's avatar
Iustin Pop committed
688
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
689

Iustin Pop's avatar
Iustin Pop committed
690
    """
691
    return cls._StaticSingleNodeCall(node, "node_start_master",
692
                                     [start_daemons, no_voting])
Iustin Pop's avatar
Iustin Pop committed
693

694
695
  @classmethod
  def call_node_stop_master(cls, node, stop_daemons):
Iustin Pop's avatar
Iustin Pop committed
696
    """Tells a node to demote itself from master status.
Iustin Pop's avatar
Iustin Pop committed
697

Iustin Pop's avatar
Iustin Pop committed
698
    This is a single-node call.
699

Iustin Pop's avatar
Iustin Pop committed
700
    """
701
    return cls._StaticSingleNodeCall(node, "node_stop_master", [stop_daemons])
702

703
704
  @classmethod
  def call_master_info(cls, node_list):
Iustin Pop's avatar
Iustin Pop committed
705
    """Query master info.
706

Iustin Pop's avatar
Iustin Pop committed
707
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
708

Iustin Pop's avatar
Iustin Pop committed
709
710
    """
    # TODO: should this method query down nodes?
711
    return cls._StaticMultiNodeCall(node_list, "master_info", [])
Iustin Pop's avatar
Iustin Pop committed
712

Iustin Pop's avatar
Iustin Pop committed
713
714
  def call_version(self, node_list):
    """Query node version.
Iustin Pop's avatar
Iustin Pop committed
715

Iustin Pop's avatar
Iustin Pop committed
716
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
717

Iustin Pop's avatar
Iustin Pop committed
718
    """
719
    return self._MultiNodeCall(node_list, "version", [])
Iustin Pop's avatar
Iustin Pop committed
720

Iustin Pop's avatar
Iustin Pop committed
721
722
  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
723

Iustin Pop's avatar
Iustin Pop committed
724
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
725

Iustin Pop's avatar
Iustin Pop committed
726
    """
727
728
    return self._SingleNodeCall(node, "blockdev_create",
                                [bdev.ToDict(), size, owner, on_primary, info])
Iustin Pop's avatar
Iustin Pop committed
729

Iustin Pop's avatar
Iustin Pop committed
730
731
  def call_blockdev_remove(self, node, bdev):
    """Request removal of a given block device.
Iustin Pop's avatar
Iustin Pop committed
732

Iustin Pop's avatar
Iustin Pop committed
733
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
734

Iustin Pop's avatar
Iustin Pop committed
735
    """
736
    return self._SingleNodeCall(node, "blockdev_remove", [bdev.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
737

Iustin Pop's avatar
Iustin Pop committed
738
739
  def call_blockdev_rename(self, node, devlist):
    """Request rename of the given block devices.
Iustin Pop's avatar
Iustin Pop committed
740

Iustin Pop's avatar
Iustin Pop committed
741
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
742

Iustin Pop's avatar
Iustin Pop committed
743
    """
744
745
    return self._SingleNodeCall(node, "blockdev_rename",
                                [(d.ToDict(), uid) for d, uid in devlist])
Iustin Pop's avatar
Iustin Pop committed
746

Iustin Pop's avatar
Iustin Pop committed
747
748
  def call_blockdev_assemble(self, node, disk, owner, on_primary):
    """Request assembling of a given block device.
Iustin Pop's avatar
Iustin Pop committed
749

Iustin Pop's avatar
Iustin Pop committed
750
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
751

Iustin Pop's avatar
Iustin Pop committed
752
    """
753
754
    return self._SingleNodeCall(node, "blockdev_assemble",
                                [disk.ToDict(), owner, on_primary])
Iustin Pop's avatar
Iustin Pop committed
755

Iustin Pop's avatar
Iustin Pop committed
756
757
  def call_blockdev_shutdown(self, node, disk):
    """Request shutdown of a given block device.
Iustin Pop's avatar
Iustin Pop committed
758

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

Iustin Pop's avatar
Iustin Pop committed
761
    """
762
    return self._SingleNodeCall(node, "blockdev_shutdown", [disk.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
763

Iustin Pop's avatar
Iustin Pop committed
764
765
  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
766

Iustin Pop's avatar
Iustin Pop committed
767
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
768

Iustin Pop's avatar
Iustin Pop committed
769
    """
770
771
772
    return self._SingleNodeCall(node, "blockdev_addchildren",
                                [bdev.ToDict(),
                                 [disk.ToDict() for disk in ndevs]])
Iustin Pop's avatar
Iustin Pop committed
773

Iustin Pop's avatar
Iustin Pop committed
774
775
  def call_blockdev_removechildren(self, node, bdev, ndevs):
    """Request removing a list of children from a (mirroring) device.
Iustin Pop's avatar
Iustin Pop committed
776

Iustin Pop's avatar
Iustin Pop committed
777
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
778

Iustin Pop's avatar
Iustin Pop committed
779
    """
780
781
782
    return self._SingleNodeCall(node, "blockdev_removechildren",
                                [bdev.ToDict(),
                                 [disk.ToDict() for disk in ndevs]])
Iustin Pop's avatar
Iustin Pop committed
783

Iustin Pop's avatar
Iustin Pop committed
784
785
  def call_blockdev_getmirrorstatus(self, node, disks):
    """Request status of a (mirroring) device.
Iustin Pop's avatar
Iustin Pop committed
786

Iustin Pop's avatar
Iustin Pop committed
787
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
788

Iustin Pop's avatar
Iustin Pop committed
789
    """
790
791
    return self._SingleNodeCall(node, "blockdev_getmirrorstatus",
                                [dsk.ToDict() for dsk in disks])
Iustin Pop's avatar
Iustin Pop committed
792

Iustin Pop's avatar
Iustin Pop committed
793
794
795
796
  def call_blockdev_find(self, node, disk):
    """Request identification of a given block device.

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

Iustin Pop's avatar
Iustin Pop committed
798
    """
799
    return self._SingleNodeCall(node, "blockdev_find", [disk.ToDict()])
800

801
  def call_blockdev_close(self, node, instance_name, disks):
Iustin Pop's avatar
Iustin Pop committed
802
    """Closes the given block devices.
803

Iustin Pop's avatar
Iustin Pop committed
804
    This is a single-node call.
805

Iustin Pop's avatar
Iustin Pop committed
806
    """
807
808
    params = [instance_name, [cf.ToDict() for cf in disks]]
    return self._SingleNodeCall(node, "blockdev_close", params)
Iustin Pop's avatar
Iustin Pop committed
809

810
811
812
813
814
815
816
817
818
  def call_blockdev_getsizes(self, node, disks):
    """Returns the size of the given disks.

    This is a single-node call.

    """
    params = [[cf.ToDict() for cf in disks]]
    return self._SingleNodeCall(node, "blockdev_getsize", params)

Iustin Pop's avatar
Iustin Pop committed
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
  def call_drbd_disconnect_net(self, node_list, nodes_ip, disks):
    """Disconnects the network of the given drbd devices.

    This is a multi-node call.

    """
    return self._MultiNodeCall(node_list, "drbd_disconnect_net",
                               [nodes_ip, [cf.ToDict() for cf in disks]])

  def call_drbd_attach_net(self, node_list, nodes_ip,
                           disks, instance_name, multimaster):
    """Disconnects the given drbd devices.

    This is a multi-node call.

    """
    return self._MultiNodeCall(node_list, "drbd_attach_net",
                               [nodes_ip, [cf.ToDict() for cf in disks],
                                instance_name, multimaster])

  def call_drbd_wait_sync(self, node_list, nodes_ip, disks):
    """Waits for the synchronization of drbd devices is complete.

    This is a multi-node call.

    """
    return self._MultiNodeCall(node_list, "drbd_wait_sync",
                               [nodes_ip, [cf.ToDict() for cf in disks]])

848
849
  @classmethod
  def call_upload_file(cls, node_list, file_name, address_list=None):
Iustin Pop's avatar
Iustin Pop committed
850
851
852
853
854
855
    """Upload a file.

    The node will refuse the operation in case the file is not on the
    approved file list.

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

857
858
859
860
861
862
863
864
    @type node_list: list
    @param node_list: the list of node names to upload to
    @type file_name: str
    @param file_name: the filename to upload
    @type address_list: list or None
    @keyword address_list: an optional list of node addresses, in order
        to optimize the RPC speed

Iustin Pop's avatar
Iustin Pop committed
865
    """
866
867
    file_contents = utils.ReadFile(file_name)
    data = cls._Compress(file_contents)
Iustin Pop's avatar
Iustin Pop committed
868
869
870
    st = os.stat(file_name)
    params = [file_name, data, st.st_mode, st.st_uid, st.st_gid,
              st.st_atime, st.st_mtime]
871
872
    return cls._StaticMultiNodeCall(node_list, "upload_file", params,
                                    address_list=address_list)
Iustin Pop's avatar
Iustin Pop committed
873

874
  @classmethod
875
  def call_write_ssconf_files(cls, node_list, values):
876
877
878
879
880
    """Write ssconf files.

    This is a multi-node call.

    """
881
    return cls._StaticMultiNodeCall(node_list, "write_ssconf_files", [values])
882

Iustin Pop's avatar
Iustin Pop committed
883
884
885
886
  def call_os_diagnose(self, node_list):
    """Request a diagnose of OS definitions.

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

Iustin Pop's avatar
Iustin Pop committed
888
    """
889
890
    result = self._MultiNodeCall(node_list, "os_diagnose", [])

891
    for node_result in result.values():
892
893
894
895
      if not node_result.failed and node_result.data:
        node_result.data = [objects.OS.FromDict(oss)
                            for oss in node_result.data]
    return result
Iustin Pop's avatar
Iustin Pop committed
896

Iustin Pop's avatar
Iustin Pop committed
897
898
  def call_os_get(self, node, name):
    """Returns an OS definition.
Iustin Pop's avatar
Iustin Pop committed
899

Iustin Pop's avatar
Iustin Pop committed
900
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
901

Iustin Pop's avatar
Iustin Pop committed
902
    """
903
    result = self._SingleNodeCall(node, "os_get", [name])
904
905
906
    if not result.failed and isinstance(result.data, dict):
      result.data = objects.OS.FromDict(result.data)
    return result
Iustin Pop's avatar
Iustin Pop committed
907

Iustin Pop's avatar
Iustin Pop committed
908
909
  def call_hooks_runner(self, node_list, hpath, phase, env):
    """Call the hooks runner.
Iustin Pop's avatar
Iustin Pop committed
910

Iustin Pop's avatar
Iustin Pop committed
911
912
913
    Args:
      - op: the OpCode instance
      - env: a dictionary with the environment
Iustin Pop's avatar
Iustin Pop committed
914

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

Iustin Pop's avatar
Iustin Pop committed
917
918
    """
    params = [hpath, phase, env]
919
    return self._MultiNodeCall(node_list, "hooks_runner", params)
Iustin Pop's avatar
Iustin Pop committed
920

Iustin Pop's avatar
Iustin Pop committed
921
922
  def call_iallocator_runner(self, node, name, idata):
    """Call an iallocator on a remote node
923

Iustin Pop's avatar
Iustin Pop committed
924
925
926
    Args:
      - name: the iallocator name
      - input: the json-encoded input string
927

Iustin Pop's avatar
Iustin Pop committed
928
    This is a single-node call.
929

Iustin Pop's avatar
Iustin Pop committed
930
    """
931
    return self._SingleNodeCall(node, "iallocator_runner", [name, idata])
932

Iustin Pop's avatar
Iustin Pop committed
933
934
  def call_blockdev_grow(self, node, cf_bdev, amount):
    """Request a snapshot of the given block device.
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
940
    return self._SingleNodeCall(node, "blockdev_grow",
                                [cf_bdev.ToDict(), amount])
941

Iustin Pop's avatar
Iustin Pop committed
942
943
  def call_blockdev_snapshot(self, node, cf_bdev):
    """Request a snapshot of the given block device.
Iustin Pop's avatar
Iustin Pop committed
944

Iustin Pop's avatar
Iustin Pop committed
945
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
946

Iustin Pop's avatar
Iustin Pop committed
947
    """
948
    return self._SingleNodeCall(node, "blockdev_snapshot", [cf_bdev.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
949

Iustin Pop's avatar
Iustin Pop committed
950
  def call_snapshot_export(self, node, snap_bdev, dest_node, instance,
951
                           cluster_name, idx):
Iustin Pop's avatar
Iustin Pop committed
952
    """Request the export of a given snapshot.
Iustin Pop's avatar
Iustin Pop committed
953

Iustin Pop's avatar
Iustin Pop committed
954
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
955

Iustin Pop's avatar
Iustin Pop committed
956
    """
957
958
959
    return self._SingleNodeCall(node, "snapshot_export",
                                [snap_bdev.ToDict(), dest_node,
                                 self._InstDict(instance), cluster_name, idx])
Iustin Pop's avatar
Iustin Pop committed
960

Iustin Pop's avatar
Iustin Pop committed
961
962
  def call_finalize_export(self, node, instance, snap_disks):
    """Request the completion of an export operation.
Iustin Pop's avatar
Iustin Pop committed
963

Iustin Pop's avatar
Iustin Pop committed
964
    This writes the export config file, etc.
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
    """
    flat_disks = []
    for disk in snap_disks:
971
972
973
974
      if isinstance(disk, bool):
        flat_disks.append(disk)
      else:
        flat_disks.append(disk.ToDict())
975
976
977

    return self._SingleNodeCall(node, "finalize_export",
                                [self._InstDict(instance), flat_disks])
Iustin Pop's avatar
Iustin Pop committed
978

Iustin Pop's avatar
Iustin Pop committed
979
980
  def call_export_info(self, node, path):
    """Queries the export information in a given path.
Iustin Pop's avatar
Iustin Pop committed
981

Iustin Pop's avatar