Revision 880478f8

b/daemons/ganeti-master
1
#!/usr/bin/python
2
#
3

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

  
21

  
22
"""Ganeti master script
23

  
24
Exit codes, for both start and stop:
25
  - 0: master setup successful
26
  - 1: some generic error (this exit code can also be thrown by exceptions)
27
  - 11: node is not master, nothing to do
28
  - 12: node setup incomplete, cannot start
29
  - 13: node should be master, but someone has the ip address already
30

  
31
Only exit codes 0 and 11 represent an ok state. Code 1 was left for
32
generic errors as other python code can cause exit with code 1.
33

  
34
"""
35

  
36
import os
37
import sys
38
import socket
39

  
40
from optparse import OptionParser
41

  
42
from ganeti import logger
43
from ganeti import constants
44
from ganeti import errors
45
from ganeti import ssconf
46
from ganeti import utils
47

  
48
EXIT_OK = 0
49
EXIT_SOME_ERROR = 1
50
EXIT_NOTMASTER = 11
51
EXIT_NODESETUP_ERROR = 12
52
EXIT_DUPLICATE_IP = 13
53
EXIT_ARGS_ERROR = 14
54

  
55

  
56
def ParseOptions():
57
  """Parse the command line options.
58

  
59
  Returns:
60
    (options, args) as from OptionParser.parse_args()
61

  
62
  """
63
  parser = OptionParser(description="Ganeti master",
64
                        usage="%prog [-d]",
65
                        version="%%prog (ganeti) %s" %
66
                        constants.RELEASE_VERSION)
67

  
68
  parser.add_option("-d", "--debug", dest="debug",
69
                    help="Enable some debug messages",
70
                    default=False, action="store_true")
71
  options, args = parser.parse_args()
72

  
73
  if len(args) != 1 or args[0] not in ("start", "stop"):
74
    sys.stderr.write("Usage: %s [-d] start|stop\n" % sys.argv[0])
75
    sys.exit(EXIT_ARGS_ERROR)
76

  
77
  return options, args
78

  
79

  
80
def CheckNodeSetup(debug):
81
  """Checks the node setup.
82

  
83
  If the node setup if ok, this function will return the tuple
84
  (master_hostname, master_netdev, master_ip). Otherwise the return
85
  value will be None.
86

  
87
  """
88
  for fname in (constants.SSL_CERT_FILE,):
89
    if not os.path.isfile(fname):
90
      if debug:
91
        sys.stderr.write("Missing config file %s.\n" % fname)
92
      return None
93
  try:
94
    ss = ssconf.SimpleStore()
95
    port = ss.GetNodeDaemonPort()
96
    pwdata = ss.GetNodeDaemonPassword()
97
    master_name = ss.GetMasterNode()
98
    master_netdev = ss.GetMasterNetdev()
99
    master_ip = ss.GetMasterIP()
100
  except errors.ConfigurationError, err:
101
    if debug:
102
      sys.stderr.write("Cluster configuration incomplete: '%s'\n" % str(err))
103
    return None
104
  return (master_name, master_netdev, master_ip)
105

  
106

  
107
def StartMaster(master_netdev, master_ip, debug):
108
  """Starts the master.
109

  
110
  """
111
  result = utils.RunCmd(["fping", "-q", master_ip])
112
  if not result.failed:
113
    r2 = utils.RunCmd(["fping", "-q", "-S127.0.0.1", master_ip])
114
    if not result.failed:
115
      # we already have the ip:
116
      if debug:
117
        sys.stderr.write("Notice: already started.\n")
118
      return EXIT_OK
119
    else:
120
      return EXIT_DUPLICATE_IP
121
  result = utils.RunCmd(["ip", "address", "add", "%s/32" % master_ip,
122
                         "dev", master_netdev, "label",
123
                         "%s:0" % master_netdev])
124
  if result.failed:
125
    if debug:
126
      sys.stderr.write("Can't activate master IP: %s\n" % result.output)
127
    return EXIT_SOME_ERROR
128

  
129
  result = utils.RunCmd(["arping", "-q", "-U", "-c 3", "-I", master_netdev,
130
                         "-s", master_ip, master_ip])
131
  # we'll ignore the exit code of arping
132
  return EXIT_OK
133

  
134

  
135
def StopMaster(master_netdev, master_ip, debug):
136
  """Stops the master.
137

  
138
  """
139
  result = utils.RunCmd(["ip", "address", "del", "%s/32" % master_ip,
140
                         "dev", master_netdev])
141
  if result.failed:
142
    if debug:
143
      sys.stderr.write("Can't remove the master IP, error: %s" % result.output)
144
    # but otherwise ignore the failure
145
  return EXIT_OK
146

  
147

  
148
def main():
149
  """Main function.
150

  
151
  """
152
  options, args = ParseOptions()
