rpc.py 28.2 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 107 108
    self.offline = offline
    self.call = call
    self.node = node
    if offline:
      self.failed = True
      self.error = "Node is marked offline"
      self.data = None
    elif failed:
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
      self.error = data
      self.data = None
    else:
      self.data = data
      self.error = None

  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))


Iustin Pop's avatar
Iustin Pop committed
127 128 129
class Client:
  """RPC Client class.

Alexander Schreiber's avatar
Alexander Schreiber committed
130
  This class, given a (remote) method name, a list of parameters and a
Iustin Pop's avatar
Iustin Pop committed
131 132 133 134 135 136 137 138
  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.

  """
139
  def __init__(self, procedure, body, port):
Iustin Pop's avatar
Iustin Pop committed
140
    self.procedure = procedure
141 142
    self.body = body
    self.port = port
143
    self.nc = {}
Iustin Pop's avatar
Iustin Pop committed
144

145 146 147 148
    self._ssl_params = \
      http.HttpSslParams(ssl_key_path=constants.SSL_CERT_FILE,
                         ssl_cert_path=constants.SSL_CERT_FILE)

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

152 153
    @type node_list: list
    @param node_list: the list of node names to connect
154 155 156
    @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
157

Iustin Pop's avatar
Iustin Pop committed
158
    """
159 160 161 162 163 164 165 166 167
    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
168 169
    """Add a node to the target list.

170 171 172 173 174
    @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
175
    """
176 177 178
    if address is None:
      address = name

179 180 181 182 183 184
    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
185

186
  def GetResults(self):
187 188 189 190
    """Call nodes and return results.

    @rtype: list
    @returns: List of RPC results
Iustin Pop's avatar
Iustin Pop committed
191 192

    """
193 194 195
    assert _http_manager, "RPC module not intialized"

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

197
    results = {}
Iustin Pop's avatar
Iustin Pop committed
198

199
    for name, req in self.nc.iteritems():
200
      if req.success and req.resp_status_code == http.HTTP_OK:
201 202
        results[name] = RpcResult(data=serializer.LoadJson(req.resp_body),
                                  node=name, call=self.procedure)
203
        continue
Iustin Pop's avatar
Iustin Pop committed
204

205
      # TODO: Better error reporting
206 207 208 209 210 211
      if req.error:
        msg = req.error
      else:
        msg = req.resp_body

      logging.error("RPC error from node %s: %s", name, msg)
212 213
      results[name] = RpcResult(data=msg, failed=True, node=name,
                                call=self.procedure)
214 215

    return results
Iustin Pop's avatar
Iustin Pop committed
216 217


Iustin Pop's avatar
Iustin Pop committed
218 219
class RpcRunner(object):
  """RPC runner class"""
Iustin Pop's avatar
Iustin Pop committed
220

Iustin Pop's avatar
Iustin Pop committed
221 222
  def __init__(self, cfg):
    """Initialized the rpc runner.
Iustin Pop's avatar
Iustin Pop committed
223

Iustin Pop's avatar
Iustin Pop committed
224 225 226
    @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
227

Iustin Pop's avatar
Iustin Pop committed
228 229
    """
    self._cfg = cfg
230
    self.port = utils.GetNodeDaemonPort()
Iustin Pop's avatar
Iustin Pop committed
231

232 233 234 235 236 237 238 239 240 241 242 243 244 245
  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()
246 247 248
    cluster = self._cfg.GetClusterInfo()
    idict["hvparams"] = cluster.FillHV(instance)
    idict["beparams"] = cluster.FillBE(instance)
249 250
    return idict

251 252 253 254 255 256 257 258 259 260
  def _ConnectList(self, client, node_list):
    """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

    """
    all_nodes = self._cfg.GetAllNodesInfo()
261
    name_list = []
262
    addr_list = []
263
    skip_dict = {}
264 265
    for node in node_list:
      if node in all_nodes:
266 267 268
        if all_nodes[node].offline:
          skip_dict[node] = RpcResult(node=node, offline=True)
          continue
269 270 271 272
        val = all_nodes[node].primary_ip
      else:
        val = None
      addr_list.append(val)
273 274 275 276
      name_list.append(node)
    if name_list:
      client.ConnectList(name_list, address_list=addr_list)
    return skip_dict
277 278 279 280 281 282 283 284 285 286 287 288

  def _ConnectNode(self, client, node):
    """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

    """
    node_info = self._cfg.GetNodeInfo(node)
    if node_info is not None:
289 290
      if node_info.offline:
        return RpcResult(node=node, offline=True)
291 292 293 294 295
      addr = node_info.primary_ip
    else:
      addr = None
    client.ConnectNode(node, address=addr)

296
  def _MultiNodeCall(self, node_list, procedure, args):
297 298 299 300 301
    """Helper for making a multi-node call

    """
    body = serializer.DumpJson(args, indent=False)
    c = Client(procedure, body, self.port)
302 303 304
    skip_dict = self._ConnectList(c, node_list)
    skip_dict.update(c.GetResults())
    return skip_dict
305 306 307 308

  @classmethod
  def _StaticMultiNodeCall(cls, node_list, procedure, args,
                           address_list=None):
309 310 311 312 313
    """Helper for making a multi-node static call

    """
    body = serializer.DumpJson(args, indent=False)
    c = Client(procedure, body, utils.GetNodeDaemonPort())
314 315 316 317
    c.ConnectList(node_list, address_list=address_list)
    return c.GetResults()

  def _SingleNodeCall(self, node, procedure, args):
318
    """Helper for making a single-node call
