rpc.py 24.7 KB
Newer Older
Iustin Pop's avatar
Iustin Pop committed
1
#
Iustin Pop's avatar
Iustin Pop committed
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#

# Copyright (C) 2006, 2007 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.


22
"""Inter-node RPC library.
Iustin Pop's avatar
Iustin Pop committed
23
24
25

"""

Iustin Pop's avatar
Iustin Pop committed
26
27
28
29
30
31
# pylint: disable-msg=C0103,R0201,R0904
# C0103: Invalid name, since call_ are not valid
# R0201: Method could be a function, we keep all rpcs instance methods
# as not to change them back and forth between static/instance methods
# if they need to start using instance attributes
# R0904: Too many public methods
Iustin Pop's avatar
Iustin Pop committed
32
33

import os
Iustin Pop's avatar
Iustin Pop committed
34
35
import socket
import httplib
36
import logging
Iustin Pop's avatar
Iustin Pop committed
37
38
39

from ganeti import utils
from ganeti import objects
40
from ganeti import http
41
from ganeti import serializer
Iustin Pop's avatar
Iustin Pop committed
42
43


44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# 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"

  _http_manager = http.HttpClientManager()


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


Iustin Pop's avatar
Iustin Pop committed
74
75
76
class Client:
  """RPC Client class.

Alexander Schreiber's avatar
Alexander Schreiber committed
77
  This class, given a (remote) method name, a list of parameters and a
Iustin Pop's avatar
Iustin Pop committed
78
79
80
81
82
83
84
85
86
87
88
  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.

  """
  def __init__(self, procedure, args):
    self.procedure = procedure
    self.args = args
89
    self.body = serializer.DumpJson(args, indent=False)
Iustin Pop's avatar
Iustin Pop committed
90

91
92
93
    self.port = utils.GetNodeDaemonPort()
    self.nodepw = utils.GetNodeDaemonPassword()
    self.nc = {}
Iustin Pop's avatar
Iustin Pop committed
94

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

98
99
    @type node_list: list
    @param node_list: the list of node names to connect
100
101
102
    @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
103

Iustin Pop's avatar
Iustin Pop committed
104
    """
105
106
107
108
109
110
111
112
113
    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
114
115
    """Add a node to the target list.

116
117
118
119
120
    @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
121
    """
122
123
124
125
126
127
    if address is None:
      address = name

    self.nc[name] = http.HttpClientRequest(address, self.port, http.HTTP_PUT,
                                           "/%s" % self.procedure,
                                           post_data=self.body)
Iustin Pop's avatar
Iustin Pop committed
128

129
  def GetResults(self):
130
131
132
133
    """Call nodes and return results.

    @rtype: list
    @returns: List of RPC results
Iustin Pop's avatar
Iustin Pop committed
134
135

    """
136
137
138
    assert _http_manager, "RPC module not intialized"

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

140
    results = {}
Iustin Pop's avatar
Iustin Pop committed
141

142
143
    for name, req in self.nc.iteritems():
      if req.success and req.resp_status == http.HTTP_OK:
144
        results[name] = serializer.LoadJson(req.resp_body)
145
        continue
Iustin Pop's avatar
Iustin Pop committed
146

147
148
149
150
151
152
153
154
155
      if req.error:
        msg = req.error
      else:
        msg = req.resp_body

      logging.error("RPC error from node %s: %s", name, msg)
      results[name] = False

    return results
Iustin Pop's avatar
Iustin Pop committed
156
157


Iustin Pop's avatar
Iustin Pop committed
158
159
class RpcRunner(object):
  """RPC runner class"""
Iustin Pop's avatar
Iustin Pop committed
160

Iustin Pop's avatar
Iustin Pop committed
161
162
  def __init__(self, cfg):
    """Initialized the rpc runner.
Iustin Pop's avatar
Iustin Pop committed
163

Iustin Pop's avatar
Iustin Pop committed
164
165
166
    @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
