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

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


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

"""

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

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

from ganeti import utils
from ganeti import objects
41
from ganeti import http
42
from ganeti import serializer
43
from ganeti import constants
44
from ganeti import errors
Iustin Pop's avatar
Iustin Pop committed
45

46
47
import ganeti.http.client

Iustin Pop's avatar
Iustin Pop committed
48

49
50
51
52
53
54
55
56
57
58
59
60
61
62
# Module level variable
_http_manager = None


def Init():
  """Initializes the module-global HTTP client manager.

  Must be called before using any RPC function.

  """
  global _http_manager

  assert not _http_manager, "RPC module initialized more than once"

63
  _http_manager = http.client.HttpClientManager()
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78


def Shutdown():
  """Stops the module-global HTTP client manager.

  Must be called before quitting the program.

  """
  global _http_manager

  if _http_manager:
    _http_manager.Shutdown()
    _http_manager = None


79
80
81
82
83
84
85
class RpcResult(object):
  """RPC Result class.

  This class holds an RPC result. It is needed since in multi-node
  calls we can't raise an exception just because one one out of many
  failed, and therefore we use this class to encapsulate the result.

86
87
88
89
90
91
92
93
94
95
96
  @ivar data: the data payload, for successfull results, or None
  @type failed: boolean
  @ivar failed: whether the operation failed at RPC level (not
      application level on the remote node)
  @ivar call: the name of the RPC call
  @ivar node: the name of the node to which we made the call
  @ivar offline: whether the operation failed because the node was
      offline, as opposed to actual failure; offline=True will always
      imply failed=True, in order to allow simpler checking if
      the user doesn't care about the exact failure mode

97
  """
98
99
  def __init__(self, data=None, failed=False, offline=False,
               call=None, node=None):
100
    self.failed = failed
101
102
103
104
105
106
107
108
    self.offline = offline
    self.call = call
    self.node = node
    if offline:
      self.failed = True
      self.error = "Node is marked offline"
      self.data = None
    elif failed:
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
      self.error = data
      self.data = None
    else:
      self.data = data
      self.error = None

  def Raise(self):
    """If the result has failed, raise an OpExecError.

    This is used so that LU code doesn't have to check for each
    result, but instead can call this function.

    """
    if self.failed:
      raise errors.OpExecError("Call '%s' to node '%s' has failed: %s" %
                               (self.call, self.node, self.error))


Iustin Pop's avatar
Iustin Pop committed
127
128
129
class Client:
  """RPC Client class.

Alexander Schreiber's avatar
Alexander Schreiber committed
130
  This class, given a (remote) method name, a list of parameters and a
Iustin Pop's avatar
Iustin Pop committed
131
132
133
134
135
136
137
138
  list of nodes, will contact (in parallel) all nodes, and return a
  dict of results (key: node name, value: result).

  One current bug is that generic failure is still signalled by
  'False' result, which is not good. This overloading of values can
  cause bugs.

  """
139
  def __init__(self, procedure, body, port):
Iustin Pop's avatar
Iustin Pop committed
140
    self.procedure = procedure
141
142
    self.body = body
    self.port = port
143
    self.nc = {}
Iustin Pop's avatar
Iustin Pop committed
144

145
146
147
148
    self._ssl_params = \
      http.HttpSslParams(ssl_key_path=constants.SSL_CERT_FILE,
                         ssl_cert_path=constants.SSL_CERT_FILE)

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

152
153
    @type node_list: list
    @param node_list: the list of node names to connect
154
155
156
    @type address_list: list or None
    @keyword address_list: either None or a list with node addresses,
        which must have the same length as the node list
157

Iustin Pop's avatar
Iustin Pop committed
158
    """
159
160
161
162
163
164
165
166
167
    if address_list is None:
      address_list = [None for _ in node_list]
    else:
      assert len(node_list) == len(address_list), \
             "Name and address lists should have the same length"
    for node, address in zip(node_list, address_list):
      self.ConnectNode(node, address)

  def ConnectNode(self, name, address=None):
Iustin Pop's avatar
Iustin Pop committed
168
169
    """Add a node to the target list.

170
171
172
173
174
    @type name: str
    @param name: the node name
    @type address: str
    @keyword address: the node address, if known

Iustin Pop's avatar
Iustin Pop committed
175
    """
