Revision a0c9f010 lib/cmdlib.py

b/lib/cmdlib.py
305 305
  return _BuildInstanceHookEnv(**args)
306 306

  
307 307

  
308
def _InitSSHSetup(node):
309
  """Setup the SSH configuration for the cluster.
310

  
311

  
312
  This generates a dsa keypair for root, adds the pub key to the
313
  permitted hosts and adds the hostkey to its own known hosts.
314

  
315
  Args:
316
    node: the name of this host as a fqdn
317

  
318
  """
319
  priv_key, pub_key, auth_keys = ssh.GetUserFiles(constants.GANETI_RUNAS)
320

  
321
  for name in priv_key, pub_key:
322
    if os.path.exists(name):
323
      utils.CreateBackup(name)
324
    utils.RemoveFile(name)
325

  
326
  result = utils.RunCmd(["ssh-keygen", "-t", "dsa",
327
                         "-f", priv_key,
328
                         "-q", "-N", ""])
329
  if result.failed:
330
    raise errors.OpExecError("Could not generate ssh keypair, error %s" %
331
                             result.output)
332

  
333
  f = open(pub_key, 'r')
334
  try:
335
    utils.AddAuthorizedKey(auth_keys, f.read(8192))
336
  finally:
337
    f.close()
338

  
339

  
340
def _InitGanetiServerSetup(ss):
341
  """Setup the necessary configuration for the initial node daemon.
342

  
343
  This creates the nodepass file containing the shared password for
344
  the cluster and also generates the SSL certificate.
345

  
346
  """
347
  # Create pseudo random password
348
  randpass = sha.new(os.urandom(64)).hexdigest()
349
  # and write it into sstore
350
  ss.SetKey(ss.SS_NODED_PASS, randpass)
351

  
352
  result = utils.RunCmd(["openssl", "req", "-new", "-newkey", "rsa:1024",
353
                         "-days", str(365*5), "-nodes", "-x509",
354
                         "-keyout", constants.SSL_CERT_FILE,
355
                         "-out", constants.SSL_CERT_FILE, "-batch"])
356
  if result.failed:
357
    raise errors.OpExecError("could not generate server ssl cert, command"
358
                             " %s had exitcode %s and error message %s" %
359
                             (result.cmd, result.exit_code, result.output))
360

  
361
  os.chmod(constants.SSL_CERT_FILE, 0400)
362

  
363
  result = utils.RunCmd([constants.NODE_INITD_SCRIPT, "restart"])
364

  
365
  if result.failed:
366
    raise errors.OpExecError("Could not start the node daemon, command %s"
367
                             " had exitcode %s and error %s" %
368
                             (result.cmd, result.exit_code, result.output))
369

  
370

  
371 308
def _CheckInstanceBridgesExist(instance):
372 309
  """Check that the brigdes needed by an instance exist.
373 310

  
......
380 317
                               (brlist, instance.primary_node))
381 318

  
382 319

  
383
class LUInitCluster(LogicalUnit):
384
  """Initialise the cluster.
385

  
386
  """
387
  HPATH = "cluster-init"
388
  HTYPE = constants.HTYPE_CLUSTER
389
  _OP_REQP = ["cluster_name", "hypervisor_type", "mac_prefix",
390
              "def_bridge", "master_netdev", "file_storage_dir"]
391
  REQ_CLUSTER = False
392

  
393
  def BuildHooksEnv(self):
394
    """Build hooks env.
395

  
396
    Notes: Since we don't require a cluster, we must manually add
397
    ourselves in the post-run node list.
398

  
399
    """
400
    env = {"OP_TARGET": self.op.cluster_name}
401
    return env, [], [self.hostname.name]
402

  
403
  def CheckPrereq(self):
404
    """Verify that the passed name is a valid one.
