rpc.py 32.7 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
Iustin Pop's avatar
Iustin Pop committed
34
import socket
35
import logging
36
37
import zlib
import base64
Iustin Pop's avatar
Iustin Pop committed
38
39
40

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

46
47
import ganeti.http.client

Iustin Pop's avatar
Iustin Pop committed
48

49
50
51
52
53
54
55
56
57
58
59
60
61
62
# 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"

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


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


79
80
81
82
83
84
85
class RpcResult(object):
  """RPC Result class.

  This class holds an RPC result. It is needed since in multi-node
  calls we can't raise an exception just because one one out of many
  failed, and therefore we use this class to encapsulate the result.

86
87
88
89
90
91
92
93
94
95
96
  @ivar data: the data payload, for successfull results, or None
  @type failed: boolean
  @ivar failed: whether the operation failed at RPC level (not
      application level on the remote node)
  @ivar call: the name of the RPC call
  @ivar node: the name of the node to which we made the call
  @ivar offline: whether the operation failed because the node was
      offline, as opposed to actual failure; offline=True will always
      imply failed=True, in order to allow simpler checking if
      the user doesn't care about the exact failure mode

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

  def Raise(self):
    """If the result has failed, raise an OpExecError.

    This is used so that LU code doesn't have to check for each
    result, but instead can call this function.

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

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

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

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

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

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

156

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

Alexander Schreiber's avatar
Alexander Schreiber committed
160
  This class, given a (remote) method name, a list of parameters and a
Iustin Pop's avatar
Iustin Pop committed
161
162
163
164
165
166
167
168
  list of nodes, will contact (in parallel) all nodes, and return a
  dict of results (key: node name, value: result).

  One current bug is that generic failure is still signalled by
  'False' result, which is not good. This overloading of values can
  cause bugs.

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

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

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

182
183
    @type node_list: list
    @param node_list: the list of node names to connect
184
185
186
    @type address_list: list or None
    @keyword address_list: either None or a list with node addresses,
        which must have the same length as the node list
187

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

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

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

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

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

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

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

    """
223
224
225
    assert _http_manager, "RPC module not intialized"

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

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

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

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

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

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


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

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

Iustin Pop's avatar
Iustin Pop committed
255
256
257
    @type cfg:  C{config.ConfigWriter}
    @param cfg: the configuration object that will be used to get data
                about the cluster
Iustin Pop's avatar
Iustin Pop committed
258

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

263
264
265
266
267
268
269
270
271
272
273
274
275
276
  def _InstDict(self, instance):
    """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
    @rtype: dict
    @return: the instance dict, with the hvparams filled with the
        cluster defaults

    """
    idict = instance.ToDict()
277
278
279
    cluster = self._cfg.GetClusterInfo()
    idict["hvparams"] = cluster.FillHV(instance)
    idict["beparams"] = cluster.FillBE(instance)
280
281
    return idict

282
  def _ConnectList(self, client, node_list, call):
283
284
285
286
287
288
    """Helper for computing node addresses.

    @type client: L{Client}
    @param client: a C{Client} instance
    @type node_list: list
    @param node_list: the node list we should connect
289
290
291
    @type call: string
    @param call: the name of the remote procedure call, for filling in
        correctly any eventual offline nodes' results
292
293
294

    """
    all_nodes = self._cfg.GetAllNodesInfo()
295
    name_list = []
296
    addr_list = []
297
    skip_dict = {}
298
299
    for node in node_list:
      if node in all_nodes:
300
        if all_nodes[node].offline:
301
          skip_dict[node] = RpcResult(node=node, offline=True, call=call)
302
          continue
303
304
305
306
        val = all_nodes[node].primary_ip
      else:
        val = None
      addr_list.append(val)
307
308
309
310
      name_list.append(node)
    if name_list:
      client.ConnectList(name_list, address_list=addr_list)
    return skip_dict
311

312
  def _ConnectNode(self, client, node, call):
313
314
315
316
317
318
    """Helper for computing one node's address.

    @type client: L{Client}
    @param client: a C{Client} instance
    @type node: str
    @param node: the node we should connect
319
320
321
    @type call: string
    @param call: the name of the remote procedure call, for filling in
        correctly any eventual offline nodes' results