319 320

    """
321 322
    body = serializer.DumpJson(args, indent=False)
    c = Client(procedure, body, self.port)
323 324 325 326 327
    result = self._ConnectNode(c, node)
    if result is None:
      # we did connect, node is not offline
      result = c.GetResults()[node]
    return result
328 329 330

  @classmethod
  def _StaticSingleNodeCall(cls, node, procedure, args):
331
    """Helper for making a single-node static call
332 333

    """
334 335
    body = serializer.DumpJson(args, indent=False)
    c = Client(procedure, body, utils.GetNodeDaemonPort())
336
    c.ConnectNode(node)
337
    return c.GetResults()[node]
338

339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
  @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)))

359 360 361 362
  #
  # Begin RPC calls
  #

Iustin Pop's avatar
Iustin Pop committed
363 364
  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
365

Iustin Pop's avatar
Iustin Pop committed
366
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
367

Iustin Pop's avatar
Iustin Pop committed
368
    """
369
    return self._MultiNodeCall(node_list, "volume_list", [vg_name])
Iustin Pop's avatar
Iustin Pop committed
370

Iustin Pop's avatar
Iustin Pop committed
371 372
  def call_vg_list(self, node_list):
    """Gets the volume group list.
Iustin Pop's avatar
Iustin Pop committed
373

Iustin Pop's avatar
Iustin Pop committed
374
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
375

Iustin Pop's avatar
Iustin Pop committed
376
    """
377
    return self._MultiNodeCall(node_list, "vg_list", [])
Iustin Pop's avatar
Iustin Pop committed
378

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

Iustin Pop's avatar
Iustin Pop committed
382 383 384
    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
385

Iustin Pop's avatar
Iustin Pop committed
386
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
387

Iustin Pop's avatar
Iustin Pop committed
388
    """
389
    return self._SingleNodeCall(node, "bridges_exist", [bridges_list])
Iustin Pop's avatar
Iustin Pop committed
390

Iustin Pop's avatar
Iustin Pop committed
391 392
  def call_instance_start(self, node, instance, extra_args):
    """Starts an instance.
Iustin Pop's avatar
Iustin Pop committed
393

Iustin Pop's avatar
Iustin Pop committed
394
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
395

Iustin Pop's avatar
Iustin Pop committed
396
    """
397 398
    return self._SingleNodeCall(node, "instance_start",
                                [self._InstDict(instance), extra_args])
Iustin Pop's avatar
Iustin Pop committed
399

Iustin Pop's avatar
Iustin Pop committed
400 401
  def call_instance_shutdown(self, node, instance):
    """Stops an instance.
Iustin Pop's avatar
Iustin Pop committed
402

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

Iustin Pop's avatar
Iustin Pop committed
405
    """
406 407
    return self._SingleNodeCall(node, "instance_shutdown",
                                [self._InstDict(instance)])
408

Iustin Pop's avatar
Iustin Pop committed
409 410
  def call_instance_migrate(self, node, instance, target, live):
    """Migrate an instance.
411

Iustin Pop's avatar
Iustin Pop committed
412
    This is a single-node call.
413

Iustin Pop's avatar
Iustin Pop committed
414 415 416 417 418 419 420 421 422
    @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)
423

Iustin Pop's avatar
Iustin Pop committed
424
    """
425 426
    return self._SingleNodeCall(node, "instance_migrate",
                                [self._InstDict(instance), target, live])
427

Iustin Pop's avatar
Iustin Pop committed
428 429
  def call_instance_reboot(self, node, instance, reboot_type, extra_args):
    """Reboots an instance.
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 436
    return self._SingleNodeCall(node, "instance_reboot",
                                [self._InstDict(instance), reboot_type,
                                 extra_args])