153
  debug = options.debug
154
  result = CheckNodeSetup(debug)
155
  if not result:
156
    if debug:
157
      sys.stderr.write("Node configuration incomplete.\n")
158
    return EXIT_NODESETUP_ERROR
159

  
160
  master_node, master_netdev, master_ip = result
161
  if socket.gethostname() != master_node and args[0] == "start":
162
    if debug:
163
      sys.stderr.write("Not master, ignoring request.\n")
164
    return EXIT_NOTMASTER
165

  
166
  if args[0] == "start":
167
    fn = StartMaster
168
  else:
169
    fn = StopMaster
170

  
171
  return fn(master_netdev, master_ip, debug)
172

  
173

  
174
if __name__=='__main__':
175
  exit_code = main()
176
  sys.exit(exit_code)
b/docs/examples/ganeti.initd
2 2
# ganeti node daemon starter script
3 3
# based on skeleton from Debian GNU/Linux
4 4

  
5
PATH=/sbin:/bin:/usr/sbin:/usr/bin
5
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
6 6
DAEMON=/usr/local/sbin/ganeti-noded
7 7
NAME=ganeti-noded
8 8
SCRIPTNAME=/etc/init.d/ganeti
b/lib/backend.py
40 40
from ganeti import constants
41 41
from ganeti import bdev
42 42
from ganeti import objects
43
from ganeti import ssconf
43 44

  
44 45

  
45 46
def ListConfigFiles():
......
63 64
  """Activate local node as master node.
64 65

  
65 66
  There are two needed steps for this:
66
    - register the master init script, and also run it now
67
    - run the master script
67 68
    - register the cron script
68 69

  
69 70
  """
70
  result = utils.RunCmd(["update-rc.d", constants.MASTER_INITD_NAME,
71
                         "defaults", "21", "79"])
72

  
73
  if result.failed:
74
    logger.Error("could not register the master init.d script with command"
75
                 " %s, error %s" % (result.cmd, result.output))
76
    return False
77

  
78
  result = utils.RunCmd([constants.MASTER_INITD_SCRIPT, "start"])
71
  result = utils.RunCmd([constants.MASTER_SCRIPT, "-d", "start"])
79 72

  
80 73
  if result.failed:
81 74
    logger.Error("could not activate cluster interface with command %s,"
82
                 " error %s" % (result.cmd, result.output))
75
                 " error: '%s'" % (result.cmd, result.output))
83 76
    return False
84 77

  
85 78
  utils.RemoveFile(constants.MASTER_CRON_LINK)
......
91 84
  """Deactivate this node as master.
92 85

  
93 86
  This does two things:
94
    - remove links to master's startup script
87
    - run the master stop script
95 88
    - remove link to master cron script.
96 89

  
97 90
  """
98
  result = utils.RunCmd(["update-rc.d", "-f",
99
                          constants.MASTER_INITD_NAME, "remove"])
100
  if result.failed:
101
    logger.Error("could not unregister the master script with command"
102
                 " %s, error %s" % (result.cmd, result.output))
103
    return False
104

  
105
  output = utils.RunCmd([constants.MASTER_INITD_SCRIPT, "stop"])
91
  result = utils.RunCmd([constants.MASTER_SCRIPT, "-d", "stop"])
106 92

  
107 93
  if result.failed:
108 94
    logger.Error("could not deactivate cluster interface with command %s,"
109
                 " error %s" % (result.cmd, result.output))
95
                 " error: '%s'" % (result.cmd, result.output))
110 96
    return False
111 97

  
112 98
  utils.RemoveFile(constants.MASTER_CRON_LINK)
......
170 156
      if dirpath == constants.DATA_DIR:
171 157
        for i in filenames:
172 158
          os.unlink(os.path.join(dirpath, i))
173
  utils.RemoveFile(constants.CLUSTER_NAME_FILE)
174 159

  
175 160
  f = open('/root/.ssh/id_dsa.pub', 'r')
176 161
  try:
......
794 779
                 file_name)
795 780
    return False
796 781

  
797
  if file_name not in [constants.CLUSTER_CONF_FILE, "/etc/hosts",
798
                       "/etc/ssh/ssh_known_hosts"]:
782
  allowed_files = [constants.CLUSTER_CONF_FILE, "/etc/hosts",
783
                   "/etc/ssh/ssh_known_hosts"]
784
  allowed_files.extend(ssconf.SimpleStore().GetFileList())
785
  if file_name not in allowed_files:
799 786
    logger.Error("Filename passed to UploadFile not in allowed"
800 787
                 " upload targets: '%s'" % file_name)
801 788
    return False
