Statistics
| Branch: | Tag: | Revision:

root / lib / bootstrap.py @ 8b3fd458

History | View | Annotate | Download (13.1 kB)

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

24 a0c9f010 Michael Hanselmann
"""
25 a0c9f010 Michael Hanselmann
26 a0c9f010 Michael Hanselmann
import os
27 a0c9f010 Michael Hanselmann
import os.path
28 a0c9f010 Michael Hanselmann
import sha
29 a0c9f010 Michael Hanselmann
import re
30 b1b6ea87 Iustin Pop
import logging
31 a0c9f010 Michael Hanselmann
32 a0c9f010 Michael Hanselmann
from ganeti import rpc
33 a0c9f010 Michael Hanselmann
from ganeti import ssh
34 a0c9f010 Michael Hanselmann
from ganeti import utils
35 a0c9f010 Michael Hanselmann
from ganeti import errors
36 a0c9f010 Michael Hanselmann
from ganeti import config
37 a0c9f010 Michael Hanselmann
from ganeti import constants
38 b9eeeb02 Michael Hanselmann
from ganeti import objects
39 a0c9f010 Michael Hanselmann
from ganeti import ssconf
40 a0c9f010 Michael Hanselmann
41 72737a7f Iustin Pop
from ganeti.rpc import RpcRunner
42 a0c9f010 Michael Hanselmann
43 a0c9f010 Michael Hanselmann
def _InitSSHSetup(node):
44 a0c9f010 Michael Hanselmann
  """Setup the SSH configuration for the cluster.
45 a0c9f010 Michael Hanselmann

46 a0c9f010 Michael Hanselmann

47 a0c9f010 Michael Hanselmann
  This generates a dsa keypair for root, adds the pub key to the
48 a0c9f010 Michael Hanselmann
  permitted hosts and adds the hostkey to its own known hosts.
49 a0c9f010 Michael Hanselmann

50 a0c9f010 Michael Hanselmann
  Args:
51 a0c9f010 Michael Hanselmann
    node: the name of this host as a fqdn
52 a0c9f010 Michael Hanselmann

53 a0c9f010 Michael Hanselmann
  """
54 a0c9f010 Michael Hanselmann
  priv_key, pub_key, auth_keys = ssh.GetUserFiles(constants.GANETI_RUNAS)
55 a0c9f010 Michael Hanselmann
56 a0c9f010 Michael Hanselmann
  for name in priv_key, pub_key:
57 a0c9f010 Michael Hanselmann
    if os.path.exists(name):
58 a0c9f010 Michael Hanselmann
      utils.CreateBackup(name)
59 a0c9f010 Michael Hanselmann
    utils.RemoveFile(name)
60 a0c9f010 Michael Hanselmann
61 a0c9f010 Michael Hanselmann
  result = utils.RunCmd(["ssh-keygen", "-t", "dsa",
62 a0c9f010 Michael Hanselmann
                         "-f", priv_key,
63 a0c9f010 Michael Hanselmann
                         "-q", "-N", ""])
64 a0c9f010 Michael Hanselmann
  if result.failed:
65 a0c9f010 Michael Hanselmann
    raise errors.OpExecError("Could not generate ssh keypair, error %s" %
66 a0c9f010 Michael Hanselmann
                             result.output)
67 a0c9f010 Michael Hanselmann
68 a0c9f010 Michael Hanselmann
  f = open(pub_key, 'r')
69 a0c9f010 Michael Hanselmann
  try:
70 a0c9f010 Michael Hanselmann
    utils.AddAuthorizedKey(auth_keys, f.read(8192))
71 a0c9f010 Michael Hanselmann
  finally:
72 a0c9f010 Michael Hanselmann
    f.close()
73 a0c9f010 Michael Hanselmann
74 a0c9f010 Michael Hanselmann
75 d23ef431 Michael Hanselmann
def _InitGanetiServerSetup():
76 a0c9f010 Michael Hanselmann
  """Setup the necessary configuration for the initial node daemon.
77 a0c9f010 Michael Hanselmann

78 a0c9f010 Michael Hanselmann
  This creates the nodepass file containing the shared password for
79 a0c9f010 Michael Hanselmann
  the cluster and also generates the SSL certificate.