Iustin Pop's avatar
Iustin Pop committed
437

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

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

Iustin Pop's avatar
Iustin Pop committed
443
    """
444 445
    return self._SingleNodeCall(node, "instance_os_add",
                                [self._InstDict(inst)])
446

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

Iustin Pop's avatar
Iustin Pop committed
450
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
451

Iustin Pop's avatar
Iustin Pop committed
452
    """
453 454
    return self._SingleNodeCall(node, "instance_run_rename",
                                [self._InstDict(inst), old_name])
Iustin Pop's avatar
Iustin Pop committed
455

Iustin Pop's avatar
Iustin Pop committed
456 457
  def call_instance_info(self, node, instance, hname):
    """Returns information about a single instance.
Iustin Pop's avatar
Iustin Pop committed
458

Iustin Pop's avatar
Iustin Pop committed
459
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
460

461 462
    @type node: list
    @param node: the list of nodes to query
Iustin Pop's avatar
Iustin Pop committed
463 464 465 466
    @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
467

Iustin Pop's avatar
Iustin Pop committed
468
    """
469
    return self._SingleNodeCall(node, "instance_info", [instance, hname])
470

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

Iustin Pop's avatar
Iustin Pop committed
474
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
475

Iustin Pop's avatar
Iustin Pop committed
476 477 478 479
    @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
480

Iustin Pop's avatar
Iustin Pop committed
481
    """
482 483
    return self._MultiNodeCall(node_list, "all_instances_info",
                               [hypervisor_list])
484

Iustin Pop's avatar
Iustin Pop committed
485 486
  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
487

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

Iustin Pop's avatar
Iustin Pop committed
490 491 492 493
    @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
494

Iustin Pop's avatar
Iustin Pop committed
495
    """
496
    return self._MultiNodeCall(node_list, "instance_list", [hypervisor_list])
497

Iustin Pop's avatar
Iustin Pop committed
498 499 500
  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
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
    return self._SingleNodeCall(node, "node_tcp_ping",
                                [source, target, port, timeout,
Iustin Pop's avatar
Iustin Pop committed
507
                                 live_port_needed])
Iustin Pop's avatar
Iustin Pop committed
508

509 510 511 512 513 514
  def call_node_has_ip_address(self, node, address):
    """Checks if a node has the given IP address.

    This is a single-node call.

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

Iustin Pop's avatar
Iustin Pop committed
517 518
  def call_node_info(self, node_list, vg_name, hypervisor_type):
    """Return node information.
519

Iustin Pop's avatar
Iustin Pop committed
520 521
    This will return memory information and volume group size and free
    space.
Iustin Pop's avatar
Iustin Pop committed
522

Iustin Pop's avatar
Iustin Pop committed
523
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
524

Iustin Pop's avatar
Iustin Pop committed
525 526
    @type node_list: list
    @param node_list: the list of nodes to query
Iustin Pop's avatar
Iustin Pop committed
527 528
    @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
529 530 531 532
        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
533

Iustin Pop's avatar
Iustin Pop committed
534
    """
535 536
    retux = self._MultiNodeCall(node_list, "node_info",
                                [vg_name, hypervisor_type])
Iustin Pop's avatar
Iustin Pop committed
537

538 539 540 541 542 543 544 545 546 547 548
    for result in retux.itervalues():
      if result.failed or not isinstance(result.data, dict):
        result.data = {}

      utils.CheckDict(result.data, {
        'memory_total' : '-',
        'memory_dom0' : '-',
        'memory_free' : '-',
        'vg_size' : 'node_unreachable',
        'vg_free' : '-',
        }, "call_node_info")
Iustin Pop's avatar
Iustin Pop committed
549
    return retux
Iustin Pop's avatar
Iustin Pop committed
550

Iustin Pop's avatar
Iustin Pop committed
551 552
  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
553

Iustin Pop's avatar
Iustin Pop committed
554
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
555

Iustin Pop's avatar
Iustin Pop committed
556
    """
557 558
    return self._SingleNodeCall(node, "node_add",
                                [dsa, dsapub, rsa, rsapub, ssh, sshpub])
Iustin Pop's avatar
Iustin Pop committed
559

Iustin Pop's avatar
Iustin Pop committed
560 561
  def call_node_verify(self, node_list, checkdict, cluster_name):
    """Request verification of given parameters.
Iustin Pop's avatar
Iustin Pop committed
562

Iustin Pop's avatar
Iustin Pop committed
563
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
564

Iustin Pop's avatar
Iustin Pop committed
565
    """
