rpc.py 24.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
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


class Client:
  """RPC Client class.

Alexander Schreiber's avatar
Alexander Schreiber committed
47
  This class, given a (remote) method name, a list of parameters and a
Iustin Pop's avatar
Iustin Pop committed
48
49
50
51
52
53
54
55
56
57
58
  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
59
    self.body = serializer.DumpJson(args, indent=False)
Iustin Pop's avatar
Iustin Pop committed
60

61
62
63
    self.port = utils.GetNodeDaemonPort()
    self.nodepw = utils.GetNodeDaemonPassword()
    self.nc = {}
Iustin Pop's avatar
Iustin Pop committed
64

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

68
69
    @type node_list: list
    @param node_list: the list of node names to connect
70
71
72
    @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
73

Iustin Pop's avatar
Iustin Pop committed
74
    """
75
76
77
78
79
80
81
82
83
    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
84
85
    """Add a node to the target list.

86
87
88
89
90
    @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
91
    """
92
93
94
95
96
97
    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
98

99
  def GetResults(self):
100
101
102
103
    """Call nodes and return results.

    @rtype: list
    @returns: List of RPC results
Iustin Pop's avatar
Iustin Pop committed
104
105

    """
106
107
108
109
110
111
    # TODO: Shared and reused manager
    mgr = http.HttpClientManager()
    try:
      mgr.ExecRequests(self.nc.values())
    finally:
      mgr.Shutdown()
Iustin Pop's avatar
Iustin Pop committed
112

113
    results = {}
Iustin Pop's avatar
Iustin Pop committed
114

115
116
    for name, req in self.nc.iteritems():
      if req.success and req.resp_status == http.HTTP_OK:
117
        results[name] = serializer.LoadJson(req.resp_body)
118
        continue
Iustin Pop's avatar
Iustin Pop committed
119

120
121
122
123
124
125
126
127
128
      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
129
130


Iustin Pop's avatar
Iustin Pop committed
131
132
class RpcRunner(object):
  """RPC runner class"""
Iustin Pop's avatar
Iustin Pop committed
133

Iustin Pop's avatar
Iustin Pop committed
134
135
  def __init__(self, cfg):
    """Initialized the rpc runner.
Iustin Pop's avatar
Iustin Pop committed
136

Iustin Pop's avatar
Iustin Pop committed
137
138
139
    @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
140

Iustin Pop's avatar
Iustin Pop committed
141
142
    """
    self._cfg = cfg
Iustin Pop's avatar
Iustin Pop committed
143

144
145
146
147
148
149
150
151
152
153
154
155
156
157
  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()
158
159
160
    cluster = self._cfg.GetClusterInfo()
    idict["hvparams"] = cluster.FillHV(instance)
    idict["beparams"] = cluster.FillBE(instance)
161
162
    return idict

163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
  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)

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 _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
231
232
  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
233

Iustin Pop's avatar
Iustin Pop committed
234
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
235

Iustin Pop's avatar
Iustin Pop committed
236
    """
237
    return self._MultiNodeCall(node_list, "volume_list", [vg_name])
Iustin Pop's avatar
Iustin Pop committed
238

Iustin Pop's avatar
Iustin Pop committed
239
240
  def call_vg_list(self, node_list):
    """Gets the volume group list.
Iustin Pop's avatar
Iustin Pop committed
241

Iustin Pop's avatar
Iustin Pop committed
242
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
243

Iustin Pop's avatar
Iustin Pop committed
244
    """
245
    return self._MultiNodeCall(node_list, "vg_list", [])
Iustin Pop's avatar
Iustin Pop committed
246

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

Iustin Pop's avatar
Iustin Pop committed
250
251
252
    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
253

Iustin Pop's avatar
Iustin Pop committed
254
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
255

Iustin Pop's avatar
Iustin Pop committed
256
    """
257
    return self._SingleNodeCall(node, "bridges_exist", [bridges_list])
Iustin Pop's avatar
Iustin Pop committed
258

Iustin Pop's avatar
Iustin Pop committed
259
260
  def call_instance_start(self, node, instance, extra_args):
    """Starts an instance.
Iustin Pop's avatar
Iustin Pop committed
261

Iustin Pop's avatar
Iustin Pop committed
262
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
263

Iustin Pop's avatar
Iustin Pop committed
264
    """