b/lib/cmdlib.py
19 19
# 02110-1301, USA.
20 20

  
21 21

  
22
"""Module implementing the commands used by gnt-* programs."""
22
"""Module implementing the master-side code."""
23 23

  
24 24
# pylint: disable-msg=W0613,W0201
25 25

  
......
84 84
        raise errors.OpPrereqError, ("Cluster not initialized yet,"
85 85
                                     " use 'gnt-cluster init' first.")
86 86
      if self.REQ_MASTER:
87
        master = cfg.GetMaster()
87
        master = sstore.GetMasterNode()
88 88
        if master != socket.gethostname():
89 89
          raise errors.OpPrereqError, ("Commands must be run on the master"
90 90
                                       " node %s" % master)
......
406 406
                               (result.cmd, result.exit_code, result.output))
407 407

  
408 408

  
409
def _InitClusterInterface(fullname, name, ip):
410
  """Initialize the master startup script.
411

  
412
  """
413
  f = file(constants.CLUSTER_NAME_FILE, 'w')
414
  f.write("%s\n" % fullname)
415
  f.close()
416

  
417
  f = file(constants.MASTER_INITD_SCRIPT, 'w')
418
  f.write ("#!/bin/sh\n")
419
  f.write ("\n")
420
  f.write ("# Start Ganeti Master Virtual Address\n")
421
  f.write ("\n")
422
  f.write ("DESC=\"Ganeti Master IP\"\n")
423
  f.write ("MASTERNAME=\"%s\"\n" % name)
424
  f.write ("MASTERIP=\"%s\"\n" % ip)
425
  f.write ("case \"$1\" in\n")
426
  f.write ("  start)\n")
427
  f.write ("    if fping -q -c 3 ${MASTERIP} &>/dev/null; then\n")
428
  f.write ("        echo \"$MASTERNAME no-go - there is already a master.\"\n")
429
  f.write ("        rm -f %s\n" % constants.MASTER_CRON_LINK)
430
  f.write ("        scp ${MASTERNAME}:%s %s\n" %
431
           (constants.CLUSTER_CONF_FILE, constants.CLUSTER_CONF_FILE))
432
  f.write ("    else\n")
433
  f.write ("        echo -n \"Starting $DESC: \"\n")
434
  f.write ("        ip address add ${MASTERIP}/32 dev xen-br0"
435
           " label xen-br0:0\n")
436
  f.write ("        arping -q -U -c 3 -I xen-br0 -s ${MASTERIP} ${MASTERIP}\n")
437
  f.write ("        echo \"$MASTERNAME.\"\n")
438
  f.write ("    fi\n")
439
  f.write ("    ;;\n")
440
  f.write ("  stop)\n")
441
  f.write ("    echo -n \"Stopping $DESC: \"\n")
442
  f.write ("    ip address del ${MASTERIP}/32 dev xen-br0\n")
443
  f.write ("    echo \"$MASTERNAME.\"\n")
444
  f.write ("    ;;\n")
445
  f.write ("  *)\n")
446
  f.write ("    echo \"Usage: $0 {start|stop}\" >&2\n")
447
  f.write ("    exit 1\n")
448
  f.write ("    ;;\n")
449
  f.write ("esac\n")
450
  f.write ("\n")
451
  f.write ("exit 0\n")
452
  f.flush()
453
  os.fsync(f.fileno())
454
  f.close()
455
  os.chmod(constants.MASTER_INITD_SCRIPT, 0755)
456

  
457

  
458 409
class LUInitCluster(LogicalUnit):
459 410
  """Initialise the cluster.
460 411

  
......
462 413
  HPATH = "cluster-init"
463 414
  HTYPE = constants.HTYPE_CLUSTER
464 415
  _OP_REQP = ["cluster_name", "hypervisor_type", "vg_name", "mac_prefix",
465
              "def_bridge"]
416
              "def_bridge", "master_netdev"]
466 417
  REQ_CLUSTER = False
467 418

  
468 419
  def BuildHooksEnv(self):
......
474 425
    """
475 426

  
476 427
    env = {"CLUSTER": self.op.cluster_name,
477
           "MASTER": self.hostname}
428
           "MASTER": self.hostname['hostname_full']}
478 429
    return env, [], [self.hostname['hostname_full']]
479 430

  
480 431
  def CheckPrereq(self):
......
528 479
      raise errors.OpPrereqError, ("Invalid hypervisor type given '%s'" %
529 480
                                   self.op.hypervisor_type)
530 481

  
482
    result = utils.RunCmd(["ip", "link", "show", "dev", self.op.master_netdev])
483
    if result.failed:
484
      raise errors.OpPrereqError, ("Invalid master netdev given (%s): '%s'" %
485
                                   (self.op.master_netdev, result.output))
486

  
531 487
  def Exec(self, feedback_fn):
