ganeti-noded 18.9 KB
Newer Older
Iustin Pop's avatar
Iustin Pop committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
#!/usr/bin/python
#

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


"""Ganeti node daemon"""

24 25 26
# functions in this module need to have a given name structure, so:
# pylint: disable-msg=C0103

Iustin Pop's avatar
Iustin Pop committed
27 28 29
import os
import sys
import traceback
Guido Trotter's avatar
Guido Trotter committed
30
import SocketServer
31
import errno
Iustin Pop's avatar
Iustin Pop committed
32
import logging
33
import signal
Iustin Pop's avatar
Iustin Pop committed
34 35 36 37 38 39 40

from optparse import OptionParser

from ganeti import backend
from ganeti import constants
from ganeti import objects
from ganeti import errors
41
from ganeti import jstore
42
from ganeti import daemon
43
from ganeti import http
44
from ganeti import utils
Iustin Pop's avatar
Iustin Pop committed
45

46 47
import ganeti.http.server

Iustin Pop's avatar
Iustin Pop committed
48

49 50 51
queue_lock = None


52 53 54 55
def _RequireJobQueueLock(fn):
  """Decorator for job queue manipulating functions.

  """
56 57
  QUEUE_LOCK_TIMEOUT = 10

58 59
  def wrapper(*args, **kwargs):
    # Locking in exclusive, blocking mode because there could be several
60
    # children running at the same time. Waiting up to 10 seconds.
61
    queue_lock.Exclusive(blocking=True, timeout=QUEUE_LOCK_TIMEOUT)
62 63 64 65
    try:
      return fn(*args, **kwargs)
    finally:
      queue_lock.Unlock()
66

67 68 69
  return wrapper


70
class NodeHttpServer(http.server.HttpServer):
71 72 73 74 75
  """The server implementation.

  This class holds all methods exposed over the RPC interface.

  """
76
  def __init__(self, *args, **kwargs):
77
    http.server.HttpServer.__init__(self, *args, **kwargs)
78 79 80
    self.noded_pid = os.getpid()

  def HandleRequest(self, req):
81
    """Handle a request.
Iustin Pop's avatar
Iustin Pop committed
82

83
    """
84
    if req.request_method.upper() != http.HTTP_PUT:
85
      raise http.HttpBadRequest()
86

87
    path = req.request_path
Iustin Pop's avatar
Iustin Pop committed
88 89 90
    if path.startswith("/"):
      path = path[1:]

91 92
    method = getattr(self, "perspective_%s" % path, None)
    if method is None:
93
      raise http.HttpNotFound()
Iustin Pop's avatar
Iustin Pop committed
94

Iustin Pop's avatar
Iustin Pop committed
95
    try:
96
      try:
97
        return method(req.request_body)
98 99 100
      except:
        logging.exception("Error in RPC call")
        raise
101
    except errors.QuitGanetiException, err:
102
      # Tell parent to quit
103
      os.kill(self.noded_pid, signal.SIGTERM)
Iustin Pop's avatar
Iustin Pop committed
104 105 106

  # the new block devices  --------------------------

107 108 109 110 111
  @staticmethod
  def perspective_blockdev_create(params):
    """Create a block device.

    """
112
    bdev_s, size, owner, on_primary, info = params
113
    bdev = objects.Disk.FromDict(bdev_s)
Iustin Pop's avatar
Iustin Pop committed
114 115
    if bdev is None:
      raise ValueError("can't unserialize data!")
116
    return backend.CreateBlockDevice(bdev, size, owner, on_primary, info)
Iustin Pop's avatar
Iustin Pop committed
117

118 119 120 121 122
  @staticmethod
  def perspective_blockdev_remove(params):
    """Remove a block device.

    """
Iustin Pop's avatar
Iustin Pop committed
123
    bdev_s = params[0]
124
    bdev = objects.Disk.FromDict(bdev_s)
Iustin Pop's avatar
Iustin Pop committed
125 126
    return backend.RemoveBlockDevice(bdev)

Iustin Pop's avatar
Iustin Pop committed
127 128 129 130 131 132 133 134
  @staticmethod
  def perspective_blockdev_rename(params):
    """Remove a block device.

    """
    devlist = [(objects.Disk.FromDict(ds), uid) for ds, uid in params]
    return backend.RenameBlockDevices(devlist)

