Statistics
| Branch: | Tag: | Revision:

root / lib / bootstrap.py @ a0c9f010

History | View | Annotate | Download (7.9 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2008 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
"""Functions to bootstrap a new cluster.
23

24
"""
25

    
26
import os
27
import os.path
28
import sha
29
import re
30

    
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
38

    
39

    
40
def _InitSSHSetup(node):
41
  """Setup the SSH configuration for the cluster.
42

43

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.
46

47
  Args:
48
    node: the name of this host as a fqdn
49

50
  """
51
  priv_key, pub_key, auth_keys = ssh.GetUserFiles(constants.GANETI_RUNAS)
52

    
53
  for name in priv_key, pub_key:
54
    if os.path.exists(name):
55
      utils.CreateBackup(name)
56
    utils.RemoveFile(name)
57

    
58
  result = utils.RunCmd(["ssh-keygen", "-t", "dsa",
59
                         "-f", priv_key,
60
                         "-q", "-N", ""])
61
  if result.failed:
62
    raise errors.OpExecError("Could not generate ssh keypair, error %s" %
63
                             result.output)
64

    
65
  f = open(pub_key, 'r')
66
  try:
67
    utils.AddAuthorizedKey(auth_keys, f.read(8192))
68
  finally:
69
    f.close()
70

    
71

    
72
def _InitGanetiServerSetup(ss):
73
  """Setup the necessary configuration for the initial node daemon.
74

75
  This creates the nodepass file containing the shared password for
76
  the cluster and also generates the SSL certificate.
77

78
  """
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)
83

    
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"])
88
  if result.failed:
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))
92

    
93
  os.chmod(constants.SSL_CERT_FILE, 0400)
94

    
95
  result = utils.RunCmd([constants.NODE_INITD_SCRIPT, "restart"])
96

    
97
  if result.failed:
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))
101

    
102

    
103
def InitCluster(cluster_name, hypervisor_type, mac_prefix, def_bridge,
104
                master_netdev, file_storage_dir,
105
                secondary_ip=None,
106
                vg_name=None):
107
  """Initialise the cluster.
108

109
  """
110
  if config.ConfigWriter.IsCluster():
111
    raise errors.OpPrereqError("Cluster is already initialised")
112

    
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"
116
                                 "password file %s" %
117
                                 constants.VNC_PASSWORD_FILE)
118

    
119
  hostname = utils.HostInfo()
120

    
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))
125

    
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)
132

    
133
  clustername = utils.HostInfo(cluster_name)
134

    
135
  if utils.TcpPing(clustername.ip, constants.DEFAULT_NODED_PORT,
136
                   timeout=5):
137
    raise errors.OpPrereqError("Cluster IP already active. Aborting.")
138

    
139
  if secondary_ip:
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." %
147
                                 secondary_ip)
148

    
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)
153
    if vgstatus:
154
      raise errors.OpPrereqError("Error: %s\nspecify --no-lvm-storage if"
155
                                 " you are not using lvm" % vgstatus)
156

    
157
  file_storage_dir = os.path.normpath(file_storage_dir)
158

    
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.")
162

    
163
  if not os.path.exists(file_storage_dir):
164
    try:
165
      os.makedirs(file_storage_dir, 0750)
166
    except OSError, err:
167
      raise errors.OpPrereqError("Cannot create file storage directory"
168
                                 " '%s': %s" %
169
                                 (file_storage_dir, err))
170

    
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)
174

    
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)
177

    
178
  if hypervisor_type not in constants.HYPER_TYPES:
179
    raise errors.OpPrereqError("Invalid hypervisor type given '%s'" %
180
                               hypervisor_type)
181

    
182
  result = utils.RunCmd(["ip", "link", "show", "dev", master_netdev])
183
  if result.failed:
184
    raise errors.OpPrereqError("Invalid master netdev given (%s): '%s'" %
185
                               (master_netdev,
186
                                result.output.strip()))
187

    
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)
192

    
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)
202

    
203
  # set up the inter-node password and certificate
204
  _InitGanetiServerSetup(ss)
205

    
206
  # start the master ip
207
  # TODO: Review rpc call from bootstrap
208
  rpc.call_node_start_master(hostname.name)
209

    
210
  # set up ssh config and /etc/hosts
211
  f = open(constants.SSH_HOST_RSA_PUB, 'r')
212
  try:
213
    sshline = f.read()
214
  finally:
215
    f.close()
216
  sshkey = sshline.split(" ")[1]
217

    
218
  utils.AddHostToEtcHosts(hostname.name)
219
  _InitSSHSetup(hostname.name)
220

    
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)
225

    
226
  ssh.WriteKnownHostsFile(cfg, ss, constants.SSH_KNOWN_HOSTS_FILE)