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

Iustin Pop's avatar
Iustin Pop committed
38
import simplejson
Iustin Pop's avatar
Iustin Pop committed
39
40
41
42

from ganeti import utils
from ganeti import objects

43

Iustin Pop's avatar
Iustin Pop committed
44
45
46
47
48
49
50
51
class NodeController:
  """Node-handling class.

  For each node that we speak with, we create an instance of this
  class, so that we have a safe place to store the details of this
  individual call.

  """
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
  def __init__(self, parent, node, address=None):
    """Constructor for the node controller.

    @type parent: L{Client}
    @param parent: the C{Client} instance which holds global parameters for
        the call
    @type node: str
    @param node: the name of the node we connect to; it is used for error
        messages and in cases we the address paramater is not passed
    @type address: str
    @keyword address: the node's address, in case we know it, so that we
        don't need to resolve it; testing shows that httplib has high
        overhead in resolving addresses (even when speficied in /etc/hosts)

    """
Iustin Pop's avatar
Iustin Pop committed
67
68
    self.parent = parent
    self.node = node
69
70
    if address is None:
      address = node
Iustin Pop's avatar
Iustin Pop committed
71
    self.failed = False
Iustin Pop's avatar
Iustin Pop committed
72

73
    self.http_conn = hc = httplib.HTTPConnection(address, parent.port)
Iustin Pop's avatar
Iustin Pop committed
74
    try:
Iustin Pop's avatar
Iustin Pop committed
75
      hc.connect()
76
      hc.putrequest('PUT', "/%s" % parent.procedure,
Iustin Pop's avatar
Iustin Pop committed
77
                    skip_accept_encoding=True)
78
      hc.putheader('Content-Length', parent.body_length)
Iustin Pop's avatar
Iustin Pop committed
79
80
      hc.endheaders()
      hc.send(parent.body)
81
    except socket.error:
82
      logging.exception("Error connecting to node %s", node)
Iustin Pop's avatar
Iustin Pop committed
83
84
      self.failed = True

85
  def GetResponse(self):
Iustin Pop's avatar
Iustin Pop committed
86
    """Try to process the response from the node.
Iustin Pop's avatar
Iustin Pop committed
87
88

    """
Iustin Pop's avatar
Iustin Pop committed
89
90
    if self.failed:
      # we already failed in connect
Iustin Pop's avatar
Iustin Pop committed
91
      return False
Iustin Pop's avatar
Iustin Pop committed
92
93
    resp = self.http_conn.getresponse()
    if resp.status != 200:
Iustin Pop's avatar
Iustin Pop committed
94
      return False
Iustin Pop's avatar
Iustin Pop committed
95
96
97
98
99
    try:
      length = int(resp.getheader('Content-Length', '0'))
    except ValueError:
      return False
    if not length:
100
      logging.error("Zero-length reply from node %s", self.node)
Iustin Pop's avatar
Iustin Pop committed
101
102
103
104
      return False
    payload = resp.read(length)
    unload = simplejson.loads(payload)
    return unload
Iustin Pop's avatar
Iustin Pop committed
105
106
107
108
109


class Client:
  """RPC Client class.

Alexander Schreiber's avatar
Alexander Schreiber committed
110
  This class, given a (remote) method name, a list of parameters and a
Iustin Pop's avatar
Iustin Pop committed
111
112
113
114
115
116
117
  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.

118
119
120
  @var body_length: cached string value of the length of the body (so that
      individual C{NodeController} instances don't have to recompute it)

Iustin Pop's avatar
Iustin Pop committed
121
122
123
124
125
126
  """
  result_set = False
  result = False
  allresult = []

  def __init__(self, procedure, args):
Michael Hanselmann's avatar
Michael Hanselmann committed
127
128
    self.port = utils.GetNodeDaemonPort()
    self.nodepw = utils.GetNodeDaemonPassword()
Iustin Pop's avatar
Iustin Pop committed
129
130
131
132
    self.nc = {}
    self.results = {}
    self.procedure = procedure
    self.args = args
Iustin Pop's avatar
Iustin Pop committed
133
    self.body = simplejson.dumps(args)
134
    self.body_length = str(len(self.body))
