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
Iustin Pop committed
825

Iustin Pop's avatar
Iustin Pop committed
826
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
827

Iustin Pop's avatar
Iustin Pop committed
828
    """
829
    result = self._SingleNodeCall(node, "export_info", [path])
830
831
832
    if not result.failed and result.data:
      result.data = objects.SerializableConfigParser.Loads(str(result.data))
    return result
Iustin Pop's avatar
Iustin Pop committed
833

834
835
  def call_instance_os_import(self, node, inst, src_node, src_images,
                              cluster_name):
Iustin Pop's avatar
Iustin Pop committed
836
    """Request the import of a backup into an instance.
Iustin Pop's avatar
Iustin Pop committed
837

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

Iustin Pop's avatar
Iustin Pop committed
840
    """
841
842
843
    return self._SingleNodeCall(node, "instance_os_import",
                                [self._InstDict(inst), src_node, src_images,
                                 cluster_name])
Iustin Pop's avatar
Iustin Pop committed
844

Iustin Pop's avatar
Iustin Pop committed
845
846
  def call_export_list(self, node_list):
    """Gets the stored exports list.
Iustin Pop's avatar
Iustin Pop committed
847

Iustin Pop's avatar
Iustin Pop committed
848
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
849

Iustin Pop's avatar
Iustin Pop committed
850
    """
851
    return self._MultiNodeCall(node_list, "export_list", [])
Iustin Pop's avatar
Iustin Pop committed
852

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

Iustin Pop's avatar
Iustin Pop committed
856
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
857

Iustin Pop's avatar
Iustin Pop committed
858
    """
859
    return self._SingleNodeCall(node, "export_remove", [export])
Iustin Pop's avatar
Iustin Pop committed
860

861
862
  @classmethod
  def call_node_leave_cluster(cls, node):
Iustin Pop's avatar
Iustin Pop committed
863
    """Requests a node to clean the cluster information it has.
Iustin Pop's avatar
Iustin Pop committed
864

Iustin Pop's avatar
Iustin Pop committed
865
866
    This will remove the configuration information from the ganeti data
    dir.
Iustin Pop's avatar
Iustin Pop committed
867

Iustin Pop's avatar
Iustin Pop committed
868
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
869

Iustin Pop's avatar
Iustin Pop committed
870
    """
871
    return cls._StaticSingleNodeCall(node, "node_leave_cluster", [])
872

Iustin Pop's avatar
Iustin Pop committed
873
874
  def call_node_volumes(self, node_list):
    """Gets all volumes on node(s).
875

Iustin Pop's avatar
Iustin Pop committed
876
    This is a multi-node call.
877

Iustin Pop's avatar
Iustin Pop committed
878
    """
879
    return self._MultiNodeCall(node_list, "node_volumes", [])
880

881
882
883
884
885
886
887
888
  def call_node_demote_from_mc(self, node):
    """Demote a node from the master candidate role.

    This is a single-node call.

    """
    return self._SingleNodeCall(node, "node_demote_from_mc", [])

Iustin Pop's avatar
Iustin Pop committed
889
890
  def call_test_delay(self, node_list, duration):
    """Sleep for a fixed time on given node(s).
891

Iustin Pop's avatar
Iustin Pop committed
892
    This is a multi-node call.
893

Iustin Pop's avatar
Iustin Pop committed
894
    """
895
    return self._MultiNodeCall(node_list, "test_delay", [duration])
896

Iustin Pop's avatar
Iustin Pop committed
897
898
  def call_file_storage_dir_create(self, node, file_storage_dir):
    """Create the given file storage directory.
899

Iustin Pop's avatar
Iustin Pop committed
900
    This is a single-node call.
901

Iustin Pop's avatar
Iustin Pop committed
902
    """
903
904
    return self._SingleNodeCall(node, "file_storage_dir_create",
                                [file_storage_dir])
905

Iustin Pop's avatar
Iustin Pop committed
906
907
  def call_file_storage_dir_remove(self, node, file_storage_dir):
    """Remove the given file storage directory.
908

Iustin Pop's avatar
Iustin Pop committed
909
    This is a single-node call.
910

Iustin Pop's avatar
Iustin Pop committed
911
    """
912
913
    return self._SingleNodeCall(node, "file_storage_dir_remove",
                                [file_storage_dir])
914

Iustin Pop's avatar
Iustin Pop committed
915
916
917
  def call_file_storage_dir_rename(self, node, old_file_storage_dir,
                                   new_file_storage_dir):
    """Rename file storage directory.
918

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

Iustin Pop's avatar
Iustin Pop committed
921
    """