566 567
    return self._MultiNodeCall(node_list, "node_verify",
                               [checkdict, cluster_name])
Iustin Pop's avatar
Iustin Pop committed
568

569 570
  @classmethod
  def call_node_start_master(cls, node, start_daemons):
Iustin Pop's avatar
Iustin Pop committed
571
    """Tells a node to activate itself as a master.
Iustin Pop's avatar
Iustin Pop committed
572

Iustin Pop's avatar
Iustin Pop committed
573
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
574

Iustin Pop's avatar
Iustin Pop committed
575
    """
576 577
    return cls._StaticSingleNodeCall(node, "node_start_master",
                                     [start_daemons])
Iustin Pop's avatar
Iustin Pop committed
578

579 580
  @classmethod
  def call_node_stop_master(cls, node, stop_daemons):
Iustin Pop's avatar
Iustin Pop committed
581
    """Tells a node to demote itself from master status.
Iustin Pop's avatar
Iustin Pop committed
582

Iustin Pop's avatar
Iustin Pop committed
583
    This is a single-node call.
584

Iustin Pop's avatar
Iustin Pop committed
585
    """
586
    return cls._StaticSingleNodeCall(node, "node_stop_master", [stop_daemons])
587

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

Iustin Pop's avatar
Iustin Pop committed
598 599
  def call_version(self, node_list):
    """Query node version.
Iustin Pop's avatar
Iustin Pop committed
600

Iustin Pop's avatar
Iustin Pop committed
601
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
602

Iustin Pop's avatar
Iustin Pop committed
603
    """
604
    return self._MultiNodeCall(node_list, "version", [])
Iustin Pop's avatar
Iustin Pop committed
605

Iustin Pop's avatar
Iustin Pop committed
606 607
  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
608

Iustin Pop's avatar
Iustin Pop committed
609
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
610

Iustin Pop's avatar
Iustin Pop committed
611
    """
612 613
    return self._SingleNodeCall(node, "blockdev_create",
                                [bdev.ToDict(), size, owner, on_primary, info])
Iustin Pop's avatar
Iustin Pop committed
614

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

Iustin Pop's avatar
Iustin Pop committed
618
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
619

Iustin Pop's avatar
Iustin Pop committed
620
    """