167

Iustin Pop's avatar
Iustin Pop committed
168
169
    """
    self._cfg = cfg
Iustin Pop's avatar
Iustin Pop committed
170

171
172
173
174
175
176
177
178
179
180
181
182
183
184
  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()
185
186
187
    cluster = self._cfg.GetClusterInfo()
    idict["hvparams"] = cluster.FillHV(instance)
    idict["beparams"] = cluster.FillBE(instance)
188
189
    return idict

190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
  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()
    addr_list = []
    for node in node_list:
      if node in all_nodes:
        val = all_nodes[node].primary_ip
      else:
        val = None
      addr_list.append(val)
    client.ConnectList(node_list, address_list=addr_list)

  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:
      addr = node_info.primary_ip
    else:
      addr = None
    client.ConnectNode(node, address=addr)

225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
  def _MultiNodeCall(self, node_list, procedure, args,
                     address_list=None):
    c = Client(procedure, args)
    if address_list is None:
      self._ConnectList(c, node_list)
    else:
      c.ConnectList(node_list, address_list=address_list)
    return c.GetResults()

  @classmethod
  def _StaticMultiNodeCall(cls, node_list, procedure, args,
                           address_list=None):
    c = Client(procedure, args)
    c.ConnectList(node_list, address_list=address_list)
    return c.GetResults()

  def _SingleNodeCall(self, node, procedure, args):
    """

    """
    c = Client(procedure, args)
    self._ConnectNode(c, node)
    return c.GetResults().get(node, False)

  @classmethod
  def _StaticSingleNodeCall(cls, node, procedure, args):
    """

    """
    c = Client(procedure, args)
    c.ConnectNode(c, node)
    return c.GetResults().get(node, False)

Iustin Pop's avatar
Iustin Pop committed
258
259
  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
260

Iustin Pop's avatar
Iustin Pop committed
261
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
262

Iustin Pop's avatar
Iustin Pop committed
263
    """
264
    return self._MultiNodeCall(node_list, "volume_list", [vg_name])
Iustin Pop's avatar
Iustin Pop committed
265

Iustin Pop's avatar
Iustin Pop committed
266
267
  def call_vg_list(self, node_list):
    """Gets the volume group list.
Iustin Pop's avatar
Iustin Pop committed
268

Iustin Pop's avatar
Iustin Pop committed
269
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
270

Iustin Pop's avatar
Iustin Pop committed
271
    """
272
    return self._MultiNodeCall(node_list, "vg_list", [])
Iustin Pop's avatar
Iustin Pop committed
273

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

Iustin Pop's avatar
Iustin Pop committed
277
278
279
    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
280

Iustin Pop's avatar
Iustin Pop committed
281
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
282

Iustin Pop's avatar
Iustin Pop committed
283
    """
284
    return self._SingleNodeCall(node, "bridges_exist", [bridges_list])
Iustin Pop's avatar
Iustin Pop committed
285

Iustin Pop's avatar
Iustin Pop committed
286
287
  def call_instance_start(self, node, instance, extra_args):
    """Starts an instance.
Iustin Pop's avatar
Iustin Pop committed
288

Iustin Pop's avatar
Iustin Pop committed
289
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
290

Iustin Pop's avatar
Iustin Pop committed
291
    """
292
293
    return self._SingleNodeCall(node, "instance_start",
                                [self._InstDict(instance), extra_args])
Iustin Pop's avatar
Iustin Pop committed
294

Iustin Pop's avatar
Iustin Pop committed
295
296
  def call_instance_shutdown(self, node, instance):
    """Stops an instance.
Iustin Pop's avatar
Iustin Pop committed
297

Iustin Pop's avatar
Iustin Pop committed
298
    This is a single-node call.
299

