rpc.py 34.9 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
87
88
89
90
91
  @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
92
  @ivar fail_msg: the error message if the call failed
93

94
  """
95
96
97
98
99
  def __init__(self, data=None, failed=False, offline=False,
               call=None, node=None):
    self.offline = offline
    self.call = call
    self.node = node
100

101
    if offline:
102
      self.fail_msg = "Node is marked offline"
103
      self.data = self.payload = None
104
    elif failed:
105
      self.fail_msg = self._EnsureErr(data)
106
      self.data = self.payload = None
107
108
    else:
      self.data = data
109
      if not isinstance(self.data, (tuple, list)):
110
111
        self.fail_msg = ("RPC layer error: invalid result type (%s)" %
                         type(self.data))
112
        self.payload = None
113
      elif len(data) != 2:
114
115
        self.fail_msg = ("RPC layer error: invalid result length (%d), "
                         "expected 2" % len(self.data))
116
        self.payload = None
117
      elif not self.data[0]:
118
        self.fail_msg = self._EnsureErr(self.data[1])
119
        self.payload = None
120
      else:
121
        # finally success
122
        self.fail_msg = None
123
124
        self.payload = data[1]

125
126
127
128
129
130
131
    assert hasattr(self, "call")
    assert hasattr(self, "data")
    assert hasattr(self, "fail_msg")
    assert hasattr(self, "node")
    assert hasattr(self, "offline")
    assert hasattr(self, "payload")

132
133
134
135
136
137
138
  @staticmethod
  def _EnsureErr(val):
    """Helper to ensure we return a 'True' value for error."""
    if val:
      return val
    else:
      return "No error information"
139

140
  def Raise(self, msg, prereq=False, ecode=None):
141
142
143
144
145
146
    """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.

    """
147
148
149
150
151
152
153
154
155
156
157
158
    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
159
160
161
162
163
    if ecode is not None:
      args = (msg, prereq)
    else:
      args = (msg, )
    raise ec(*args)
164
165


Iustin Pop's avatar
Iustin Pop committed
166
167
168
class Client:
  """RPC Client class.

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

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

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

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

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

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

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

218
219
220
221
222
223
    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
224

225
  def GetResults(self):
226
227
228
    """Call nodes and return results.

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

    """
Michael Hanselmann's avatar
Michael Hanselmann committed
232
    assert _http_manager, "RPC module not initialized"
233
234

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

236
    results = {}
Iustin Pop's avatar
Iustin Pop committed
237

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

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

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

    return results
Iustin Pop's avatar
Iustin Pop committed
256
257


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

417
418
419
420
  #
  # Begin RPC calls
  #

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

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

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

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

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

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

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

    This is a multi-node call.

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

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

455
456
457
458
459
460
461
462
463
  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
464
465
  def call_bridges_exist(self, node, bridges_list):
    """Checks if a node has all the bridges given.
Iustin Pop's avatar
Iustin Pop committed
466

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

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

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

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

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

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

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

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

Iustin Pop's avatar
Iustin Pop committed
490
    """
491
    return self._SingleNodeCall(node, "instance_shutdown",
492
                                [self._InstDict(instance), timeout])
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
545
546
  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
547
548
  def call_instance_migrate(self, node, instance, target, live):
    """Migrate an instance.
549

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

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

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

566
  def call_instance_reboot(self, node, inst, reboot_type, shutdown_timeout):
Iustin Pop's avatar
Iustin Pop committed
567
    """Reboots an instance.
568

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

Iustin Pop's avatar
Iustin Pop committed
571
    """
572
    return self._SingleNodeCall(node, "instance_reboot",
573
574
                                [self._InstDict(inst), reboot_type,
                                 shutdown_timeout])
Iustin Pop's avatar
Iustin Pop committed
575

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

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

Iustin Pop's avatar
Iustin Pop committed
581
    """
582
    return self._SingleNodeCall(node, "instance_os_add",
583
                                [self._InstDict(inst), reinstall])
584

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

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

Iustin Pop's avatar
Iustin Pop committed
590
    """
591
592
    return self._SingleNodeCall(node, "instance_run_rename",
                                [self._InstDict(inst), old_name])
Iustin Pop's avatar
Iustin Pop committed
593

Iustin Pop's avatar
Iustin Pop committed
594
595
  def call_instance_info(self, node, instance, hname):
    """Returns information about a single instance.
Iustin Pop's avatar
Iustin Pop committed
596

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

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

Iustin Pop's avatar
Iustin Pop committed
606
    """
607
    return self._SingleNodeCall(node, "instance_info", [instance, hname])
608

609
610
611
612
613
614
615
616
617
618
619
620
621
622
  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
623
624
  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
625

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

Iustin Pop's avatar
Iustin Pop committed
628
629
630
631
    @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
632

Iustin Pop's avatar
Iustin Pop committed
633
    """