80 a0c9f010 Michael Hanselmann

81 a0c9f010 Michael Hanselmann
  """
82 a0c9f010 Michael Hanselmann
  # Create pseudo random password
83 33081d90 Iustin Pop
  randpass = utils.GenerateSecret()
84 d23ef431 Michael Hanselmann
85 d23ef431 Michael Hanselmann
  # and write it into the config file
86 d23ef431 Michael Hanselmann
  utils.WriteFile(constants.CLUSTER_PASSWORD_FILE,
87 d23ef431 Michael Hanselmann
                  data="%s\n" % randpass, mode=0400)
88 a0c9f010 Michael Hanselmann
89 a0c9f010 Michael Hanselmann
  result = utils.RunCmd(["openssl", "req", "-new", "-newkey", "rsa:1024",
90 a0c9f010 Michael Hanselmann
                         "-days", str(365*5), "-nodes", "-x509",
91 a0c9f010 Michael Hanselmann
                         "-keyout", constants.SSL_CERT_FILE,
92 a0c9f010 Michael Hanselmann
                         "-out", constants.SSL_CERT_FILE, "-batch"])
93 a0c9f010 Michael Hanselmann
  if result.failed:
94 a0c9f010 Michael Hanselmann
    raise errors.OpExecError("could not generate server ssl cert, command"
95 a0c9f010 Michael Hanselmann
                             " %s had exitcode %s and error message %s" %
96 a0c9f010 Michael Hanselmann
                             (result.cmd, result.exit_code, result.output))
97 a0c9f010 Michael Hanselmann
98 a0c9f010 Michael Hanselmann
  os.chmod(constants.SSL_CERT_FILE, 0400)
99 a0c9f010 Michael Hanselmann
100 a0c9f010 Michael Hanselmann
  result = utils.RunCmd([constants.NODE_INITD_SCRIPT, "restart"])
101 a0c9f010 Michael Hanselmann
102 a0c9f010 Michael Hanselmann
  if result.failed:
103 a0c9f010 Michael Hanselmann
    raise errors.OpExecError("Could not start the node daemon, command %s"
104 a0c9f010 Michael Hanselmann
                             " had exitcode %s and error %s" %
105 a0c9f010 Michael Hanselmann
                             (result.cmd, result.exit_code, result.output))
106 a0c9f010 Michael Hanselmann
107 a0c9f010 Michael Hanselmann
108 a0c9f010 Michael Hanselmann
def InitCluster(cluster_name, hypervisor_type, mac_prefix, def_bridge,
109 a0c9f010 Michael Hanselmann
                master_netdev, file_storage_dir,
110 a0c9f010 Michael Hanselmann
                secondary_ip=None,
111 a0c9f010 Michael Hanselmann
                vg_name=None):
112 a0c9f010 Michael Hanselmann
  """Initialise the cluster.
113 a0c9f010 Michael Hanselmann