Iustin Pop's avatar
Iustin Pop committed
135
136
137

  #--- generic connector -------------

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

141
142
    @type node_list: list
    @param node_list: the list of node names to connect
143
144
145
    @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
146

Iustin Pop's avatar
Iustin Pop committed
147
    """
148
149
150
151
152
153
154
155
156
    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
157
158
    """Add a node to the target list.

159
160
161
162
163
    @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
164
    """
165
    self.nc[name] = NodeController(self, name, address)
Iustin Pop's avatar
Iustin Pop committed
166

167
  def GetResults(self):
Iustin Pop's avatar
Iustin Pop committed
168
169
170
171
172
    """Return the results of the call.

    """
    return self.results

173
174
  def Run(self):
    """Gather results from the node controllers.
Iustin Pop's avatar
Iustin Pop committed
175

176
177
    This function simply calls GetResponse() for each of our node
    controllers.
Iustin Pop's avatar
Iustin Pop committed
178
179

    """
Iustin Pop's avatar
Iustin Pop committed
180
    for node, nc in self.nc.items():
181
      self.results[node] = nc.GetResponse()
Iustin Pop's avatar
Iustin Pop committed
182
183


Iustin Pop's avatar
Iustin Pop committed
184
185
class RpcRunner(object):
  """RPC runner class"""
Iustin Pop's avatar
Iustin Pop committed
186

Iustin Pop's avatar
Iustin Pop committed
187
188
  def __init__(self, cfg):
    """Initialized the rpc runner.
Iustin Pop's avatar
Iustin Pop committed
189

Iustin Pop's avatar
Iustin Pop committed
190
191
192
    @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
193

Iustin Pop's avatar
Iustin Pop committed
194
195
    """
    self._cfg = cfg
Iustin Pop's avatar
Iustin Pop committed
196

197
198
199
200
201
202
203
204
205
206
207
208
209
210
  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()
211
212
213
    cluster = self._cfg.GetClusterInfo()
    idict["hvparams"] = cluster.FillHV(instance)
    idict["beparams"] = cluster.FillBE(instance)
214
215
    return idict

216
217
218
219
220
221
222
223
224
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
  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)

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

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

Iustin Pop's avatar
Iustin Pop committed
256
257
    """
    c = Client("volume_list", [vg_name])
258
    self._ConnectList(c, node_list)
259
260
    c.Run()
    return c.GetResults()
Iustin Pop's avatar
Iustin Pop committed
261

Iustin Pop's avatar
Iustin Pop committed
262
263
  def call_vg_list(self, node_list):
    """Gets the volume group list.
Iustin Pop's avatar
Iustin Pop committed
264

Iustin Pop's avatar
Iustin Pop committed
265
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
266

Iustin Pop's avatar
Iustin Pop committed
267
268
    """
    c = Client("vg_list", [])
269
    self._ConnectList(c, node_list)
270
271
    c.Run()
    return c.GetResults()
Iustin Pop's avatar
Iustin Pop committed
272

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

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

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

Iustin Pop's avatar
Iustin Pop committed
282
283
    """
    c = Client("bridges_exist", [bridges_list])
284
    self._ConnectNode(c, node)
285
286
    c.Run()
    return c.GetResults().get(node, False)
Iustin Pop's avatar
Iustin Pop committed
287

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

Iustin Pop's avatar
Iustin Pop committed
291
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
292

Iustin Pop's avatar
Iustin Pop committed
293
    """
294
    c = Client("instance_start", [self._InstDict(instance), extra_args])
295
    self._ConnectNode(c, node)
296
297
    c.Run()
    return c.GetResults().get(node, False)
Iustin Pop's avatar
Iustin Pop committed
298

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

Iustin Pop's avatar
Iustin Pop committed
302
    This is a single-node call.
303

Iustin Pop's avatar
Iustin Pop committed
304
    """
305
    c = Client("instance_shutdown", [self._InstDict(instance)])
306
    self._ConnectNode(c, node)
307
308
    c.Run()
    return c.GetResults().get(node, False)
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
    c = Client("instance_migrate", [self._InstDict(instance), target, live])
327
    self._ConnectNode(c, node)
328
329
    c.Run()
    return c.GetResults().get(node, False)
330

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

Iustin Pop's avatar
Iustin Pop committed
334
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
335

Iustin Pop's avatar
Iustin Pop committed
336
    """