634
635
    return self._MultiNodeCall(node_list, "all_instances_info",
                               [hypervisor_list])
636

Iustin Pop's avatar
Iustin Pop committed
637
638
  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
639

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

Iustin Pop's avatar
Iustin Pop committed
642
643
644
645
    @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
646

Iustin Pop's avatar
Iustin Pop committed
647
    """
648
    return self._MultiNodeCall(node_list, "instance_list", [hypervisor_list])
649

Iustin Pop's avatar
Iustin Pop committed
650
651
652
  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
653

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

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

661
662
663
664
665
666
  def call_node_has_ip_address(self, node, address):
    """Checks if a node has the given IP address.

    This is a single-node call.

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

Iustin Pop's avatar
Iustin Pop committed
669
670
  def call_node_info(self, node_list, vg_name, hypervisor_type):
    """Return node information.
671

Iustin Pop's avatar
Iustin Pop committed
672
673
    This will return memory information and volume group size and free
    space.
Iustin Pop's avatar
Iustin Pop committed
674

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

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

Iustin Pop's avatar
Iustin Pop committed
686
    """
687
688
    return self._MultiNodeCall(node_list, "node_info",
                               [vg_name, hypervisor_type])
Iustin Pop's avatar
Iustin Pop committed
689

Iustin Pop's avatar
Iustin Pop committed
690
691
  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
692

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

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

Iustin Pop's avatar
Iustin Pop committed
699
700
  def call_node_verify(self, node_list, checkdict, cluster_name):
    """Request verification of given parameters.
Iustin Pop's avatar
Iustin Pop committed
701

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

Iustin Pop's avatar
Iustin Pop committed
704
    """
705
706
    return self._MultiNodeCall(node_list, "node_verify",
                               [checkdict, cluster_name])
Iustin Pop's avatar
Iustin Pop committed
707

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

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

Iustin Pop's avatar
Iustin Pop committed
714
    """
715
    return cls._StaticSingleNodeCall(node, "node_start_master",
716
                                     [start_daemons, no_voting])
Iustin Pop's avatar
Iustin Pop committed
717

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

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

Iustin Pop's avatar
Iustin Pop committed
724
    """
725
    return cls._StaticSingleNodeCall(node, "node_stop_master", [stop_daemons])
726

727
728
  @classmethod
  def call_master_info(cls, node_list):
Iustin Pop's avatar
Iustin Pop committed
729
    """Query master info.
730

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

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

737
738
  @classmethod
  def call_version(cls, node_list):
Iustin Pop's avatar
Iustin Pop committed
739
    """Query node version.
Iustin Pop's avatar
Iustin Pop committed
740

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

Iustin Pop's avatar
Iustin Pop committed
743
    """
744
    return cls._StaticMultiNodeCall(node_list, "version", [])
Iustin Pop's avatar
Iustin Pop committed
745

Iustin Pop's avatar
Iustin Pop committed
746
747
  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
748

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

Iustin Pop's avatar
Iustin Pop committed
751
    """
752
753
    return self._SingleNodeCall(node, "blockdev_create",
                                [bdev.ToDict(), size, owner, on_primary, info])
Iustin Pop's avatar
Iustin Pop committed
754

Iustin Pop's avatar
Iustin Pop committed
755
756
  def call_blockdev_remove(self, node, bdev):
    """Request removal of a given block device.
Iustin Pop's avatar
Iustin Pop committed
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, "blockdev_remove", [bdev.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
762

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

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

Iustin Pop's avatar
Iustin Pop committed
768
    """
769
770
    return self._SingleNodeCall(node, "blockdev_rename",
                                [(d.ToDict(), uid) for d, uid in devlist])
Iustin Pop's avatar
Iustin Pop committed
771

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

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

Iustin Pop's avatar
Iustin Pop committed
777
    """
778
779
    return self._SingleNodeCall(node, "blockdev_assemble",
                                [disk.ToDict(), owner, on_primary])
Iustin Pop's avatar
Iustin Pop committed
780

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

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

Iustin Pop's avatar
Iustin Pop committed
786
    """