135 136 137 138 139
  @staticmethod
  def perspective_blockdev_assemble(params):
    """Assemble a block device.

    """
140
    bdev_s, owner, on_primary = params
141
    bdev = objects.Disk.FromDict(bdev_s)
Iustin Pop's avatar
Iustin Pop committed
142 143
    if bdev is None:
      raise ValueError("can't unserialize data!")
144
    return backend.AssembleBlockDevice(bdev, owner, on_primary)
Iustin Pop's avatar
Iustin Pop committed
145

146 147 148 149 150
  @staticmethod
  def perspective_blockdev_shutdown(params):
    """Shutdown a block device.

    """
Iustin Pop's avatar
Iustin Pop committed
151
    bdev_s = params[0]
152
    bdev = objects.Disk.FromDict(bdev_s)
Iustin Pop's avatar
Iustin Pop committed
153 154 155 156
    if bdev is None:
      raise ValueError("can't unserialize data!")
    return backend.ShutdownBlockDevice(bdev)

157
  @staticmethod
158
  def perspective_blockdev_addchildren(params):
159 160 161 162 163 164
    """Add a child to a mirror device.

    Note: this is only valid for mirror devices. It's the caller's duty
    to send a correct disk, otherwise we raise an error.

    """
Iustin Pop's avatar
Iustin Pop committed
165
    bdev_s, ndev_s = params
166
    bdev = objects.Disk.FromDict(bdev_s)
167 168
    ndevs = [objects.Disk.FromDict(disk_s) for disk_s in ndev_s]
    if bdev is None or ndevs.count(None) > 0:
Iustin Pop's avatar
Iustin Pop committed
169
      raise ValueError("can't unserialize data!")
170
    return backend.MirrorAddChildren(bdev, ndevs)
Iustin Pop's avatar
Iustin Pop committed
171

172
  @staticmethod
173
  def perspective_blockdev_removechildren(params):
174 175 176 177 178 179
    """Remove a child from a mirror device.

    This is only valid for mirror devices, of course. It's the callers
    duty to send a correct disk, otherwise we raise an error.

    """
Iustin Pop's avatar
Iustin Pop committed
180
    bdev_s, ndev_s = params
181
    bdev = objects.Disk.FromDict(bdev_s)
182 183
    ndevs = [objects.Disk.FromDict(disk_s) for disk_s in ndev_s]
    if bdev is None or ndevs.count(None) > 0:
Iustin Pop's avatar
Iustin Pop committed
184
      raise ValueError("can't unserialize data!")
185
    return backend.MirrorRemoveChildren(bdev, ndevs)
Iustin Pop's avatar
Iustin Pop committed
186

187 188 189 190 191
  @staticmethod
  def perspective_blockdev_getmirrorstatus(params):
    """Return the mirror status for a list of disks.

    """
192
    disks = [objects.Disk.FromDict(dsk_s)
Iustin Pop's avatar
Iustin Pop committed
193 194 195
            for dsk_s in params]
    return backend.GetMirrorStatus(disks)

196 197 198 199 200 201 202
  @staticmethod
  def perspective_blockdev_find(params):
    """Expose the FindBlockDevice functionality for a disk.

    This will try to find but not activate a disk.

    """
203
    disk = objects.Disk.FromDict(params[0])
Iustin Pop's avatar
Iustin Pop committed
204 205
    return backend.FindBlockDevice(disk)

206 207 208 209 210 211 212 213 214
  @staticmethod
  def perspective_blockdev_snapshot(params):
    """Create a snapshot device.

    Note that this is only valid for LVM disks, if we get passed
    something else we raise an exception. The snapshot device can be
    remove by calling the generic block device remove call.

    """
215
    cfbd = objects.Disk.FromDict(params[0])
Iustin Pop's avatar
Iustin Pop committed
216 217
    return backend.SnapshotBlockDevice(cfbd)

218 219 220 221 222 223 224 225 226
  @staticmethod
  def perspective_blockdev_grow(params):
    """Grow a stack of devices.

    """
    cfbd = objects.Disk.FromDict(params[0])
    amount = params[1]
    return backend.GrowBlockDevice(cfbd, amount)

227 228 229 230 231 232 233 234
  @staticmethod
  def perspective_blockdev_close(params):
    """Closes the given block devices.

    """
    disks = [objects.Disk.FromDict(cf) for cf in params]
    return backend.CloseBlockDevices(disks)