337
338
    c = Client("instance_reboot", [self._InstDict(instance),
                                   reboot_type, extra_args])
339
    self._ConnectNode(c, node)
340
341
    c.Run()
    return c.GetResults().get(node, False)
Iustin Pop's avatar
Iustin Pop committed
342

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

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

Iustin Pop's avatar
Iustin Pop committed
348
    """
349
    params = [self._InstDict(inst)]
Iustin Pop's avatar
Iustin Pop committed
350
    c = Client("instance_os_add", params)
351
    self._ConnectNode(c, node)
352
353
    c.Run()
    return c.GetResults().get(node, False)
354

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

Iustin Pop's avatar
Iustin Pop committed
358
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
359

Iustin Pop's avatar
Iustin Pop committed
360
    """
361
    params = [self._InstDict(inst), old_name]
Iustin Pop's avatar
Iustin Pop committed
362
    c = Client("instance_run_rename", params)
363
    self._ConnectNode(c, node)
364
365
    c.Run()
    return c.GetResults().get(node, False)
Iustin Pop's avatar
Iustin Pop committed
366

Iustin Pop's avatar
Iustin Pop committed
367
368
  def call_instance_info(self, node, instance, hname):
    """Returns information about a single instance.
Iustin Pop's avatar
Iustin Pop committed
369

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

Iustin Pop's avatar
Iustin Pop committed
372
373
374
375
376
377
    @type node_list: list
    @param node_list: the list of nodes to query
    @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
378

Iustin Pop's avatar
Iustin Pop committed
379
    """
Iustin Pop's avatar
Iustin Pop committed
380
    c = Client("instance_info", [instance, hname])
381
    self._ConnectNode(c, node)
382
383
    c.Run()
    return c.GetResults().get(node, False)
384

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

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

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

Iustin Pop's avatar
Iustin Pop committed
395
396
    """
    c = Client("all_instances_info", [hypervisor_list])
397
    self._ConnectList(c, node_list)
398
399
    c.Run()
    return c.GetResults()
400

Iustin Pop's avatar
Iustin Pop committed
401
402
  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
403

Iustin Pop's avatar
Iustin Pop committed
404
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
405

Iustin Pop's avatar
Iustin Pop committed
406
407
408
409
    @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
410

Iustin Pop's avatar
Iustin Pop committed
411
412
    """
    c = Client("instance_list", [hypervisor_list])
413
    self._ConnectList(c, node_list)
414
415
    c.Run()
    return c.GetResults()
416

Iustin Pop's avatar
Iustin Pop committed
417
418
419
  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
420

Iustin Pop's avatar
Iustin Pop committed
421
    This is a single-node call.
422

Iustin Pop's avatar
Iustin Pop committed
423
424
425
    """
    c = Client("node_tcp_ping", [source, target, port, timeout,
                                 live_port_needed])
426
    self._ConnectNode(c, node)
427
428
    c.Run()
    return c.GetResults().get(node, False)
Iustin Pop's avatar
Iustin Pop committed
429

430
431
432
433
434
435
436
  def call_node_has_ip_address(self, node, address):
    """Checks if a node has the given IP address.

    This is a single-node call.

    """
    c = Client("node_has_ip_address", [address])
437
    self._ConnectNode(c, node)
438
439
    c.Run()
    return c.GetResults().get(node, False)
Iustin Pop's avatar
Iustin Pop committed
440

Iustin Pop's avatar
Iustin Pop committed
441
442
  def call_node_info(self, node_list, vg_name, hypervisor_type):
    """Return node information.
443

Iustin Pop's avatar
Iustin Pop committed
444
445
    This will return memory information and volume group size and free
    space.
Iustin Pop's avatar
Iustin Pop committed
446

Iustin Pop's avatar
Iustin Pop committed
447
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
448

Iustin Pop's avatar
Iustin Pop committed
449
450
451
452
453
454
455
456
    @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
457

