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

45
46
import ganeti.http.client

Iustin Pop's avatar
Iustin Pop committed
47

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


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

  Must be called before using any RPC function.

  """
  global _http_manager

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

62
  _http_manager = http.client.HttpClientManager()
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77


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

  Must be called before quitting the program.

  """
  global _http_manager

  if _http_manager:
    _http_manager.Shutdown()
    _http_manager = None


78
79
80
81
82
83
84
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
85
  @ivar data: the data payload, for successful results, or None
86
  @type failed: boolean
87
  @ivar failed: whether the operation failed at transport level (not
88
89
90
91
92
93
94
      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
95
  @ivar fail_msg: the error message if the call failed
96

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
    self.offline = offline
    self.call = call
    self.node = node
    if offline:
      self.failed = True
106
      self.fail_msg = "Node is marked offline"
107
      self.data = self.payload = None
108
    elif failed:
109
      self.fail_msg = self._EnsureErr(data)
110
      self.data = self.payload = None
111
112
    else:
      self.data = data
113
      if not isinstance(self.data, (tuple, list)):
114
115
        self.fail_msg = ("RPC layer error: invalid result type (%s)" %
                         type(self.data))
116
      elif len(data) != 2:
117
118
        self.fail_msg = ("RPC layer error: invalid result length (%d), "
                         "expected 2" % len(self.data))
119
      elif not self.data[0]:
120
        self.fail_msg = self._EnsureErr(self.data[1])
121
      else:
122
        # finally success
123
        self.fail_msg = None
124
125
126
127
128
129
130
131
132
        self.payload = data[1]

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

134
  def Raise(self, msg, prereq=False):
135
136
137
138
139
140
    """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.

    """
141
142
143
144
145
146
147
148
149
150
151
152
153
    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
    raise ec(msg)
154

155
156
157
  def RemoteFailMsg(self):
    """Check if the remote procedure failed.

158
    @return: the fail_msg attribute
159
160

    """
161
    return self.fail_msg
162

163

Iustin Pop's avatar
Iustin Pop committed
164
165
166
class Client:
  """RPC Client class.

Alexander Schreiber's avatar
Alexander Schreiber committed
167
  This class, given a (remote) method name, a list of parameters and a
Iustin Pop's avatar
Iustin Pop committed
168
169
170
  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
171
  One current bug is that generic failure is still signaled by
Iustin Pop's avatar
Iustin Pop committed
172
173
174
175
  'False' result, which is not good. This overloading of values can
  cause bugs.

  """
176
  def __init__(self, procedure, body, port):
Iustin Pop's avatar
Iustin Pop committed
177
    self.procedure = procedure
178
179
    self.body = body
    self.port = port
180
    self.nc = {}
Iustin Pop's avatar
Iustin Pop committed
181

182
183
184
185
    self._ssl_params = \
      http.HttpSslParams(ssl_key_path=constants.SSL_CERT_FILE,
                         ssl_cert_path=constants.SSL_CERT_FILE)

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

189
190
    @type node_list: list
    @param node_list: the list of node names to connect
191
192
193
    @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
194

Iustin Pop's avatar
Iustin Pop committed
195
    """
196
197
198
199
200
201
202
203
204
    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
205
206
    """Add a node to the target list.

207
208
209
210
211
    @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
212
    """
213
214
215
    if address is None:
      address = name

216
217
218
219
220
221
    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
222

223
  def GetResults(self):
224
225
226
    """Call nodes and return results.

    @rtype: list
Iustin Pop's avatar
Iustin Pop committed
227
    @return: List of RPC results
Iustin Pop's avatar
Iustin Pop committed
228
229

    """
Michael Hanselmann's avatar
Michael Hanselmann committed
230
    assert _http_manager, "RPC module not initialized"
231
232

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

234
    results = {}
Iustin Pop's avatar
Iustin Pop committed
235

236
    for name, req in self.nc.iteritems():
237
      if req.success and req.resp_status_code == http.HTTP_OK:
238
239
        results[name] = RpcResult(data=serializer.LoadJson(req.resp_body),
                                  node=name, call=self.procedure)
240
        continue
Iustin Pop's avatar
Iustin Pop committed
241

242
      # TODO: Better error reporting
243
244
245
246
247
      if req.error:
        msg = req.error
      else:
        msg = req.resp_body

248
249
      logging.error("RPC error in %s from node %s: %s",
                    self.procedure, name, msg)
250
251
      results[name] = RpcResult(data=msg, failed=True, node=name,
                                call=self.procedure)
252
253

    return results
Iustin Pop's avatar
Iustin Pop committed
254
255


Iustin Pop's avatar
Iustin Pop committed
256
257
class RpcRunner(object):
  """RPC runner class"""
Iustin Pop's avatar
Iustin Pop committed
258

Iustin Pop's avatar
Iustin Pop committed
259
260
  def __init__(self, cfg):
    """Initialized the rpc runner.
Iustin Pop's avatar
Iustin Pop committed
261

Iustin Pop's avatar
Iustin Pop committed
262
263
264
    @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
265

Iustin Pop's avatar
Iustin Pop committed
266
267
    """
    self._cfg = cfg
268
    self.port = utils.GetDaemonPort(constants.NODED)
Iustin Pop's avatar
Iustin Pop committed
269

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

    """
    idict = instance.ToDict()
288
289
    cluster = self._cfg.GetClusterInfo()
    idict["hvparams"] = cluster.FillHV(instance)
290
291
    if hvp is not None:
      idict["hvparams"].update(hvp)
292
    idict["beparams"] = cluster.FillBE(instance)
293
294
    if bep is not None:
      idict["beparams"].update(bep)
295
296
297
298
    for nic in idict["nics"]:
      nic['nicparams'] = objects.FillDict(
        cluster.nicparams[constants.PP_DEFAULT],
        nic['nicparams'])
299
300
    return idict

301
  def _ConnectList(self, client, node_list, call):
302
303
    """Helper for computing node addresses.

Iustin Pop's avatar
Iustin Pop committed
304
    @type client: L{ganeti.rpc.Client}
305
306
307
    @param client: a C{Client} instance
    @type node_list: list
    @param node_list: the node list we should connect
308
309
310
    @type call: string
    @param call: the name of the remote procedure call, for filling in
        correctly any eventual offline nodes' results
311
312
313

    """
    all_nodes = self._cfg.GetAllNodesInfo()
314
    name_list = []
315
    addr_list = []
316
    skip_dict = {}
317
318
    for node in node_list:
      if node in all_nodes:
319
        if all_nodes[node].offline:
320
          skip_dict[node] = RpcResult(node=node, offline=True, call=call)
321
          continue
322
323
324
325
        val = all_nodes[node].primary_ip
      else:
        val = None
      addr_list.append(val)
326
327
328
329
      name_list.append(node)
    if name_list:
      client.ConnectList(name_list, address_list=addr_list)
    return skip_dict
330

331
  def _ConnectNode(self, client, node, call):
332
333
    """Helper for computing one node's address.

Iustin Pop's avatar
Iustin Pop committed
334
    @type client: L{ganeti.rpc.Client}
335
336
337
    @param client: a C{Client} instance
    @type node: str
    @param node: the node we should connect
338
339
340
    @type call: string
    @param call: the name of the remote procedure call, for filling in
        correctly any eventual offline nodes' results
341
342
343
344

    """
    node_info = self._cfg.GetNodeInfo(node)
    if node_info is not None:
345
      if node_info.offline:
346
        return RpcResult(node=node, offline=True, call=call)
347
348
349
350
351
      addr = node_info.primary_ip
    else:
      addr = None
    client.ConnectNode(node, address=addr)

352
  def _MultiNodeCall(self, node_list, procedure, args):
353
354
355
356
357
    """Helper for making a multi-node call

    """
    body = serializer.DumpJson(args, indent=False)
    c = Client(procedure, body, self.port)
358
    skip_dict = self._ConnectList(c, node_list, procedure)
359
360
    skip_dict.update(c.GetResults())
    return skip_dict
361
362
363
364

  @classmethod
  def _StaticMultiNodeCall(cls, node_list, procedure, args,
                           address_list=None):
365
366
367
368
    """Helper for making a multi-node static call

    """
    body = serializer.DumpJson(args, indent=False)
369
    c = Client(procedure, body, utils.GetDaemonPort(constants.NODED))
370
371
372
373
    c.ConnectList(node_list, address_list=address_list)
    return c.GetResults()

  def _SingleNodeCall(self, node, procedure, args):
374
    """Helper for making a single-node call
375
376

    """
377
378
    body = serializer.DumpJson(args, indent=False)
    c = Client(procedure, body, self.port)
379
    result = self._ConnectNode(c, node, procedure)
380
381
382
383
    if result is None:
      # we did connect, node is not offline
      result = c.GetResults()[node]
    return result
384
385
386

  @classmethod
  def _StaticSingleNodeCall(cls, node, procedure, args):
387
    """Helper for making a single-node static call
388
389

    """
390
    body = serializer.DumpJson(args, indent=False)
391
    c = Client(procedure, body, utils.GetDaemonPort(constants.NODED))
392
    c.ConnectNode(node)
393
    return c.GetResults()[node]
394

395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
  @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)))