Iustin Pop's avatar
Iustin Pop committed
235 236
  # export/import  --------------------------

237 238 239 240 241
  @staticmethod
  def perspective_snapshot_export(params):
    """Export a given snapshot.

    """
242
    disk = objects.Disk.FromDict(params[0])
Iustin Pop's avatar
Iustin Pop committed
243
    dest_node = params[1]
244
    instance = objects.Instance.FromDict(params[2])
245
    cluster_name = params[3]
246 247 248
    dev_idx = params[4]
    return backend.ExportSnapshot(disk, dest_node, instance,
                                  cluster_name, dev_idx)
249 250 251 252

  @staticmethod
  def perspective_finalize_export(params):
    """Expose the finalize export functionality.
Iustin Pop's avatar
Iustin Pop committed
253

254
    """
255 256
    instance = objects.Instance.FromDict(params[0])
    snap_disks = [objects.Disk.FromDict(str_data)
Iustin Pop's avatar
Iustin Pop committed
257 258 259
                  for str_data in params[1]]
    return backend.FinalizeExport(instance, snap_disks)

260 261 262 263 264 265 266 267 268 269
  @staticmethod
  def perspective_export_info(params):
    """Query information about an existing export on this node.

    The given path may not contain an export, in which case we return
    None.

    """
    path = params[0]
    einfo = backend.ExportInfo(path)
Iustin Pop's avatar
Iustin Pop committed
270 271 272 273
    if einfo is None:
      return einfo
    return einfo.Dumps()

274 275 276 277 278 279 280 281 282
  @staticmethod
  def perspective_export_list(params):
    """List the available exports on this node.

    Note that as opposed to export_info, which may query data about an
    export in any path, this only queries the standard Ganeti path
    (constants.EXPORT_DIR).

    """
Iustin Pop's avatar
Iustin Pop committed
283 284
    return backend.ListExports()

285 286 287 288 289
  @staticmethod
  def perspective_export_remove(params):
    """Remove an export.

    """
Iustin Pop's avatar
Iustin Pop committed
290 291 292 293 294
    export = params[0]
    return backend.RemoveExport(export)

  # volume  --------------------------

295 296 297 298 299
  @staticmethod
  def perspective_volume_list(params):
    """Query the list of logical volumes in a given volume group.

    """
Iustin Pop's avatar
Iustin Pop committed
300 301 302
    vgname = params[0]
    return backend.GetVolumeList(vgname)

303 304 305 306 307
  @staticmethod
  def perspective_vg_list(params):
    """Query the list of volume groups.

    """
Iustin Pop's avatar
Iustin Pop committed
308 309 310 311
    return backend.ListVolumeGroups()

  # bridge  --------------------------

312 313 314 315 316
  @staticmethod
  def perspective_bridges_exist(params):
    """Check if all bridges given exist on this node.

    """
Iustin Pop's avatar
Iustin Pop committed
317 318 319 320 321
    bridges_list = params[0]
    return backend.BridgesExist(bridges_list)

  # instance  --------------------------

322 323 324 325 326
  @staticmethod
  def perspective_instance_os_add(params):
    """Install an OS on a given instance.

    """
327
    inst_s = params[0]
328
    inst = objects.Instance.FromDict(inst_s)
329
    return backend.AddOSToInstance(inst)
Iustin Pop's avatar
Iustin Pop committed
330

331 332 333 334 335
  @staticmethod
  def perspective_instance_run_rename(params):
    """Runs the OS rename script for an instance.

    """
336
    inst_s, old_name = params
337
    inst = objects.Instance.FromDict(inst_s)
338
    return backend.RunRenameInstance(inst, old_name)
339

340 341 342 343 344
  @staticmethod
  def perspective_instance_os_import(params):
    """Run the import function of an OS onto a given instance.

    """
345
    inst_s, src_node, src_images, cluster_name = params
346
    inst = objects.Instance.FromDict(inst_s)
347 348
    return backend.ImportOSIntoInstance(inst, src_node, src_images,
                                        cluster_name)
Iustin Pop's avatar
Iustin Pop committed
349

350 351 352 353 354
  @staticmethod
  def perspective_instance_shutdown(params):
    """Shutdown an instance.

    """
355
    instance = objects.Instance.FromDict(params[0])
Iustin Pop's avatar
Iustin Pop committed
356 357
    return backend.ShutdownInstance(instance)