Iustin Pop's avatar
Iustin Pop committed
458
459
    """
    c = Client("node_info", [vg_name, hypervisor_type])
460
    self._ConnectList(c, node_list)
461
462
    c.Run()
    retux = c.GetResults()
Iustin Pop's avatar
Iustin Pop committed
463

Iustin Pop's avatar
Iustin Pop committed
464
465
466
    for node_name in retux:
      ret = retux.get(node_name, False)
      if type(ret) != dict:
467
        logging.error("could not connect to node %s", node_name)
Iustin Pop's avatar
Iustin Pop committed
468
        ret = {}
Iustin Pop's avatar
Iustin Pop committed
469

Iustin Pop's avatar
Iustin Pop committed
470
471
472
473
474
475
476
477
478
      utils.CheckDict(ret,
                      { 'memory_total' : '-',
                        'memory_dom0' : '-',
                        'memory_free' : '-',
                        'vg_size' : 'node_unreachable',
                        'vg_free' : '-' },
                      "call_node_info",
                      )
    return retux
Iustin Pop's avatar
Iustin Pop committed
479

Iustin Pop's avatar
Iustin Pop committed
480
481
  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
482

Iustin Pop's avatar
Iustin Pop committed
483
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
484

Iustin Pop's avatar
Iustin Pop committed
485
486
487
    """
    params = [dsa, dsapub, rsa, rsapub, ssh, sshpub]
    c = Client("node_add", params)
488
    self._ConnectNode(c, node)
489
490
    c.Run()
    return c.GetResults().get(node, False)
Iustin Pop's avatar
Iustin Pop committed
491

Iustin Pop's avatar
Iustin Pop committed
492
493
  def call_node_verify(self, node_list, checkdict, cluster_name):
    """Request verification of given parameters.
Iustin Pop's avatar
Iustin Pop committed
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
    """
    c = Client("node_verify", [checkdict, cluster_name])
499
    self._ConnectList(c, node_list)
500
501
    c.Run()
    return c.GetResults()
Iustin Pop's avatar
Iustin Pop committed
502

Iustin Pop's avatar
Iustin Pop committed
503
504
505
  @staticmethod
  def call_node_start_master(node, start_daemons):
    """Tells a node to activate itself as a master.
Iustin Pop's avatar
Iustin Pop committed
506

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

Iustin Pop's avatar
Iustin Pop committed
509
510
    """
    c = Client("node_start_master", [start_daemons])
511
512
513
    c.ConnectNode(node)
    c.Run()
    return c.GetResults().get(node, False)
Iustin Pop's avatar
Iustin Pop committed
514

Iustin Pop's avatar
Iustin Pop committed
515
516
517
  @staticmethod
  def call_node_stop_master(node, stop_daemons):
    """Tells a node to demote itself from master status.
Iustin Pop's avatar
Iustin Pop committed
518

Iustin Pop's avatar
Iustin Pop committed
519
    This is a single-node call.
520

Iustin Pop's avatar
Iustin Pop committed
521
522
    """
    c = Client("node_stop_master", [stop_daemons])
523
524
525
    c.ConnectNode(node)
    c.Run()
    return c.GetResults().get(node, False)
526

Iustin Pop's avatar
Iustin Pop committed
527
528
529
  @staticmethod
  def call_master_info(node_list):
    """Query master info.
530

Iustin Pop's avatar
Iustin Pop committed
531
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
532

Iustin Pop's avatar
Iustin Pop committed
533
534
535
    """
    # TODO: should this method query down nodes?
    c = Client("master_info", [])
536
537
538
    c.ConnectList(node_list)
    c.Run()
    return c.GetResults()
Iustin Pop's avatar
Iustin Pop committed
539

Iustin Pop's avatar
Iustin Pop committed
540
541
  def call_version(self, node_list):
    """Query node version.
Iustin Pop's avatar
Iustin Pop committed
542

Iustin Pop's avatar
Iustin Pop committed
543
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
544

Iustin Pop's avatar
Iustin Pop committed
545
546
    """
    c = Client("version", [])
547
    self._ConnectList(c, node_list)
548
549
    c.Run()
    return c.GetResults()
Iustin Pop's avatar
Iustin Pop committed
550

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

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