176
177
178
    if address is None:
      address = name

179
180
181
182
183
184
    self.nc[name] = \
      http.client.HttpClientRequest(address, self.port, http.HTTP_PUT,
                                    "/%s" % self.procedure,
                                    post_data=self.body,
                                    ssl_params=self._ssl_params,
                                    ssl_verify_peer=True)
Iustin Pop's avatar
Iustin Pop committed
185

186
  def GetResults(self):
187
188
189
190
    """Call nodes and return results.

    @rtype: list
    @returns: List of RPC results
Iustin Pop's avatar
Iustin Pop committed
191
192

    """
193
194
195
    assert _http_manager, "RPC module not intialized"

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

197
    results = {}
Iustin Pop's avatar
Iustin Pop committed
198

199
    for name, req in self.nc.iteritems():
200
      if req.success and req.resp_status_code == http.HTTP_OK:
201
202
        results[name] = RpcResult(data=serializer.LoadJson(req.resp_body),
                                  node=name, call=self.procedure)
203
        continue
Iustin Pop's avatar
Iustin Pop committed
204

205
      # TODO: Better error reporting
206
207
208
209
210
211
      if req.error:
        msg = req.error
      else:
        msg = req.resp_body

      logging.error("RPC error from node %s: %s", name, msg)
212
213
      results[name] = RpcResult(data=msg, failed=True, node=name,
                                call=self.procedure)
214
215

    return results
Iustin Pop's avatar
Iustin Pop committed
216
217


Iustin Pop's avatar
Iustin Pop committed
218
219
class RpcRunner(object):
  """RPC runner class"""
Iustin Pop's avatar
Iustin Pop committed
220

Iustin Pop's avatar
Iustin Pop committed
221
222
  def __init__(self, cfg):
    """Initialized the rpc runner.
Iustin Pop's avatar
Iustin Pop committed
223

Iustin Pop's avatar
Iustin Pop committed
224
225
226
    @type cfg:  C{config.ConfigWriter}
    @param cfg: the configuration object that will be used to get data
                about the cluster
Iustin Pop's avatar
Iustin Pop committed
227

Iustin Pop's avatar
Iustin Pop committed
228
229
    """
    self._cfg = cfg
230
    self.port = utils.GetNodeDaemonPort()
Iustin Pop's avatar
Iustin Pop committed
231

232
233
234
235
236
237
238
239
240
241
242
243
244
245
  def _InstDict(self, instance):
    """Convert the given instance to a dict.

    This is done via the instance's ToDict() method and additionally
    we fill the hvparams with the cluster defaults.

    @type instance: L{objects.Instance}
    @param instance: an Instance object
    @rtype: dict
    @return: the instance dict, with the hvparams filled with the
        cluster defaults

    """
    idict = instance.ToDict()
246
247
248
    cluster = self._cfg.GetClusterInfo()
    idict["hvparams"] = cluster.FillHV(instance)
    idict["beparams"] = cluster.FillBE(instance)
249
250
    return idict

251
252
253
254
255
256
257
258
259
260
  def _ConnectList(self, client, node_list):
    """Helper for computing node addresses.

    @type client: L{Client}
    @param client: a C{Client} instance
    @type node_list: list
    @param node_list: the node list we should connect

    """
    all_nodes = self._cfg.GetAllNodesInfo()
261
    name_list = []
262
    addr_list = []
263
    skip_dict = {}
264
265
    for node in node_list:
      if node in all_nodes:
266
267
268
        if all_nodes[node].offline:
          skip_dict[node] = RpcResult(node=node, offline=True)
          continue
269
270
271
272
        val = all_nodes[node].primary_ip
      else:
        val = None
      addr_list.append(val)
273
274
275
276
      name_list.append(node)
    if name_list:
      client.ConnectList(name_list, address_list=addr_list)
    return skip_dict
277
278
279
280
281
282
283
284
285
286
287
288

  def _ConnectNode(self, client, node):
    """Helper for computing one node's address.

    @type client: L{Client}
    @param client: a C{Client} instance
    @type node: str
    @param node: the node we should connect

    """
    node_info = self._cfg.GetNodeInfo(node)
    if node_info is not None:
289
290
      if node_info.offline:
        return RpcResult(node=node, offline=True)
291
292
293
294
295
      addr = node_info.primary_ip
    else:
      addr = None
    client.ConnectNode(node, address=addr)

296
  def _MultiNodeCall(self, node_list, procedure, args):
297
298
299
300
301
    """Helper for making a multi-node call

    """
    body = serializer.DumpJson(args, indent=False)
    c = Client(procedure, body, self.port)
302
303
304
    skip_dict = self._ConnectList(c, node_list)
    skip_dict.update(c.GetResults())
    return skip_dict
305
306
307
308

  @classmethod
  def _StaticMultiNodeCall(cls, node_list, procedure, args,
                           address_list=None):
309
310
311
312
313
    """Helper for making a multi-node static call

    """
    body = serializer.DumpJson(args, indent=False)
    c = Client(procedure, body, utils.GetNodeDaemonPort())
314
315
316
317
    c.ConnectList(node_list, address_list=address_list)
    return c.GetResults()

  def _SingleNodeCall(self, node, procedure, args):
318
    """Helper for making a single-node call
319
320

    """
321
322
    body = serializer.DumpJson(args, indent=False)
    c = Client(procedure, body, self.port)
323
324
325
326
327
    result = self._ConnectNode(c, node)
    if result is None:
      # we did connect, node is not offline
      result = c.GetResults()[node]
    return result
328
329
330

  @classmethod
  def _StaticSingleNodeCall(cls, node, procedure, args):
331
    """Helper for making a single-node static call
332
333

    """
334
335
    body = serializer.DumpJson(args, indent=False)
    c = Client(procedure, body, utils.GetNodeDaemonPort())
336
    c.ConnectNode(node)
337
    return c.GetResults()[node]
338

339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
  @staticmethod
  def _Compress(data):
    """Compresses a string for transport over RPC.

    Small amounts of data are not compressed.

    @type data: str
    @param data: Data
    @rtype: tuple
    @return: Encoded data to send

    """
    # Small amounts of data are not compressed
    if len(data) < 512:
      return (constants.RPC_ENCODING_NONE, data)

    # Compress with zlib and encode in base64
    return (constants.RPC_ENCODING_ZLIB_BASE64,
            base64.b64encode(zlib.compress(data, 3)))

359
360
361
362
  #
  # Begin RPC calls
  #

Iustin Pop's avatar
Iustin Pop committed
363
364
  def call_volume_list(self, node_list, vg_name):
    """Gets the logical volumes present in a given volume group.
Iustin Pop's avatar
Iustin Pop committed
365

Iustin Pop's avatar
Iustin Pop committed
366
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
367

Iustin Pop's avatar
Iustin Pop committed
368
    """
369
    return self._MultiNodeCall(node_list, "volume_list", [vg_name])
Iustin Pop's avatar
Iustin Pop committed
370

Iustin Pop's avatar
Iustin Pop committed
371
372
  def call_vg_list(self, node_list):
    """Gets the volume group list.
Iustin Pop's avatar
Iustin Pop committed
373

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

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

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

Iustin Pop's avatar
Iustin Pop committed
382
383
384
    This method checks if all bridges given in the bridges_list are
    present on the remote node, so that an instance that uses interfaces
    on those bridges can be started.
Iustin Pop's avatar
Iustin Pop committed
385

Iustin Pop's avatar
Iustin Pop committed
386
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
387

Iustin Pop's avatar
Iustin Pop committed
388
    """
389
    return self._SingleNodeCall(node, "bridges_exist", [bridges_list])
Iustin Pop's avatar
Iustin Pop committed
390

Iustin Pop's avatar
Iustin Pop committed
391
392
  def call_instance_start(self, node, instance, extra_args):
    """Starts an instance.
Iustin Pop's avatar
Iustin Pop committed
393

Iustin Pop's avatar
Iustin Pop committed
394
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
395

Iustin Pop's avatar
Iustin Pop committed
396
    """