322
323
324
325

    """
    node_info = self._cfg.GetNodeInfo(node)
    if node_info is not None:
326
      if node_info.offline:
327
        return RpcResult(node=node, offline=True, call=call)
328
329
330
331
332
      addr = node_info.primary_ip
    else:
      addr = None
    client.ConnectNode(node, address=addr)

333
  def _MultiNodeCall(self, node_list, procedure, args):
334
335
336
337
338
    """Helper for making a multi-node call

    """
    body = serializer.DumpJson(args, indent=False)
    c = Client(procedure, body, self.port)
339
    skip_dict = self._ConnectList(c, node_list, procedure)
340
341
    skip_dict.update(c.GetResults())
    return skip_dict
342
343
344
345

  @classmethod
  def _StaticMultiNodeCall(cls, node_list, procedure, args,
                           address_list=None):
346
347
348
349
350
    """Helper for making a multi-node static call

    """
    body = serializer.DumpJson(args, indent=False)
    c = Client(procedure, body, utils.GetNodeDaemonPort())
351
352
353
354
    c.ConnectList(node_list, address_list=address_list)
    return c.GetResults()

  def _SingleNodeCall(self, node, procedure, args):
355
    """Helper for making a single-node call
356
357

    """
358
359
    body = serializer.DumpJson(args, indent=False)
    c = Client(procedure, body, self.port)
360
    result = self._ConnectNode(c, node, procedure)
361
362
363
364
    if result is None:
      # we did connect, node is not offline
      result = c.GetResults()[node]
    return result
365
366
367

  @classmethod
  def _StaticSingleNodeCall(cls, node, procedure, args):
368
    """Helper for making a single-node static call
369
370

    """
371
372
    body = serializer.DumpJson(args, indent=False)
    c = Client(procedure, body, utils.GetNodeDaemonPort())
373
    c.ConnectNode(node)
374
    return c.GetResults()[node]
375

376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
  @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)))

396
397
398
399
  #
  # Begin RPC calls
  #

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

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

Iustin Pop's avatar
Iustin Pop committed
405
    """
406
    return self._MultiNodeCall(node_list, "volume_list", [vg_name])
Iustin Pop's avatar
Iustin Pop committed
407

Iustin Pop's avatar
Iustin Pop committed
408
409
  def call_vg_list(self, node_list):
    """Gets the volume group list.
Iustin Pop's avatar
Iustin Pop committed
410

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

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

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

Iustin Pop's avatar
Iustin Pop committed
419
420
421
    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
422

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

Iustin Pop's avatar
Iustin Pop committed
425
    """
426
    return self._SingleNodeCall(node, "bridges_exist", [bridges_list])
Iustin Pop's avatar
Iustin Pop committed
427

428
  def call_instance_start(self, node, instance):
Iustin Pop's avatar
Iustin Pop committed
429
    """Starts an instance.
Iustin Pop's avatar
Iustin Pop committed
430

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

Iustin Pop's avatar
Iustin Pop committed
433
    """
434
    return self._SingleNodeCall(node, "instance_start",
435
                                [self._InstDict(instance)])
Iustin Pop's avatar
Iustin Pop committed
436

Iustin Pop's avatar
Iustin Pop committed
437
438
  def call_instance_shutdown(self, node, instance):
    """Stops an instance.
Iustin Pop's avatar
Iustin Pop committed
439

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

Iustin Pop's avatar
Iustin Pop committed
442
    """
443
444
    return self._SingleNodeCall(node, "instance_shutdown",
                                [self._InstDict(instance)])
445

446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
  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
499
500
  def call_instance_migrate(self, node, instance, target, live):
    """Migrate an instance.
501

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

Iustin Pop's avatar
Iustin Pop committed
504
505
506
507
508
509
510
511
512
    @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)
513

Iustin Pop's avatar
Iustin Pop committed
514
    """
515
516
    return self._SingleNodeCall(node, "instance_migrate",
                                [self._InstDict(instance), target, live])
517

518
  def call_instance_reboot(self, node, instance, reboot_type):
Iustin Pop's avatar
Iustin Pop committed
519
    """Reboots an instance.
520

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

Iustin Pop's avatar
Iustin Pop committed
523
    """
524
    return self._SingleNodeCall(node, "instance_reboot",
525
                                [self._InstDict(instance), reboot_type])
Iustin Pop's avatar
Iustin Pop committed
526

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

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

Iustin Pop's avatar
Iustin Pop committed
532
    """
533
534
    return self._SingleNodeCall(node, "instance_os_add",
                                [self._InstDict(inst)])
535

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

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

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

Iustin Pop's avatar
Iustin Pop committed
545
546
  def call_instance_info(self, node, instance, hname):
    """Returns information about a single instance.
Iustin Pop's avatar
Iustin Pop committed
547

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

550
551
    @type node: list
    @param node: the list of nodes to query