114 a0c9f010 Michael Hanselmann
  """
115 a0c9f010 Michael Hanselmann
  if config.ConfigWriter.IsCluster():
116 a0c9f010 Michael Hanselmann
    raise errors.OpPrereqError("Cluster is already initialised")
117 a0c9f010 Michael Hanselmann
118 00cd937c Iustin Pop
  if hypervisor_type == constants.HT_XEN_HVM:
119 a0c9f010 Michael Hanselmann
    if not os.path.exists(constants.VNC_PASSWORD_FILE):
120 a0c9f010 Michael Hanselmann
      raise errors.OpPrereqError("Please prepare the cluster VNC"
121 a0c9f010 Michael Hanselmann
                                 "password file %s" %
122 a0c9f010 Michael Hanselmann
                                 constants.VNC_PASSWORD_FILE)
123 a0c9f010 Michael Hanselmann
124 a0c9f010 Michael Hanselmann
  hostname = utils.HostInfo()
125 a0c9f010 Michael Hanselmann
126 a0c9f010 Michael Hanselmann
  if hostname.ip.startswith("127."):
127 a0c9f010 Michael Hanselmann
    raise errors.OpPrereqError("This host's IP resolves to the private"
128 a0c9f010 Michael Hanselmann
                               " range (%s). Please fix DNS or %s." %
129 a0c9f010 Michael Hanselmann
                               (hostname.ip, constants.ETC_HOSTS))
130 a0c9f010 Michael Hanselmann
131 caad16e2 Iustin Pop
  if not utils.OwnIpAddress(hostname.ip):
132 a0c9f010 Michael Hanselmann
    raise errors.OpPrereqError("Inconsistency: this host's name resolves"
133 a0c9f010 Michael Hanselmann
                               " to %s,\nbut this ip address does not"
134 a0c9f010 Michael Hanselmann
                               " belong to this host."
135 a0c9f010 Michael Hanselmann
                               " Aborting." % hostname.ip)
136 a0c9f010 Michael Hanselmann
137 a0c9f010 Michael Hanselmann
  clustername = utils.HostInfo(cluster_name)
138 a0c9f010 Michael Hanselmann
139 a0c9f010 Michael Hanselmann
  if utils.TcpPing(clustername.ip, constants.DEFAULT_NODED_PORT,
140 a0c9f010 Michael Hanselmann
                   timeout=5):
141 a0c9f010 Michael Hanselmann
    raise errors.OpPrereqError("Cluster IP already active. Aborting.")
142 a0c9f010 Michael Hanselmann
143 a0c9f010 Michael Hanselmann
  if secondary_ip:
144 a0c9f010 Michael Hanselmann
    if not utils.IsValidIP(secondary_ip):
145 a0c9f010 Michael Hanselmann
      raise errors.OpPrereqError("Invalid secondary ip given")
146 a0c9f010 Michael Hanselmann
    if (secondary_ip != hostname.ip and
147 caad16e2 Iustin Pop
        not utils.OwnIpAddress(secondary_ip)):
148 a0c9f010 Michael Hanselmann
      raise errors.OpPrereqError("You gave %s as secondary IP,"
149 a0c9f010 Michael Hanselmann
                                 " but it does not belong to this host." %
150 a0c9f010 Michael Hanselmann
                                 secondary_ip)
151 b9eeeb02 Michael Hanselmann
  else:
152 b9eeeb02 Michael Hanselmann
    secondary_ip = hostname.ip
153 a0c9f010 Michael Hanselmann
154 a0c9f010 Michael Hanselmann
  if vg_name is not None:
155 a0c9f010 Michael Hanselmann
    # Check if volume group is valid
156 a0c9f010 Michael Hanselmann
    vgstatus = utils.CheckVolumeGroupSize(utils.ListVolumeGroups(), vg_name,
157 a0c9f010 Michael Hanselmann
                                          constants.MIN_VG_SIZE)
158 a0c9f010 Michael Hanselmann
    if vgstatus:
159 a0c9f010 Michael Hanselmann
      raise errors.OpPrereqError("Error: %s\nspecify --no-lvm-storage if"
160 a0c9f010 Michael Hanselmann
                                 " you are not using lvm" % vgstatus)
161 a0c9f010 Michael Hanselmann
162 a0c9f010 Michael Hanselmann
  file_storage_dir = os.path.normpath(file_storage_dir)
163 a0c9f010 Michael Hanselmann
164 a0c9f010 Michael Hanselmann
  if not os.path.isabs(file_storage_dir):
165 a0c9f010 Michael Hanselmann
    raise errors.OpPrereqError("The file storage directory you passed is"
166 a0c9f010 Michael Hanselmann
                               " not an absolute path.")
167 a0c9f010 Michael Hanselmann
168 a0c9f010 Michael Hanselmann
  if not os.path.exists(file_storage_dir):
169 a0c9f010 Michael Hanselmann
    try:
170 a0c9f010 Michael Hanselmann
      os.makedirs(file_storage_dir, 0750)
171 a0c9f010 Michael Hanselmann
    except OSError, err:
172 a0c9f010 Michael Hanselmann
      raise errors.OpPrereqError("Cannot create file storage directory"
173 a0c9f010 Michael Hanselmann
                                 " '%s': %s" %
174 a0c9f010 Michael Hanselmann
                                 (file_storage_dir, err))
175 a0c9f010 Michael Hanselmann
176 a0c9f010 Michael Hanselmann
  if not os.path.isdir(file_storage_dir):
177 a0c9f010 Michael Hanselmann
    raise errors.OpPrereqError("The file storage directory '%s' is not"
178 a0c9f010 Michael Hanselmann
                               " a directory." % file_storage_dir)
179 a0c9f010 Michael Hanselmann
180 a0c9f010 Michael Hanselmann
  if not re.match("^[0-9a-z]{2}:[0-9a-z]{2}:[0-9a-z]{2}$", mac_prefix):
181 a0c9f010 Michael Hanselmann
    raise errors.OpPrereqError("Invalid mac prefix given '%s'" % mac_prefix)
182 a0c9f010 Michael Hanselmann
183 a0c9f010 Michael Hanselmann
  if hypervisor_type not in constants.HYPER_TYPES:
184 a0c9f010 Michael Hanselmann
    raise errors.OpPrereqError("Invalid hypervisor type given '%s'" %
185 a0c9f010 Michael Hanselmann
                               hypervisor_type)
186 a0c9f010 Michael Hanselmann
187 a0c9f010 Michael Hanselmann
  result = utils.RunCmd(["ip", "link", "show", "dev", master_netdev])
188 a0c9f010 Michael Hanselmann
  if result.failed:
189 a0c9f010 Michael Hanselmann
    raise errors.OpPrereqError("Invalid master netdev given (%s): '%s'" %
190 a0c9f010 Michael Hanselmann
                               (master_netdev,
191 a0c9f010 Michael Hanselmann
                                result.output.strip()))
192 a0c9f010 Michael Hanselmann
193 a0c9f010 Michael Hanselmann
  if not (os.path.isfile(constants.NODE_INITD_SCRIPT) and
194 a0c9f010 Michael Hanselmann
          os.access(constants.NODE_INITD_SCRIPT, os.X_OK)):
195 a0c9f010 Michael Hanselmann
    raise errors.OpPrereqError("Init.d script '%s' missing or not"
196 a0c9f010 Michael Hanselmann
                               " executable." % constants.NODE_INITD_SCRIPT)
197 a0c9f010 Michael Hanselmann
198 a0c9f010 Michael Hanselmann
  # set up the inter-node password and certificate
199 d23ef431 Michael Hanselmann
  _InitGanetiServerSetup()
200 a0c9f010 Michael Hanselmann
201 a0c9f010 Michael Hanselmann
  # set up ssh config and /etc/hosts
202 a0c9f010 Michael Hanselmann
  f = open(constants.SSH_HOST_RSA_PUB, 'r')
203 a0c9f010 Michael Hanselmann
  try:
204 a0c9f010 Michael Hanselmann
    sshline = f.read()
205 a0c9f010 Michael Hanselmann
  finally:
206 a0c9f010 Michael Hanselmann
    f.close()
207 a0c9f010 Michael Hanselmann
  sshkey = sshline.split(" ")[1]
208 a0c9f010 Michael Hanselmann
209 a0c9f010 Michael Hanselmann
  utils.AddHostToEtcHosts(hostname.name)
210 a0c9f010 Michael Hanselmann
  _InitSSHSetup(hostname.name)
211 a0c9f010 Michael Hanselmann
212 a0c9f010 Michael Hanselmann
  # init of cluster config file
213 b9eeeb02 Michael Hanselmann
  cluster_config = objects.Cluster(
214 b9eeeb02 Michael Hanselmann
    serial_no=1,
215 b9eeeb02 Michael Hanselmann
    rsahostkeypub=sshkey,
216 b9eeeb02 Michael Hanselmann
    highest_used_port=(constants.FIRST_DRBD_PORT - 1),
217 b9eeeb02 Michael Hanselmann
    mac_prefix=mac_prefix,
218 b9eeeb02 Michael Hanselmann
    volume_group_name=vg_name,
219 b9eeeb02 Michael Hanselmann
    default_bridge=def_bridge,
220 b9eeeb02 Michael Hanselmann
    tcpudp_port_pool=set(),
221 f6bd6e98 Michael Hanselmann
    hypervisor=hypervisor_type,
222 f6bd6e98 Michael Hanselmann
    master_node=hostname.name,
223 f6bd6e98 Michael Hanselmann
    master_ip=clustername.ip,
224 f6bd6e98 Michael Hanselmann
    master_netdev=master_netdev,
225 f6bd6e98 Michael Hanselmann
    cluster_name=clustername.name,
226 f6bd6e98 Michael Hanselmann
    file_storage_dir=file_storage_dir,
227 b9eeeb02 Michael Hanselmann
    )
228 b9eeeb02 Michael Hanselmann
  master_node_config = objects.Node(name=hostname.name,
229 b9eeeb02 Michael Hanselmann
                                    primary_ip=hostname.ip,
230 b9eeeb02 Michael Hanselmann
                                    secondary_ip=secondary_ip)
231 a0c9f010 Michael Hanselmann
232 02f99608 Oleksiy Mishchenko
  cfg = InitConfig(constants.CONFIG_VERSION,
233 02f99608 Oleksiy Mishchenko
                   cluster_config, master_node_config)
234 7688d0d3 Michael Hanselmann
  ssh.WriteKnownHostsFile(cfg, constants.SSH_KNOWN_HOSTS_FILE)
235 827f753e Guido Trotter
236 b3f1cf6f Iustin Pop
  # start the master ip
237 b3f1cf6f Iustin Pop
  # TODO: Review rpc call from bootstrap
238 72737a7f Iustin Pop
  RpcRunner.call_node_start_master(hostname.name, True)
239 b3f1cf6f Iustin Pop
240 b1b6ea87 Iustin Pop
241 02f99608 Oleksiy Mishchenko
def InitConfig(version, cluster_config, master_node_config,
242 02f99608 Oleksiy Mishchenko
               cfg_file=constants.CLUSTER_CONF_FILE):
243 7b3a8fb5 Iustin Pop
  """Create the initial cluster configuration.