Iustin Pop's avatar
Iustin Pop committed
300
    """
301
302
    return self._SingleNodeCall(node, "instance_shutdown",
                                [self._InstDict(instance)])
303

Iustin Pop's avatar
Iustin Pop committed
304
305
  def call_instance_migrate(self, node, instance, target, live):
    """Migrate an instance.
306

Iustin Pop's avatar
Iustin Pop committed
307
    This is a single-node call.
308

Iustin Pop's avatar
Iustin Pop committed
309
310
311
312
313
314
315
316
317
    @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)
318

Iustin Pop's avatar
Iustin Pop committed
319
    """
320
321
    return self._SingleNodeCall(node, "instance_migrate",
                                [self._InstDict(instance), target, live])
322

Iustin Pop's avatar
Iustin Pop committed
323
324
  def call_instance_reboot(self, node, instance, reboot_type, extra_args):
    """Reboots an instance.
325

Iustin Pop's avatar
Iustin Pop committed
326
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
327

Iustin Pop's avatar
Iustin Pop committed
328
    """
329
330
331
    return self._SingleNodeCall(node, "instance_reboot",
                                [self._InstDict(instance), reboot_type,
                                 extra_args])
Iustin Pop's avatar
Iustin Pop committed
332

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

Iustin Pop's avatar
Iustin Pop committed
336
    This is a single-node call.
337

Iustin Pop's avatar
Iustin Pop committed
338
    """
339
340
    return self._SingleNodeCall(node, "instance_os_add",
                                [self._InstDict(inst)])
341

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

Iustin Pop's avatar
Iustin Pop committed
345
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
346

Iustin Pop's avatar
Iustin Pop committed
347
    """
348
349
    return self._SingleNodeCall(node, "instance_run_rename",
                                [self._InstDict(inst), old_name])
Iustin Pop's avatar
Iustin Pop committed
350

Iustin Pop's avatar
Iustin Pop committed
351
352
  def call_instance_info(self, node, instance, hname):
    """Returns information about a single instance.
Iustin Pop's avatar
Iustin Pop committed
353

Iustin Pop's avatar
Iustin Pop committed
354
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
355

356
357
    @type node: list
    @param node: the list of nodes to query
Iustin Pop's avatar
Iustin Pop committed
358
359
360
361
    @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
362

Iustin Pop's avatar
Iustin Pop committed
363
    """
364
    return self._SingleNodeCall(node, "instance_info", [instance, hname])
365

Iustin Pop's avatar
Iustin Pop committed
366
367
  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
368

Iustin Pop's avatar
Iustin Pop committed
369
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
370

Iustin Pop's avatar
Iustin Pop committed
371
372
373
374
    @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
375

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

Iustin Pop's avatar
Iustin Pop committed
380
381
  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
382

Iustin Pop's avatar
Iustin Pop committed
383
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
384

Iustin Pop's avatar
Iustin Pop committed
385
386
387
388
    @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
389

Iustin Pop's avatar
Iustin Pop committed
390
    """
391
    return self._MultiNodeCall(node_list, "instance_list", [hypervisor_list])
392

Iustin Pop's avatar
Iustin Pop committed
393
394
395
  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
396

Iustin Pop's avatar
Iustin Pop committed
397
    This is a single-node call.
398

Iustin Pop's avatar
Iustin Pop committed
399
    """
400
401
    return self._SingleNodeCall(node, "node_tcp_ping",
                                [source, target, port, timeout,
Iustin Pop's avatar
Iustin Pop committed
402
                                 live_port_needed])
Iustin Pop's avatar
Iustin Pop committed
403

404
405
406
407
408
409
  def call_node_has_ip_address(self, node, address):
    """Checks if a node has the given IP address.

    This is a single-node call.

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

