rpc.py 32.8 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
220
    """Call nodes and return results.

    @rtype: list
    @returns: 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

Iustin Pop's avatar
Iustin Pop committed
428
429
  def call_instance_start(self, node, instance, extra_args):
    """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
435
    return self._SingleNodeCall(node, "instance_start",
                                [self._InstDict(instance), extra_args])
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

Iustin Pop's avatar
Iustin Pop committed
518
519
  def call_instance_reboot(self, node, instance, reboot_type, extra_args):
    """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
525
526
    return self._SingleNodeCall(node, "instance_reboot",
                                [self._InstDict(instance), reboot_type,
                                 extra_args])
Iustin Pop's avatar
Iustin Pop committed
527

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    This is a single-node call.

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

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

Iustin Pop's avatar
Iustin Pop committed
624
625
    This will return memory information and volume group size and free
    space.
Iustin Pop's avatar
Iustin Pop committed
626

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

Iustin Pop's avatar
Iustin Pop committed
629
630
    @type node_list: list
    @param node_list: the list of nodes to query
Iustin Pop's avatar
Iustin Pop committed
631
632
    @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
633
634
635
636
        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
637

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

696
697
  @classmethod
  def call_master_info(cls, node_list):
Iustin Pop's avatar
Iustin Pop committed
698
    """Query master info.
699

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

Iustin Pop's avatar
Iustin Pop committed
702
703
    """
    # TODO: should this method query down nodes?
704
    return cls._StaticMultiNodeCall(node_list, "master_info", [])
Iustin Pop's avatar
Iustin Pop committed
705

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Iustin Pop's avatar
Iustin Pop committed
754
    """
755
    return self._SingleNodeCall(node, "blockdev_shutdown", [disk.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
756

Iustin Pop's avatar
Iustin Pop committed
757
758
  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
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
764
765
    return self._SingleNodeCall(node, "blockdev_addchildren",
                                [bdev.ToDict(),
                                 [disk.ToDict() for disk in ndevs]])
Iustin Pop's avatar
Iustin Pop committed
766

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

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

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

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

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

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

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

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

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

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

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

Iustin Pop's avatar
Iustin Pop committed
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
831
  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]])

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

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

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

    This is a multi-node call.

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

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

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

875
    for node_result in result.values():
876
877
878
879
      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
880

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Iustin Pop's avatar
Iustin Pop committed
931
    """
932
    return self._SingleNodeCall(node, "blockdev_snapshot", [cf_bdev.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
933

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

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

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

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

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

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

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

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

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

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

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

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