358 359 360 361 362
  @staticmethod
  def perspective_instance_start(params):
    """Start an instance.

    """
363
    instance = objects.Instance.FromDict(params[0])
Iustin Pop's avatar
Iustin Pop committed
364 365 366
    extra_args = params[1]
    return backend.StartInstance(instance, extra_args)

367 368 369 370 371 372
  @staticmethod
  def perspective_instance_migrate(params):
    """Migrates an instance.

    """
    instance, target, live = params
373
    instance = objects.Instance.FromDict(instance)
374 375
    return backend.MigrateInstance(instance, target, live)

376 377 378 379 380 381 382 383 384 385
  @staticmethod
  def perspective_instance_reboot(params):
    """Reboot an instance.

    """
    instance = objects.Instance.FromDict(params[0])
    reboot_type = params[1]
    extra_args = params[2]
    return backend.RebootInstance(instance, reboot_type, extra_args)

386 387 388 389 390
  @staticmethod
  def perspective_instance_info(params):
    """Query instance information.

    """
Iustin Pop's avatar
Iustin Pop committed
391
    return backend.GetInstanceInfo(params[0], params[1])
Iustin Pop's avatar
Iustin Pop committed
392

393 394 395 396 397
  @staticmethod
  def perspective_all_instances_info(params):
    """Query information about all instances.

    """
398
    return backend.GetAllInstancesInfo(params[0])
Iustin Pop's avatar
Iustin Pop committed
399

400 401 402 403 404
  @staticmethod
  def perspective_instance_list(params):
    """Query the list of running instances.

    """
405
    return backend.GetInstanceList(params[0])
Iustin Pop's avatar
Iustin Pop committed
406 407 408

  # node --------------------------

409 410 411 412 413
  @staticmethod
  def perspective_node_tcp_ping(params):
    """Do a TcpPing on the remote node.

    """
414 415
    return utils.TcpPing(params[1], params[2], timeout=params[3],
                         live_port_needed=params[4], source=params[0])
416

417 418 419 420 421 422 423
  @staticmethod
  def perspective_node_has_ip_address(params):
    """Checks if a node has the given ip address.

    """
    return utils.OwnIpAddress(params[0])

424 425 426 427 428
  @staticmethod
  def perspective_node_info(params):
    """Query node information.

    """
429 430
    vgname, hypervisor_type = params
    return backend.GetNodeInfo(vgname, hypervisor_type)
Iustin Pop's avatar
Iustin Pop committed
431

432 433 434 435 436
  @staticmethod
  def perspective_node_add(params):
    """Complete the registration of this node in the cluster.

    """
Iustin Pop's avatar
Iustin Pop committed
437 438 439
    return backend.AddNode(params[0], params[1], params[2],
                           params[3], params[4], params[5])

440 441 442 443 444
  @staticmethod
  def perspective_node_verify(params):
    """Run a verify sequence on this node.

    """
445
    return backend.VerifyNode(params[0], params[1])
Iustin Pop's avatar
Iustin Pop committed
446

447 448 449 450 451
  @staticmethod
  def perspective_node_start_master(params):
    """Promote this node to master status.

    """
452
    return backend.StartMaster(params[0])
Iustin Pop's avatar
Iustin Pop committed
453

454 455 456 457 458
  @staticmethod
  def perspective_node_stop_master(params):
    """Demote this node from master status.

    """
459
    return backend.StopMaster(params[0])
Iustin Pop's avatar
Iustin Pop committed
460

461 462 463 464 465
  @staticmethod
  def perspective_node_leave_cluster(params):
    """Cleanup after leaving a cluster.

    """
Iustin Pop's avatar
Iustin Pop committed
466 467
    return backend.LeaveCluster()

468 469 470 471 472
  @staticmethod
  def perspective_node_volumes(params):
    """Query the list of all logical volume groups.

    """
473 474
    return backend.NodeVolumes()

475 476 477 478 479 480 481 482
  @staticmethod
  def perspective_node_demote_from_mc(params):
    """Demote a node from the master candidate role.

    """
    return backend.DemoteFromMC()


Iustin Pop's avatar
Iustin Pop committed
483 484
  # cluster --------------------------

485 486 487 488 489
  @staticmethod
  def perspective_version(params):
    """Query version information.

    """
Iustin Pop's avatar
Iustin Pop committed
490 491
    return constants.PROTOCOL_VERSION

