rpc.py 25 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
42
from ganeti import constants
Iustin Pop's avatar
Iustin Pop committed
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
74
# 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
75
76
77
class Client:
  """RPC Client class.

Alexander Schreiber's avatar
Alexander Schreiber committed
78
  This class, given a (remote) method name, a list of parameters and a
Iustin Pop's avatar
Iustin Pop committed
79
80
81
82
83
84
85
86
87
88
89
  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
90
    self.body = serializer.DumpJson(args, indent=False)
Iustin Pop's avatar
Iustin Pop committed
91

92
93
    self.port = utils.GetNodeDaemonPort()
    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
    if address is None:
      address = name

125
126
127
128
    # TODO: Cache key and certificate for different requests
    ssl_params = http.HttpSslParams(ssl_key_path=constants.SSL_CERT_FILE,
                                    ssl_cert_path=constants.SSL_CERT_FILE)

129
130
    self.nc[name] = http.HttpClientRequest(address, self.port, http.HTTP_PUT,
                                           "/%s" % self.procedure,
131
132
133
                                           post_data=self.body,
                                           ssl_params=ssl_params,
                                           ssl_verify_peer=True)
Iustin Pop's avatar
Iustin Pop committed
134

135
  def GetResults(self):
136
137
138
139
    """Call nodes and return results.

    @rtype: list
    @returns: List of RPC results
Iustin Pop's avatar
Iustin Pop committed
140
141

    """
142
143
144
    assert _http_manager, "RPC module not intialized"

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

146
    results = {}
Iustin Pop's avatar
Iustin Pop committed
147

148
149
    for name, req in self.nc.iteritems():
      if req.success and req.resp_status == http.HTTP_OK:
150
        results[name] = serializer.LoadJson(req.resp_body)
151
        continue
Iustin Pop's avatar
Iustin Pop committed
152

153
154
155
156
157
158
159
160
161
      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
162
163


Iustin Pop's avatar
Iustin Pop committed
164
165
class RpcRunner(object):
  """RPC runner class"""
Iustin Pop's avatar
Iustin Pop committed
166

Iustin Pop's avatar
Iustin Pop committed
167
168
  def __init__(self, cfg):
    """Initialized the rpc runner.
Iustin Pop's avatar
Iustin Pop committed
169

Iustin Pop's avatar
Iustin Pop committed
170
171
172
    @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
173

Iustin Pop's avatar
Iustin Pop committed
174
175
    """
    self._cfg = cfg
Iustin Pop's avatar
Iustin Pop committed
176

177
178
179
180
181
182
183
184
185
186
187
188
189
190
  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()
191
192
193
    cluster = self._cfg.GetClusterInfo()
    idict["hvparams"] = cluster.FillHV(instance)
    idict["beparams"] = cluster.FillBE(instance)
194
195
    return idict

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
225
226
227
228
229
230
  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)

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
258
259
260
261
262
263
  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
264
265
  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
266

Iustin Pop's avatar
Iustin Pop committed
267
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
268

Iustin Pop's avatar
Iustin Pop committed
269
    """
270
    return self._MultiNodeCall(node_list, "volume_list", [vg_name])
Iustin Pop's avatar
Iustin Pop committed
271

Iustin Pop's avatar
Iustin Pop committed
272
273
  def call_vg_list(self, node_list):
    """Gets the volume group list.
Iustin Pop's avatar
Iustin Pop committed
274

Iustin Pop's avatar
Iustin Pop committed
275
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
276

Iustin Pop's avatar
Iustin Pop committed
277
    """
278
    return self._MultiNodeCall(node_list, "vg_list", [])
Iustin Pop's avatar
Iustin Pop committed
279

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

Iustin Pop's avatar
Iustin Pop committed
283
284
285
    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
286

Iustin Pop's avatar
Iustin Pop committed
287
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
288

Iustin Pop's avatar
Iustin Pop committed
289
    """
290
    return self._SingleNodeCall(node, "bridges_exist", [bridges_list])
Iustin Pop's avatar
Iustin Pop committed
291

Iustin Pop's avatar
Iustin Pop committed
292
293
  def call_instance_start(self, node, instance, extra_args):
    """Starts an instance.
Iustin Pop's avatar
Iustin Pop committed
294