415
416
417
418
  #
  # Begin RPC calls
  #

419
  def call_lv_list(self, node_list, vg_name):
Iustin Pop's avatar
Iustin Pop committed
420
    """Gets the logical volumes present in a given volume group.
Iustin Pop's avatar
Iustin Pop committed
421

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

Iustin Pop's avatar
Iustin Pop committed
424
    """
425
    return self._MultiNodeCall(node_list, "lv_list", [vg_name])
Iustin Pop's avatar
Iustin Pop committed
426

Iustin Pop's avatar
Iustin Pop committed
427
428
  def call_vg_list(self, node_list):
    """Gets the volume group list.
Iustin Pop's avatar
Iustin Pop committed
429

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

Iustin Pop's avatar
Iustin Pop committed
432
    """
433
    return self._MultiNodeCall(node_list, "vg_list", [])
Iustin Pop's avatar
Iustin Pop committed
434

435
  def call_storage_list(self, node_list, su_name, su_args, name, fields):
436
    """Get list of storage units.
437
438
439
440
441
442
443

    This is a multi-node call.

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

444
445
446
447
448
449
450
451
452
  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])

453
454
455
456
457
458
459
460
461
  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])

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

Iustin Pop's avatar
Iustin Pop committed
465
466
467
    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
468

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

Iustin Pop's avatar
Iustin Pop committed
471
    """