532 488
    """Initialize the cluster.
533 489

  
......
535 491
    clustername = self.clustername
536 492
    hostname = self.hostname
537 493

  
538
    # adds the cluste name file and master startup script
539
    _InitClusterInterface(clustername['hostname_full'],
540
                          clustername['hostname'],
541
                          clustername['ip'])
542

  
543 494
    # set up the simple store
544 495
    ss = ssconf.SimpleStore()
545 496
    ss.SetKey(ss.SS_HYPERVISOR, self.op.hypervisor_type)
497
    ss.SetKey(ss.SS_MASTER_NODE, hostname['hostname_full'])
498
    ss.SetKey(ss.SS_MASTER_IP, clustername['ip'])
499
    ss.SetKey(ss.SS_MASTER_NETDEV, self.op.master_netdev)
546 500

  
547 501
    # set up the inter-node password and certificate
548 502
    _InitGanetiServerSetup(ss)
......
590 544
    Any errors are signalled by raising errors.OpPrereqError.
591 545

  
592 546
    """
593
    master = self.cfg.GetMaster()
547
    master = self.sstore.GetMasterNode()
594 548

  
595 549
    nodelist = self.cfg.GetNodeList()
596 550
    if len(nodelist) > 0 and nodelist != [master]:
597
        raise errors.OpPrereqError, ("There are still %d node(s) in "
598
                                     "this cluster." % (len(nodelist) - 1))
551
      raise errors.OpPrereqError, ("There are still %d node(s) in "
552
                                   "this cluster." % (len(nodelist) - 1))
599 553

  
600 554
  def Exec(self, feedback_fn):
601 555
    """Destroys the cluster.
......
603 557
    """
604 558
    utils.CreateBackup('/root/.ssh/id_dsa')
605 559
    utils.CreateBackup('/root/.ssh/id_dsa.pub')
606
    rpc.call_node_leave_cluster(self.cfg.GetMaster())
560
    rpc.call_node_leave_cluster(self.sstore.GetMasterNode())
607 561

  
608 562

  
609 563
class LUVerifyCluster(NoHooksLU):
......
795 749
    feedback_fn("* Verifying global settings")
796 750
    self.cfg.VerifyConfig()
797 751

  
798
    master = self.cfg.GetMaster()
752
    master = self.sstore.GetMasterNode()
799 753
    vg_name = self.cfg.GetVGName()
800 754
    nodelist = utils.NiceSort(self.cfg.GetNodeList())
801 755
    instancelist = utils.NiceSort(self.cfg.GetInstanceList())
......
1029 983

  
1030 984
    instance_list = self.cfg.GetInstanceList()
1031 985

  
1032
    masternode = self.cfg.GetMaster()
986
    masternode = self.sstore.GetMasterNode()
1033 987
    if node.name == masternode:
1034 988
      raise errors.OpPrereqError, ("Node is the master node,"
1035 989
                                   " you need to failover first.")
......
1253 1207

  
1254 1208
    # check that the type of the node (single versus dual homed) is the
1255 1209
    # same as for the master
1256
    myself = cfg.GetNodeInfo(cfg.GetMaster())
1210
    myself = cfg.GetNodeInfo(self.sstore.GetMasterNode())
1257 1211
    master_singlehomed = myself.secondary_ip == myself.primary_ip
1258 1212
    newbie_singlehomed = secondary_ip == primary_ip
1259 1213
    if master_singlehomed != newbie_singlehomed:
......
1378 1332

  
1379 1333
    # Distribute updated /etc/hosts and known_hosts to all nodes,
1380 1334
    # including the node just added
1381
    myself = self.cfg.GetNodeInfo(self.cfg.GetMaster())
1335
    myself = self.cfg.GetNodeInfo(self.sstore.GetMasterNode())
1382 1336
    dist_nodes = self.cfg.GetNodeList() + [node]
1383 1337
    if myself.name in dist_nodes:
1384 1338
      dist_nodes.remove(myself.name)
......
1391 1345
          logger.Error("copy of file %s to node %s failed" %
1392 1346
                       (fname, to_node))
1393 1347

  
1394
    to_copy = [constants.MASTER_CRON_FILE,
1395
               constants.MASTER_INITD_SCRIPT,
1396
               constants.CLUSTER_NAME_FILE]
1348
    to_copy = [constants.MASTER_CRON_FILE]
1397 1349
    to_copy.extend(ss.GetFileList())
1398 1350
    for fname in to_copy:
1399 1351
      if not ssh.CopyFileToNode(node, fname):