Iustin Pop's avatar
Iustin Pop committed
552
553
554
555
    @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
556

Iustin Pop's avatar
Iustin Pop committed
557
    """
558
    return self._SingleNodeCall(node, "instance_info", [instance, hname])
559

560
561
562
563
564
565
566
567
568
569
570
571
572
573
  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
574
575
  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
576

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

Iustin Pop's avatar
Iustin Pop committed
579
580
581
582
    @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
583

Iustin Pop's avatar
Iustin Pop committed
584
    """
585
586
    return self._MultiNodeCall(node_list, "all_instances_info",
                               [hypervisor_list])
587

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

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

Iustin Pop's avatar
Iustin Pop committed
593
594
595
596
    @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
597

Iustin Pop's avatar
Iustin Pop committed
598
    """
599
    return self._MultiNodeCall(node_list, "instance_list", [hypervisor_list])
600

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

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

Iustin Pop's avatar
Iustin Pop committed
607
    """
608
609
    return self._SingleNodeCall(node, "node_tcp_ping",
                                [source, target, port, timeout,
Iustin Pop's avatar
Iustin Pop committed
610
                                 live_port_needed])
Iustin Pop's avatar
Iustin Pop committed
611

612
613
614
615
616
617
  def call_node_has_ip_address(self, node, address):
    """Checks if a node has the given IP address.

    This is a single-node call.

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

Iustin Pop's avatar
Iustin Pop committed
620
621
  def call_node_info(self, node_list, vg_name, hypervisor_type):
    """Return node information.
622

Iustin Pop's avatar
Iustin Pop committed
623
624
    This will return memory information and volume group size and free
    space.
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
    @type node_list: list
    @param node_list: the list of nodes to query
Iustin Pop's avatar
Iustin Pop committed
630
631
    @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
632
633
634
635
        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
636

Iustin Pop's avatar
Iustin Pop committed
637
    """
638
639
    retux = self._MultiNodeCall(node_list, "node_info",
                                [vg_name, hypervisor_type])
Iustin Pop's avatar
Iustin Pop committed
640

641
642
643
    for result in retux.itervalues():
      if result.failed or not isinstance(result.data, dict):
        result.data = {}
644
645
646
647
      if result.offline:
        log_name = None
      else:
        log_name = "call_node_info"
648
649
650
651
652
653
654

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

Iustin Pop's avatar
Iustin Pop committed
658
659
  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
660

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

Iustin Pop's avatar
Iustin Pop committed
663
    """
664
665
    return self._SingleNodeCall(node, "node_add",
                                [dsa, dsapub, rsa, rsapub, ssh, sshpub])
Iustin Pop's avatar
Iustin Pop committed
666

Iustin Pop's avatar
Iustin Pop committed
667
668
  def call_node_verify(self, node_list, checkdict, cluster_name):
    """Request verification of given parameters.
Iustin Pop's avatar
Iustin Pop committed
669

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

Iustin Pop's avatar
Iustin Pop committed
672
    """
673
674
    return self._MultiNodeCall(node_list, "node_verify",
                               [checkdict, cluster_name])
Iustin Pop's avatar
Iustin Pop committed
675

676
677
  @classmethod
  def call_node_start_master(cls, node, start_daemons):
Iustin Pop's avatar
Iustin Pop committed
678
    """Tells a node to activate itself as a master.
Iustin Pop's avatar
Iustin Pop committed
679

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

Iustin Pop's avatar
Iustin Pop committed
682
    """
683
684
    return cls._StaticSingleNodeCall(node, "node_start_master",
                                     [start_daemons])
Iustin Pop's avatar
Iustin Pop committed
685

686
687
  @classmethod
  def call_node_stop_master(cls, node, stop_daemons):
Iustin Pop's avatar
Iustin Pop committed
688
    """Tells a node to demote itself from master status.
Iustin Pop's avatar
Iustin Pop committed
689

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

Iustin Pop's avatar
Iustin Pop committed
692
    """
693
    return cls._StaticSingleNodeCall(node, "node_stop_master", [stop_daemons])
694

695
696
  @classmethod
  def call_master_info(cls, node_list):
Iustin Pop's avatar
Iustin Pop committed
697
    """Query master info.
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
    """
    # TODO: should this method query down nodes?
703
    return cls._StaticMultiNodeCall(node_list, "master_info", [])
Iustin Pop's avatar
Iustin Pop committed
704

Iustin Pop's avatar
Iustin Pop committed
705
706
  def call_version(self, node_list):
    """Query node version.
Iustin Pop's avatar
Iustin Pop committed
707

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

Iustin Pop's avatar
Iustin Pop committed
710
    """