Iustin Pop's avatar
Iustin Pop committed
556
557
558
    """
    params = [bdev.ToDict(), size, owner, on_primary, info]
    c = Client("blockdev_create", params)
559
    self._ConnectNode(c, node)
560
561
    c.Run()
    return c.GetResults().get(node, False)
Iustin Pop's avatar
Iustin Pop committed
562

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

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

Iustin Pop's avatar
Iustin Pop committed
568
569
    """
    c = Client("blockdev_remove", [bdev.ToDict()])
570
    self._ConnectNode(c, node)
571
572
    c.Run()
    return c.GetResults().get(node, False)
Iustin Pop's avatar
Iustin Pop committed
573

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

Iustin Pop's avatar
Iustin Pop committed
577
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
578

Iustin Pop's avatar
Iustin Pop committed
579
580
581
    """
    params = [(d.ToDict(), uid) for d, uid in devlist]
    c = Client("blockdev_rename", params)
582
    self._ConnectNode(c, node)
583
584
    c.Run()
    return c.GetResults().get(node, False)
Iustin Pop's avatar
Iustin Pop committed
585

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

Iustin Pop's avatar
Iustin Pop committed
589
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
590

Iustin Pop's avatar
Iustin Pop committed
591
592
593
    """
    params = [disk.ToDict(), owner, on_primary]
    c = Client("blockdev_assemble", params)
594
    self._ConnectNode(c, node)
595
596
    c.Run()
    return c.GetResults().get(node, False)
Iustin Pop's avatar
Iustin Pop committed
597

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

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

Iustin Pop's avatar
Iustin Pop committed
603
604
    """
    c = Client("blockdev_shutdown", [disk.ToDict()])
605
    self._ConnectNode(c, node)
606
607
    c.Run()
    return c.GetResults().get(node, False)
Iustin Pop's avatar
Iustin Pop committed
608

Iustin Pop's avatar
Iustin Pop committed
609
610
  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
611

Iustin Pop's avatar
Iustin Pop committed
612
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
613

Iustin Pop's avatar
Iustin Pop committed
614
615
616
    """
    params = [bdev.ToDict(), [disk.ToDict() for disk in ndevs]]
    c = Client("blockdev_addchildren", params)
617
    self._ConnectNode(c, node)
618
619
    c.Run()
    return c.GetResults().get(node, False)
Iustin Pop's avatar
Iustin Pop committed
620

Iustin Pop's avatar
Iustin Pop committed
621
622
  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
623

Iustin Pop's avatar
Iustin Pop committed
624
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
625

Iustin Pop's avatar
Iustin Pop committed
626
627
628
    """
    params = [bdev.ToDict(), [disk.ToDict() for disk in ndevs]]
    c = Client("blockdev_removechildren", params)
629
    self._ConnectNode(c, node)
630
631
    c.Run()
    return c.GetResults().get(node, False)
Iustin Pop's avatar
Iustin Pop committed
632

Iustin Pop's avatar
Iustin Pop committed
633
634
  def call_blockdev_getmirrorstatus(self, node, disks):
    """Request status of a (mirroring) device.
Iustin Pop's avatar
Iustin Pop committed
635

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

Iustin Pop's avatar
Iustin Pop committed
638
639
640
    """
    params = [dsk.ToDict() for dsk in disks]
    c = Client("blockdev_getmirrorstatus", params)
641
    self._ConnectNode(c, node)
642
643
    c.Run()
    return c.GetResults().get(node, False)
Iustin Pop's avatar
Iustin Pop committed
644

Iustin Pop's avatar
Iustin Pop committed
645
646
647
648
  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
649

Iustin Pop's avatar
Iustin Pop committed
650
651
    """
    c = Client("blockdev_find", [disk.ToDict()])
652
    self._ConnectNode(c, node)
653
654
    c.Run()
    return c.GetResults().get(node, False)
655

Iustin Pop's avatar
Iustin Pop committed
656
657
  def call_blockdev_close(self, node, disks):
    """Closes the given block devices.
658

Iustin Pop's avatar
Iustin Pop committed
659
    This is a single-node call.
660

Iustin Pop's avatar
Iustin Pop committed
661
662
663
    """
    params = [cf.ToDict() for cf in disks]
    c = Client("blockdev_close", params)