265
266
    return self._SingleNodeCall(node, "instance_start",
                                [self._InstDict(instance), extra_args])
Iustin Pop's avatar
Iustin Pop committed
267

Iustin Pop's avatar
Iustin Pop committed
268
269
  def call_instance_shutdown(self, node, instance):
    """Stops an instance.
Iustin Pop's avatar
Iustin Pop committed
270

Iustin Pop's avatar
Iustin Pop committed
271
    This is a single-node call.
272

Iustin Pop's avatar
Iustin Pop committed
273
    """
274
275
    return self._SingleNodeCall(node, "instance_shutdown",
                                [self._InstDict(instance)])
276

Iustin Pop's avatar
Iustin Pop committed
277
278
  def call_instance_migrate(self, node, instance, target, live):
    """Migrate an instance.
279

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

Iustin Pop's avatar
Iustin Pop committed
282
283
284
285
286
287
288
289
290
    @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)
291

Iustin Pop's avatar
Iustin Pop committed
292
    """
293
294
    return self._SingleNodeCall(node, "instance_migrate",
                                [self._InstDict(instance), target, live])
295

Iustin Pop's avatar
Iustin Pop committed
296
297
  def call_instance_reboot(self, node, instance, reboot_type, extra_args):
    """Reboots an instance.
298

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

Iustin Pop's avatar
Iustin Pop committed
301
    """
302
303
304
    return self._SingleNodeCall(node, "instance_reboot",
                                [self._InstDict(instance), reboot_type,
                                 extra_args])
Iustin Pop's avatar
Iustin Pop committed
305

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

Iustin Pop's avatar
Iustin Pop committed
309
    This is a single-node call.
310

Iustin Pop's avatar
Iustin Pop committed
311
    """
312
313
    return self._SingleNodeCall(node, "instance_os_add",
                                [self._InstDict(inst)])
314

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

Iustin Pop's avatar
Iustin Pop committed
318
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
319

Iustin Pop's avatar
Iustin Pop committed
320
    """
321
322
    return self._SingleNodeCall(node, "instance_run_rename",
                                [self._InstDict(inst), old_name])
Iustin Pop's avatar
Iustin Pop committed
323

Iustin Pop's avatar
Iustin Pop committed
324
325
  def call_instance_info(self, node, instance, hname):
    """Returns information about a single instance.
Iustin Pop's avatar
Iustin Pop committed
326

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

329
330
    @type node: list
    @param node: the list of nodes to query
Iustin Pop's avatar
Iustin Pop committed
331
332
333
334
    @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
335

Iustin Pop's avatar
Iustin Pop committed
336
    """
337
    return self._SingleNodeCall(node, "instance_info", [instance, hname])
338

Iustin Pop's avatar
Iustin Pop committed
339
340
  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
341

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

Iustin Pop's avatar
Iustin Pop committed
344
345
346
347
    @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
348

Iustin Pop's avatar
Iustin Pop committed
349
    """
350
351
    return self._MultiNodeCall(node_list, "all_instances_info",
                               [hypervisor_list])
352

Iustin Pop's avatar
Iustin Pop committed
353
354
  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
355

Iustin Pop's avatar
Iustin Pop committed
356
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
357

Iustin Pop's avatar
Iustin Pop committed
358
359
360
361
    @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
362

Iustin Pop's avatar
Iustin Pop committed
363
    """
364
    return self._MultiNodeCall(node_list, "instance_list", [hypervisor_list])
365

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

Iustin Pop's avatar
Iustin Pop committed
370
    This is a single-node call.
371

Iustin Pop's avatar
Iustin Pop committed
372
    """
373
374
    return self._SingleNodeCall(node, "node_tcp_ping",
                                [source, target, port, timeout,
Iustin Pop's avatar
Iustin Pop committed
375
                                 live_port_needed])
Iustin Pop's avatar
Iustin Pop committed
376

377
378
379
380
381
382
  def call_node_has_ip_address(self, node, address):
    """Checks if a node has the given IP address.

    This is a single-node call.

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

Iustin Pop's avatar
Iustin Pop committed
385
386
  def call_node_info(self, node_list, vg_name, hypervisor_type):
    """Return node information.
387

Iustin Pop's avatar
Iustin Pop committed
388
389
    This will return memory information and volume group size and free
    space.
Iustin Pop's avatar
Iustin Pop committed
390

Iustin Pop's avatar
Iustin Pop committed
391
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
392

Iustin Pop's avatar
Iustin Pop committed
393
394
395
396
397
398
399
400
    @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
401

Iustin Pop's avatar
Iustin Pop committed
402
    """