Iustin Pop's avatar
Iustin Pop committed
412
413
  def call_node_info(self, node_list, vg_name, hypervisor_type):
    """Return node information.
414

Iustin Pop's avatar
Iustin Pop committed
415
416
    This will return memory information and volume group size and free
    space.
Iustin Pop's avatar
Iustin Pop committed
417

Iustin Pop's avatar
Iustin Pop committed
418
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
419

Iustin Pop's avatar
Iustin Pop committed
420
421
422
423
424
425
426
427
    @type node_list: list
    @param node_list: the list of nodes to query
    @type vgname: C{string}
    @param vgname: the name of the volume group to ask for disk space
        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
428

Iustin Pop's avatar
Iustin Pop committed
429
    """
430
431
    retux = self._MultiNodeCall(node_list, "node_info",
                                [vg_name, hypervisor_type])
Iustin Pop's avatar
Iustin Pop committed
432

Iustin Pop's avatar
Iustin Pop committed
433
434
435
    for node_name in retux:
      ret = retux.get(node_name, False)
      if type(ret) != dict:
436
        logging.error("could not connect to node %s", node_name)
Iustin Pop's avatar
Iustin Pop committed
437
        ret = {}
Iustin Pop's avatar
Iustin Pop committed
438

439
440
      utils.CheckDict(ret, {
                        'memory_total' : '-',
Iustin Pop's avatar
Iustin Pop committed
441
442
443
                        'memory_dom0' : '-',
                        'memory_free' : '-',
                        'vg_size' : 'node_unreachable',
444
445
                        'vg_free' : '-',
                      }, "call_node_info")
Iustin Pop's avatar
Iustin Pop committed
446
    return retux
Iustin Pop's avatar
Iustin Pop committed
447

Iustin Pop's avatar
Iustin Pop committed
448
449
  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
450

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

Iustin Pop's avatar
Iustin Pop committed
453
    """
454
455
    return self._SingleNodeCall(node, "node_add",
                                [dsa, dsapub, rsa, rsapub, ssh, sshpub])
Iustin Pop's avatar
Iustin Pop committed
456

Iustin Pop's avatar
Iustin Pop committed
457
458
  def call_node_verify(self, node_list, checkdict, cluster_name):
    """Request verification of given parameters.
Iustin Pop's avatar
Iustin Pop committed
459

Iustin Pop's avatar
Iustin Pop committed
460
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
461

Iustin Pop's avatar
Iustin Pop committed
462
    """
463
464
    return self._MultiNodeCall(node_list, "node_verify",
                               [checkdict, cluster_name])
Iustin Pop's avatar
Iustin Pop committed
465

466
467
  @classmethod
  def call_node_start_master(cls, node, start_daemons):
Iustin Pop's avatar
Iustin Pop committed
468
    """Tells a node to activate itself as a master.
Iustin Pop's avatar
Iustin Pop committed
469

Iustin Pop's avatar
Iustin Pop committed
470
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
471

Iustin Pop's avatar
Iustin Pop committed
472
    """
473
474
    return cls._StaticSingleNodeCall(node, "node_start_master",
                                     [start_daemons])
Iustin Pop's avatar
Iustin Pop committed
475

476
477
  @classmethod
  def call_node_stop_master(cls, node, stop_daemons):
Iustin Pop's avatar
Iustin Pop committed
478
    """Tells a node to demote itself from master status.
Iustin Pop's avatar
Iustin Pop committed
479

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

Iustin Pop's avatar
Iustin Pop committed
482
    """
483
    return cls._StaticSingleNodeCall(node, "node_stop_master", [stop_daemons])
484

485
486
  @classmethod
  def call_master_info(cls, node_list):
Iustin Pop's avatar
Iustin Pop committed
487
    """Query master info.
488

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

Iustin Pop's avatar
Iustin Pop committed
491
492
    """
    # TODO: should this method query down nodes?
493
    return cls._StaticMultiNodeCall(node_list, "master_info", [])
Iustin Pop's avatar
Iustin Pop committed
494

Iustin Pop's avatar
Iustin Pop committed
495
496
  def call_version(self, node_list):
    """Query node version.
Iustin Pop's avatar
Iustin Pop committed
497