472
    return self._SingleNodeCall(node, "bridges_exist", [bridges_list])
Iustin Pop's avatar
Iustin Pop committed
473

474
  def call_instance_start(self, node, instance, hvp, bep):
Iustin Pop's avatar
Iustin Pop committed
475
    """Starts an instance.
Iustin Pop's avatar
Iustin Pop committed
476

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

Iustin Pop's avatar
Iustin Pop committed
479
    """
480
481
    idict = self._InstDict(instance, hvp=hvp, bep=bep)
    return self._SingleNodeCall(node, "instance_start", [idict])
Iustin Pop's avatar
Iustin Pop committed
482

Iustin Pop's avatar
Iustin Pop committed
483
484
  def call_instance_shutdown(self, node, instance):
    """Stops an instance.
Iustin Pop's avatar
Iustin Pop committed
485

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

Iustin Pop's avatar
Iustin Pop committed
488
    """
489
490
    return self._SingleNodeCall(node, "instance_shutdown",
                                [self._InstDict(instance)])
491

492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
  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
545
546
  def call_instance_migrate(self, node, instance, target, live):
    """Migrate an instance.
547

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

Iustin Pop's avatar
Iustin Pop committed
550
551
552
553
554
555
556
557
558
    @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)
559

Iustin Pop's avatar
Iustin Pop committed
560
    """
561
562
    return self._SingleNodeCall(node, "instance_migrate",
                                [self._InstDict(instance), target, live])
563

564
  def call_instance_reboot(self, node, instance, reboot_type):
Iustin Pop's avatar
Iustin Pop committed
565
    """Reboots an instance.
566

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

Iustin Pop's avatar
Iustin Pop committed
569
    """
570
    return self._SingleNodeCall(node, "instance_reboot",
571
                                [self._InstDict(instance), reboot_type])
Iustin Pop's avatar
Iustin Pop committed
572

573
  def call_instance_os_add(self, node, inst, reinstall):