403
404
    retux = self._MultiNodeCall(node_list, "node_info",
                                [vg_name, hypervisor_type])
Iustin Pop's avatar
Iustin Pop committed
405

Iustin Pop's avatar
Iustin Pop committed
406
407
408
    for node_name in retux:
      ret = retux.get(node_name, False)
      if type(ret) != dict:
409
        logging.error("could not connect to node %s", node_name)
Iustin Pop's avatar
Iustin Pop committed
410
        ret = {}
Iustin Pop's avatar
Iustin Pop committed
411

412
413
      utils.CheckDict(ret, {
                        'memory_total' : '-',
Iustin Pop's avatar
Iustin Pop committed
414
415
416
                        'memory_dom0' : '-',
                        'memory_free' : '-',
                        'vg_size' : 'node_unreachable',
417
418
                        'vg_free' : '-',
                      }, "call_node_info")
Iustin Pop's avatar
Iustin Pop committed
419
    return retux
Iustin Pop's avatar
Iustin Pop committed
420

Iustin Pop's avatar
Iustin Pop committed
421
422
  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
423

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

Iustin Pop's avatar
Iustin Pop committed
426
    """
427
428
    return self._SingleNodeCall(node, "node_add",
                                [dsa, dsapub, rsa, rsapub, ssh, sshpub])
Iustin Pop's avatar
Iustin Pop committed
429

Iustin Pop's avatar
Iustin Pop committed
430
431
  def call_node_verify(self, node_list, checkdict, cluster_name):
    """Request verification of given parameters.
Iustin Pop's avatar
Iustin Pop committed
432

Iustin Pop's avatar
Iustin Pop committed
433
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
434

Iustin Pop's avatar
Iustin Pop committed
435
    """
436
437
    return self._MultiNodeCall(node_list, "node_verify",
                               [checkdict, cluster_name])
Iustin Pop's avatar
Iustin Pop committed
438

439
440
  @classmethod
  def call_node_start_master(cls, node, start_daemons):
Iustin Pop's avatar
Iustin Pop committed
441
    """Tells a node to activate itself as a master.
Iustin Pop's avatar
Iustin Pop committed
442

Iustin Pop's avatar
Iustin Pop committed
443
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
444

Iustin Pop's avatar
Iustin Pop committed
445
    """
446
447
    return cls._StaticSingleNodeCall(node, "node_start_master",
                                     [start_daemons])
Iustin Pop's avatar
Iustin Pop committed
448

449
450
  @classmethod
  def call_node_stop_master(cls, node, stop_daemons):
Iustin Pop's avatar
Iustin Pop committed
451
    """Tells a node to demote itself from master status.
Iustin Pop's avatar
Iustin Pop committed
452

Iustin Pop's avatar
Iustin Pop committed
453
    This is a single-node call.
454

Iustin Pop's avatar
Iustin Pop committed
455
    """
456
    return cls._StaticSingleNodeCall(node, "node_stop_master", [stop_daemons])
457

458
459
  @classmethod
  def call_master_info(cls, node_list):
Iustin Pop's avatar
Iustin Pop committed
460
    """Query master info.
461

Iustin Pop's avatar
Iustin Pop committed
462
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
463

Iustin Pop's avatar
Iustin Pop committed
464
465
    """
    # TODO: should this method query down nodes?
466
    return cls._StaticMultiNodeCall(node_list, "master_info", [])
Iustin Pop's avatar
Iustin Pop committed
467

Iustin Pop's avatar
Iustin Pop committed
468
469
  def call_version(self, node_list):
    """Query node version.
Iustin Pop's avatar
Iustin Pop committed
470

Iustin Pop's avatar
Iustin Pop committed
471
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
472

Iustin Pop's avatar
Iustin Pop committed
473
    """
474
    return self._MultiNodeCall(node_list, "version", [])
Iustin Pop's avatar
Iustin Pop committed
475

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

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

Iustin Pop's avatar
Iustin Pop committed
481
    """
482
483
    return self._SingleNodeCall(node, "blockdev_create",
                                [bdev.ToDict(), size, owner, on_primary, info])