Iustin Pop's avatar
Iustin Pop committed
295
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
296

Iustin Pop's avatar
Iustin Pop committed
297
    """
298
299
    return self._SingleNodeCall(node, "instance_start",
                                [self._InstDict(instance), extra_args])
Iustin Pop's avatar
Iustin Pop committed
300

Iustin Pop's avatar
Iustin Pop committed
301
302
  def call_instance_shutdown(self, node, instance):
    """Stops an instance.
Iustin Pop's avatar
Iustin Pop committed
303

Iustin Pop's avatar
Iustin Pop committed
304
    This is a single-node call.
305

Iustin Pop's avatar
Iustin Pop committed
306
    """
307
308
    return self._SingleNodeCall(node, "instance_shutdown",
                                [self._InstDict(instance)])
309

Iustin Pop's avatar
Iustin Pop committed
310
311
  def call_instance_migrate(self, node, instance, target, live):
    """Migrate an instance.
312

Iustin Pop's avatar
Iustin Pop committed
313
    This is a single-node call.
314

Iustin Pop's avatar
Iustin Pop committed
315
316
317
318
319
320
321
322
323
    @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)
324

Iustin Pop's avatar
Iustin Pop committed
325
    """
326
327
    return self._SingleNodeCall(node, "instance_migrate",
                                [self._InstDict(instance), target, live])
328

Iustin Pop's avatar
Iustin Pop committed
329
330
  def call_instance_reboot(self, node, instance, reboot_type, extra_args):
    """Reboots an instance.
331

Iustin Pop's avatar
Iustin Pop committed
332
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
333

Iustin Pop's avatar
Iustin Pop committed
334
    """
335
336
337
    return self._SingleNodeCall(node, "instance_reboot",
                                [self._InstDict(instance), reboot_type,
                                 extra_args])
Iustin Pop's avatar
Iustin Pop committed
338

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

Iustin Pop's avatar
Iustin Pop committed
342
    This is a single-node call.
343

Iustin Pop's avatar
Iustin Pop committed
344
    """
345
346
    return self._SingleNodeCall(node, "instance_os_add",
                                [self._InstDict(inst)])
347

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

Iustin Pop's avatar
Iustin Pop committed
351
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
352

Iustin Pop's avatar
Iustin Pop committed
353
    """
354
355
    return self._SingleNodeCall(node, "instance_run_rename",
                                [self._InstDict(inst), old_name])
Iustin Pop's avatar
Iustin Pop committed
356

Iustin Pop's avatar
Iustin Pop committed
357
358
  def call_instance_info(self, node, instance, hname):
    """Returns information about a single instance.
Iustin Pop's avatar
Iustin Pop committed
359

Iustin Pop's avatar
Iustin Pop committed
360
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
361

362
363
    @type node: list
    @param node: the list of nodes to query
Iustin Pop's avatar
Iustin Pop committed
364
365
366
367
    @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
368

Iustin Pop's avatar
Iustin Pop committed
369
    """
370
    return self._SingleNodeCall(node, "instance_info", [instance, hname])
371

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

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

Iustin Pop's avatar
Iustin Pop committed
377
378
379
380
    @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
381

Iustin Pop's avatar
Iustin Pop committed
382
    """
383
384
    return self._MultiNodeCall(node_list, "all_instances_info",
                               [hypervisor_list])
385

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

Iustin Pop's avatar
Iustin Pop committed
389
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
390

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

Iustin Pop's avatar
Iustin Pop committed
396
    """
397
    return self._MultiNodeCall(node_list, "instance_list", [hypervisor_list])
398

Iustin Pop's avatar
Iustin Pop committed
399
400
401
  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
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, "node_tcp_ping",
                                [source, target, port, timeout,
Iustin Pop's avatar
Iustin Pop committed
408
                                 live_port_needed])
Iustin Pop's avatar
Iustin Pop committed
409

410
411
412
413
414
415
  def call_node_has_ip_address(self, node, address):
    """Checks if a node has the given IP address.

    This is a single-node call.

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

Iustin Pop's avatar
Iustin Pop committed
418
419
  def call_node_info(self, node_list, vg_name, hypervisor_type):
    """Return node information.
420

Iustin Pop's avatar
Iustin Pop committed
421
422
    This will return memory information and volume group size and free
    space.
Iustin Pop's avatar
Iustin Pop committed
423

Iustin Pop's avatar
Iustin Pop committed
424
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
425

Iustin Pop's avatar
Iustin Pop committed
426
427
428
429
430
431
432
433
    @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
434

Iustin Pop's avatar
Iustin Pop committed
435
    """