Iustin Pop's avatar
Iustin Pop committed
574
    """Installs an OS on the given instance.
Iustin Pop's avatar
Iustin Pop committed
575

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

Iustin Pop's avatar
Iustin Pop committed
578
    """
579
    return self._SingleNodeCall(node, "instance_os_add",
580
                                [self._InstDict(inst), reinstall])
581

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

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

Iustin Pop's avatar
Iustin Pop committed
587
    """
588
589
    return self._SingleNodeCall(node, "instance_run_rename",
                                [self._InstDict(inst), old_name])
Iustin Pop's avatar
Iustin Pop committed
590

Iustin Pop's avatar
Iustin Pop committed
591
592
  def call_instance_info(self, node, instance, hname):
    """Returns information about a single instance.
Iustin Pop's avatar
Iustin Pop committed
593

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

596
597
    @type node: list
    @param node: the list of nodes to query
Iustin Pop's avatar
Iustin Pop committed
598
599
600
601
    @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
602

Iustin Pop's avatar
Iustin Pop committed
603
    """
604
    return self._SingleNodeCall(node, "instance_info", [instance, hname])
605

606
607
608
609
610
611
612
613
614
615
616
617
618
619
  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
620
621
  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
622

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

Iustin Pop's avatar
Iustin Pop committed
625
626
627
628
    @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
629

Iustin Pop's avatar
Iustin Pop committed
630
    """
631
632
    return self._MultiNodeCall(node_list, "all_instances_info",
                               [hypervisor_list])
633

Iustin Pop's avatar
Iustin Pop committed
634
635
  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
636

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

Iustin Pop's avatar
Iustin Pop committed
639
640
641
642
    @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
643

Iustin Pop's avatar
Iustin Pop committed
644
    """
645
    return self._MultiNodeCall(node_list, "instance_list", [hypervisor_list])
646

Iustin Pop's avatar
Iustin Pop committed
647
648
649
  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
650

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

Iustin Pop's avatar
Iustin Pop committed
653
    """
654
655
    return self._SingleNodeCall(node, "node_tcp_ping",
                                [source, target, port, timeout,
Iustin Pop's avatar
Iustin Pop committed
656
                                 live_port_needed])
Iustin Pop's avatar
Iustin Pop committed
657

658
659
660
661
662
663
  def call_node_has_ip_address(self, node, address):
    """Checks if a node has the given IP address.

    This is a single-node call.

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

Iustin Pop's avatar
Iustin Pop committed
666
667
  def call_node_info(self, node_list, vg_name, hypervisor_type):
    """Return node information.
668

Iustin Pop's avatar
Iustin Pop committed
669
670
    This will return memory information and volume group size and free
    space.
Iustin Pop's avatar
Iustin Pop committed
671

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

Iustin Pop's avatar
Iustin Pop committed
674
675
    @type node_list: list
    @param node_list: the list of nodes to query
Iustin Pop's avatar
Iustin Pop committed
676
677
    @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
678
679
680
681
        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
682

Iustin Pop's avatar
Iustin Pop committed
683
    """
684
685
    return self._MultiNodeCall(node_list, "node_info",
                               [vg_name, hypervisor_type])
Iustin Pop's avatar
Iustin Pop committed
686

Iustin Pop's avatar
Iustin Pop committed
687
688
  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
689

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

Iustin Pop's avatar
Iustin Pop committed
692
    """
693
694
    return self._SingleNodeCall(node, "node_add",
                                [dsa, dsapub, rsa, rsapub, ssh, sshpub])
Iustin Pop's avatar
Iustin Pop committed
695

Iustin Pop's avatar
Iustin Pop committed
696
697
  def call_node_verify(self, node_list, checkdict, cluster_name):
    """Request verification of given parameters.
Iustin Pop's avatar
Iustin Pop committed
698

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

Iustin Pop's avatar
Iustin Pop committed
701
    """
702
703
    return self._MultiNodeCall(node_list, "node_verify",
                               [checkdict, cluster_name])
Iustin Pop's avatar
Iustin Pop committed
704

705
  @classmethod
706
  def call_node_start_master(cls, node, start_daemons, no_voting):
Iustin Pop's avatar
Iustin Pop committed
707
    """Tells a node to activate itself as a master.
Iustin Pop's avatar
Iustin Pop committed
708

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

Iustin Pop's avatar
Iustin Pop committed
711
    """