Iustin Pop's avatar
Iustin Pop committed
484

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

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

Iustin Pop's avatar
Iustin Pop committed
490
    """
491
    return self._SingleNodeCall(node, "blockdev_remove", [bdev.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
492

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

Iustin Pop's avatar
Iustin Pop committed
496
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
497

Iustin Pop's avatar
Iustin Pop committed
498
    """
499
500
    return self._SingleNodeCall(node, "blockdev_rename",
                                [(d.ToDict(), uid) for d, uid in devlist])
Iustin Pop's avatar
Iustin Pop committed
501

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

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

Iustin Pop's avatar
Iustin Pop committed
507
    """
508
509
    return self._SingleNodeCall(node, "blockdev_assemble",
                                [disk.ToDict(), owner, on_primary])
Iustin Pop's avatar
Iustin Pop committed
510

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

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

Iustin Pop's avatar
Iustin Pop committed
516
    """
517
    return self._SingleNodeCall(node, "blockdev_shutdown", [disk.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
518

Iustin Pop's avatar
Iustin Pop committed
519
520
  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
521

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

Iustin Pop's avatar
Iustin Pop committed
524
    """
525
526
527
    return self._SingleNodeCall(node, "blockdev_addchildren",
                                [bdev.ToDict(),
                                 [disk.ToDict() for disk in ndevs]])
Iustin Pop's avatar
Iustin Pop committed
528

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

Iustin Pop's avatar
Iustin Pop committed
539
540
  def call_blockdev_getmirrorstatus(self, node, disks):
    """Request status of a (mirroring) device.
Iustin Pop's avatar
Iustin Pop committed
541

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

Iustin Pop's avatar
Iustin Pop committed
544
    """
545
546
    return self._SingleNodeCall(node, "blockdev_getmirrorstatus",
                                [dsk.ToDict() for dsk in disks])
Iustin Pop's avatar
Iustin Pop committed
547

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

Iustin Pop's avatar
Iustin Pop committed
553
    """
554
    return self._SingleNodeCall(node, "blockdev_find", [disk.ToDict()])
555

Iustin Pop's avatar
Iustin Pop committed
556
557
  def call_blockdev_close(self, node, disks):
    """Closes the given block devices.
558

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

Iustin Pop's avatar
Iustin Pop committed
561
    """
562
563
    return self._SingleNodeCall(node, "blockdev_close",
                                [cf.ToDict() for cf in disks])
Iustin Pop's avatar
Iustin Pop committed
564

565
566
  @classmethod
  def call_upload_file(cls, node_list, file_name, address_list=None):
Iustin Pop's avatar
Iustin Pop committed
567
568
569
570
571
572
    """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
573

574
575
576
577
578
579
580
581
    @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
582
    """
583
    data = utils.ReadFile(file_name)
Iustin Pop's avatar
Iustin Pop committed
584
585
586
    st = os.stat(file_name)
    params = [file_name, data, st.st_mode, st.st_uid, st.st_gid,
              st.st_atime, st.st_mtime]
587
588
    return cls._StaticMultiNodeCall(node_list, "upload_file", params,
                                    address_list=address_list)
Iustin Pop's avatar
Iustin Pop committed
589

590
591
592
593
594
595
596
597
598
  @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
599
600
601
602
  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
603

Iustin Pop's avatar
Iustin Pop committed
604
    """
605
606
    result = self._MultiNodeCall(node_list, "os_diagnose", [])

Iustin Pop's avatar
Iustin Pop committed
607
608
609
610
611
612
613
614
    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
615

Iustin Pop's avatar
Iustin Pop committed
616
617
  def call_os_get(self, node, name):
    """Returns an OS definition.
Iustin Pop's avatar
Iustin Pop committed
618

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

Iustin Pop's avatar
Iustin Pop committed
621
    """
622
    result = self._SingleNodeCall(node, "os_get", [name])
Iustin Pop's avatar
Iustin Pop committed
623
624
625
626
    if isinstance(result, dict):
      return objects.OS.FromDict(result)
    else:
      return result
Iustin Pop's avatar
Iustin Pop committed
627

Iustin Pop's avatar
Iustin Pop committed
628
629
  def call_hooks_runner(self, node_list, hpath, phase, env):
    """Call the hooks runner.
Iustin Pop's avatar
Iustin Pop committed
630

Iustin Pop's avatar
Iustin Pop committed
631
632
633
    Args:
      - op: the OpCode instance
      - env: a dictionary with the environment
Iustin Pop's avatar
Iustin Pop committed
634

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

Iustin Pop's avatar
Iustin Pop committed
637
638
    """
    params = [hpath, phase, env]
639
    return self._MultiNodeCall(node_list, "hooks_runner", params)
Iustin Pop's avatar
Iustin Pop committed
640

Iustin Pop's avatar
Iustin Pop committed
641
642
  def call_iallocator_runner(self, node, name, idata):
    """Call an iallocator on a remote node
643

Iustin Pop's avatar
Iustin Pop committed
644
645
646
    Args:
      - name: the iallocator name
      - input: the json-encoded input string
647

Iustin Pop's avatar
Iustin Pop committed
648
    This is a single-node call.
649

Iustin Pop's avatar
Iustin Pop committed
650
    """
651
    return self._SingleNodeCall(node, "iallocator_runner", [name, idata])
652

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

Iustin Pop's avatar
Iustin Pop committed
656
    This is a single-node call.
657

Iustin Pop's avatar
Iustin Pop committed
658
    """
659
660
    return self._SingleNodeCall(node, "blockdev_grow",
                                [cf_bdev.ToDict(), amount])
661

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

Iustin Pop's avatar
Iustin Pop committed
665
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
666

Iustin Pop's avatar
Iustin Pop committed
667
    """
668
    return self._SingleNodeCall(node, "blockdev_snapshot", [cf_bdev.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
669

Iustin Pop's avatar
Iustin Pop committed
670
  def call_snapshot_export(self, node, snap_bdev, dest_node, instance,
671
                           cluster_name, idx):
Iustin Pop's avatar
Iustin Pop committed
672
    """Request the export of a given snapshot.
Iustin Pop's avatar
Iustin Pop committed
673

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

Iustin Pop's avatar
Iustin Pop committed
676
    """
677
678
679
    return self._SingleNodeCall(node, "snapshot_export",
                                [snap_bdev.ToDict(), dest_node,
                                 self._InstDict(instance), cluster_name, idx])
Iustin Pop's avatar
Iustin Pop committed
680

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

Iustin Pop's avatar
Iustin Pop committed
684
    This writes the export config file, etc.
Iustin Pop's avatar
Iustin Pop committed
685

Iustin Pop's avatar
Iustin Pop committed
686
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
687

Iustin Pop's avatar
Iustin Pop committed
688
689
690
691
    """
    flat_disks = []
    for disk in snap_disks:
      flat_disks.append(disk.ToDict())
692
693
694

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

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

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

Iustin Pop's avatar
Iustin Pop committed
701
    """
702
    result = self._SingleNodeCall(node, "export_info", [path])
Iustin Pop's avatar
Iustin Pop committed
703
704
705
    if not result:
      return result
    return objects.SerializableConfigParser.Loads(str(result))
Iustin Pop's avatar
Iustin Pop committed
706

707
708
  def call_instance_os_import(self, node, inst, src_node, src_images,
                              cluster_name):
Iustin Pop's avatar
Iustin Pop committed
709
    """Request the import of a backup into an instance.
Iustin Pop's avatar
Iustin Pop committed
710

Iustin Pop's avatar
Iustin Pop committed
711
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
712

Iustin Pop's avatar
Iustin Pop committed
713
    """
714
715
716
    return self._SingleNodeCall(node, "instance_os_import",
                                [self._InstDict(inst), src_node, src_images,
                                 cluster_name])
Iustin Pop's avatar
Iustin Pop committed
717

Iustin Pop's avatar
Iustin Pop committed
718
719
  def call_export_list(self, node_list):
    """Gets the stored exports list.
Iustin Pop's avatar
Iustin Pop committed
720

Iustin Pop's avatar
Iustin Pop committed
721
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
722

Iustin Pop's avatar
Iustin Pop committed
723
    """
724
    return self._MultiNodeCall(node_list, "export_list", [])
Iustin Pop's avatar
Iustin Pop committed
725

Iustin Pop's avatar
Iustin Pop committed
726
727
  def call_export_remove(self, node, export):
    """Requests removal of a given export.
Iustin Pop's avatar
Iustin Pop committed
728

Iustin Pop's avatar
Iustin Pop committed
729
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
730

Iustin Pop's avatar
Iustin Pop committed
731
    """
732
    return self._SingleNodeCall(node, "export_remove", [export])
Iustin Pop's avatar
Iustin Pop committed
733

734
735
  @classmethod
  def call_node_leave_cluster(cls, node):
Iustin Pop's avatar
Iustin Pop committed
736
    """Requests a node to clean the cluster information it has.
Iustin Pop's avatar
Iustin Pop committed
737

Iustin Pop's avatar
Iustin Pop committed
738
739
    This will remove the configuration information from the ganeti data
    dir.
Iustin Pop's avatar
Iustin Pop committed
740

Iustin Pop's avatar
Iustin Pop committed
741
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
742

Iustin Pop's avatar
Iustin Pop committed
743
    """
744
    return cls._StaticSingleNodeCall(node, "node_leave_cluster", [])
745

Iustin Pop's avatar
Iustin Pop committed
746
747
  def call_node_volumes(self, node_list):
    """Gets all volumes on node(s).
748

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

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

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

Iustin Pop's avatar
Iustin Pop committed
757
    This is a multi-node call.
758

Iustin Pop's avatar
Iustin Pop committed
759
    """
760
    return self._MultiNodeCall(node_list, "test_delay", [duration])
761

Iustin Pop's avatar
Iustin Pop committed
762
763
  def call_file_storage_dir_create(self, node, file_storage_dir):
    """Create the given file storage directory.
764

Iustin Pop's avatar
Iustin Pop committed
765
    This is a single-node call.
766

Iustin Pop's avatar
Iustin Pop committed
767
    """
768
769
    return self._SingleNodeCall(node, "file_storage_dir_create",
                                [file_storage_dir])
770

Iustin Pop's avatar
Iustin Pop committed
771
772
  def call_file_storage_dir_remove(self, node, file_storage_dir):
    """Remove the given file storage directory.
773

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

Iustin Pop's avatar
Iustin Pop committed
776
    """
777
778
    return self._SingleNodeCall(node, "file_storage_dir_remove",
                                [file_storage_dir])
779

Iustin Pop's avatar
Iustin Pop committed
780
781
782
  def call_file_storage_dir_rename(self, node, old_file_storage_dir,
                                   new_file_storage_dir):
    """Rename file storage directory.
783

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

Iustin Pop's avatar
Iustin Pop committed
786
    """
787
788
    return self._SingleNodeCall(node, "file_storage_dir_rename",
                                [old_file_storage_dir, new_file_storage_dir])
789

790
791
  @classmethod
  def call_jobqueue_update(cls, node_list, address_list, file_name, content):
Iustin Pop's avatar
Iustin Pop committed
792
    """Update job queue.
793

Iustin Pop's avatar
Iustin Pop committed
794
    This is a multi-node call.
795

Iustin Pop's avatar
Iustin Pop committed
796
    """
797
798
799
    return cls._StaticMultiNodeCall(node_list, "jobqueue_update",
                                    [file_name, content],
                                    address_list=address_list)
800

801
802
  @classmethod
  def call_jobqueue_purge(cls, node):
Iustin Pop's avatar
Iustin Pop committed
803
    """Purge job queue.
804

Iustin Pop's avatar
Iustin Pop committed
805
    This is a single-node call.
806

Iustin Pop's avatar
Iustin Pop committed
807
    """
808
    return cls._StaticSingleNodeCall(node, "jobqueue_purge", [])
809

810
811
  @classmethod
  def call_jobqueue_rename(cls, node_list, address_list, old, new):
Iustin Pop's avatar
Iustin Pop committed
812
    """Rename a job queue file.
813

Iustin Pop's avatar
Iustin Pop committed
814
    This is a multi-node call.
815

Iustin Pop's avatar
Iustin Pop committed
816
    """
817
818
    return cls._StaticMultiNodeCall(node_list, "jobqueue_rename", [old, new],
                                    address_list=address_list)
819

820
821
  @classmethod
  def call_jobqueue_set_drain(cls, node_list, drain_flag):
822
823
824
825
826
827
828
829
830
831
    """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.

    """
832
833
    return cls._StaticMultiNodeCall(node_list, "jobqueue_set_drain",
                                    [drain_flag])
834

835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
  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)
850
851
    return self._MultiNodeCall(node_list, "hypervisor_validate_params",
                               [hvname, hv_full])