664
    self._ConnectNode(c, node)
665
666
    c.Run()
    return c.GetResults().get(node, False)
Iustin Pop's avatar
Iustin Pop committed
667

Iustin Pop's avatar
Iustin Pop committed
668
  @staticmethod
669
  def call_upload_file(node_list, file_name, address_list=None):
Iustin Pop's avatar
Iustin Pop committed
670
671
672
673
674
675
    """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
676

677
678
679
680
681
682
683
684
    @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
685
686
687
688
689
690
691
692
693
694
    """
    fh = file(file_name)
    try:
      data = fh.read()
    finally:
      fh.close()
    st = os.stat(file_name)
    params = [file_name, data, st.st_mode, st.st_uid, st.st_gid,
              st.st_atime, st.st_mtime]
    c = Client("upload_file", params)
695
    c.ConnectList(node_list, address_list=address_list)
696
697
    c.Run()
    return c.GetResults()
Iustin Pop's avatar
Iustin Pop committed
698
699
700
701
702

  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
703

Iustin Pop's avatar
Iustin Pop committed
704
705
    """
    c = Client("os_diagnose", [])
706
    self._ConnectList(c, node_list)
707
708
    c.Run()
    result = c.GetResults()
Iustin Pop's avatar
Iustin Pop committed
709
710
711
712
713
714
715
716
    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
717

Iustin Pop's avatar
Iustin Pop committed
718
719
  def call_os_get(self, node, name):
    """Returns an OS definition.
Iustin Pop's avatar
Iustin Pop committed
720

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

Iustin Pop's avatar
Iustin Pop committed
723
724
    """
    c = Client("os_get", [name])
725
    self._ConnectNode(c, node)
726
727
    c.Run()
    result = c.GetResults().get(node, False)
Iustin Pop's avatar
Iustin Pop committed
728
729
730
731
    if isinstance(result, dict):
      return objects.OS.FromDict(result)
    else:
      return result
Iustin Pop's avatar
Iustin Pop committed
732

Iustin Pop's avatar
Iustin Pop committed
733
734
  def call_hooks_runner(self, node_list, hpath, phase, env):
    """Call the hooks runner.
Iustin Pop's avatar
Iustin Pop committed
735

Iustin Pop's avatar
Iustin Pop committed
736
737
738
    Args:
      - op: the OpCode instance
      - env: a dictionary with the environment
Iustin Pop's avatar
Iustin Pop committed
739

Iustin Pop's avatar
Iustin Pop committed
740
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
741

Iustin Pop's avatar
Iustin Pop committed
742
743
744
    """
    params = [hpath, phase, env]
    c = Client("hooks_runner", params)
745
    self._ConnectList(c, node_list)
746
747
    c.Run()
    result = c.GetResults()
Iustin Pop's avatar
Iustin Pop committed
748
    return result
Iustin Pop's avatar
Iustin Pop committed
749

Iustin Pop's avatar
Iustin Pop committed
750
751
  def call_iallocator_runner(self, node, name, idata):
    """Call an iallocator on a remote node
752

Iustin Pop's avatar
Iustin Pop committed
753
754
755
    Args:
      - name: the iallocator name
      - input: the json-encoded input string
756

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

Iustin Pop's avatar
Iustin Pop committed
759
760
761
    """
    params = [name, idata]
    c = Client("iallocator_runner", params)
762
    self._ConnectNode(c, node)
763
764
    c.Run()
    result = c.GetResults().get(node, False)
Iustin Pop's avatar
Iustin Pop committed
765
    return result
766

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

Iustin Pop's avatar
Iustin Pop committed
770
    This is a single-node call.
771

Iustin Pop's avatar
Iustin Pop committed
772
773
    """
    c = Client("blockdev_grow", [cf_bdev.ToDict(), amount])
774
    self._ConnectNode(c, node)
775
776
    c.Run()
    return c.GetResults().get(node, False)
777

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

Iustin Pop's avatar
Iustin Pop committed
781
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
782