712
    return cls._StaticSingleNodeCall(node, "node_start_master",
713
                                     [start_daemons, no_voting])
Iustin Pop's avatar
Iustin Pop committed
714

715
716
  @classmethod
  def call_node_stop_master(cls, node, stop_daemons):
Iustin Pop's avatar
Iustin Pop committed
717
    """Tells a node to demote itself from master status.
Iustin Pop's avatar
Iustin Pop committed
718

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

Iustin Pop's avatar
Iustin Pop committed
721
    """
722
    return cls._StaticSingleNodeCall(node, "node_stop_master", [stop_daemons])
723

724
725
  @classmethod
  def call_master_info(cls, node_list):
Iustin Pop's avatar
Iustin Pop committed
726
    """Query master info.
727

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

Iustin Pop's avatar
Iustin Pop committed
730
731
    """
    # TODO: should this method query down nodes?
732
    return cls._StaticMultiNodeCall(node_list, "master_info", [])
Iustin Pop's avatar
Iustin Pop committed
733

Iustin Pop's avatar
Iustin Pop committed
734
735
  def call_version(self, node_list):
    """Query node version.
Iustin Pop's avatar
Iustin Pop committed
736

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

Iustin Pop's avatar
Iustin Pop committed
739
    """
740
    return self._MultiNodeCall(node_list, "version", [])
Iustin Pop's avatar
Iustin Pop committed
741

Iustin Pop's avatar
Iustin Pop committed
742
743
  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
744

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

Iustin Pop's avatar
Iustin Pop committed
747
    """
748
749
    return self._SingleNodeCall(node, "blockdev_create",
                                [bdev.ToDict(), size, owner, on_primary, info])
Iustin Pop's avatar
Iustin Pop committed
750

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

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

Iustin Pop's avatar
Iustin Pop committed
756
    """
757
    return self._SingleNodeCall(node, "blockdev_remove", [bdev.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
758

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

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

Iustin Pop's avatar
Iustin Pop committed
764
    """
765
766
    return self._SingleNodeCall(node, "blockdev_rename",
                                [(d.ToDict(), uid) for d, uid in devlist])
Iustin Pop's avatar
Iustin Pop committed
767

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

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

Iustin Pop's avatar
Iustin Pop committed
773
    """
774
775
    return self._SingleNodeCall(node, "blockdev_assemble",
                                [disk.ToDict(), owner, on_primary])
Iustin Pop's avatar
Iustin Pop committed
776

Iustin Pop's avatar
Iustin Pop committed
777
778
  def call_blockdev_shutdown(self, node, disk):
    """Request shutdown of a given block device.
Iustin Pop's avatar
Iustin Pop committed
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, "blockdev_shutdown", [disk.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
784

Iustin Pop's avatar
Iustin Pop committed
785
786
  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
787

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

Iustin Pop's avatar
Iustin Pop committed
790
    """
791
792
793
    return self._SingleNodeCall(node, "blockdev_addchildren",
                                [bdev.ToDict(),
                                 [disk.ToDict() for disk in ndevs]])
Iustin Pop's avatar
Iustin Pop committed
794

Iustin Pop's avatar
Iustin Pop committed
795
796
  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
797

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

Iustin Pop's avatar
Iustin Pop committed
800
    """
801
802
803
    return self._SingleNodeCall(node, "blockdev_removechildren",
                                [bdev.ToDict(),
                                 [disk.ToDict() for disk in ndevs]])
Iustin Pop's avatar
Iustin Pop committed
804

Iustin Pop's avatar
Iustin Pop committed
805
806
  def call_blockdev_getmirrorstatus(self, node, disks):
    """Request status of a (mirroring) device.
Iustin Pop's avatar
Iustin Pop committed
807

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

Iustin Pop's avatar
Iustin Pop committed
810
    """
811
812
813
814
815
816
    result = self._SingleNodeCall(node, "blockdev_getmirrorstatus",
                                  [dsk.ToDict() for dsk in disks])
    if not result.failed:
      result.payload = [objects.BlockDevStatus.FromDict(i)
                        for i in result.payload]
    return result
Iustin Pop's avatar
Iustin Pop committed
817

Iustin Pop's avatar
Iustin Pop committed
818
819
820
821
  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
822

Iustin Pop's avatar
Iustin Pop committed
823
    """
824
825
826
827
    result = self._SingleNodeCall(node, "blockdev_find", [disk.ToDict()])
    if not result.failed and result.payload is not None:
      result.payload = objects.BlockDevStatus.FromDict(result.payload)
    return result
828

829
  def call_blockdev_close(self, node, instance_name, disks):
Iustin Pop's avatar
Iustin Pop committed
830
    """Closes the given block devices.
831

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

Iustin Pop's avatar
Iustin Pop committed
834
    """
835
836
    params = [instance_name, [cf.ToDict() for cf in disks]]
    return self._SingleNodeCall(node, "blockdev_close", params)
Iustin Pop's avatar
Iustin Pop committed
837

838
839
840
841
842
843
844
845
846
  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
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
  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]])