244 7b3a8fb5 Iustin Pop

245 7b3a8fb5 Iustin Pop
  It will contain the current node, which will also be the master
246 7b3a8fb5 Iustin Pop
  node, and no instances.
247 7b3a8fb5 Iustin Pop

248 7b3a8fb5 Iustin Pop
  @type version: int
249 7b3a8fb5 Iustin Pop
  @param version: Configuration version
250 7b3a8fb5 Iustin Pop
  @type cluster_config: objects.Cluster
251 7b3a8fb5 Iustin Pop
  @param cluster_config: Cluster configuration
252 7b3a8fb5 Iustin Pop
  @type master_node_config: objects.Node
253 7b3a8fb5 Iustin Pop
  @param master_node_config: Master node configuration
254 7b3a8fb5 Iustin Pop
  @type file_name: string
255 7b3a8fb5 Iustin Pop
  @param file_name: Configuration file path
256 7b3a8fb5 Iustin Pop

257 7b3a8fb5 Iustin Pop
  @rtype: ssconf.SimpleConfigWriter
258 7b3a8fb5 Iustin Pop
  @returns: Initialized config instance
259 7b3a8fb5 Iustin Pop

260 7b3a8fb5 Iustin Pop
  """
261 7b3a8fb5 Iustin Pop
  nodes = {
262 7b3a8fb5 Iustin Pop
    master_node_config.name: master_node_config,
263 7b3a8fb5 Iustin Pop
    }
264 7b3a8fb5 Iustin Pop
265 7b3a8fb5 Iustin Pop
  config_data = objects.ConfigData(version=version,
266 7b3a8fb5 Iustin Pop
                                   cluster=cluster_config,
267 7b3a8fb5 Iustin Pop
                                   nodes=nodes,
268 7b3a8fb5 Iustin Pop
                                   instances={},
269 7b3a8fb5 Iustin Pop
                                   serial_no=1)
270 7b3a8fb5 Iustin Pop
  cfg = ssconf.SimpleConfigWriter.FromDict(config_data.ToDict(), cfg_file)
271 7b3a8fb5 Iustin Pop
  cfg.Save()
272 7b3a8fb5 Iustin Pop
273 7b3a8fb5 Iustin Pop
  return cfg
274 02f99608 Oleksiy Mishchenko
275 02f99608 Oleksiy Mishchenko
276 140aa4a8 Iustin Pop
def FinalizeClusterDestroy(master):
277 140aa4a8 Iustin Pop
  """Execute the last steps of cluster destroy