436
437
    retux = self._MultiNodeCall(node_list, "node_info",
                                [vg_name, hypervisor_type])
Iustin Pop's avatar
Iustin Pop committed
438

Iustin Pop's avatar
Iustin Pop committed
439
440
441
    for node_name in retux:
      ret = retux.get(node_name, False)
      if type(ret) != dict:
442
        logging.error("could not connect to node %s", node_name)
Iustin Pop's avatar
Iustin Pop committed
443
        ret = {}
Iustin Pop's avatar
Iustin Pop committed
444

445
446
      utils.CheckDict(ret, {
                        'memory_total' : '-',
Iustin Pop's avatar
Iustin Pop committed
447
448
449
                        'memory_dom0' : '-',
                        'memory_free' : '-',
                        'vg_size' : 'node_unreachable',
450
451
                        'vg_free' : '-',
                      }, "call_node_info")
Iustin Pop's avatar
Iustin Pop committed
452
    return retux
Iustin Pop's avatar
Iustin Pop committed
453

Iustin Pop's avatar
Iustin Pop committed
454
455
  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
456

Iustin Pop's avatar
Iustin Pop committed
457
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
458

Iustin Pop's avatar
Iustin Pop committed
459
    """
460
461
    return self._SingleNodeCall(node, "node_add",
                                [dsa, dsapub, rsa, rsapub, ssh, sshpub])
Iustin Pop's avatar
Iustin Pop committed
462

Iustin Pop's avatar
Iustin Pop committed
463
464
  def call_node_verify(self, node_list, checkdict, cluster_name):
    """Request verification of given parameters.
Iustin Pop's avatar
Iustin Pop committed
465

Iustin Pop's avatar
Iustin Pop committed
466
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
467

Iustin Pop's avatar
Iustin Pop committed
468
    """
469
470
    return self._MultiNodeCall(node_list, "node_verify",
                               [checkdict, cluster_name])
Iustin Pop's avatar
Iustin Pop committed
471

472
473
  @classmethod
  def call_node_start_master(cls, node, start_daemons):
Iustin Pop's avatar
Iustin Pop committed
474
    """Tells a node to activate itself as a master.
Iustin Pop's avatar
Iustin Pop committed
475

Iustin Pop's avatar
Iustin Pop committed
476
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
477

Iustin Pop's avatar
Iustin Pop committed
478
    """
479
480
    return cls._StaticSingleNodeCall(node, "node_start_master",
                                     [start_daemons])
Iustin Pop's avatar
Iustin Pop committed
481

482
483
  @classmethod
  def call_node_stop_master(cls, node, stop_daemons):
Iustin Pop's avatar
Iustin Pop committed
484
    """Tells a node to demote itself from master status.
Iustin Pop's avatar
Iustin Pop committed
485

Iustin Pop's avatar
Iustin Pop committed
486
    This is a single-node call.
487

Iustin Pop's avatar
Iustin Pop committed
488
    """
489
    return cls._StaticSingleNodeCall(node, "node_stop_master", [stop_daemons])
490

491
492
  @classmethod
  def call_master_info(cls, node_list):
Iustin Pop's avatar
Iustin Pop committed
493
    """Query master info.
494

Iustin Pop's avatar
Iustin Pop committed
495
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
496

Iustin Pop's avatar
Iustin Pop committed
497
498
    """
    # TODO: should this method query down nodes?
499
    return cls._StaticMultiNodeCall(node_list, "master_info", [])
Iustin Pop's avatar
Iustin Pop committed
500

Iustin Pop's avatar
Iustin Pop committed
501
502
  def call_version(self, node_list):
    """Query node version.
Iustin Pop's avatar
Iustin Pop committed
503

Iustin Pop's avatar
Iustin Pop committed
504
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
505

Iustin Pop's avatar
Iustin Pop committed
506
    """
507
    return self._MultiNodeCall(node_list, "version", [])
Iustin Pop's avatar
Iustin Pop committed
508

Iustin Pop's avatar
Iustin Pop committed
509
510
  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
511

Iustin Pop's avatar
Iustin Pop committed
512
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
513

Iustin Pop's avatar
Iustin Pop committed
514
    """