Iustin Pop's avatar
Iustin Pop committed
783
784
    """
    c = Client("blockdev_snapshot", [cf_bdev.ToDict()])
785
    self._ConnectNode(c, node)
786
787
    c.Run()
    return c.GetResults().get(node, False)
Iustin Pop's avatar
Iustin Pop committed
788

Iustin Pop's avatar
Iustin Pop committed
789
790
791
  def call_snapshot_export(self, node, snap_bdev, dest_node, instance,
                           cluster_name):
    """Request the export of a given snapshot.
Iustin Pop's avatar
Iustin Pop committed
792

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

Iustin Pop's avatar
Iustin Pop committed
795
    """
796
797
    params = [snap_bdev.ToDict(), dest_node,
              self._InstDict(instance), cluster_name]
Iustin Pop's avatar
Iustin Pop committed
798
    c = Client("snapshot_export", params)
799
    self._ConnectNode(c, node)
800
801
    c.Run()
    return c.GetResults().get(node, False)
Iustin Pop's avatar
Iustin Pop committed
802

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

Iustin Pop's avatar
Iustin Pop committed
806
    This writes the export config file, etc.
Iustin Pop's avatar
Iustin Pop committed
807

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

Iustin Pop's avatar
Iustin Pop committed
810
811
812
813
    """
    flat_disks = []
    for disk in snap_disks:
      flat_disks.append(disk.ToDict())
814
    params = [self._InstDict(instance), flat_disks]
Iustin Pop's avatar
Iustin Pop committed
815
    c = Client("finalize_export", params)
816
    self._ConnectNode(c, node)
817
818
    c.Run()
    return c.GetResults().get(node, False)
Iustin Pop's avatar
Iustin Pop committed
819

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

Iustin Pop's avatar
Iustin Pop committed
823
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
824

Iustin Pop's avatar
Iustin Pop committed
825
826
    """
    c = Client("export_info", [path])
827
    self._ConnectNode(c, node)
828
829
    c.Run()
    result = c.GetResults().get(node, False)
Iustin Pop's avatar
Iustin Pop committed
830
831
832
    if not result:
      return result
    return objects.SerializableConfigParser.Loads(str(result))
Iustin Pop's avatar
Iustin Pop committed
833

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

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

Iustin Pop's avatar
Iustin Pop committed
840
    """
841
    params = [self._InstDict(inst), src_node, src_images, cluster_name]
Iustin Pop's avatar
Iustin Pop committed
842
    c = Client("instance_os_import", params)
843
    self._ConnectNode(c, node)
844
845
    c.Run()
    return c.GetResults().get(node, False)
Iustin Pop's avatar
Iustin Pop committed
846

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

Iustin Pop's avatar
Iustin Pop committed
850
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
851

Iustin Pop's avatar
Iustin Pop committed
852
853
    """
    c = Client("export_list", [])
854
    self._ConnectList(c, node_list)
855
856
    c.Run()
    result = c.GetResults()
Iustin Pop's avatar
Iustin Pop committed
857
    return result
Iustin Pop's avatar
Iustin Pop committed
858

Iustin Pop's avatar
Iustin Pop committed
859
860
  def call_export_remove(self, node, export):
    """Requests removal of a given export.
Iustin Pop's avatar
Iustin Pop committed
861

Iustin Pop's avatar
Iustin Pop committed
862
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
863

Iustin Pop's avatar
Iustin Pop committed
864
865
    """
    c = Client("export_remove", [export])
866
    self._ConnectNode(c, node)
867
868
    c.Run()
    return c.GetResults().get(node, False)
Iustin Pop's avatar
Iustin Pop committed
869

870
871
  @staticmethod
  def call_node_leave_cluster(node):
Iustin Pop's avatar
Iustin Pop committed
872
    """Requests a node to clean the cluster information it has.
Iustin Pop's avatar
Iustin Pop committed
873

Iustin Pop's avatar
Iustin Pop committed
874
875
    This will remove the configuration information from the ganeti data
    dir.
Iustin Pop's avatar
Iustin Pop committed
876

Iustin Pop's avatar
Iustin Pop committed
877
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
878

Iustin Pop's avatar
Iustin Pop committed
879
880
    """
    c = Client("node_leave_cluster", [])
881
882
883
    c.ConnectNode(node)
    c.Run()
    return c.GetResults().get(node, False)