Iustin Pop's avatar
Iustin Pop committed
498
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
499

Iustin Pop's avatar
Iustin Pop committed
500
    """
501
    return self._MultiNodeCall(node_list, "version", [])
Iustin Pop's avatar
Iustin Pop committed
502

Iustin Pop's avatar
Iustin Pop committed
503
504
  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
505

Iustin Pop's avatar
Iustin Pop committed
506
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
507

Iustin Pop's avatar
Iustin Pop committed
508
    """
509
510
    return self._SingleNodeCall(node, "blockdev_create",
                                [bdev.ToDict(), size, owner, on_primary, info])
Iustin Pop's avatar
Iustin Pop committed
511

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

Iustin Pop's avatar
Iustin Pop committed
515
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
516

Iustin Pop's avatar
Iustin Pop committed
517
    """
518
    return self._SingleNodeCall(node, "blockdev_remove", [bdev.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
519

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

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

Iustin Pop's avatar
Iustin Pop committed
525
    """
526
527
    return self._SingleNodeCall(node, "blockdev_rename",
                                [(d.ToDict(), uid) for d, uid in devlist])
Iustin Pop's avatar
Iustin Pop committed
528

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

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

Iustin Pop's avatar
Iustin Pop committed
534
    """
535
536
    return self._SingleNodeCall(node, "blockdev_assemble",
                                [disk.ToDict(), owner, on_primary])
Iustin Pop's avatar
Iustin Pop committed
537

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

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

Iustin Pop's avatar
Iustin Pop committed
543
    """