397
398
    return self._SingleNodeCall(node, "instance_start",
                                [self._InstDict(instance), extra_args])
Iustin Pop's avatar
Iustin Pop committed
399

Iustin Pop's avatar
Iustin Pop committed
400
401
  def call_instance_shutdown(self, node, instance):
    """Stops an instance.
Iustin Pop's avatar
Iustin Pop committed
402

Iustin Pop's avatar
Iustin Pop committed
403
    This is a single-node call.
404

Iustin Pop's avatar
Iustin Pop committed
405
    """
406
407
    return self._SingleNodeCall(node, "instance_shutdown",
                                [self._InstDict(instance)])
408

Iustin Pop's avatar
Iustin Pop committed
409
410
  def call_instance_migrate(self, node, instance, target, live):
    """Migrate an instance.
411

Iustin Pop's avatar
Iustin Pop committed
412
    This is a single-node call.
413

Iustin Pop's avatar
Iustin Pop committed
414
415
416
417
418
419
420
421
422
    @type node: string
    @param node: the node on which the instance is currently running
    @type instance: C{objects.Instance}
    @param instance: the instance definition
    @type target: string
    @param target: the target node name
    @type live: boolean
    @param live: whether the migration should be done live or not (the
        interpretation of this parameter is left to the hypervisor)
423

Iustin Pop's avatar
Iustin Pop committed
424
    """
425
426
    return self._SingleNodeCall(node, "instance_migrate",
                                [self._InstDict(instance), target, live])
427

Iustin Pop's avatar
Iustin Pop committed
428
429
  def call_instance_reboot(self, node, instance, reboot_type, extra_args):
    """Reboots an instance.
430

Iustin Pop's avatar
Iustin Pop committed
431
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
432

Iustin Pop's avatar
Iustin Pop committed
433
    """
434
435
436
    return self._SingleNodeCall(node, "instance_reboot",
                                [self._InstDict(instance), reboot_type,
                                 extra_args])
Iustin Pop's avatar
Iustin Pop committed
437

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

Iustin Pop's avatar
Iustin Pop committed
441
    This is a single-node call.
442

Iustin Pop's avatar
Iustin Pop committed
443
    """
444
445
    return self._SingleNodeCall(node, "instance_os_add",
                                [self._InstDict(inst)])
446

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

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

Iustin Pop's avatar
Iustin Pop committed
452
    """
453
454
    return self._SingleNodeCall(node, "instance_run_rename",
                                [self._InstDict(inst), old_name])
Iustin Pop's avatar
Iustin Pop committed
455

Iustin Pop's avatar
Iustin Pop committed
456
457
  def call_instance_info(self, node, instance, hname):
    """Returns information about a single instance.
Iustin Pop's avatar
Iustin Pop committed
458

Iustin Pop's avatar
Iustin Pop committed
459
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
460

461
462
    @type node: list
    @param node: the list of nodes to query
Iustin Pop's avatar
Iustin Pop committed
463
464
465
466
    @type instance: string
    @param instance: the instance name
    @type hname: string
    @param hname: the hypervisor type of the instance
Iustin Pop's avatar
Iustin Pop committed
467

Iustin Pop's avatar
Iustin Pop committed
468
    """
469
    return self._SingleNodeCall(node, "instance_info", [instance, hname])
470

471
472
473
474
475
476
477
478
479
480
481
482
483
484
  def call_instance_migratable(self, node, instance):
    """Checks whether the given instance can be migrated.

    This is a single-node call.

    @param node: the node to query
    @type instance: L{objects.Instance}
    @param instance: the instance to check


    """
    return self._SingleNodeCall(node, "instance_migratable",
                                [self._InstDict(instance)])

Iustin Pop's avatar
Iustin Pop committed
485
486
  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
487

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

Iustin Pop's avatar
Iustin Pop committed
490
491
492
493
    @type node_list: list
    @param node_list: the list of nodes to query
    @type hypervisor_list: list
    @param hypervisor_list: the hypervisors to query for instances
Iustin Pop's avatar
Iustin Pop committed
494