621
    return self._SingleNodeCall(node, "blockdev_remove", [bdev.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
622

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

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

Iustin Pop's avatar
Iustin Pop committed
628
    """
629 630
    return self._SingleNodeCall(node, "blockdev_rename",
                                [(d.ToDict(), uid) for d, uid in devlist])
Iustin Pop's avatar
Iustin Pop committed
631

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

Iustin Pop's avatar
Iustin Pop committed
635
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
636

Iustin Pop's avatar
Iustin Pop committed
637
    """
638 639
    return self._SingleNodeCall(node, "blockdev_assemble",
                                [disk.ToDict(), owner, on_primary])
Iustin Pop's avatar
Iustin Pop committed
640

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

Iustin Pop's avatar
Iustin Pop committed
644
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
645

Iustin Pop's avatar
Iustin Pop committed
646
    """
647
    return self._SingleNodeCall(node, "blockdev_shutdown", [disk.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
648

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

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

Iustin Pop's avatar
Iustin Pop committed
654
    """
655 656 657
    return self._SingleNodeCall(node, "blockdev_addchildren",
                                [bdev.ToDict(),
                                 [disk.ToDict() for disk in ndevs]])
Iustin Pop's avatar
Iustin Pop committed
658

Iustin Pop's avatar
Iustin Pop committed
659 660
  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
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 667
    return self._SingleNodeCall(node, "blockdev_removechildren",
                                [bdev.ToDict(),
                                 [disk.ToDict() for disk in ndevs]])
Iustin Pop's avatar
Iustin Pop committed
668

Iustin Pop's avatar
Iustin Pop committed
669 670
  def call_blockdev_getmirrorstatus(self, node, disks):
    """Request status of a (mirroring) device.
Iustin Pop's avatar
Iustin Pop committed
671

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

Iustin Pop's avatar
Iustin Pop committed
674
    """
675 676
    return self._SingleNodeCall(node, "blockdev_getmirrorstatus",
                                [dsk.ToDict() for dsk in disks])
Iustin Pop's avatar
Iustin Pop committed
677

Iustin Pop's avatar
Iustin Pop committed
678 679 680 681
  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
682

Iustin Pop's avatar
Iustin Pop committed
683
    """
684
    return self._SingleNodeCall(node, "blockdev_find", [disk.ToDict()])
685

Iustin Pop's avatar
Iustin Pop committed
686 687
  def call_blockdev_close(self, node, disks):
    """Closes the given block devices.
688

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

Iustin Pop's avatar
Iustin Pop committed
691
    """
692 693
    return self._SingleNodeCall(node, "blockdev_close",
                                [cf.ToDict() for cf in disks])
Iustin Pop's avatar
Iustin Pop committed
694

695 696
  @classmethod
  def call_upload_file(cls, node_list, file_name, address_list=None):
Iustin Pop's avatar
Iustin Pop committed
697 698 699 700 701 702
    """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
703

704 705 706 707 708 709 710 711
    @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
712
    """
713 714
    file_contents = utils.ReadFile(file_name)
    data = cls._Compress(file_contents)
Iustin Pop's avatar
Iustin Pop committed
715 716 717
    st = os.stat(file_name)
    params = [file_name, data, st.st_mode, st.st_uid, st.st_gid,
              st.st_atime, st.st_mtime]
718 719
    return cls._StaticMultiNodeCall(node_list, "upload_file", params,
                                    address_list=address_list)
Iustin Pop's avatar
Iustin Pop committed
720

721
  @classmethod
722
  def call_write_ssconf_files(cls, node_list, values):
723 724 725 726 727
    """Write ssconf files.

    This is a multi-node call.

    """
728
    return cls._StaticMultiNodeCall(node_list, "write_ssconf_files", [values])
729

Iustin Pop's avatar
Iustin Pop committed
730 731 732 733
  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
734

Iustin Pop's avatar
Iustin Pop committed
735
    """
736 737
    result = self._MultiNodeCall(node_list, "os_diagnose", [])

738
    for node_result in result.values():
739 740 741 742
      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
743

Iustin Pop's avatar
Iustin Pop committed
744 745
  def call_os_get(self, node, name):
    """Returns an OS definition.
Iustin Pop's avatar
Iustin Pop committed
746

Iustin Pop's avatar
Iustin Pop committed
747
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
748

Iustin Pop's avatar
Iustin Pop committed
749
    """
750
    result = self._SingleNodeCall(node, "os_get", [name])
751 752 753
    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
754

Iustin Pop's avatar
Iustin Pop committed
755 756
  def call_hooks_runner(self, node_list, hpath, phase, env):
    """Call the hooks runner.
Iustin Pop's avatar
Iustin Pop committed
757

Iustin Pop's avatar
Iustin Pop committed
758 759 760
    Args:
      - op: the OpCode instance
      - env: a dictionary with the environment
Iustin Pop's avatar
Iustin Pop committed
761

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

Iustin Pop's avatar
Iustin Pop committed
764 765
    """
    params = [hpath, phase, env]
766
    return self._MultiNodeCall(node_list, "hooks_runner", params)
Iustin Pop's avatar
Iustin Pop committed
767

Iustin Pop's avatar
Iustin Pop committed
768 769
  def call_iallocator_runner(self, node, name, idata):
    """Call an iallocator on a remote node
770

Iustin Pop's avatar
Iustin Pop committed
771 772 773
    Args:
      - name: the iallocator name
      - input: the json-encoded input string
774

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

Iustin Pop's avatar
Iustin Pop committed
777
    """
778
    return self._SingleNodeCall(node, "iallocator_runner", [name, idata])
779

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

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

Iustin Pop's avatar
Iustin Pop committed
785
    """
786 787
    return self._SingleNodeCall(node, "blockdev_grow",
                                [cf_bdev.ToDict(), amount])
788

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

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

Iustin Pop's avatar
Iustin Pop committed
794
    """
795
    return self._SingleNodeCall(node, "blockdev_snapshot", [cf_bdev.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
796

Iustin Pop's avatar
Iustin Pop committed
797
  def call_snapshot_export(self, node, snap_bdev, dest_node, instance,
798
                           cluster_name, idx):
Iustin Pop's avatar
Iustin Pop committed
799
    """Request the export of a given snapshot.
Iustin Pop's avatar
Iustin Pop committed
800

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

Iustin Pop's avatar
Iustin Pop committed
803
    """
804 805 806
    return self._SingleNodeCall(node, "snapshot_export",
                                [snap_bdev.ToDict(), dest_node,
                                 self._InstDict(instance), cluster_name, idx])
Iustin Pop's avatar
Iustin Pop committed
807

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

Iustin Pop's avatar
Iustin Pop committed
811
    This writes the export config file, etc.
Iustin Pop's avatar
Iustin Pop committed
812

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

Iustin Pop's avatar
Iustin Pop committed
815 816 817 818
    """
    flat_disks = []
    for disk in snap_disks:
      flat_disks.append(disk.ToDict())
819 820 821

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

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