492 493 494 495 496 497 498 499
  @staticmethod
  def perspective_upload_file(params):
    """Upload a file.

    Note that the backend implementation imposes strict rules on which
    files are accepted.

    """
Iustin Pop's avatar
Iustin Pop committed
500 501
    return backend.UploadFile(*params)

502 503 504 505 506 507
  @staticmethod
  def perspective_master_info(params):
    """Query master information.

    """
    return backend.GetMasterInfo()
Iustin Pop's avatar
Iustin Pop committed
508

509 510 511 512 513
  @staticmethod
  def perspective_write_ssconf_files(params):
    """Write ssconf files.

    """
514 515
    (values,) = params
    return backend.WriteSsconfFiles(values)
516

Iustin Pop's avatar
Iustin Pop committed
517 518
  # os -----------------------

519 520 521 522 523
  @staticmethod
  def perspective_os_diagnose(params):
    """Query detailed information about existing OSes.

    """
524
    return [os.ToDict() for os in backend.DiagnoseOS()]
Iustin Pop's avatar
Iustin Pop committed
525

526 527 528 529 530
  @staticmethod
  def perspective_os_get(params):
    """Query information about a given OS.

    """
Iustin Pop's avatar
Iustin Pop committed
531 532
    name = params[0]
    try:
533
      os_obj = backend.OSFromDisk(name)
Iustin Pop's avatar
Iustin Pop committed
534
    except errors.InvalidOS, err:
535 536
      os_obj = objects.OS.FromInvalidOS(err)
    return os_obj.ToDict()
Iustin Pop's avatar
Iustin Pop committed
537 538 539

  # hooks -----------------------

540 541 542 543 544
  @staticmethod
  def perspective_hooks_runner(params):
    """Run hook scripts.

    """
Iustin Pop's avatar
Iustin Pop committed
545 546 547 548
    hpath, phase, env = params
    hr = backend.HooksRunner()
    return hr.RunHooks(hpath, phase, env)

549 550 551 552 553 554 555 556 557 558 559
  # iallocator -----------------

  @staticmethod
  def perspective_iallocator_runner(params):
    """Run an iallocator script.

    """
    name, idata = params
    iar = backend.IAllocatorRunner()
    return iar.Run(name, idata)

560 561 562 563 564 565 566 567 568 569
  # test -----------------------

  @staticmethod
  def perspective_test_delay(params):
    """Run test delay.

    """
    duration = params[0]
    return utils.TestDelay(duration)

570 571
  # file storage ---------------

572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597
  @staticmethod
  def perspective_file_storage_dir_create(params):
    """Create the file storage directory.

    """
    file_storage_dir = params[0]
    return backend.CreateFileStorageDir(file_storage_dir)

  @staticmethod
  def perspective_file_storage_dir_remove(params):
    """Remove the file storage directory.

    """
    file_storage_dir = params[0]
    return backend.RemoveFileStorageDir(file_storage_dir)

  @staticmethod
  def perspective_file_storage_dir_rename(params):
    """Rename the file storage directory.

    """
    old_file_storage_dir = params[0]
    new_file_storage_dir = params[1]
    return backend.RenameFileStorageDir(old_file_storage_dir,
                                        new_file_storage_dir)

598 599
  # jobs ------------------------

600
  @staticmethod
601
  @_RequireJobQueueLock
602 603 604 605 606
  def perspective_jobqueue_update(params):
    """Update job queue.

    """
    (file_name, content) = params
607
    return backend.JobQueueUpdate(file_name, content)
608 609

  @staticmethod
610
  @_RequireJobQueueLock
611 612 613 614 615 616
  def perspective_jobqueue_purge(params):
    """Purge job queue.

    """
    return backend.JobQueuePurge()

617 618 619 620 621 622
  @staticmethod
  @_RequireJobQueueLock
  def perspective_jobqueue_rename(params):
    """Rename a job queue file.

    """
623 624
    # TODO: What if a file fails to rename?
    return [backend.JobQueueRename(old, new) for old, new in params]
625

626 627 628 629 630 631 632 633 634
  @staticmethod
  def perspective_jobqueue_set_drain(params):
    """Set/unset the queue drain flag.

    """
    drain_flag = params[0]
    return backend.JobQueueSetDrainFlag(drain_flag)