Iustin Pop's avatar
Iustin Pop committed
495
    """
496
497
    return self._MultiNodeCall(node_list, "all_instances_info",
                               [hypervisor_list])
498

Iustin Pop's avatar
Iustin Pop committed
499
500
  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
501

Iustin Pop's avatar
Iustin Pop committed
502
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
503

Iustin Pop's avatar
Iustin Pop committed
504
505
506
507
    @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
508

Iustin Pop's avatar
Iustin Pop committed
509
    """
510
    return self._MultiNodeCall(node_list, "instance_list", [hypervisor_list])
511

Iustin Pop's avatar
Iustin Pop committed
512
513
514
  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
515

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

Iustin Pop's avatar
Iustin Pop committed
518
    """
519
520
    return self._SingleNodeCall(node, "node_tcp_ping",
                                [source, target, port, timeout,
Iustin Pop's avatar
Iustin Pop committed
521
                                 live_port_needed])
Iustin Pop's avatar
Iustin Pop committed
522

523
524
525
526
527
528
  def call_node_has_ip_address(self, node, address):
    """Checks if a node has the given IP address.

    This is a single-node call.

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

Iustin Pop's avatar
Iustin Pop committed
531
532
  def call_node_info(self, node_list, vg_name, hypervisor_type):
    """Return node information.
533

Iustin Pop's avatar
Iustin Pop committed
534
535
    This will return memory information and volume group size and free
    space.
Iustin Pop's avatar
Iustin Pop committed
536

Iustin Pop's avatar
Iustin Pop committed
537
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
538

Iustin Pop's avatar
Iustin Pop committed
539
540
    @type node_list: list
    @param node_list: the list of nodes to query
Iustin Pop's avatar
Iustin Pop committed
541
542
    @type vg_name: C{string}
    @param vg_name: the name of the volume group to ask for disk space
Iustin Pop's avatar
Iustin Pop committed
543
544
545
546
        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
547

Iustin Pop's avatar
Iustin Pop committed
548
    """
549
550
    retux = self._MultiNodeCall(node_list, "node_info",
                                [vg_name, hypervisor_type])
Iustin Pop's avatar
Iustin Pop committed
551

552
553
554
555
556
557
558
559
560
561
562
    for result in retux.itervalues():
      if result.failed or not isinstance(result.data, dict):
        result.data = {}

      utils.CheckDict(result.data, {
        'memory_total' : '-',
        'memory_dom0' : '-',
        'memory_free' : '-',
        'vg_size' : 'node_unreachable',
        'vg_free' : '-',
        }, "call_node_info")
Iustin Pop's avatar
Iustin Pop committed
563
    return retux
Iustin Pop's avatar
Iustin Pop committed
564

Iustin Pop's avatar
Iustin Pop committed
565
566
  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
567

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

Iustin Pop's avatar
Iustin Pop committed
570
    """
571
572
    return self._SingleNodeCall(node, "node_add",
                                [dsa, dsapub, rsa, rsapub, ssh, sshpub])
Iustin Pop's avatar
Iustin Pop committed
573

Iustin Pop's avatar
Iustin Pop committed
574
575
  def call_node_verify(self, node_list, checkdict, cluster_name):
    """Request verification of given parameters.
Iustin Pop's avatar
Iustin Pop committed
576

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

Iustin Pop's avatar
Iustin Pop committed
579
    """
580
581
    return self._MultiNodeCall(node_list, "node_verify",
                               [checkdict, cluster_name])
Iustin Pop's avatar
Iustin Pop committed
582

583
584
  @classmethod
  def call_node_start_master(cls, node, start_daemons):
Iustin Pop's avatar
Iustin Pop committed
585
    """Tells a node to activate itself as a master.
Iustin Pop's avatar
Iustin Pop committed
586

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

Iustin Pop's avatar
Iustin Pop committed
589
    """
590
591
    return cls._StaticSingleNodeCall(node, "node_start_master",
                                     [start_daemons])
Iustin Pop's avatar
Iustin Pop committed
592

593
594
  @classmethod
  def call_node_stop_master(cls, node, stop_daemons):
Iustin Pop's avatar
Iustin Pop committed
595
    """Tells a node to demote itself from master status.
Iustin Pop's avatar
Iustin Pop committed
596

Iustin Pop's avatar
Iustin Pop committed
597
    This is a single-node call.
