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
63
  http.InitSsl()

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


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


80
81
82
83
84
85
86
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
87
  @ivar data: the data payload, for successful results, or None
88
89
90
91
92
93
  @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
94
  @ivar fail_msg: the error message if the call failed
95

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

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

127
128
129
130
131
132
133
    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")

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

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

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


Iustin Pop's avatar
Iustin Pop committed
168
169
170
class Client:
  """RPC Client class.

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

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

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

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

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

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

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

220
221
222
223
224
225
    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
226

227
  def GetResults(self):
228
229
230
    """Call nodes and return results.

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

    """
Michael Hanselmann's avatar
Michael Hanselmann committed
234
    assert _http_manager, "RPC module not initialized"
235
236

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

238
    results = {}
Iustin Pop's avatar
Iustin Pop committed
239

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

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

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

    return results
Iustin Pop's avatar
Iustin Pop committed
258
259


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

419
420
421
422
  #
  # Begin RPC calls
  #

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

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

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

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

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

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

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

    This is a multi-node call.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    This is a single-node call.

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

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

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

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

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

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

Iustin Pop's avatar
Iustin Pop committed
692
693
  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
694

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Iustin Pop's avatar
Iustin Pop committed
748
749
  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
750

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

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

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

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

Iustin Pop's avatar
Iustin Pop committed
762
    """
763
    return self._SingleNodeCall(node, "blockdev_remove", [bdev.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
764

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

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

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

Iustin Pop's avatar
Iustin Pop committed
774
775
  def call_blockdev_assemble(self, node, disk, owner, on_primary):
    """Request assembling of a given block 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
    return self._SingleNodeCall(node, "blockdev_assemble",
                                [disk.ToDict(), owner, on_primary])
Iustin Pop's avatar
Iustin Pop committed
782

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

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

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

Iustin Pop's avatar
Iustin Pop committed
791
792
  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
793

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

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

Iustin Pop's avatar
Iustin Pop committed
801
802
  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
803

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

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

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

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

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

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

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

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

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

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

844
845
846
847
848
849
850
851
852
  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
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
880
881
  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]])

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

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

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

    This is a multi-node call.

    """
915
    return cls._StaticMultiNodeCall(node_list, "write_ssconf_files", [values])
916

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

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

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

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

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

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

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

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

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

Iustin Pop's avatar
Iustin Pop committed