405

  
406
    """
407
    if config.ConfigWriter.IsCluster():
408
      raise errors.OpPrereqError("Cluster is already initialised")
409

  
410
    if self.op.hypervisor_type == constants.HT_XEN_HVM31:
411
      if not os.path.exists(constants.VNC_PASSWORD_FILE):
412
        raise errors.OpPrereqError("Please prepare the cluster VNC"
413
                                   "password file %s" %
414
                                   constants.VNC_PASSWORD_FILE)
415

  
416
    self.hostname = hostname = utils.HostInfo()
417

  
418
    if hostname.ip.startswith("127."):
419
      raise errors.OpPrereqError("This host's IP resolves to the private"
420
                                 " range (%s). Please fix DNS or %s." %
421
                                 (hostname.ip, constants.ETC_HOSTS))
422

  
423
    if not utils.TcpPing(hostname.ip, constants.DEFAULT_NODED_PORT,
424
                         source=constants.LOCALHOST_IP_ADDRESS):
425
      raise errors.OpPrereqError("Inconsistency: this host's name resolves"
426
                                 " to %s,\nbut this ip address does not"
427
                                 " belong to this host."
428
                                 " Aborting." % hostname.ip)
429

  
430
    self.clustername = clustername = utils.HostInfo(self.op.cluster_name)
431

  
432
    if utils.TcpPing(clustername.ip, constants.DEFAULT_NODED_PORT,
433
                     timeout=5):
434
      raise errors.OpPrereqError("Cluster IP already active. Aborting.")
435

  
436
    secondary_ip = getattr(self.op, "secondary_ip", None)
437
    if secondary_ip and not utils.IsValidIP(secondary_ip):
438
      raise errors.OpPrereqError("Invalid secondary ip given")
439
    if (secondary_ip and
440
        secondary_ip != hostname.ip and
441
        (not utils.TcpPing(secondary_ip, constants.DEFAULT_NODED_PORT,
442
                           source=constants.LOCALHOST_IP_ADDRESS))):
443
      raise errors.OpPrereqError("You gave %s as secondary IP,"
444
                                 " but it does not belong to this host." %
445
                                 secondary_ip)
446
    self.secondary_ip = secondary_ip
447

  
448
    if not hasattr(self.op, "vg_name"):
449
      self.op.vg_name = None
450
    # if vg_name not None, checks if volume group is valid
451
    if self.op.vg_name:
452
      vgstatus = utils.CheckVolumeGroupSize(utils.ListVolumeGroups(), vg_name,
453
                                            constants.MIN_VG_SIZE)
454
      if vgstatus:
455
        raise errors.OpPrereqError("Error: %s\nspecify --no-lvm-storage if"
456
                                   " you are not using lvm" % vgstatus)
457

  
458
    self.op.file_storage_dir = os.path.normpath(self.op.file_storage_dir)
459

  
460
    if not os.path.isabs(self.op.file_storage_dir):
461
      raise errors.OpPrereqError("The file storage directory you have is"
462
                                 " not an absolute path.")
463

  
464
    if not os.path.exists(self.op.file_storage_dir):
465
      try:
466
        os.makedirs(self.op.file_storage_dir, 0750)
467
      except OSError, err:
468
        raise errors.OpPrereqError("Cannot create file storage directory"
469
                                   " '%s': %s" %
470
                                   (self.op.file_storage_dir, err))
471

  
472
    if not os.path.isdir(self.op.file_storage_dir):
473
      raise errors.OpPrereqError("The file storage directory '%s' is not"
474
                                 " a directory." % self.op.file_storage_dir)
475

  
476
    if not re.match("^[0-9a-z]{2}:[0-9a-z]{2}:[0-9a-z]{2}$",
477
                    self.op.mac_prefix):
478
      raise errors.OpPrereqError("Invalid mac prefix given '%s'" %
479
                                 self.op.mac_prefix)
480

  
481
    if self.op.hypervisor_type not in constants.HYPER_TYPES:
482
      raise errors.OpPrereqError("Invalid hypervisor type given '%s'" %
483
                                 self.op.hypervisor_type)
484

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

  
491
    if not (os.path.isfile(constants.NODE_INITD_SCRIPT) and
492
            os.access(constants.NODE_INITD_SCRIPT, os.X_OK)):
493
      raise errors.OpPrereqError("Init.d script '%s' missing or not"
494
                                 " executable." % constants.NODE_INITD_SCRIPT)
495

  
496
  def Exec(self, feedback_fn):
497
    """Initialize the cluster.
498

  
499
    """
500
    clustername = self.clustername
501
    hostname = self.hostname
502

  
503
    # set up the simple store
504
    self.sstore = ss = ssconf.SimpleStore()
505
    ss.SetKey(ss.SS_HYPERVISOR, self.op.hypervisor_type)
506
    ss.SetKey(ss.SS_MASTER_NODE, hostname.name)
507
    ss.SetKey(ss.SS_MASTER_IP, clustername.ip)
508
    ss.SetKey(ss.SS_MASTER_NETDEV, self.op.master_netdev)
509
    ss.SetKey(ss.SS_CLUSTER_NAME, clustername.name)
510
    ss.SetKey(ss.SS_FILE_STORAGE_DIR, self.op.file_storage_dir)
511
    ss.SetKey(ss.SS_CONFIG_VERSION, constants.CONFIG_VERSION)
512

  
513
    # set up the inter-node password and certificate
514
    _InitGanetiServerSetup(ss)
515

  
516
    # start the master ip
517
    rpc.call_node_start_master(hostname.name)
518

  
519
    # set up ssh config and /etc/hosts
520
    f = open(constants.SSH_HOST_RSA_PUB, 'r')
521
    try:
522
      sshline = f.read()
523
    finally:
524
      f.close()
525
    sshkey = sshline.split(" ")[1]
526

  
527
    utils.AddHostToEtcHosts(hostname.name)
528
    _InitSSHSetup(hostname.name)
529

  
530
    # init of cluster config file
531
    self.cfg = cfgw = config.ConfigWriter()
532
    cfgw.InitConfig(hostname.name, hostname.ip, self.secondary_ip,
533
                    sshkey, self.op.mac_prefix,
534
                    self.op.vg_name, self.op.def_bridge)
535

  
536
    ssh.WriteKnownHostsFile(cfgw, ss, constants.SSH_KNOWN_HOSTS_FILE)
537

  
538

  
539 320
class LUDestroyCluster(NoHooksLU):
540 321
  """Logical unit for destroying the cluster.
541 322

  

Also available in: Unified diff