635 636 637 638 639 640 641 642 643 644
  # hypervisor ---------------

  @staticmethod
  def perspective_hypervisor_validate_params(params):
    """Validate the hypervisor parameters.

    """
    (hvname, hvparams) = params
    return backend.ValidateHVParams(hvname, hvparams)

Iustin Pop's avatar
Iustin Pop committed
645 646 647 648

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

Iustin Pop's avatar
Iustin Pop committed
649
  @return: (options, args) as from OptionParser.parse_args()
Iustin Pop's avatar
Iustin Pop committed
650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666

  """
  parser = OptionParser(description="Ganeti node daemon",
                        usage="%prog [-f] [-d]",
                        version="%%prog (ganeti) %s" %
                        constants.RELEASE_VERSION)

  parser.add_option("-f", "--foreground", dest="fork",
                    help="Don't detach from the current terminal",
                    default=True, action="store_false")
  parser.add_option("-d", "--debug", dest="debug",
                    help="Enable some debug messages",
                    default=False, action="store_true")
  options, args = parser.parse_args()
  return options, args


667 668 669 670 671 672 673 674
def EnsureRuntimeEnvironment():
  """Ensure our run-time environment is complete.

  Currently this creates directories which could be missing, either
  due to directories being on a tmpfs mount, or due to incomplete
  packaging.

  """
675
  dirs = [(val, constants.RUN_DIRS_MODE) for val in constants.SUB_RUN_DIRS]
676 677 678 679 680 681 682 683 684 685 686 687 688 689 690
  dirs.append((constants.LOG_OS_DIR, 0750))
  for dir_name, dir_mode in dirs:
    if not os.path.exists(dir_name):
      try:
        os.mkdir(dir_name, dir_mode)
      except EnvironmentError, err:
        if err.errno != errno.EEXIST:
          print ("Node setup wrong, cannot create directory '%s': %s" %
                 (dir_name, err))
          sys.exit(5)
    if not os.path.isdir(dir_name):
      print ("Node setup wrong, '%s' is not a directory" % dir_name)
      sys.exit(5)


Iustin Pop's avatar
Iustin Pop committed
691
def main():
692 693 694
  """Main function for the node daemon.

  """
695 696
  global queue_lock

Iustin Pop's avatar
Iustin Pop committed
697
  options, args = ParseOptions()
698
  utils.debug = options.debug
Iustin Pop's avatar
Iustin Pop committed
699 700 701 702 703 704
  for fname in (constants.SSL_CERT_FILE,):
    if not os.path.isfile(fname):
      print "config %s not there, will not run." % fname
      sys.exit(5)

  try:
Michael Hanselmann's avatar
Michael Hanselmann committed
705
    port = utils.GetNodeDaemonPort()
Iustin Pop's avatar
Iustin Pop committed
706 707 708 709
  except errors.ConfigurationError, err:
    print "Cluster configuration incomplete: '%s'" % str(err)
    sys.exit(5)

710
  EnsureRuntimeEnvironment()
711

Iustin Pop's avatar
Iustin Pop committed
712 713
  # become a daemon
  if options.fork:
714
    utils.Daemonize(logfile=constants.LOG_NODESERVER)
Iustin Pop's avatar
Iustin Pop committed
715

716
  utils.WritePidFile(constants.NODED_PID)
717
  try:
Iustin Pop's avatar
Iustin Pop committed
718 719
    utils.SetupLogging(logfile=constants.LOG_NODESERVER, debug=options.debug,
                       stderr_logging=not options.fork)
720
    logging.info("ganeti node daemon startup")
721

722 723 724 725
    # Read SSL certificate
    ssl_params = http.HttpSslParams(ssl_key_path=constants.SSL_CERT_FILE,
                                    ssl_cert_path=constants.SSL_CERT_FILE)

726 727
    # Prepare job queue
    queue_lock = jstore.InitAndVerifyQueue(must_lock=False)
Iustin Pop's avatar
Iustin Pop committed
728

729
    mainloop = daemon.Mainloop()
730 731
    server = NodeHttpServer(mainloop, "", port,
                            ssl_params=ssl_params, ssl_verify_peer=True)
732 733 734 735 736
    server.Start()
    try:
      mainloop.Run()
    finally:
      server.Stop()
737
  finally:
738
    utils.RemovePidFile(constants.NODED_PID)
739

Iustin Pop's avatar
Iustin Pop committed
740

741
if __name__ == '__main__':
Iustin Pop's avatar
Iustin Pop committed
742
  main()