......
1435 1387
    """
1436 1388
    self.new_master = socket.gethostname()
1437 1389

  
1438
    self.old_master = self.cfg.GetMaster()
1390
    self.old_master = self.sstore.GetMasterNode()
1439 1391

  
1440 1392
    if self.old_master == self.new_master:
1441 1393
      raise errors.OpPrereqError, ("This commands must be run on the node"
......
1460 1412
      logger.Error("could disable the master role on the old master"
1461 1413
                   " %s, please disable manually" % self.old_master)
1462 1414

  
1415
    ss = self.sstore
1416
    ss.SetKey(ss.SS_MASTER_NODE, self.new_master)
1417
    if not rpc.call_upload_file(self.cfg.GetNodeList(),
1418
                                ss.KeyToFilename(ss.SS_MASTER_NODE)):
1419
      logger.Error("could not distribute the new simple store master file"
1420
                   " to the other nodes, please check.")
1421

  
1463 1422
    if not rpc.call_node_start_master(self.new_master):
1464 1423
      logger.Error("could not start the master role on the new master"
1465 1424
                   " %s, please check" % self.new_master)
1425
      feedback_fn("Error in activating the master IP on the new master,\n"
1426
                  "please fix manually.")
1466 1427

  
1467
    self.cfg.SetMaster(self.new_master)
1468 1428

  
1469 1429

  
1470 1430
class LUQueryClusterInfo(NoHooksLU):
......
1492 1452
      "config_version": constants.CONFIG_VERSION,
1493 1453
      "os_api_version": constants.OS_API_VERSION,
1494 1454
      "export_version": constants.EXPORT_VERSION,
1495
      "master": self.cfg.GetMaster(),
1455
      "master": self.sstore.GetMasterNode(),
1496 1456
      "architecture": (platform.architecture()[0], platform.machine()),
1497 1457
      "instances": [(instance.name, instance.primary_node)
1498 1458
                    for instance in instances],
......
1748 1708
      "INSTANCE_SECONDARIES": " ".join(self.instance.secondary_nodes),
1749 1709
      "FORCE": self.op.force,
1750 1710
      }
1751
    nl = ([self.cfg.GetMaster(), self.instance.primary_node] +
1711
    nl = ([self.sstore.GetMasterNode(), self.instance.primary_node] +
1752 1712
          list(self.instance.secondary_nodes))
1753 1713
    return env, nl, nl
1754 1714

  
......
1833 1793
      "INSTANCE_PRIMARY": self.instance.primary_node,
1834 1794
      "INSTANCE_SECONDARIES": " ".join(self.instance.secondary_nodes),
1835 1795
      }
1836
    nl = ([self.cfg.GetMaster(), self.instance.primary_node] +
1796
    nl = ([self.sstore.GetMasterNode(), self.instance.primary_node] +
1837 1797
          list(self.instance.secondary_nodes))
1838 1798
    return env, nl, nl
1839 1799

  
......
1882 1842
      "INSTANCE_PRIMARY": self.instance.primary_node,
1883 1843
      "INSTANCE_SECONDARIES": " ".join(self.instance.secondary_nodes),
1884 1844
      }
1885
    nl = ([self.cfg.GetMaster(), self.instance.primary_node] +
1845
    nl = ([self.sstore.GetMasterNode(), self.instance.primary_node] +
1886 1846
          list(self.instance.secondary_nodes))
1887 1847
    return env, nl, nl
1888 1848

  
......
2044 2004
      "INSTANCE_SECONDARIES": " ".join(self.instance.secondary_nodes),
2045 2005
      "IGNORE_CONSISTENCY": self.op.ignore_consistency,
2046 2006
      }
2047
    nl = [self.cfg.GetMaster()] + list(self.instance.secondary_nodes)
2007
    nl = [self.sstore.GetMasterNode()] + list(self.instance.secondary_nodes)
2048 2008
    return env, nl, nl
2049 2009

  
2050 2010
  def CheckPrereq(self):
......
2367 2327
    if self.inst_ip:
2368 2328
      env["INSTANCE_IP"] = self.inst_ip
2369 2329

  
2370
    nl = ([self.cfg.GetMaster(), self.op.pnode] +
2330
    nl = ([self.sstore.GetMasterNode(), self.op.pnode] +
2371 2331
          self.secondaries)
2372 2332
    return env, nl, nl
2373 2333

  
......
2675 2635
      "NEW_SECONDARY": self.op.remote_node,
2676 2636
      "DISK_NAME": self.op.disk_name,
2677 2637
      }
2678
    nl = [self.cfg.GetMaster(), self.instance.primary_node,
2638
    nl = [self.sstore.GetMasterNode(), self.instance.primary_node,
2679 2639
          self.op.remote_node,] + list(self.instance.secondary_nodes)
2680 2640
    return env, nl, nl
2681 2641

  
......
2787 2747
      "DISK_ID": self.op.disk_id,
2788 2748
      "OLD_SECONDARY": self.old_secondary,
2789 2749
      }
2790
    nl = [self.cfg.GetMaster(),
2750
    nl = [self.sstore.GetMasterNode(),
2791 2751
          self.instance.primary_node] + list(self.instance.secondary_nodes)
2792 2752
    return env, nl, nl
2793 2753

  
......
2872 2832
      "NEW_SECONDARY": self.op.remote_node,
2873 2833
      "OLD_SECONDARY": self.instance.secondary_nodes[0],
2874 2834
      }
2875
    nl = [self.cfg.GetMaster(),
2835
    nl = [self.sstore.GetMasterNode(),
2876 2836
          self.instance.primary_node] + list(self.instance.secondary_nodes)
2877 2837
    return env, nl, nl
2878 2838

  
......
2920 2880
    # start of work
2921 2881
    remote_node = self.op.remote_node
2922 2882
    cfg = self.cfg
2883
    vgname = cfg.GetVGName()
2923 2884
    for dev in instance.disks:
2924 2885
      size = dev.size
2925
      new_drbd = _GenerateMDDRBDBranch(cfg, self.cfg.GetVGName(),
2926
                                       instance.primary_node, remote_node, size,
2886
      new_drbd = _GenerateMDDRBDBranch(cfg, vgname, instance.primary_node,
2887
                                       remote_node, size,
2927 2888
                                       "%s-%s" % (instance.name, dev.iv_name))
2928 2889
      iv_names[dev.iv_name] = (dev, dev.children[0], new_drbd)
2929 2890
      logger.Info("adding new mirror component on secondary for %s" %
......
2948 2909
      # call the primary node to add the mirror to md
2949 2910
      logger.Info("adding new mirror component to md")
2950 2911
      if not rpc.call_blockdev_addchild(instance.primary_node, dev,
2951
                                             new_drbd):
2912
                                        new_drbd):
2952 2913
        logger.Error("Can't add mirror compoment to md!")
2953 2914
        cfg.SetDiskID(new_drbd, remote_node)
2954 2915
        if not rpc.call_blockdev_remove(remote_node, new_drbd):
......
3172 3133
    if self.bridge:
3173 3134
      env["BRIDGE"] = self.bridge
3174 3135

  
3175
    nl = [self.cfg.GetMaster(),
3136
    nl = [self.sstore.GetMasterNode(),
3176 3137
          self.instance.primary_node] + list(self.instance.secondary_nodes)
3177 3138

  
3178 3139
    return env, nl, nl
......
3295 3256
      "EXPORT_NODE": self.op.target_node,
3296 3257
      "EXPORT_DO_SHUTDOWN": self.op.shutdown,
3297 3258
      }
3298
    nl = [self.cfg.GetMaster(), self.instance.primary_node,
3259
    nl = [self.sstore.GetMasterNode(), self.instance.primary_node,
3299 3260
          self.op.target_node]
3300 3261
    return env, nl, nl
3301 3262

  
b/lib/config.py
516 516
    self._OpenConfig()
517 517
    self._ReleaseLock()
518 518
    return self._config_data.cluster.mac_prefix
519

  
520
  def GetMaster(self):
521
    """Get the name of the master.