515
516
    return self._SingleNodeCall(node, "blockdev_create",
                                [bdev.ToDict(), size, owner, on_primary, info])
Iustin Pop's avatar
Iustin Pop committed
517

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

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

Iustin Pop's avatar
Iustin Pop committed
523
    """
524
    return self._SingleNodeCall(node, "blockdev_remove", [bdev.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
525

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

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

Iustin Pop's avatar
Iustin Pop committed
531
    """
532
533
    return self._SingleNodeCall(node, "blockdev_rename",
                                [(d.ToDict(), uid) for d, uid in devlist])
Iustin Pop's avatar
Iustin Pop committed
534

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

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

Iustin Pop's avatar
Iustin Pop committed
540
    """
541
542
    return self._SingleNodeCall(node, "blockdev_assemble",
                                [disk.ToDict(), owner, on_primary])
Iustin Pop's avatar
Iustin Pop committed
543

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

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

Iustin Pop's avatar
Iustin Pop committed
549
    """
550
    return self._SingleNodeCall(node, "blockdev_shutdown", [disk.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
551

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

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

Iustin Pop's avatar
Iustin Pop committed
557
    """
558
559
560
    return self._SingleNodeCall(node, "blockdev_addchildren",
                                [bdev.ToDict(),
                                 [disk.ToDict() for disk in ndevs]])
Iustin Pop's avatar
Iustin Pop committed
561

Iustin Pop's avatar
Iustin Pop committed
562
563
  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
564

Iustin Pop's avatar
Iustin Pop committed
565
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
566

Iustin Pop's avatar
Iustin Pop committed
567
    """
568
569
570
    return self._SingleNodeCall(node, "blockdev_removechildren",
                                [bdev.ToDict(),
                                 [disk.ToDict() for disk in ndevs]])
Iustin Pop's avatar
Iustin Pop committed
571

Iustin Pop's avatar
Iustin Pop committed
572
573
  def call_blockdev_getmirrorstatus(self, node, disks):
    """Request status of a (mirroring) device.
Iustin Pop's avatar
Iustin Pop committed
574

Iustin Pop's avatar
Iustin Pop committed
575
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
576

Iustin Pop's avatar
Iustin Pop committed
577
    """
578
579
    return self._SingleNodeCall(node, "blockdev_getmirrorstatus",
                                [dsk.ToDict() for dsk in disks])
Iustin Pop's avatar
Iustin Pop committed
580

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

Iustin Pop's avatar
Iustin Pop committed
586
    """
587
    return self._SingleNodeCall(node, "blockdev_find", [disk.ToDict()])
588

Iustin Pop's avatar
Iustin Pop committed
589
590
  def call_blockdev_close(self, node, disks):
    """Closes the given block devices.
591

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

Iustin Pop's avatar
Iustin Pop committed
594
    """
595
596
    return self._SingleNodeCall(node, "blockdev_close",
                                [cf.ToDict() for cf in disks])
Iustin Pop's avatar
Iustin Pop committed
597

598
599
  @classmethod
  def call_upload_file(cls, node_list, file_name, address_list=None):
Iustin Pop's avatar
Iustin Pop committed
600
601
602
603
604
605
    """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
606

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

623
624
625
626
627
628
629
630
631
  @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
632
633
634
635
  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
636

Iustin Pop's avatar
Iustin Pop committed
637
    """
638
639
    result = self._MultiNodeCall(node_list, "os_diagnose", [])

Iustin Pop's avatar
Iustin Pop committed
640
641
642
643
644
645
646
647
    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
648

Iustin Pop's avatar
Iustin Pop committed
649
650
  def call_os_get(self, node, name):
    """Returns an OS definition.
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
    result = self._SingleNodeCall(node, "os_get", [name])
Iustin Pop's avatar
Iustin Pop committed
656
657
658
659
    if isinstance(result, dict):
      return objects.OS.FromDict(result)
    else:
      return result
Iustin Pop's avatar
Iustin Pop committed
660

Iustin Pop's avatar
Iustin Pop committed
661
662
  def call_hooks_runner(self, node_list, hpath, phase, env):
    """Call the hooks runner.
Iustin Pop's avatar
Iustin Pop committed
663

Iustin Pop's avatar
Iustin Pop committed
664
665
666
    Args:
      - op: the OpCode instance
      - env: a dictionary with the environment
Iustin Pop's avatar
Iustin Pop committed
667

Iustin Pop's avatar
Iustin Pop committed
668
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
669

Iustin Pop's avatar
Iustin Pop committed
670
671
    """
    params = [hpath, phase, env]
672
    return self._MultiNodeCall(node_list, "hooks_runner", params)
Iustin Pop's avatar
Iustin Pop committed
673

Iustin Pop's avatar
Iustin Pop committed
674
675
  def call_iallocator_runner(self, node, name, idata):
    """Call an iallocator on a remote node
676

Iustin Pop's avatar
Iustin Pop committed
677
678
679
    Args:
      - name: the iallocator name
      - input: the json-encoded input string
680

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

Iustin Pop's avatar
Iustin Pop committed
683
    """
684
    return self._SingleNodeCall(node, "iallocator_runner", [name, idata])
685

Iustin Pop's avatar
Iustin Pop committed
686
687
  def call_blockdev_grow(self, node, cf_bdev, amount):
    """Request a snapshot of the given block device.
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_grow",
                                [cf_bdev.ToDict(), amount])
694

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

Iustin Pop's avatar
Iustin Pop committed
698
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
699

Iustin Pop's avatar
Iustin Pop committed
700
    """
701
    return self._SingleNodeCall(node, "blockdev_snapshot", [cf_bdev.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
702

Iustin Pop's avatar
Iustin Pop committed
703
  def call_snapshot_export(self, node, snap_bdev, dest_node, instance,
704
                           cluster_name, idx):
Iustin Pop's avatar
Iustin Pop committed
705
    """Request the export of a given snapshot.
Iustin Pop's avatar
Iustin Pop committed
706

Iustin Pop's avatar
Iustin Pop committed
707
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
708

Iustin Pop's avatar
Iustin Pop committed
709
    """
710
711
712
    return self._SingleNodeCall(node, "snapshot_export",
                                [snap_bdev.ToDict(), dest_node,
                                 self._InstDict(instance), cluster_name, idx])
Iustin Pop's avatar
Iustin Pop committed
713

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

Iustin Pop's avatar
Iustin Pop committed
717
    This writes the export config file, etc.
Iustin Pop's avatar
Iustin Pop committed
718

Iustin Pop's avatar
Iustin Pop committed
719
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
720

Iustin Pop's avatar
Iustin Pop committed
721
722
723
724
    """
    flat_disks = []
    for disk in snap_disks:
      flat_disks.append(disk.ToDict())
725
726
727

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

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

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

Iustin Pop's avatar
Iustin Pop committed
734
    """
735
    result = self._SingleNodeCall(node, "export_info", [path])
Iustin Pop's avatar
Iustin Pop committed
736
737
738
    if not result:
      return result
    return objects.SerializableConfigParser.Loads(str(result))
Iustin Pop's avatar
Iustin Pop committed
739

740
741
  def call_instance_os_import(self, node, inst, src_node, src_images,
                              cluster_name):
Iustin Pop's avatar
Iustin Pop committed
742
    """Request the import of a backup into an instance.
Iustin Pop's avatar
Iustin Pop committed
743

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

Iustin Pop's avatar
Iustin Pop committed
746
    """
747
748
749
    return self._SingleNodeCall(node, "instance_os_import",
                                [self._InstDict(inst), src_node, src_images,
                                 cluster_name])
Iustin Pop's avatar
Iustin Pop committed
750

Iustin Pop's avatar
Iustin Pop committed
751
752
  def call_export_list(self, node_list):
    """Gets the stored exports list.
Iustin Pop's avatar
Iustin Pop committed
753

Iustin Pop's avatar
Iustin Pop committed
754
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
755

Iustin Pop's avatar
Iustin Pop committed
756
    """
757
    return self._MultiNodeCall(node_list, "export_list", [])
Iustin Pop's avatar
Iustin Pop committed
758

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

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

Iustin Pop's avatar
Iustin Pop committed
764
    """
765
    return self._SingleNodeCall(node, "export_remove", [export])
Iustin Pop's avatar
Iustin Pop committed
766

767
768
  @classmethod
  def call_node_leave_cluster(cls, node):
Iustin Pop's avatar
Iustin Pop committed
769
    """Requests a node to clean the cluster information it has.
Iustin Pop's avatar
Iustin Pop committed
770

Iustin Pop's avatar
Iustin Pop committed
771
772
    This will remove the configuration information from the ganeti data
    dir.
Iustin Pop's avatar
Iustin Pop committed
773

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

Iustin Pop's avatar
Iustin Pop committed
776
    """
777
    return cls._StaticSingleNodeCall(node, "node_leave_cluster", [])
778

Iustin Pop's avatar
Iustin Pop committed
779
780
  def call_node_volumes(self, node_list):
    """Gets all volumes on node(s).
781

Iustin Pop's avatar
Iustin Pop committed
782
    This is a multi-node call.
783

Iustin Pop's avatar
Iustin Pop committed
784
    """
785
    return self._MultiNodeCall(node_list, "node_volumes", [])
786

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

Iustin Pop's avatar
Iustin Pop committed
790
    This is a multi-node call.
791

Iustin Pop's avatar
Iustin Pop committed
792
    """
793
    return self._MultiNodeCall(node_list, "test_delay", [duration])
794

Iustin Pop's avatar
Iustin Pop committed
795
796
  def call_file_storage_dir_create(self, node, file_storage_dir):
    """Create the given file storage directory.
797

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

Iustin Pop's avatar
Iustin Pop committed
800
    """
801
802
    return self._SingleNodeCall(node, "file_storage_dir_create",
                                [file_storage_dir])
803

Iustin Pop's avatar
Iustin Pop committed
804
805
  def call_file_storage_dir_remove(self, node, file_storage_dir):
    """Remove the given file storage directory.
806

Iustin Pop's avatar
Iustin Pop committed
807
    This is a single-node call.
808

Iustin Pop's avatar
Iustin Pop committed
809
    """
810
811
    return self._SingleNodeCall(node, "file_storage_dir_remove",
                                [file_storage_dir])
812

Iustin Pop's avatar
Iustin Pop committed
813
814
815
  def call_file_storage_dir_rename(self, node, old_file_storage_dir,
                                   new_file_storage_dir):
    """Rename file storage directory.
816

Iustin Pop's avatar
Iustin Pop committed
817
    This is a single-node call.
818

Iustin Pop's avatar
Iustin Pop committed
819
    """
820
821
    return self._SingleNodeCall(node, "file_storage_dir_rename",
                                [old_file_storage_dir, new_file_storage_dir])
822

823
824
  @classmethod
  def call_jobqueue_update(cls, node_list, address_list, file_name, content):
Iustin Pop's avatar
Iustin Pop committed
825
    """Update job queue.
826

Iustin Pop's avatar
Iustin Pop committed
827
    This is a multi-node call.
828

Iustin Pop's avatar
Iustin Pop committed
829
    """
830
831
832
    return cls._StaticMultiNodeCall(node_list, "jobqueue_update",
                                    [file_name, content],
                                    address_list=address_list)
833

834
835
  @classmethod
  def call_jobqueue_purge(cls, node):
Iustin Pop's avatar
Iustin Pop committed
836
    """Purge job queue.
837

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

Iustin Pop's avatar
Iustin Pop committed
840
    """
841
    return cls._StaticSingleNodeCall(node, "jobqueue_purge", [])
842

843
844
  @classmethod
  def call_jobqueue_rename(cls, node_list, address_list, old, new):
Iustin Pop's avatar
Iustin Pop committed
845
    """Rename a job queue file.
846

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

Iustin Pop's avatar
Iustin Pop committed
849
    """
850
851
    return cls._StaticMultiNodeCall(node_list, "jobqueue_rename", [old, new],
                                    address_list=address_list)
852

853
854
  @classmethod
  def call_jobqueue_set_drain(cls, node_list, drain_flag):
855
856
857
858
859
860
861
862
863
864
    """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.

    """
865
866
    return cls._StaticMultiNodeCall(node_list, "jobqueue_set_drain",
                                    [drain_flag])
867

868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
  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)
883
884
    return self._MultiNodeCall(node_list, "hypervisor_validate_params",
                               [hvname, hv_full])