544
    return self._SingleNodeCall(node, "blockdev_shutdown", [disk.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
545

Iustin Pop's avatar
Iustin Pop committed
546
547
  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
548

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

Iustin Pop's avatar
Iustin Pop committed
551
    """
552
553
554
    return self._SingleNodeCall(node, "blockdev_addchildren",
                                [bdev.ToDict(),
                                 [disk.ToDict() for disk in ndevs]])
Iustin Pop's avatar
Iustin Pop committed
555

Iustin Pop's avatar
Iustin Pop committed
556
557
  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
558

Iustin Pop's avatar
Iustin Pop committed
559
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
560

Iustin Pop's avatar
Iustin Pop committed
561
    """
562
563
564
    return self._SingleNodeCall(node, "blockdev_removechildren",
                                [bdev.ToDict(),
                                 [disk.ToDict() for disk in ndevs]])
Iustin Pop's avatar
Iustin Pop committed
565

Iustin Pop's avatar
Iustin Pop committed
566
567
  def call_blockdev_getmirrorstatus(self, node, disks):
    """Request status of a (mirroring) device.
Iustin Pop's avatar
Iustin Pop committed
568

Iustin Pop's avatar
Iustin Pop committed
569
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
570

Iustin Pop's avatar
Iustin Pop committed
571
    """
572
573
    return self._SingleNodeCall(node, "blockdev_getmirrorstatus",
                                [dsk.ToDict() for dsk in disks])
Iustin Pop's avatar
Iustin Pop committed
574

Iustin Pop's avatar
Iustin Pop committed
575
576
577
578
  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
579

Iustin Pop's avatar
Iustin Pop committed
580
    """
581
    return self._SingleNodeCall(node, "blockdev_find", [disk.ToDict()])
582

Iustin Pop's avatar
Iustin Pop committed
583
584
  def call_blockdev_close(self, node, disks):
    """Closes the given block devices.
585

Iustin Pop's avatar
Iustin Pop committed
586
    This is a single-node call.
587

Iustin Pop's avatar
Iustin Pop committed
588
    """
589
590
    return self._SingleNodeCall(node, "blockdev_close",
                                [cf.ToDict() for cf in disks])
Iustin Pop's avatar
Iustin Pop committed
591

592
593
  @classmethod
  def call_upload_file(cls, node_list, file_name, address_list=None):
Iustin Pop's avatar
Iustin Pop committed
594
595
596
597
598
599
    """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
600

601
602
603
604
605
606
607
608
    @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
609
    """
610
    data = utils.ReadFile(file_name)
Iustin Pop's avatar
Iustin Pop committed
611
612
613
    st = os.stat(file_name)
    params = [file_name, data, st.st_mode, st.st_uid, st.st_gid,
              st.st_atime, st.st_mtime]
614
615
    return cls._StaticMultiNodeCall(node_list, "upload_file", params,
                                    address_list=address_list)
Iustin Pop's avatar
Iustin Pop committed
616

617
618
619
620
621
622
623
624
625
  @classmethod
  def call_write_ssconf_files(cls, node_list):
    """Write ssconf files.

    This is a multi-node call.

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

Iustin Pop's avatar
Iustin Pop committed
626
627
628
629
  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
630

Iustin Pop's avatar
Iustin Pop committed
631
    """
632
633
    result = self._MultiNodeCall(node_list, "os_diagnose", [])

Iustin Pop's avatar
Iustin Pop committed
634
635
636
637
638
639
640
641
    new_result = {}
    for node_name in result:
      if result[node_name]:
        nr = [objects.OS.FromDict(oss) for oss in result[node_name]]
      else:
        nr = []
      new_result[node_name] = nr
    return new_result
Iustin Pop's avatar
Iustin Pop committed
642

Iustin Pop's avatar
Iustin Pop committed
643
644
  def call_os_get(self, node, name):
    """Returns an OS definition.
Iustin Pop's avatar
Iustin Pop committed
645

Iustin Pop's avatar
Iustin Pop committed
646
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
647

Iustin Pop's avatar
Iustin Pop committed
648
    """
649
    result = self._SingleNodeCall(node, "os_get", [name])
Iustin Pop's avatar
Iustin Pop committed
650
651
652
653
    if isinstance(result, dict):
      return objects.OS.FromDict(result)
    else:
      return result
Iustin Pop's avatar
Iustin Pop committed
654

Iustin Pop's avatar
Iustin Pop committed
655
656
  def call_hooks_runner(self, node_list, hpath, phase, env):
    """Call the hooks runner.
Iustin Pop's avatar
Iustin Pop committed
657

Iustin Pop's avatar
Iustin Pop committed
658
659
660
    Args:
      - op: the OpCode instance
      - env: a dictionary with the environment
Iustin Pop's avatar
Iustin Pop committed
661

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

Iustin Pop's avatar
Iustin Pop committed
664
665
    """
    params = [hpath, phase, env]
666
    return self._MultiNodeCall(node_list, "hooks_runner", params)
Iustin Pop's avatar
Iustin Pop committed
667

Iustin Pop's avatar
Iustin Pop committed
668
669
  def call_iallocator_runner(self, node, name, idata):
    """Call an iallocator on a remote node
670

Iustin Pop's avatar
Iustin Pop committed
671
672
673
    Args:
      - name: the iallocator name
      - input: the json-encoded input string
674

Iustin Pop's avatar
Iustin Pop committed
675
    This is a single-node call.
676

Iustin Pop's avatar
Iustin Pop committed
677
    """
678
    return self._SingleNodeCall(node, "iallocator_runner", [name, idata])
679

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

Iustin Pop's avatar
Iustin Pop committed
683
    This is a single-node call.
684

Iustin Pop's avatar
Iustin Pop committed
685
    """
686
687
    return self._SingleNodeCall(node, "blockdev_grow",
                                [cf_bdev.ToDict(), amount])
688

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

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

Iustin Pop's avatar
Iustin Pop committed
694
    """
695
    return self._SingleNodeCall(node, "blockdev_snapshot", [cf_bdev.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
696

Iustin Pop's avatar
Iustin Pop committed
697
  def call_snapshot_export(self, node, snap_bdev, dest_node, instance,
698
                           cluster_name, idx):
Iustin Pop's avatar
Iustin Pop committed
699
    """Request the export of a given snapshot.
Iustin Pop's avatar
Iustin Pop committed
700

Iustin Pop's avatar
Iustin Pop committed
701
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
702

Iustin Pop's avatar
Iustin Pop committed
703
    """
704
705
706
    return self._SingleNodeCall(node, "snapshot_export",
                                [snap_bdev.ToDict(), dest_node,
                                 self._InstDict(instance), cluster_name, idx])
Iustin Pop's avatar
Iustin Pop committed
707

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

Iustin Pop's avatar
Iustin Pop committed
711
    This writes the export config file, etc.
Iustin Pop's avatar
Iustin Pop committed
712

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

Iustin Pop's avatar
Iustin Pop committed
715
716
717
718
    """
    flat_disks = []
    for disk in snap_disks:
      flat_disks.append(disk.ToDict())
719
720
721

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

Iustin Pop's avatar
Iustin Pop committed
723
724
  def call_export_info(self, node, path):
    """Queries the export information in a given path.
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
    result = self._SingleNodeCall(node, "export_info", [path])
Iustin Pop's avatar
Iustin Pop committed
730
731
732
    if not result:
      return result
    return objects.SerializableConfigParser.Loads(str(result))
Iustin Pop's avatar
Iustin Pop committed
733

734
735
  def call_instance_os_import(self, node, inst, src_node, src_images,
                              cluster_name):
Iustin Pop's avatar
Iustin Pop committed
736
    """Request the import of a backup into an instance.
Iustin Pop's avatar
Iustin Pop committed
737

Iustin Pop's avatar
Iustin Pop committed
738
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
739

Iustin Pop's avatar
Iustin Pop committed
740
    """
741
742
743
    return self._SingleNodeCall(node, "instance_os_import",
                                [self._InstDict(inst), src_node, src_images,
                                 cluster_name])
Iustin Pop's avatar
Iustin Pop committed
744

Iustin Pop's avatar
Iustin Pop committed
745
746
  def call_export_list(self, node_list):
    """Gets the stored exports list.
Iustin Pop's avatar
Iustin Pop committed
747

Iustin Pop's avatar
Iustin Pop committed
748
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
749

Iustin Pop's avatar
Iustin Pop committed
750
    """
751
    return self._MultiNodeCall(node_list, "export_list", [])
Iustin Pop's avatar
Iustin Pop committed
752

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

Iustin Pop's avatar
Iustin Pop committed
756
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
757

Iustin Pop's avatar
Iustin Pop committed
758
    """
759
    return self._SingleNodeCall(node, "export_remove", [export])
Iustin Pop's avatar
Iustin Pop committed
760

761
762
  @classmethod
  def call_node_leave_cluster(cls, node):
Iustin Pop's avatar
Iustin Pop committed
763
    """Requests a node to clean the cluster information it has.
Iustin Pop's avatar
Iustin Pop committed
764

Iustin Pop's avatar
Iustin Pop committed
765
766
    This will remove the configuration information from the ganeti data
    dir.
Iustin Pop's avatar
Iustin Pop committed
767

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

Iustin Pop's avatar
Iustin Pop committed
770
    """
771
    return cls._StaticSingleNodeCall(node, "node_leave_cluster", [])
772

Iustin Pop's avatar
Iustin Pop committed
773
774
  def call_node_volumes(self, node_list):
    """Gets all volumes on node(s).
775

Iustin Pop's avatar
Iustin Pop committed
776
    This is a multi-node call.
777

Iustin Pop's avatar
Iustin Pop committed
778
    """
779
    return self._MultiNodeCall(node_list, "node_volumes", [])
780

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

Iustin Pop's avatar
Iustin Pop committed
784
    This is a multi-node call.
785

Iustin Pop's avatar
Iustin Pop committed
786
    """
787
    return self._MultiNodeCall(node_list, "test_delay", [duration])
788

Iustin Pop's avatar
Iustin Pop committed
789
790
  def call_file_storage_dir_create(self, node, file_storage_dir):
    """Create the given file storage directory.
791

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

Iustin Pop's avatar
Iustin Pop committed
794
    """
795
796
    return self._SingleNodeCall(node, "file_storage_dir_create",
                                [file_storage_dir])
797

Iustin Pop's avatar
Iustin Pop committed
798
799
  def call_file_storage_dir_remove(self, node, file_storage_dir):
    """Remove the given file storage directory.
800

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

Iustin Pop's avatar
Iustin Pop committed
803
    """
804
805
    return self._SingleNodeCall(node, "file_storage_dir_remove",
                                [file_storage_dir])
806

Iustin Pop's avatar
Iustin Pop committed
807
808
809
  def call_file_storage_dir_rename(self, node, old_file_storage_dir,
                                   new_file_storage_dir):
    """Rename file storage directory.
810

Iustin Pop's avatar
Iustin Pop committed
811
    This is a single-node call.
812

Iustin Pop's avatar
Iustin Pop committed
813
    """
814
815
    return self._SingleNodeCall(node, "file_storage_dir_rename",
                                [old_file_storage_dir, new_file_storage_dir])
816

817
818
  @classmethod
  def call_jobqueue_update(cls, node_list, address_list, file_name, content):
Iustin Pop's avatar
Iustin Pop committed
819
    """Update job queue.
820

Iustin Pop's avatar
Iustin Pop committed
821
    This is a multi-node call.
822

Iustin Pop's avatar
Iustin Pop committed
823
    """
824
825
826
    return cls._StaticMultiNodeCall(node_list, "jobqueue_update",
                                    [file_name, content],
                                    address_list=address_list)
827

828
829
  @classmethod
  def call_jobqueue_purge(cls, node):
Iustin Pop's avatar
Iustin Pop committed
830
    """Purge job queue.
831

Iustin Pop's avatar
Iustin Pop committed
832
    This is a single-node call.
833

Iustin Pop's avatar
Iustin Pop committed
834
    """
835
    return cls._StaticSingleNodeCall(node, "jobqueue_purge", [])
836

837
838
  @classmethod
  def call_jobqueue_rename(cls, node_list, address_list, old, new):
Iustin Pop's avatar
Iustin Pop committed
839
    """Rename a job queue file.
840

Iustin Pop's avatar
Iustin Pop committed
841
    This is a multi-node call.
842

Iustin Pop's avatar
Iustin Pop committed
843
    """
844
845
    return cls._StaticMultiNodeCall(node_list, "jobqueue_rename", [old, new],
                                    address_list=address_list)
846

847
848
  @classmethod
  def call_jobqueue_set_drain(cls, node_list, drain_flag):
849
850
851
852
853
854
855
856
857
858
    """Set the drain flag on the queue.

    This is a multi-node call.

    @type node_list: list
    @param node_list: the list of nodes to query
    @type drain_flag: bool
    @param drain_flag: if True, will set the drain flag, otherwise reset it.

    """
859
860
    return cls._StaticMultiNodeCall(node_list, "jobqueue_set_drain",
                                    [drain_flag])
861

862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
  def call_hypervisor_validate_params(self, node_list, hvname, hvparams):
    """Validate the hypervisor params.

    This is a multi-node call.

    @type node_list: list
    @param node_list: the list of nodes to query
    @type hvname: string
    @param hvname: the hypervisor name
    @type hvparams: dict
    @param hvparams: the hypervisor parameters to be validated

    """
    cluster = self._cfg.GetClusterInfo()
    hv_full = cluster.FillDict(cluster.hvparams.get(hvname, {}), hvparams)
877
878
    return self._MultiNodeCall(node_list, "hypervisor_validate_params",
                               [hvname, hv_full])