598

Iustin Pop's avatar
Iustin Pop committed
599
    """
600
    return cls._StaticSingleNodeCall(node, "node_stop_master", [stop_daemons])
601

602
603
  @classmethod
  def call_master_info(cls, node_list):
Iustin Pop's avatar
Iustin Pop committed
604
    """Query master info.
605

Iustin Pop's avatar
Iustin Pop committed
606
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
607

Iustin Pop's avatar
Iustin Pop committed
608
609
    """
    # TODO: should this method query down nodes?
610
    return cls._StaticMultiNodeCall(node_list, "master_info", [])
Iustin Pop's avatar
Iustin Pop committed
611

Iustin Pop's avatar
Iustin Pop committed
612
613
  def call_version(self, node_list):
    """Query node version.
Iustin Pop's avatar
Iustin Pop committed
614

Iustin Pop's avatar
Iustin Pop committed
615
    This is a multi-node call.
Iustin Pop's avatar
Iustin Pop committed
616

Iustin Pop's avatar
Iustin Pop committed
617
    """
618
    return self._MultiNodeCall(node_list, "version", [])
Iustin Pop's avatar
Iustin Pop committed
619

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

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

Iustin Pop's avatar
Iustin Pop committed
625
    """
626
627
    return self._SingleNodeCall(node, "blockdev_create",
                                [bdev.ToDict(), size, owner, on_primary, info])
Iustin Pop's avatar
Iustin Pop committed
628

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

Iustin Pop's avatar
Iustin Pop committed
632
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
633

Iustin Pop's avatar
Iustin Pop committed
634
    """
635
    return self._SingleNodeCall(node, "blockdev_remove", [bdev.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
636

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

Iustin Pop's avatar
Iustin Pop committed
640
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
641

Iustin Pop's avatar
Iustin Pop committed
642
    """
643
644
    return self._SingleNodeCall(node, "blockdev_rename",
                                [(d.ToDict(), uid) for d, uid in devlist])
Iustin Pop's avatar
Iustin Pop committed
645

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

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

Iustin Pop's avatar
Iustin Pop committed
651
    """
652
653
    return self._SingleNodeCall(node, "blockdev_assemble",
                                [disk.ToDict(), owner, on_primary])
Iustin Pop's avatar
Iustin Pop committed
654

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

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

Iustin Pop's avatar
Iustin Pop committed
660
    """