711
    return self._MultiNodeCall(node_list, "version", [])
Iustin Pop's avatar
Iustin Pop committed
712

Iustin Pop's avatar
Iustin Pop committed
713
714
  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
715

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

Iustin Pop's avatar
Iustin Pop committed
718
    """
719
720
    return self._SingleNodeCall(node, "blockdev_create",
                                [bdev.ToDict(), size, owner, on_primary, info])
Iustin Pop's avatar
Iustin Pop committed
721

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

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

Iustin Pop's avatar
Iustin Pop committed
727
    """
728
    return self._SingleNodeCall(node, "blockdev_remove", [bdev.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
729

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

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

Iustin Pop's avatar
Iustin Pop committed
735
    """
736
737
    return self._SingleNodeCall(node, "blockdev_rename",
                                [(d.ToDict(), uid) for d, uid in devlist])
Iustin Pop's avatar
Iustin Pop committed
738

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

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

Iustin Pop's avatar
Iustin Pop committed
744
    """
745
746
    return self._SingleNodeCall(node, "blockdev_assemble",
                                [disk.ToDict(), owner, on_primary])
Iustin Pop's avatar
Iustin Pop committed
747

Iustin Pop's avatar
Iustin Pop committed
748
749
  def call_blockdev_shutdown(self, node, disk):
    """Request shutdown 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
    return self._SingleNodeCall(node, "blockdev_shutdown", [disk.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
755

Iustin Pop's avatar
Iustin Pop committed
756
757
  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
758

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

Iustin Pop's avatar
Iustin Pop committed
761
    """
762
763
764
    return self._SingleNodeCall(node, "blockdev_addchildren",
                                [bdev.ToDict(),
                                 [disk.ToDict() for disk in ndevs]])
Iustin Pop's avatar
Iustin Pop committed
765

Iustin Pop's avatar
Iustin Pop committed
766
767
  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
768

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

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

Iustin Pop's avatar
Iustin Pop committed
776
777
  def call_blockdev_getmirrorstatus(self, node, disks):
    """Request status of a (mirroring) device.
Iustin Pop's avatar
Iustin Pop committed
778

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

Iustin Pop's avatar
Iustin Pop committed
781
    """
782
783
    return self._SingleNodeCall(node, "blockdev_getmirrorstatus",
                                [dsk.ToDict() for dsk in disks])
Iustin Pop's avatar
Iustin Pop committed
784

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

Iustin Pop's avatar
Iustin Pop committed
790
    """
791
    return self._SingleNodeCall(node, "blockdev_find", [disk.ToDict()])
792

793
  def call_blockdev_close(self, node, instance_name, disks):
Iustin Pop's avatar
Iustin Pop committed
794
    """Closes the given block devices.
795

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

Iustin Pop's avatar
Iustin Pop committed
798
    """
799
800
    params = [instance_name, [cf.ToDict() for cf in disks]]
    return self._SingleNodeCall(node, "blockdev_close", params)
Iustin Pop's avatar
Iustin Pop committed
801

Iustin Pop's avatar
Iustin Pop committed
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
  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]])

831
832
  @classmethod
  def call_upload_file(cls, node_list, file_name, address_list=None):
Iustin Pop's avatar
Iustin Pop committed
833
834
835
836
837
838
    """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
839

840
841
842
843
844
845
846
847
    @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
848
    """
849
850
    file_contents = utils.ReadFile(file_name)
    data = cls._Compress(file_contents)
Iustin Pop's avatar
Iustin Pop committed
851
852
853
    st = os.stat(file_name)
    params = [file_name, data, st.st_mode, st.st_uid, st.st_gid,
              st.st_atime, st.st_mtime]
854
855
    return cls._StaticMultiNodeCall(node_list, "upload_file", params,
                                    address_list=address_list)
Iustin Pop's avatar
Iustin Pop committed
856

857
  @classmethod
858
  def call_write_ssconf_files(cls, node_list, values):
859
860
861
862
863
    """Write ssconf files.

    This is a multi-node call.

    """
864
    return cls._StaticMultiNodeCall(node_list, "write_ssconf_files", [values])
865

Iustin Pop's avatar
Iustin Pop committed
866
867
868
869
  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
870

Iustin Pop's avatar
Iustin Pop committed
871
    """
872
873
    result = self._MultiNodeCall(node_list, "os_diagnose", [])

874
    for node_result in result.values():
875
876
877
878
      if not node_result.failed and node_result.data:
        node_result.data = [objects.OS.FromDict(oss)
                            for oss in node_result.data]
    return result
Iustin Pop's avatar
Iustin Pop committed
879

Iustin Pop's avatar
Iustin Pop committed
880
881
  def call_os_get(self, node, name):
    """Returns an OS definition.
Iustin Pop's avatar
Iustin Pop committed
882

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

Iustin Pop's avatar
Iustin Pop committed
885
    """
886
    result = self._SingleNodeCall(node, "os_get", [name])
887
888
889
    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
890

Iustin Pop's avatar
Iustin Pop committed
891
892
  def call_hooks_runner(self, node_list, hpath, phase, env):
    """Call the hooks runner.
Iustin Pop's avatar
Iustin Pop committed
893

Iustin Pop's avatar
Iustin Pop committed
894
895
896
    Args:
      - op: the OpCode instance
      - env: a dictionary with the environment
Iustin Pop's avatar
Iustin Pop committed
897

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

Iustin Pop's avatar
Iustin Pop committed
900
901
    """
    params = [hpath, phase, env]
902
    return self._MultiNodeCall(node_list, "hooks_runner", params)
Iustin Pop's avatar
Iustin Pop committed
903

Iustin Pop's avatar
Iustin Pop committed
904
905
  def call_iallocator_runner(self, node, name, idata):
    """Call an iallocator on a remote node
906

Iustin Pop's avatar
Iustin Pop committed
907
908
909
    Args:
      - name: the iallocator name
      - input: the json-encoded input string
910

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

Iustin Pop's avatar
Iustin Pop committed
913
    """
914
    return self._SingleNodeCall(node, "iallocator_runner", [name, idata])
915

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

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

Iustin Pop's avatar
Iustin Pop committed
921
    """
922
923
    return self._SingleNodeCall(node, "blockdev_grow",
                                [cf_bdev.ToDict(), amount])
924

Iustin Pop's avatar
Iustin Pop committed
925
926
  def call_blockdev_snapshot(self, node, cf_bdev):
    """Request a snapshot of the given block device.
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
    return self._SingleNodeCall(node, "blockdev_snapshot", [cf_bdev.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
932

Iustin Pop's avatar
Iustin Pop committed
933
  def call_snapshot_export(self, node, snap_bdev, dest_node, instance,
934
                           cluster_name, idx):
Iustin Pop's avatar
Iustin Pop committed
935
    """Request the export of a given snapshot.
Iustin Pop's avatar
Iustin Pop committed
936

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

Iustin Pop's avatar
Iustin Pop committed
939
    """
940
941
942
    return self._SingleNodeCall(node, "snapshot_export",
                                [snap_bdev.ToDict(), dest_node,
                                 self._InstDict(instance), cluster_name, idx])
Iustin Pop's avatar
Iustin Pop committed
943

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

Iustin Pop's avatar
Iustin Pop committed
947
    This writes the export config file, etc.
Iustin Pop's avatar
Iustin Pop committed
948

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

Iustin Pop's avatar
Iustin Pop committed
951
952
953
954
    """
    flat_disks = []
    for disk in snap_disks:
      flat_disks.append(disk.ToDict())
955
956
957

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

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

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

Iustin Pop's avatar
Iustin Pop committed
964
    """
965
    result = self._SingleNodeCall(node, "export_info", [path])
966
967
968
    if not result.failed and result.data:
      result.data = objects.SerializableConfigParser.Loads(str(result.data))
    return result
Iustin Pop's avatar
Iustin Pop committed
969

970
971
  def call_instance_os_import(self, node, inst, src_node, src_images,
                              cluster_name):
Iustin Pop's avatar
Iustin Pop committed
972
    """Request the import of a backup into an instance.
Iustin Pop's avatar
Iustin Pop committed
973

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

Iustin Pop's avatar
Iustin Pop committed
976
    """
977
978
979
    return self._SingleNodeCall(node, "instance_os_import",
                                [self._InstDict(inst), src_node, src_images,
                                 cluster_name])
Iustin Pop's avatar
Iustin Pop committed
980

Iustin Pop's avatar
Iustin Pop committed
981
982
  def call_export_list(self, node_list):
    """Gets the stored exports list.
Iustin Pop's avatar
Iustin Pop committed
983

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

Iustin Pop's avatar
Iustin Pop committed
986
    """
987
    return self._MultiNodeCall(node_list, "export_list", [])
Iustin Pop's avatar
Iustin Pop committed
988

Iustin Pop's avatar
Iustin Pop committed
989
990
  def call_export_remove(self, node, export):
    """Requests removal of a given export.