278 140aa4a8 Iustin Pop

279 140aa4a8 Iustin Pop
  This function shuts down all the daemons, completing the destroy
280 140aa4a8 Iustin Pop
  begun in cmdlib.LUDestroyOpcode.
281 140aa4a8 Iustin Pop

282 140aa4a8 Iustin Pop
  """
283 72737a7f Iustin Pop
  if not RpcRunner.call_node_stop_master(master, True):
284 140aa4a8 Iustin Pop
    logging.warning("Could not disable the master role")
285 72737a7f Iustin Pop
  if not RpcRunner.call_node_leave_cluster(master):
286 140aa4a8 Iustin Pop
    logging.warning("Could not shutdown the node daemon and cleanup the node")
287 140aa4a8 Iustin Pop
288 140aa4a8 Iustin Pop
289 c4b6c29c Michael Hanselmann
def SetupNodeDaemon(node, ssh_key_check):
290 827f753e Guido Trotter
  """Add a node to the cluster.
291 827f753e Guido Trotter

292 b1b6ea87 Iustin Pop
  This function must be called before the actual opcode, and will ssh
293 b1b6ea87 Iustin Pop
  to the remote node, copy the needed files, and start ganeti-noded,
294 b1b6ea87 Iustin Pop
  allowing the master to do the rest via normal rpc calls.