661
    return self._SingleNodeCall(node, "blockdev_shutdown", [disk.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
662

Iustin Pop's avatar
Iustin Pop committed
663
664
  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
665

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

Iustin Pop's avatar
Iustin Pop committed
668
    """
669
670
671
    return self._SingleNodeCall(node, "blockdev_addchildren",
                                [bdev.ToDict(),
                                 [disk.ToDict() for disk in ndevs]])
Iustin Pop's avatar
Iustin Pop committed
672

Iustin Pop's avatar
Iustin Pop committed
673
674
  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
675

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

Iustin Pop's avatar
Iustin Pop committed
678
    """
679
680
681
    return self._SingleNodeCall(node, "blockdev_removechildren",
                                [bdev.ToDict(),
                                 [disk.ToDict() for disk in ndevs]])
Iustin Pop's avatar
Iustin Pop committed
682

Iustin Pop's avatar
Iustin Pop committed
683
684
  def call_blockdev_getmirrorstatus(self, node, disks):
    """Request status of a (mirroring) device.
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
    return self._SingleNodeCall(node, "blockdev_getmirrorstatus",
                                [dsk.ToDict() for dsk in disks])
Iustin Pop's avatar
Iustin Pop committed
691

Iustin Pop's avatar
Iustin Pop committed
692
693
694
695
  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
696

Iustin Pop's avatar
Iustin Pop committed
697
    """
698
    return self._SingleNodeCall(node, "blockdev_find", [disk.ToDict()])
699

700
  def call_blockdev_close(self, node, instance_name, disks):
Iustin Pop's avatar
Iustin Pop committed
701
    """Closes the given block devices.
702

Iustin Pop's avatar
Iustin Pop committed
703
    This is a single-node call.
704

Iustin Pop's avatar
Iustin Pop committed
705
    """
706
707
    params = [instance_name, [cf.ToDict() for cf in disks]]
    return self._SingleNodeCall(node, "blockdev_close", params)
Iustin Pop's avatar
Iustin Pop committed
708

709
710
  @classmethod
  def call_upload_file(cls, node_list, file_name, address_list=None):
Iustin Pop's avatar
Iustin Pop committed
711
712
713
714
715
716
    """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
717

718
719
720
721
722
723
724
725
    @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
726
    """
727
728
    file_contents = utils.ReadFile(file_name)
    data = cls._Compress(file_contents)
Iustin Pop's avatar
Iustin Pop committed
729
730
731
    st = os.stat(file_name)
    params = [file_name, data, st.st_mode, st.st_uid, st.st_gid,
              st.st_atime, st.st_mtime]
732
733
    return cls._StaticMultiNodeCall(node_list, "upload_file", params,
                                    address_list=address_list)
Iustin Pop's avatar
Iustin Pop committed
734

735
  @classmethod
736
  def call_write_ssconf_files(cls, node_list, values):
737
738
739
740
741
    """Write ssconf files.

    This is a multi-node call.

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

Iustin Pop's avatar
Iustin Pop committed
744
745
746
747
  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
748

Iustin Pop's avatar
Iustin Pop committed
749
    """
750
751
    result = self._MultiNodeCall(node_list, "os_diagnose", [])

752
    for node_result in result.values():
753
754
755
756
      if not node_result.failed and node_result.data:
        node_result.data = [objects.OS.FromDict(oss)
                            for oss in node_result.data]
    return result
Iustin Pop's avatar
Iustin Pop committed
757

Iustin Pop's avatar
Iustin Pop committed
758
759
  def call_os_get(self, node, name):
    """Returns an OS definition.
Iustin Pop's avatar
Iustin Pop committed
760

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

Iustin Pop's avatar
Iustin Pop committed
763
    """
764
    result = self._SingleNodeCall(node, "os_get", [name])
765
766
767
    if not result.failed and isinstance(result.data, dict):
      result.data = objects.OS.FromDict(result.data)
    return result
Iustin Pop's avatar
Iustin Pop committed
768

Iustin Pop's avatar
Iustin Pop committed
769
770
  def call_hooks_runner(self, node_list, hpath, phase, env):
    """Call the hooks runner.
Iustin Pop's avatar
Iustin Pop committed
771

Iustin Pop's avatar
Iustin Pop committed
772
773
774
    Args:
      - op: the OpCode instance
      - env: a dictionary with the environment
Iustin Pop's avatar
Iustin Pop committed
775

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

Iustin Pop's avatar
Iustin Pop committed
778
779
    """
    params = [hpath, phase, env]
780
    return self._MultiNodeCall(node_list, "hooks_runner", params)
Iustin Pop's avatar
Iustin Pop committed
781

Iustin Pop's avatar
Iustin Pop committed
782
783
  def call_iallocator_runner(self, node, name, idata):
    """Call an iallocator on a remote node
784

Iustin Pop's avatar
Iustin Pop committed
785
786
787
    Args:
      - name: the iallocator name
      - input: the json-encoded input string
788

Iustin Pop's avatar
Iustin Pop committed
789
    This is a single-node call.
790

Iustin Pop's avatar
Iustin Pop committed
791
    """
792
    return self._SingleNodeCall(node, "iallocator_runner", [name, idata])
793

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

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

Iustin Pop's avatar
Iustin Pop committed
799
    """
800
801
    return self._SingleNodeCall(node, "blockdev_grow",
                                [cf_bdev.ToDict(), amount])
802

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

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

Iustin Pop's avatar
Iustin Pop committed
808
    """
809
    return self._SingleNodeCall(node, "blockdev_snapshot", [cf_bdev.ToDict()])
Iustin Pop's avatar
Iustin Pop committed
810

Iustin Pop's avatar
Iustin Pop committed
811
  def call_snapshot_export(self, node, snap_bdev, dest_node, instance,
812
                           cluster_name, idx):
Iustin Pop's avatar
Iustin Pop committed
813
    """Request the export of a given snapshot.
Iustin Pop's avatar
Iustin Pop committed
814

Iustin Pop's avatar
Iustin Pop committed
815
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
816

Iustin Pop's avatar
Iustin Pop committed
817
    """
818
819
820
    return self._SingleNodeCall(node, "snapshot_export",
                                [snap_bdev.ToDict(), dest_node,
                                 self._InstDict(instance), cluster_name, idx])
Iustin Pop's avatar
Iustin Pop committed
821

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

Iustin Pop's avatar
Iustin Pop committed
825
    This writes the export config file, etc.
Iustin Pop's avatar
Iustin Pop committed
826

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

Iustin Pop's avatar
Iustin Pop committed
829
830
831
832
    """
    flat_disks = []
    for disk in snap_disks:
      flat_disks.append(disk.ToDict())
833
834
835

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

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

Iustin Pop's avatar
Iustin Pop committed
840
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
841

Iustin Pop's avatar
Iustin Pop committed
842
    """
843
    result = self._SingleNodeCall(node, "export_info", [path])
844
845
846
    if not result.failed and result.data:
      result.data = objects.SerializableConfigParser.Loads(str(result.data))
    return result
Iustin Pop's avatar
Iustin Pop committed
847

848
849
  def call_instance_os_import(self, node, inst, src_node, src_images,
                              cluster_name):
Iustin Pop's avatar
Iustin Pop committed
850
    """Request the import of a backup into an instance.
Iustin Pop's avatar
Iustin Pop committed
851

Iustin Pop's avatar
Iustin Pop committed
852
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
853

Iustin Pop's avatar
Iustin Pop committed
854
    """
855
856
857
    return self._SingleNodeCall(node, "instance_os_import",
                                [self._InstDict(inst), src_node, src_images,
                                 cluster_name])
Iustin Pop's avatar
Iustin Pop committed
858

Iustin Pop's avatar
Iustin Pop committed
859
860
  def call_export_list(self, node_list):
    """Gets the stored exports list.
Iustin Pop's avatar
Iustin Pop committed
861

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

Iustin Pop's avatar
Iustin Pop committed
864
    """
865
    return self._MultiNodeCall(node_list, "export_list", [])
Iustin Pop's avatar
Iustin Pop committed
866

Iustin Pop's avatar
Iustin Pop committed
867
868
  def call_export_remove(self, node, export):
    """Requests removal of a given export.
Iustin Pop's avatar
Iustin Pop committed
869

Iustin Pop's avatar
Iustin Pop committed
870
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
871

Iustin Pop's avatar
Iustin Pop committed
872
    """
873
    return self._SingleNodeCall(node, "export_remove", [export])
Iustin Pop's avatar
Iustin Pop committed
874

875
876
  @classmethod
  def call_node_leave_cluster(cls, node):
Iustin Pop's avatar
Iustin Pop committed
877
    """Requests a node to clean the cluster information it has.
Iustin Pop's avatar
Iustin Pop committed
878

Iustin Pop's avatar
Iustin Pop committed
879
880
    This will remove the configuration information from the ganeti data
    dir.
Iustin Pop's avatar
Iustin Pop committed
881

Iustin Pop's avatar
Iustin Pop committed
882
    This is a single-node call.
Iustin Pop's avatar
Iustin Pop committed
883

Iustin Pop's avatar
Iustin Pop committed
884
    """
885
    return cls._StaticSingleNodeCall(node, "node_leave_cluster", [])
886

Iustin Pop's avatar
Iustin Pop committed
887
888
  def call_node_volumes(self, node_list):
    """Gets all volumes on node(s).
889

Iustin Pop's avatar
Iustin Pop committed
890
    This is a multi-node call.
891

Iustin Pop's avatar
Iustin Pop committed
892
    """
893
    return self._MultiNodeCall(node_list, "node_volumes", [])
894

895
896
897
898
899
900
901
902
  def call_node_demote_from_mc(self, node):
    """Demote a node from the master candidate role.

    This is a single-node call.

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

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

Iustin Pop's avatar
Iustin Pop committed
906
    This is a multi-node call.
907

Iustin Pop's avatar
Iustin Pop committed
908
    """