522

  
523
    """
524
    self._OpenConfig()
525
    self._ReleaseLock()
526
    return self._config_data.cluster.master_node
527

  
528
  def SetMaster(self, master_node):
529
    """Change the master of the cluster.
530

  
531
    As with all changes, the configuration data will be distributed to
532
    all nodes.
533

  
534
    This function is used for manual master failover.
535

  
536
    """
537
    self._OpenConfig()
538
    self._config_data.cluster.master_node = master_node
539
    self._WriteConfig()
540
    self._ReleaseLock()
b/lib/constants.py
32 32
# file paths
33 33
DATA_DIR = "/var/lib/ganeti"
34 34
CLUSTER_CONF_FILE = DATA_DIR + "/config.data"
35
CLUSTER_NAME_FILE = DATA_DIR + "/cluster-name"
36 35
SSL_CERT_FILE = DATA_DIR + "/server.pem"
37 36
HYPERCONF_FILE = DATA_DIR + "/hypervisor"
38 37
WATCHER_STATEFILE = DATA_DIR + "/restart_state"
......
46 45
DEFAULT_NODED_PORT = 1811
47 46
FIRST_DRBD_PORT = 11000
48 47
LAST_DRBD_PORT = 14999
49
MASTER_INITD_SCRIPT = "/etc/init.d/ganeti-master"
50
MASTER_INITD_NAME = "ganeti-master"
48
MASTER_SCRIPT = "ganeti-master"
51 49

  
52 50
LOG_DIR = "/var/log/ganeti"
53 51
LOG_OS_DIR = LOG_DIR + "/os"
......
101 99
                      MASTER_CRON_FILE,
102 100
                      ]
103 101

  
104
MASTER_CONFIGFILES = [MASTER_CRON_LINK,
105
                      "/etc/rc2.d/S21%s" % MASTER_INITD_NAME]
102
MASTER_CONFIGFILES = [MASTER_CRON_LINK,]
106 103

  
107 104
NODE_CONFIGFILES = [NODE_INITD_SCRIPT,
108 105
                    "/etc/rc2.d/S20%s" % NODE_INITD_NAME,
b/lib/mcpu.py
115 115
    lu.CheckPrereq()
116 116
    do_hooks = lu_class.HPATH is not None
117 117
    if do_hooks:
118
      hm = HooksMaster(rpc.call_hooks_runner, self.cfg, lu)
118
      hm = HooksMaster(rpc.call_hooks_runner, self.cfg, self.sstore, lu)
119 119
      hm.RunPhase(constants.HOOKS_PHASE_PRE)
120 120
    result = lu.Exec(feedback_fn)
121 121
    if do_hooks:
......
148 148
    lu = lu_class(self, op, self.cfg, self.sstore)
149 149
    lu.CheckPrereq()
150 150
    #if do_hooks:
151
    #  hm = HooksMaster(rpc.call_hooks_runner, self.cfg, lu)
151
    #  hm = HooksMaster(rpc.call_hooks_runner, self.cfg, self.sstore, lu)
152 152
    #  hm.RunPhase(constants.HOOKS_PHASE_PRE)
153 153
    result = lu.Exec(feedback_fn)
154 154
    #if do_hooks:
......
168 168
  which behaves the same works.
169 169

  
170 170
  """
