4 # Copyright (C) 2006, 2007, 2008 Google Inc.
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.
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.
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
22 """Functions to bootstrap a new cluster.
31 from ganeti import rpc
32 from ganeti import ssh
33 from ganeti import utils
34 from ganeti import errors
35 from ganeti import config
36 from ganeti import constants
37 from ganeti import ssconf
40 def _InitSSHSetup(node):
41 """Setup the SSH configuration for the cluster.
44 This generates a dsa keypair for root, adds the pub key to the
45 permitted hosts and adds the hostkey to its own known hosts.
48 node: the name of this host as a fqdn
51 priv_key, pub_key, auth_keys = ssh.GetUserFiles(constants.GANETI_RUNAS)
53 for name in priv_key, pub_key:
54 if os.path.exists(name):
55 utils.CreateBackup(name)
56 utils.RemoveFile(name)
58 result = utils.RunCmd(["ssh-keygen", "-t", "dsa",
62 raise errors.OpExecError("Could not generate ssh keypair, error %s" %
65 f = open(pub_key, 'r')
67 utils.AddAuthorizedKey(auth_keys, f.read(8192))
72 def _InitGanetiServerSetup(ss):
73 """Setup the necessary configuration for the initial node daemon.
75 This creates the nodepass file containing the shared password for
76 the cluster and also generates the SSL certificate.
79 # Create pseudo random password
80 randpass = sha.new(os.urandom(64)).hexdigest()
81 # and write it into sstore
82 ss.SetKey(ss.SS_NODED_PASS, randpass)
84 result = utils.RunCmd(["openssl", "req", "-new", "-newkey", "rsa:1024",
85 "-days", str(365*5), "-nodes", "-x509",
86 "-keyout", constants.SSL_CERT_FILE,
87 "-out", constants.SSL_CERT_FILE, "-batch"])
89 raise errors.OpExecError("could not generate server ssl cert, command"
90 " %s had exitcode %s and error message %s" %
91 (result.cmd, result.exit_code, result.output))
93 os.chmod(constants.SSL_CERT_FILE, 0400)
95 result = utils.RunCmd([constants.NODE_INITD_SCRIPT, "restart"])
98 raise errors.OpExecError("Could not start the node daemon, command %s"
99 " had exitcode %s and error %s" %
100 (result.cmd, result.exit_code, result.output))
103 def InitCluster(cluster_name, hypervisor_type, mac_prefix, def_bridge,
104 master_netdev, file_storage_dir,
107 """Initialise the cluster.
110 if config.ConfigWriter.IsCluster():
111 raise errors.OpPrereqError("Cluster is already initialised")
113 if hypervisor_type == constants.HT_XEN_HVM31:
114 if not os.path.exists(constants.VNC_PASSWORD_FILE):
115 raise errors.OpPrereqError("Please prepare the cluster VNC"
117 constants.VNC_PASSWORD_FILE)
119 hostname = utils.HostInfo()
121 if hostname.ip.startswith("127."):
122 raise errors.OpPrereqError("This host's IP resolves to the private"
123 " range (%s). Please fix DNS or %s." %
124 (hostname.ip, constants.ETC_HOSTS))
126 if not utils.TcpPing(hostname.ip, constants.DEFAULT_NODED_PORT,
127 source=constants.LOCALHOST_IP_ADDRESS):
128 raise errors.OpPrereqError("Inconsistency: this host's name resolves"
129 " to %s,\nbut this ip address does not"
130 " belong to this host."
131 " Aborting." % hostname.ip)
133 clustername = utils.HostInfo(cluster_name)
135 if utils.TcpPing(clustername.ip, constants.DEFAULT_NODED_PORT,
137 raise errors.OpPrereqError("Cluster IP already active. Aborting.")
140 if not utils.IsValidIP(secondary_ip):
141 raise errors.OpPrereqError("Invalid secondary ip given")
142 if (secondary_ip != hostname.ip and
143 (not utils.TcpPing(secondary_ip, constants.DEFAULT_NODED_PORT,
144 source=constants.LOCALHOST_IP_ADDRESS))):
145 raise errors.OpPrereqError("You gave %s as secondary IP,"
146 " but it does not belong to this host." %
149 if vg_name is not None:
150 # Check if volume group is valid
151 vgstatus = utils.CheckVolumeGroupSize(utils.ListVolumeGroups(), vg_name,
152 constants.MIN_VG_SIZE)
154 raise errors.OpPrereqError("Error: %s\nspecify --no-lvm-storage if"
155 " you are not using lvm" % vgstatus)
157 file_storage_dir = os.path.normpath(file_storage_dir)
159 if not os.path.isabs(file_storage_dir):
160 raise errors.OpPrereqError("The file storage directory you passed is"
161 " not an absolute path.")
163 if not os.path.exists(file_storage_dir):
165 os.makedirs(file_storage_dir, 0750)
167 raise errors.OpPrereqError("Cannot create file storage directory"
169 (file_storage_dir, err))
171 if not os.path.isdir(file_storage_dir):
172 raise errors.OpPrereqError("The file storage directory '%s' is not"
173 " a directory." % file_storage_dir)
175 if not re.match("^[0-9a-z]{2}:[0-9a-z]{2}:[0-9a-z]{2}$", mac_prefix):
176 raise errors.OpPrereqError("Invalid mac prefix given '%s'" % mac_prefix)
178 if hypervisor_type not in constants.HYPER_TYPES:
179 raise errors.OpPrereqError("Invalid hypervisor type given '%s'" %
182 result = utils.RunCmd(["ip", "link", "show", "dev", master_netdev])
184 raise errors.OpPrereqError("Invalid master netdev given (%s): '%s'" %
186 result.output.strip()))
188 if not (os.path.isfile(constants.NODE_INITD_SCRIPT) and
189 os.access(constants.NODE_INITD_SCRIPT, os.X_OK)):
190 raise errors.OpPrereqError("Init.d script '%s' missing or not"
191 " executable." % constants.NODE_INITD_SCRIPT)
193 # set up the simple store
194 ss = ssconf.SimpleStore()
195 ss.SetKey(ss.SS_HYPERVISOR, hypervisor_type)
196 ss.SetKey(ss.SS_MASTER_NODE, hostname.name)
197 ss.SetKey(ss.SS_MASTER_IP, clustername.ip)
198 ss.SetKey(ss.SS_MASTER_NETDEV, master_netdev)
199 ss.SetKey(ss.SS_CLUSTER_NAME, clustername.name)
200 ss.SetKey(ss.SS_FILE_STORAGE_DIR, file_storage_dir)
201 ss.SetKey(ss.SS_CONFIG_VERSION, constants.CONFIG_VERSION)
203 # set up the inter-node password and certificate
204 _InitGanetiServerSetup(ss)
206 # start the master ip
207 # TODO: Review rpc call from bootstrap
208 rpc.call_node_start_master(hostname.name)
210 # set up ssh config and /etc/hosts
211 f = open(constants.SSH_HOST_RSA_PUB, 'r')
216 sshkey = sshline.split(" ")[1]
218 utils.AddHostToEtcHosts(hostname.name)
219 _InitSSHSetup(hostname.name)
221 # init of cluster config file
222 cfg = config.ConfigWriter()
223 cfg.InitConfig(hostname.name, hostname.ip, secondary_ip, sshkey,
224 mac_prefix, vg_name, def_bridge)
226 ssh.WriteKnownHostsFile(cfg, ss, constants.SSH_KNOWN_HOSTS_FILE)