876
877
  @classmethod
  def call_upload_file(cls, node_list, file_name, address_list=None):
Iustin Pop's avatar
Iustin Pop committed
878
879
880
881
882
883
    """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
884

885
886
887
888
889
890
891
892
    @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
893
    """
894
895
    file_contents = utils.ReadFile(file_name)
    data = cls._Compress(file_contents)
Iustin Pop's avatar
Iustin Pop committed
896
897
898
    st = os.stat(file_name)
    params = [file_name, data, st.st_mode, st.st_uid, st.st_gid,
              st.st_atime, st.st_mtime]
899
900
    return cls._StaticMultiNodeCall(node_list, "upload_file", params,
                                    address_list=address_list)
Iustin Pop's avatar
Iustin Pop committed
901

902
  @classmethod
903
  def call_write_ssconf_files(cls, node_list, values):
904
905
906
907
908
    """Write ssconf files.

    This is a multi-node call.

    """
909
    return cls._StaticMultiNodeCall(node_list, "write_ssconf_files", [values])
910

Iustin Pop's avatar
Iustin Pop committed
911
912
913
914
  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
915

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

Iustin Pop's avatar
Iustin Pop committed
919
920
  def call_os_get(self, node, name):
    """Returns an OS definition.
Iustin Pop's avatar
Iustin Pop committed
921

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

Iustin Pop's avatar
Iustin Pop committed
924
    """
925
    result = self._SingleNodeCall(node, "os_get", [name])
926
927
928
    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
929

Iustin Pop's avatar
Iustin Pop committed
930
931
  def call_hooks_runner(self, node_list, hpath, phase, env):
    """Call the hooks runner.
Iustin Pop's avatar
Iustin Pop committed
932

Iustin Pop's avatar
Iustin Pop committed
933
934
935
    Args:
      - op: the OpCode instance
      - env: a dictionary with the environment
Iustin Pop's avatar
Iustin Pop committed
936

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

Iustin Pop's avatar
Iustin Pop committed
939
940
    """
    params = [hpath, phase, env]
941
    return self._MultiNodeCall(node_list, "hooks_runner", params)
Iustin Pop's avatar
Iustin Pop committed
942

Iustin Pop's avatar
Iustin Pop committed
943
944
  def call_iallocator_runner(self, node, name, idata):
    """Call an iallocator on a remote node
945

Iustin Pop's avatar
Iustin Pop committed
946
947
948
    Args:
      - name: the iallocator name
      - input: the json-encoded input string
949

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

Iustin Pop's avatar
Iustin Pop committed
952
    """
953
    return self._SingleNodeCall(node, "iallocator_runner", [name, idata])
954

Iustin Pop's avatar
Iustin Pop committed
955
956
  def call_blockdev_grow(self, node, cf_bdev, amount):
    """Request a snapshot of the given block device.
957

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

Iustin Pop's avatar
Iustin Pop committed
960
    """
961
962
    return self._SingleNodeCall(node, "blockdev_grow",
                                [cf_bdev.ToDict(), amount])
963

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

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

Iustin Pop's avatar
Iustin Pop committed
969
    """
970
    return self._SingleNodeCall(node, "blockdev_snapshot", [cf_bdev.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
971

Iustin Pop's avatar
Iustin Pop committed
972
  def call_snapshot_export(self, node, snap_bdev, dest_node, instance,
973
                           cluster_name, idx):