171
  def __init__(self, callfn, cfg, lu):
171
  def __init__(self, callfn, cfg, sstore, lu):
172 172
    self.callfn = callfn
173 173
    self.cfg = cfg
174
    self.sstore = sstore
174 175
    self.lu = lu
175 176
    self.op = lu.op
176 177
    self.hpath = self.lu.HPATH
......
200 201

  
201 202
    if self.cfg is not None:
202 203
      env["GANETI_CLUSTER"] = self.cfg.GetClusterName()
203
      env["GANETI_MASTER"] = self.cfg.GetMaster()
204
    if self.sstore is not None:
205
      env["GANETI_MASTER"] = self.sstore.GetMasterNode()
204 206

  
205 207
    for key in env:
206 208
      if not isinstance(env[key], str):
b/lib/opcodes.py
53 53
  """Initialise the cluster."""
54 54
  OP_ID = "OP_CLUSTER_INIT"
55 55
  __slots__ = ["cluster_name", "secondary_ip", "hypervisor_type",
56
               "vg_name", "mac_prefix", "def_bridge"]
56
               "vg_name", "mac_prefix", "def_bridge", "master_netdev"]
57 57

  
58 58

  
59 59
class OpDestroyCluster(OpCode):
b/lib/ssconf.py
57 57
  _SS_FILEPREFIX = "ssconf_"
58 58
  SS_HYPERVISOR = "hypervisor"
59 59
  SS_NODED_PASS = "node_pass"
60
  _VALID_KEYS = (SS_HYPERVISOR, SS_NODED_PASS,)
60
  SS_MASTER_NODE = "master_node"
61
  SS_MASTER_IP = "master_ip"
62
  SS_MASTER_NETDEV = "master_netdev"
63
  _VALID_KEYS = (SS_HYPERVISOR, SS_NODED_PASS, SS_MASTER_NODE, SS_MASTER_IP,
64
                 SS_MASTER_NETDEV)
61 65
  _MAX_SIZE = 4096
62 66

  
63 67
  def __init__(self, cfg_location=None):
......
128 132
    """
129 133
    return self._ReadFile(self.SS_NODED_PASS)
130 134

  
135
  def GetMasterNode(self):
136
    """Get the hostname of the master node for this cluster.
137

  
138
    """
139
    return self._ReadFile(self.SS_MASTER_NODE)
140

  
141
  def GetMasterIP(self):
142
    """Get the IP of the master node for this cluster.
143

  
144
    """
145
    return self._ReadFile(self.SS_MASTER_IP)
146

  
147
  def GetMasterNetdev(self):
148
    """Get the netdev to which we'll add the master ip.
149

  
150
    """
151
    return self._ReadFile(self.SS_MASTER_NETDEV)
152

  
131 153
  def SetKey(self, key, value):
132 154
    """Set the value of a key.
133 155

  
b/scripts/gnt-cluster
40 40
                             hypervisor_type=opts.hypervisor_type,
41 41
                             vg_name=opts.vg_name,
42 42
                             mac_prefix=opts.mac_prefix,
43
                             def_bridge=opts.def_bridge)
43
                             def_bridge=opts.def_bridge,
44
                             master_netdev=opts.master_netdev)
44 45
  SubmitOpCode(op)
45 46
  return 0
46 47

  
......
210 211
                        " to connect the instances to [xen-br0]",
211 212
                        metavar="BRIDGE",
212 213
                        default="xen-br0",),