295 827f753e Guido Trotter

296 827f753e Guido Trotter
  Args:
297 827f753e Guido Trotter
    node: fully qualified domain name for the new node
298 827f753e Guido Trotter

299 827f753e Guido Trotter
  """
300 7688d0d3 Michael Hanselmann
  cfg = ssconf.SimpleConfigReader()
301 6b0469d2 Iustin Pop
  sshrunner = ssh.SshRunner(cfg.GetClusterName())
302 d23ef431 Michael Hanselmann
  gntpass = utils.GetNodeDaemonPassword()
303 827f753e Guido Trotter
  if not re.match('^[a-zA-Z0-9.]{1,64}$', gntpass):
304 827f753e Guido Trotter
    raise errors.OpExecError("ganeti password corruption detected")
305 827f753e Guido Trotter
  f = open(constants.SSL_CERT_FILE)
306 827f753e Guido Trotter
  try:
307 827f753e Guido Trotter
    gntpem = f.read(8192)
308 827f753e Guido Trotter
  finally:
309 827f753e Guido Trotter
    f.close()
310 827f753e Guido Trotter
  # in the base64 pem encoding, neither '!' nor '.' are valid chars,
311 827f753e Guido Trotter
  # so we use this to detect an invalid certificate; as long as the
312 827f753e Guido Trotter
  # cert doesn't contain this, the here-document will be correctly
313 827f753e Guido Trotter
  # parsed by the shell sequence below
314 827f753e Guido Trotter
  if re.search('^!EOF\.', gntpem, re.MULTILINE):
315 827f753e Guido Trotter
    raise errors.OpExecError("invalid PEM encoding in the SSL certificate")
316 827f753e Guido Trotter
  if not gntpem.endswith("\n"):
317 827f753e Guido Trotter
    raise errors.OpExecError("PEM must end with newline")
318 827f753e Guido Trotter
319 827f753e Guido Trotter
  # set up inter-node password and certificate and restarts the node daemon
320 827f753e Guido Trotter
  # and then connect with ssh to set password and start ganeti-noded
321 827f753e Guido Trotter
  # note that all the below variables are sanitized at this point,
322 827f753e Guido Trotter
  # either by being constants or by the checks above
323 827f753e Guido Trotter
  mycommand = ("umask 077 && "
324 827f753e Guido Trotter
               "echo '%s' > '%s' && "
325 827f753e Guido Trotter
               "cat > '%s' << '!EOF.' && \n"
326 827f753e Guido Trotter
               "%s!EOF.\n%s restart" %
327 d23ef431 Michael Hanselmann
               (gntpass, constants.CLUSTER_PASSWORD_FILE,
328 827f753e Guido Trotter
                constants.SSL_CERT_FILE, gntpem,
329 827f753e Guido Trotter
                constants.NODE_INITD_SCRIPT))
330 827f753e Guido Trotter
331 c4b6c29c Michael Hanselmann
  result = sshrunner.Run(node, 'root', mycommand, batch=False,
332 c4b6c29c Michael Hanselmann
                         ask_key=ssh_key_check,
333 c4b6c29c Michael Hanselmann
                         use_cluster_key=False,
334 c4b6c29c Michael Hanselmann
                         strict_host_check=ssh_key_check)
335 827f753e Guido Trotter
  if result.failed:
336 827f753e Guido Trotter
    raise errors.OpExecError("Remote command on node %s, error: %s,"
337 827f753e Guido Trotter
                             " output: %s" %
338 827f753e Guido Trotter
                             (node, result.fail_reason, result.output))
339 827f753e Guido Trotter
340 827f753e Guido Trotter
  return 0
341 827f753e Guido Trotter
342 b1b6ea87 Iustin Pop
343 b1b6ea87 Iustin Pop
def MasterFailover():
344 b1b6ea87 Iustin Pop
  """Failover the master node.