787
    return self._SingleNodeCall(node, "blockdev_shutdown", [disk.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
788

Iustin Pop's avatar
Iustin Pop committed
789
790
  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
791

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

Iustin Pop's avatar
Iustin Pop committed
794
    """
795
796
797
    return self._SingleNodeCall(node, "blockdev_addchildren",
                                [bdev.ToDict(),
                                 [disk.ToDict() for disk in ndevs]])
Iustin Pop's avatar
Iustin Pop committed
798

Iustin Pop's avatar
Iustin Pop committed
799
800
  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
801

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

Iustin Pop's avatar
Iustin Pop committed
804
    """
805
806
807
    return self._SingleNodeCall(node, "blockdev_removechildren",
                                [bdev.ToDict(),
                                 [disk.ToDict() for disk in ndevs]])
Iustin Pop's avatar
Iustin Pop committed
808

Iustin Pop's avatar
Iustin Pop committed
809
810
  def call_blockdev_getmirrorstatus(self, node, disks):
    """Request status of a (mirroring) device.
Iustin Pop's avatar
Iustin Pop committed
811

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

Iustin Pop's avatar
Iustin Pop committed
814
    """
815
816
    result = self._SingleNodeCall(node, "blockdev_getmirrorstatus",
                                  [dsk.ToDict() for dsk in disks])
817
    if not result.fail_msg:
818
819
820
      result.payload = [objects.BlockDevStatus.FromDict(i)
                        for i in result.payload]
    return result
Iustin Pop's avatar
Iustin Pop committed
821

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

Iustin Pop's avatar
Iustin Pop committed
827
    """
828
    result = self._SingleNodeCall(node, "blockdev_find", [disk.ToDict()])
829
    if not result.fail_msg and result.payload is not None:
830
831
      result.payload = objects.BlockDevStatus.FromDict(result.payload)
    return result
832

833
  def call_blockdev_close(self, node, instance_name, disks):
Iustin Pop's avatar
Iustin Pop committed
834
    """Closes the given block devices.
835

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

Iustin Pop's avatar
Iustin Pop committed
838
    """
839
840
    params = [instance_name, [cf.ToDict() for cf in disks]]
    return self._SingleNodeCall(node, "blockdev_close", params)
Iustin Pop's avatar
Iustin Pop committed
841

842
843
844
845
846
847
848
849
850
  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
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
876
877
878
879
  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]])

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

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

906
  @classmethod
907
  def call_write_ssconf_files(cls, node_list, values):
908
909
910
911
912
    """Write ssconf files.

    This is a multi-node call.

    """
913
    return cls._StaticMultiNodeCall(node_list, "write_ssconf_files", [values])
914

Iustin Pop's avatar
Iustin Pop committed
915
916
917
918
  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
919

Iustin Pop's avatar
Iustin Pop committed
920
    """
921
    return self._MultiNodeCall(node_list, "os_diagnose", [])
Iustin Pop's avatar
Iustin Pop committed
922

Iustin Pop's avatar
Iustin Pop committed
923
924
  def call_os_get(self, node, name):
    """Returns an OS definition.
Iustin Pop's avatar
Iustin Pop committed
925

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

Iustin Pop's avatar
Iustin Pop committed
928
    """
929
    result = self._SingleNodeCall(node, "os_get", [name])
930
931
    if not result.fail_msg and isinstance(result.payload, dict):
      result.payload = objects.OS.FromDict(result.payload)
932
    return result
Iustin Pop's avatar
Iustin Pop committed
933

Iustin Pop's avatar
Iustin Pop committed
934
935
  def call_hooks_runner(self, node_list, hpath, phase, env):
    """Call the hooks runner.
Iustin Pop's avatar
Iustin Pop committed
936

Iustin Pop's avatar
Iustin Pop committed
937
938
939
    Args:
      - op: the OpCode instance
      - env: a dictionary with the environment
Iustin Pop's avatar
Iustin Pop committed
940

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

Iustin Pop's avatar
Iustin Pop committed
943
944
    """
    params = [hpath, phase, env]
945
    return self._MultiNodeCall(node_list, "hooks_runner", params)
Iustin Pop's avatar
Iustin Pop committed
946

Iustin Pop's avatar
Iustin Pop committed
947
948
  def call_iallocator_runner(self, node, name, idata):
    """Call an iallocator on a remote node
949

Iustin Pop's avatar
Iustin Pop committed
950
951
952
    Args:
      - name: the iallocator name
      - input: the json-encoded input string
953

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

Iustin Pop's avatar
Iustin Pop committed
956
    """
957
    return self._SingleNodeCall(node, "iallocator_runner", [name, idata])
958

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

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

Iustin Pop's avatar
Iustin Pop committed
964
    """
965
966
    return self._SingleNodeCall(node, "blockdev_grow",
                                [cf_bdev.ToDict(), amount])
967

968
969
970
971
972
973
974
975
976
977
978
  def call_blockdev_export(self, node, cf_bdev,
                           dest_node, dest_path, cluster_name):
    """Export a given disk to another node.

    This is a single-node call.

    """
    return self._SingleNodeCall(node, "blockdev_export",
                                [cf_bdev.ToDict(), dest_node, dest_path,
                                 cluster_name])