214
            make_option("--master-netdev", dest="master_netdev",
215
                        help="Specify the node interface (cluster-wide)"
216
                        " on which the master IP address will be added "
217
                        " [xen-br0]",
218
                        metavar="NETDEV",
219
                        default="xen-br0",),
213 220
            ],
214 221
           "[opts...] <cluster_name>",
215 222
           "Initialises a new cluster configuration"),
/dev/null
1
#!/usr/bin/python
2
#
3

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

  
21

  
22
"""Module implementing a fake ConfigWriter"""
23

  
24
import socket
25

  
26
class FakeConfig:
27
    """Fake configuration object"""
28

  
29
    def IsCluster(self):
30
        return True
31

  
32
    def GetClusterName(self):
33
        return "test.cluster"
34

  
35
    def GetNodeList(self):
36
        return ["a", "b", "c"]
37

  
38
    def GetMaster(self):
39
        return socket.gethostname()
b/testing/ganeti.hooks_unittest.py
36 36
from ganeti import cmdlib
37 37
from ganeti.constants import HKR_SUCCESS, HKR_FAIL, HKR_SKIP
38 38

  
39
from fake_config import FakeConfig
39
from mocks import FakeConfig, FakeSStore
40 40

  
41 41
class FakeLU(cmdlib.LogicalUnit):
42 42
  HPATH = "test"
......
228 228
  def testTotalFalse(self):
229 229
    """Test complete rpc failure"""
230 230
    cfg = FakeConfig()
231
    sstore = FakeSStore()
231 232
    op = opcodes.OpCode()
232
    lu = FakeLU(None, op, cfg, None)
233
    hm = mcpu.HooksMaster(self._call_false, cfg, lu)
233
    lu = FakeLU(None, op, cfg, sstore)
234
    hm = mcpu.HooksMaster(self._call_false, cfg, sstore, lu)
234 235
    self.failUnlessRaises(errors.HooksFailure,
235 236
                          hm.RunPhase, constants.HOOKS_PHASE_PRE)
236 237
    hm.RunPhase(constants.HOOKS_PHASE_POST)
......
238 239
  def testIndividualFalse(self):
239 240
    """Test individual rpc failure"""
240 241
    cfg = FakeConfig()
242
    sstore = FakeSStore()
241 243
    op = opcodes.OpCode()
242
    lu = FakeLU(None, op, cfg, None)
243
    hm = mcpu.HooksMaster(self._call_nodes_false, cfg, lu)
244
    lu = FakeLU(None, op, cfg, sstore)
245
    hm = mcpu.HooksMaster(self._call_nodes_false, cfg, sstore, lu)
244 246
    self.failUnlessRaises(errors.HooksFailure,
245 247
                          hm.RunPhase, constants.HOOKS_PHASE_PRE)
246 248
    hm.RunPhase(constants.HOOKS_PHASE_POST)
......
249 251
    """Test individual rpc failure"""
250 252
    cfg = FakeConfig()
251 253
    op = opcodes.OpCode()
252
    lu = FakeLU(None, op, cfg, None)
253
    hm = mcpu.HooksMaster(self._call_script_fail, cfg, lu)
254
    sstore = FakeSStore()
255
    lu = FakeLU(None, op, cfg, sstore)
256
    hm = mcpu.HooksMaster(self._call_script_fail, cfg, sstore, lu)
254 257
    self.failUnlessRaises(errors.HooksAbort,
255 258
                          hm.RunPhase, constants.HOOKS_PHASE_PRE)
256 259
    hm.RunPhase(constants.HOOKS_PHASE_POST)
......
259 262
    """Test individual rpc failure"""
260 263
    cfg = FakeConfig()
261 264
    op = opcodes.OpCode()
262
    lu = FakeLU(None, op, cfg, None)
263
    hm = mcpu.HooksMaster(self._call_script_succeed, cfg, lu)
265
    sstore = FakeSStore()
266
    lu = FakeLU(None, op, cfg, sstore)
267
    hm = mcpu.HooksMaster(self._call_script_succeed, cfg, sstore, lu)
264 268
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
265 269
      hm.RunPhase(phase)
266 270

  
b/testing/mocks.py
1
#!/usr/bin/python
2
#
3

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

  
21

  
22
"""Module implementing a fake ConfigWriter"""
23

  
24
import socket
25

  
26
class FakeConfig:
27
    """Fake configuration object"""
28

  
29
    def IsCluster(self):
30
        return True
31

  
32
    def GetClusterName(self):
33
        return "test.cluster"
34

  
35
    def GetNodeList(self):
36
        return ["a", "b", "c"]
37

  
38
    def GetMaster(self):
39
        return socket.gethostname()
40

  
41

  
42
class FakeSStore:
43
    """Fake simplestore object"""
44

  
45
    def GetMasterNode(self):
46
        return socket.gethostname()

Also available in: Unified diff