345 b1b6ea87 Iustin Pop

346 b1b6ea87 Iustin Pop
  This checks that we are not already the master, and will cause the
347 b1b6ea87 Iustin Pop
  current master to cease being master, and the non-master to become
348 b1b6ea87 Iustin Pop
  new master.
349 b1b6ea87 Iustin Pop

350 b1b6ea87 Iustin Pop
  """
351 d23ef431 Michael Hanselmann
  cfg = ssconf.SimpleConfigWriter()
352 b1b6ea87 Iustin Pop
353 b1b6ea87 Iustin Pop
  new_master = utils.HostInfo().name
354 d23ef431 Michael Hanselmann
  old_master = cfg.GetMasterNode()
355 b1b6ea87 Iustin Pop
356 b1b6ea87 Iustin Pop
  if old_master == new_master:
357 b1b6ea87 Iustin Pop
    raise errors.OpPrereqError("This commands must be run on the node"
358 b1b6ea87 Iustin Pop
                               " where you want the new master to be."
359 b1b6ea87 Iustin Pop
                               " %s is already the master" %
360 b1b6ea87 Iustin Pop
                               old_master)
361 b1b6ea87 Iustin Pop
  # end checks
362 b1b6ea87 Iustin Pop
363 b1b6ea87 Iustin Pop
  rcode = 0
364 b1b6ea87 Iustin Pop
365 b1b6ea87 Iustin Pop
  logging.info("setting master to %s, old master: %s", new_master, old_master)
366 b1b6ea87 Iustin Pop
367 72737a7f Iustin Pop
  if not RpcRunner.call_node_stop_master(old_master, True):
368 b1b6ea87 Iustin Pop
    logging.error("could disable the master role on the old master"
369 b1b6ea87 Iustin Pop
                 " %s, please disable manually", old_master)
370 b1b6ea87 Iustin Pop
371 d23ef431 Michael Hanselmann
  cfg.SetMasterNode(new_master)
372 d23ef431 Michael Hanselmann
  cfg.Save()
373 b1b6ea87 Iustin Pop
374 d23ef431 Michael Hanselmann
  # Here we have a phase where no master should be running
375 b1b6ea87 Iustin Pop
376 72737a7f Iustin Pop
  if not RpcRunner.call_upload_file(cfg.GetNodeList(),
377 72737a7f Iustin Pop
                                    constants.CLUSTER_CONF_FILE):
378 3b9e6a30 Iustin Pop
    logging.error("could not distribute the new simple store master file"
379 3b9e6a30 Iustin Pop
                  " to the other nodes, please check.")
380 b1b6ea87 Iustin Pop
381 72737a7f Iustin Pop
  if not RpcRunner.call_node_start_master(new_master, True):
382 b1b6ea87 Iustin Pop
    logging.error("could not start the master role on the new master"
383 b1b6ea87 Iustin Pop
                  " %s, please check", new_master)
384 b1b6ea87 Iustin Pop
    rcode = 1
385 b1b6ea87 Iustin Pop
386 b1b6ea87 Iustin Pop
  return rcode