Statistics
| Branch: | Tag: | Revision:

root / daemons / ganeti-noded @ 8785cb30

History | View | Annotate | Download (17.9 kB)

1 a8083063 Iustin Pop
#!/usr/bin/python
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Ganeti node daemon"""
23 a8083063 Iustin Pop
24 3ecf6786 Iustin Pop
# functions in this module need to have a given name structure, so:
25 3ecf6786 Iustin Pop
# pylint: disable-msg=C0103
26 3ecf6786 Iustin Pop
27 a8083063 Iustin Pop
import os
28 a8083063 Iustin Pop
import sys
29 a8083063 Iustin Pop
import traceback
30 761ce945 Guido Trotter
import SocketServer
31 0214b0c0 Iustin Pop
import errno
32 c89189b1 Iustin Pop
import logging
33 84b58db2 Michael Hanselmann
import signal
34 a8083063 Iustin Pop
35 a8083063 Iustin Pop
from optparse import OptionParser
36 a8083063 Iustin Pop
37 a8083063 Iustin Pop
from ganeti import backend
38 a8083063 Iustin Pop
from ganeti import logger
39 a8083063 Iustin Pop
from ganeti import constants
40 a8083063 Iustin Pop
from ganeti import objects
41 a8083063 Iustin Pop
from ganeti import errors
42 25d6d12a Michael Hanselmann
from ganeti import jstore
43 a8083063 Iustin Pop
from ganeti import ssconf
44 1df6506c Michael Hanselmann
from ganeti import http
45 16abfbc2 Alexander Schreiber
from ganeti import utils
46 a8083063 Iustin Pop
47 a8083063 Iustin Pop
48 25d6d12a Michael Hanselmann
queue_lock = None
49 25d6d12a Michael Hanselmann
50 25d6d12a Michael Hanselmann
51 7f30777b Michael Hanselmann
def _RequireJobQueueLock(fn):
52 7f30777b Michael Hanselmann
  """Decorator for job queue manipulating functions.
53 7f30777b Michael Hanselmann
54 7f30777b Michael Hanselmann
  """
55 8785cb30 Michael Hanselmann
  QUEUE_LOCK_TIMEOUT = 10
56 8785cb30 Michael Hanselmann
57 7f30777b Michael Hanselmann
  def wrapper(*args, **kwargs):
58 7f30777b Michael Hanselmann
    # Locking in exclusive, blocking mode because there could be several
59 506cff12 Michael Hanselmann
    # children running at the same time. Waiting up to 10 seconds.
60 8785cb30 Michael Hanselmann
    queue_lock.Exclusive(blocking=True, timeout=QUEUE_LOCK_TIMEOUT)
61 7f30777b Michael Hanselmann
    try:
62 7f30777b Michael Hanselmann
      return fn(*args, **kwargs)
63 7f30777b Michael Hanselmann
    finally:
64 7f30777b Michael Hanselmann
      queue_lock.Unlock()
65 8785cb30 Michael Hanselmann
66 7f30777b Michael Hanselmann
  return wrapper
67 7f30777b Michael Hanselmann
68 7f30777b Michael Hanselmann
69 1df6506c Michael Hanselmann
class NodeDaemonRequestHandler(http.HTTPRequestHandler):
70 3ecf6786 Iustin Pop
  """The server implementation.
71 3ecf6786 Iustin Pop
72 3ecf6786 Iustin Pop
  This class holds all methods exposed over the RPC interface.
73 3ecf6786 Iustin Pop
74 3ecf6786 Iustin Pop
  """
75 1df6506c Michael Hanselmann
  def HandleRequest(self):
76 1df6506c Michael Hanselmann
    """Handle a request.
77 a8083063 Iustin Pop
78 098c0958 Michael Hanselmann
    """
79 1df6506c Michael Hanselmann
    if self.command.upper() != "PUT":
80 1df6506c Michael Hanselmann
      raise http.HTTPBadRequest()
81 1df6506c Michael Hanselmann
82 81010134 Iustin Pop
    path = self.path
83 81010134 Iustin Pop
    if path.startswith("/"):
84 81010134 Iustin Pop
      path = path[1:]
85 81010134 Iustin Pop
86 1df6506c Michael Hanselmann
    method = getattr(self, "perspective_%s" % path, None)
87 1df6506c Michael Hanselmann
    if method is None:
88 1df6506c Michael Hanselmann
      raise httperror.HTTPNotFound()
89 a8083063 Iustin Pop
90 81010134 Iustin Pop
    try:
91 aa9075c5 Michael Hanselmann
      try:
92 aa9075c5 Michael Hanselmann
        return method(self.post_data)
93 aa9075c5 Michael Hanselmann
      except:
94 aa9075c5 Michael Hanselmann
        logging.exception("Error in RPC call")
95 aa9075c5 Michael Hanselmann
        raise
96 9ae49f27 Guido Trotter
    except errors.QuitGanetiException, err:
97 84b58db2 Michael Hanselmann
      # Tell parent to quit
98 84b58db2 Michael Hanselmann
      os.kill(self.server.noded_pid, signal.SIGTERM)
99 a8083063 Iustin Pop
100 a8083063 Iustin Pop
  # the new block devices  --------------------------
101 a8083063 Iustin Pop
102 3ecf6786 Iustin Pop
  @staticmethod
103 3ecf6786 Iustin Pop
  def perspective_blockdev_create(params):
104 3ecf6786 Iustin Pop
    """Create a block device.
105 3ecf6786 Iustin Pop
106 3ecf6786 Iustin Pop
    """
107 3f78eef2 Iustin Pop
    bdev_s, size, owner, on_primary, info = params
108 319856a9 Michael Hanselmann
    bdev = objects.Disk.FromDict(bdev_s)
109 a8083063 Iustin Pop
    if bdev is None:
110 a8083063 Iustin Pop
      raise ValueError("can't unserialize data!")
111 3f78eef2 Iustin Pop
    return backend.CreateBlockDevice(bdev, size, owner, on_primary, info)
112 a8083063 Iustin Pop
113 3ecf6786 Iustin Pop
  @staticmethod
114 3ecf6786 Iustin Pop
  def perspective_blockdev_remove(params):
115 3ecf6786 Iustin Pop
    """Remove a block device.
116 3ecf6786 Iustin Pop
117 3ecf6786 Iustin Pop
    """
118 a8083063 Iustin Pop
    bdev_s = params[0]
119 319856a9 Michael Hanselmann
    bdev = objects.Disk.FromDict(bdev_s)
120 a8083063 Iustin Pop
    return backend.RemoveBlockDevice(bdev)
121 a8083063 Iustin Pop
122 3ecf6786 Iustin Pop
  @staticmethod
123 f3e513ad Iustin Pop
  def perspective_blockdev_rename(params):
124 f3e513ad Iustin Pop
    """Remove a block device.
125 f3e513ad Iustin Pop
126 f3e513ad Iustin Pop
    """
127 f3e513ad Iustin Pop
    devlist = [(objects.Disk.FromDict(ds), uid) for ds, uid in params]
128 f3e513ad Iustin Pop
    return backend.RenameBlockDevices(devlist)
129 f3e513ad Iustin Pop
130 f3e513ad Iustin Pop
  @staticmethod
131 3ecf6786 Iustin Pop
  def perspective_blockdev_assemble(params):
132 3ecf6786 Iustin Pop
    """Assemble a block device.
133 3ecf6786 Iustin Pop
134 3ecf6786 Iustin Pop
    """
135 3f78eef2 Iustin Pop
    bdev_s, owner, on_primary = params
136 319856a9 Michael Hanselmann
    bdev = objects.Disk.FromDict(bdev_s)
137 a8083063 Iustin Pop
    if bdev is None:
138 a8083063 Iustin Pop
      raise ValueError("can't unserialize data!")
139 3f78eef2 Iustin Pop
    return backend.AssembleBlockDevice(bdev, owner, on_primary)
140 a8083063 Iustin Pop
141 3ecf6786 Iustin Pop
  @staticmethod
142 3ecf6786 Iustin Pop
  def perspective_blockdev_shutdown(params):
143 3ecf6786 Iustin Pop
    """Shutdown a block device.
144 3ecf6786 Iustin Pop
145 3ecf6786 Iustin Pop
    """
146 a8083063 Iustin Pop
    bdev_s = params[0]
147 319856a9 Michael Hanselmann
    bdev = objects.Disk.FromDict(bdev_s)
148 a8083063 Iustin Pop
    if bdev is None:
149 a8083063 Iustin Pop
      raise ValueError("can't unserialize data!")
150 a8083063 Iustin Pop
    return backend.ShutdownBlockDevice(bdev)
151 a8083063 Iustin Pop
152 3ecf6786 Iustin Pop
  @staticmethod
153 153d9724 Iustin Pop
  def perspective_blockdev_addchildren(params):
154 3ecf6786 Iustin Pop
    """Add a child to a mirror device.
155 3ecf6786 Iustin Pop
156 3ecf6786 Iustin Pop
    Note: this is only valid for mirror devices. It's the caller's duty
157 3ecf6786 Iustin Pop
    to send a correct disk, otherwise we raise an error.
158 3ecf6786 Iustin Pop
159 3ecf6786 Iustin Pop
    """
160 a8083063 Iustin Pop
    bdev_s, ndev_s = params
161 319856a9 Michael Hanselmann
    bdev = objects.Disk.FromDict(bdev_s)
162 153d9724 Iustin Pop
    ndevs = [objects.Disk.FromDict(disk_s) for disk_s in ndev_s]
163 153d9724 Iustin Pop
    if bdev is None or ndevs.count(None) > 0:
164 a8083063 Iustin Pop
      raise ValueError("can't unserialize data!")
165 153d9724 Iustin Pop
    return backend.MirrorAddChildren(bdev, ndevs)
166 a8083063 Iustin Pop
167 3ecf6786 Iustin Pop
  @staticmethod
168 153d9724 Iustin Pop
  def perspective_blockdev_removechildren(params):
169 3ecf6786 Iustin Pop
    """Remove a child from a mirror device.
170 3ecf6786 Iustin Pop
171 3ecf6786 Iustin Pop
    This is only valid for mirror devices, of course. It's the callers
172 3ecf6786 Iustin Pop
    duty to send a correct disk, otherwise we raise an error.
173 3ecf6786 Iustin Pop
174 3ecf6786 Iustin Pop
    """
175 a8083063 Iustin Pop
    bdev_s, ndev_s = params
176 319856a9 Michael Hanselmann
    bdev = objects.Disk.FromDict(bdev_s)
177 153d9724 Iustin Pop
    ndevs = [objects.Disk.FromDict(disk_s) for disk_s in ndev_s]
178 153d9724 Iustin Pop
    if bdev is None or ndevs.count(None) > 0:
179 a8083063 Iustin Pop
      raise ValueError("can't unserialize data!")
180 153d9724 Iustin Pop
    return backend.MirrorRemoveChildren(bdev, ndevs)
181 a8083063 Iustin Pop
182 3ecf6786 Iustin Pop
  @staticmethod
183 3ecf6786 Iustin Pop
  def perspective_blockdev_getmirrorstatus(params):
184 3ecf6786 Iustin Pop
    """Return the mirror status for a list of disks.
185 3ecf6786 Iustin Pop
186 3ecf6786 Iustin Pop
    """
187 319856a9 Michael Hanselmann
    disks = [objects.Disk.FromDict(dsk_s)
188 a8083063 Iustin Pop
            for dsk_s in params]
189 a8083063 Iustin Pop
    return backend.GetMirrorStatus(disks)
190 a8083063 Iustin Pop
191 3ecf6786 Iustin Pop
  @staticmethod
192 3ecf6786 Iustin Pop
  def perspective_blockdev_find(params):
193 3ecf6786 Iustin Pop
    """Expose the FindBlockDevice functionality for a disk.
194 3ecf6786 Iustin Pop
195 3ecf6786 Iustin Pop
    This will try to find but not activate a disk.
196 3ecf6786 Iustin Pop
197 3ecf6786 Iustin Pop
    """
198 319856a9 Michael Hanselmann
    disk = objects.Disk.FromDict(params[0])
199 a8083063 Iustin Pop
    return backend.FindBlockDevice(disk)
200 a8083063 Iustin Pop
201 3ecf6786 Iustin Pop
  @staticmethod
202 3ecf6786 Iustin Pop
  def perspective_blockdev_snapshot(params):
203 3ecf6786 Iustin Pop
    """Create a snapshot device.
204 3ecf6786 Iustin Pop
205 3ecf6786 Iustin Pop
    Note that this is only valid for LVM disks, if we get passed
206 3ecf6786 Iustin Pop
    something else we raise an exception. The snapshot device can be
207 3ecf6786 Iustin Pop
    remove by calling the generic block device remove call.
208 3ecf6786 Iustin Pop
209 3ecf6786 Iustin Pop
    """
210 319856a9 Michael Hanselmann
    cfbd = objects.Disk.FromDict(params[0])
211 a8083063 Iustin Pop
    return backend.SnapshotBlockDevice(cfbd)
212 a8083063 Iustin Pop
213 4c8ba8b3 Iustin Pop
  @staticmethod
214 4c8ba8b3 Iustin Pop
  def perspective_blockdev_grow(params):
215 4c8ba8b3 Iustin Pop
    """Grow a stack of devices.
216 4c8ba8b3 Iustin Pop
217 4c8ba8b3 Iustin Pop
    """
218 4c8ba8b3 Iustin Pop
    cfbd = objects.Disk.FromDict(params[0])
219 4c8ba8b3 Iustin Pop
    amount = params[1]
220 4c8ba8b3 Iustin Pop
    return backend.GrowBlockDevice(cfbd, amount)
221 4c8ba8b3 Iustin Pop
222 d61cbe76 Iustin Pop
  @staticmethod
223 d61cbe76 Iustin Pop
  def perspective_blockdev_close(params):
224 d61cbe76 Iustin Pop
    """Closes the given block devices.
225 d61cbe76 Iustin Pop
226 d61cbe76 Iustin Pop
    """
227 d61cbe76 Iustin Pop
    disks = [objects.Disk.FromDict(cf) for cf in params]
228 d61cbe76 Iustin Pop
    return backend.CloseBlockDevices(disks)
229 d61cbe76 Iustin Pop
230 a8083063 Iustin Pop
  # export/import  --------------------------
231 a8083063 Iustin Pop
232 3ecf6786 Iustin Pop
  @staticmethod
233 3ecf6786 Iustin Pop
  def perspective_snapshot_export(params):
234 3ecf6786 Iustin Pop
    """Export a given snapshot.
235 3ecf6786 Iustin Pop
236 3ecf6786 Iustin Pop
    """
237 319856a9 Michael Hanselmann
    disk = objects.Disk.FromDict(params[0])
238 a8083063 Iustin Pop
    dest_node = params[1]
239 319856a9 Michael Hanselmann
    instance = objects.Instance.FromDict(params[2])
240 3ecf6786 Iustin Pop
    return backend.ExportSnapshot(disk, dest_node, instance)
241 3ecf6786 Iustin Pop
242 3ecf6786 Iustin Pop
  @staticmethod
243 3ecf6786 Iustin Pop
  def perspective_finalize_export(params):
244 3ecf6786 Iustin Pop
    """Expose the finalize export functionality.
245 a8083063 Iustin Pop
246 3ecf6786 Iustin Pop
    """
247 319856a9 Michael Hanselmann
    instance = objects.Instance.FromDict(params[0])
248 319856a9 Michael Hanselmann
    snap_disks = [objects.Disk.FromDict(str_data)
249 a8083063 Iustin Pop
                  for str_data in params[1]]
250 a8083063 Iustin Pop
    return backend.FinalizeExport(instance, snap_disks)
251 a8083063 Iustin Pop
252 3ecf6786 Iustin Pop
  @staticmethod
253 3ecf6786 Iustin Pop
  def perspective_export_info(params):
254 3ecf6786 Iustin Pop
    """Query information about an existing export on this node.
255 3ecf6786 Iustin Pop
256 3ecf6786 Iustin Pop
    The given path may not contain an export, in which case we return
257 3ecf6786 Iustin Pop
    None.
258 3ecf6786 Iustin Pop
259 3ecf6786 Iustin Pop
    """
260 3ecf6786 Iustin Pop
    path = params[0]
261 3ecf6786 Iustin Pop
    einfo = backend.ExportInfo(path)
262 a8083063 Iustin Pop
    if einfo is None:
263 a8083063 Iustin Pop
      return einfo
264 a8083063 Iustin Pop
    return einfo.Dumps()
265 a8083063 Iustin Pop
266 3ecf6786 Iustin Pop
  @staticmethod
267 3ecf6786 Iustin Pop
  def perspective_export_list(params):
268 3ecf6786 Iustin Pop
    """List the available exports on this node.
269 3ecf6786 Iustin Pop
270 3ecf6786 Iustin Pop
    Note that as opposed to export_info, which may query data about an
271 3ecf6786 Iustin Pop
    export in any path, this only queries the standard Ganeti path
272 3ecf6786 Iustin Pop
    (constants.EXPORT_DIR).
273 3ecf6786 Iustin Pop
274 3ecf6786 Iustin Pop
    """
275 a8083063 Iustin Pop
    return backend.ListExports()
276 a8083063 Iustin Pop
277 3ecf6786 Iustin Pop
  @staticmethod
278 3ecf6786 Iustin Pop
  def perspective_export_remove(params):
279 3ecf6786 Iustin Pop
    """Remove an export.
280 3ecf6786 Iustin Pop
281 3ecf6786 Iustin Pop
    """
282 a8083063 Iustin Pop
    export = params[0]
283 a8083063 Iustin Pop
    return backend.RemoveExport(export)
284 a8083063 Iustin Pop
285 a8083063 Iustin Pop
  # volume  --------------------------
286 a8083063 Iustin Pop
287 3ecf6786 Iustin Pop
  @staticmethod
288 3ecf6786 Iustin Pop
  def perspective_volume_list(params):
289 3ecf6786 Iustin Pop
    """Query the list of logical volumes in a given volume group.
290 3ecf6786 Iustin Pop
291 3ecf6786 Iustin Pop
    """
292 a8083063 Iustin Pop
    vgname = params[0]
293 a8083063 Iustin Pop
    return backend.GetVolumeList(vgname)
294 a8083063 Iustin Pop
295 3ecf6786 Iustin Pop
  @staticmethod
296 3ecf6786 Iustin Pop
  def perspective_vg_list(params):
297 3ecf6786 Iustin Pop
    """Query the list of volume groups.
298 3ecf6786 Iustin Pop
299 3ecf6786 Iustin Pop
    """
300 a8083063 Iustin Pop
    return backend.ListVolumeGroups()
301 a8083063 Iustin Pop
302 a8083063 Iustin Pop
  # bridge  --------------------------
303 a8083063 Iustin Pop
304 3ecf6786 Iustin Pop
  @staticmethod
305 3ecf6786 Iustin Pop
  def perspective_bridges_exist(params):
306 3ecf6786 Iustin Pop
    """Check if all bridges given exist on this node.
307 3ecf6786 Iustin Pop
308 3ecf6786 Iustin Pop
    """
309 a8083063 Iustin Pop
    bridges_list = params[0]
310 a8083063 Iustin Pop
    return backend.BridgesExist(bridges_list)
311 a8083063 Iustin Pop
312 a8083063 Iustin Pop
  # instance  --------------------------
313 a8083063 Iustin Pop
314 3ecf6786 Iustin Pop
  @staticmethod
315 3ecf6786 Iustin Pop
  def perspective_instance_os_add(params):
316 3ecf6786 Iustin Pop
    """Install an OS on a given instance.
317 3ecf6786 Iustin Pop
318 3ecf6786 Iustin Pop
    """
319 a8083063 Iustin Pop
    inst_s, os_disk, swap_disk = params
320 319856a9 Michael Hanselmann
    inst = objects.Instance.FromDict(inst_s)
321 a8083063 Iustin Pop
    return backend.AddOSToInstance(inst, os_disk, swap_disk)
322 a8083063 Iustin Pop
323 3ecf6786 Iustin Pop
  @staticmethod
324 decd5f45 Iustin Pop
  def perspective_instance_run_rename(params):
325 decd5f45 Iustin Pop
    """Runs the OS rename script for an instance.
326 decd5f45 Iustin Pop
327 decd5f45 Iustin Pop
    """
328 decd5f45 Iustin Pop
    inst_s, old_name, os_disk, swap_disk = params
329 319856a9 Michael Hanselmann
    inst = objects.Instance.FromDict(inst_s)
330 decd5f45 Iustin Pop
    return backend.RunRenameInstance(inst, old_name, os_disk, swap_disk)
331 decd5f45 Iustin Pop
332 decd5f45 Iustin Pop
  @staticmethod
333 3ecf6786 Iustin Pop
  def perspective_instance_os_import(params):
334 3ecf6786 Iustin Pop
    """Run the import function of an OS onto a given instance.
335 3ecf6786 Iustin Pop
336 3ecf6786 Iustin Pop
    """
337 a8083063 Iustin Pop
    inst_s, os_disk, swap_disk, src_node, src_image = params
338 319856a9 Michael Hanselmann
    inst = objects.Instance.FromDict(inst_s)
339 a8083063 Iustin Pop
    return backend.ImportOSIntoInstance(inst, os_disk, swap_disk,
340 a8083063 Iustin Pop
                                        src_node, src_image)
341 a8083063 Iustin Pop
342 3ecf6786 Iustin Pop
  @staticmethod
343 3ecf6786 Iustin Pop
  def perspective_instance_shutdown(params):
344 3ecf6786 Iustin Pop
    """Shutdown an instance.
345 3ecf6786 Iustin Pop
346 3ecf6786 Iustin Pop
    """
347 319856a9 Michael Hanselmann
    instance = objects.Instance.FromDict(params[0])
348 a8083063 Iustin Pop
    return backend.ShutdownInstance(instance)
349 a8083063 Iustin Pop
350 3ecf6786 Iustin Pop
  @staticmethod
351 3ecf6786 Iustin Pop
  def perspective_instance_start(params):
352 3ecf6786 Iustin Pop
    """Start an instance.
353 3ecf6786 Iustin Pop
354 3ecf6786 Iustin Pop
    """
355 319856a9 Michael Hanselmann
    instance = objects.Instance.FromDict(params[0])
356 a8083063 Iustin Pop
    extra_args = params[1]
357 a8083063 Iustin Pop
    return backend.StartInstance(instance, extra_args)
358 a8083063 Iustin Pop
359 3ecf6786 Iustin Pop
  @staticmethod
360 2a10865c Iustin Pop
  def perspective_instance_migrate(params):
361 2a10865c Iustin Pop
    """Migrates an instance.
362 2a10865c Iustin Pop
363 2a10865c Iustin Pop
    """
364 2a10865c Iustin Pop
    instance, target, live = params
365 2a10865c Iustin Pop
    return backend.MigrateInstance(instance, target, live)
366 2a10865c Iustin Pop
367 2a10865c Iustin Pop
  @staticmethod
368 007a2f3e Alexander Schreiber
  def perspective_instance_reboot(params):
369 007a2f3e Alexander Schreiber
    """Reboot an instance.
370 007a2f3e Alexander Schreiber
371 007a2f3e Alexander Schreiber
    """
372 007a2f3e Alexander Schreiber
    instance = objects.Instance.FromDict(params[0])
373 007a2f3e Alexander Schreiber
    reboot_type = params[1]
374 007a2f3e Alexander Schreiber
    extra_args = params[2]
375 007a2f3e Alexander Schreiber
    return backend.RebootInstance(instance, reboot_type, extra_args)
376 007a2f3e Alexander Schreiber
377 007a2f3e Alexander Schreiber
  @staticmethod
378 3ecf6786 Iustin Pop
  def perspective_instance_info(params):
379 3ecf6786 Iustin Pop
    """Query instance information.
380 3ecf6786 Iustin Pop
381 3ecf6786 Iustin Pop
    """
382 a8083063 Iustin Pop
    return backend.GetInstanceInfo(params[0])
383 a8083063 Iustin Pop
384 3ecf6786 Iustin Pop
  @staticmethod
385 3ecf6786 Iustin Pop
  def perspective_all_instances_info(params):
386 3ecf6786 Iustin Pop
    """Query information about all instances.
387 3ecf6786 Iustin Pop
388 3ecf6786 Iustin Pop
    """
389 a8083063 Iustin Pop
    return backend.GetAllInstancesInfo()
390 a8083063 Iustin Pop
391 3ecf6786 Iustin Pop
  @staticmethod
392 3ecf6786 Iustin Pop
  def perspective_instance_list(params):
393 3ecf6786 Iustin Pop
    """Query the list of running instances.
394 3ecf6786 Iustin Pop
395 3ecf6786 Iustin Pop
    """
396 a8083063 Iustin Pop
    return backend.GetInstanceList()
397 a8083063 Iustin Pop
398 a8083063 Iustin Pop
  # node --------------------------
399 a8083063 Iustin Pop
400 3ecf6786 Iustin Pop
  @staticmethod
401 16abfbc2 Alexander Schreiber
  def perspective_node_tcp_ping(params):
402 16abfbc2 Alexander Schreiber
    """Do a TcpPing on the remote node.
403 16abfbc2 Alexander Schreiber
404 16abfbc2 Alexander Schreiber
    """
405 b15d625f Iustin Pop
    return utils.TcpPing(params[1], params[2], timeout=params[3],
406 b15d625f Iustin Pop
                         live_port_needed=params[4], source=params[0])
407 16abfbc2 Alexander Schreiber
408 16abfbc2 Alexander Schreiber
  @staticmethod
409 3ecf6786 Iustin Pop
  def perspective_node_info(params):
410 3ecf6786 Iustin Pop
    """Query node information.
411 3ecf6786 Iustin Pop
412 3ecf6786 Iustin Pop
    """
413 a8083063 Iustin Pop
    vgname = params[0]
414 a8083063 Iustin Pop
    return backend.GetNodeInfo(vgname)
415 a8083063 Iustin Pop
416 3ecf6786 Iustin Pop
  @staticmethod
417 3ecf6786 Iustin Pop
  def perspective_node_add(params):
418 3ecf6786 Iustin Pop
    """Complete the registration of this node in the cluster.
419 3ecf6786 Iustin Pop
420 3ecf6786 Iustin Pop
    """
421 a8083063 Iustin Pop
    return backend.AddNode(params[0], params[1], params[2],
422 a8083063 Iustin Pop
                           params[3], params[4], params[5])
423 a8083063 Iustin Pop
424 3ecf6786 Iustin Pop
  @staticmethod
425 3ecf6786 Iustin Pop
  def perspective_node_verify(params):
426 3ecf6786 Iustin Pop
    """Run a verify sequence on this node.
427 3ecf6786 Iustin Pop
428 3ecf6786 Iustin Pop
    """
429 a8083063 Iustin Pop
    return backend.VerifyNode(params[0])
430 a8083063 Iustin Pop
431 3ecf6786 Iustin Pop
  @staticmethod
432 3ecf6786 Iustin Pop
  def perspective_node_start_master(params):
433 3ecf6786 Iustin Pop
    """Promote this node to master status.
434 3ecf6786 Iustin Pop
435 3ecf6786 Iustin Pop
    """
436 1c65840b Iustin Pop
    return backend.StartMaster(params[0])
437 a8083063 Iustin Pop
438 3ecf6786 Iustin Pop
  @staticmethod
439 3ecf6786 Iustin Pop
  def perspective_node_stop_master(params):
440 3ecf6786 Iustin Pop
    """Demote this node from master status.
441 3ecf6786 Iustin Pop
442 3ecf6786 Iustin Pop
    """
443 1c65840b Iustin Pop
    return backend.StopMaster(params[0])
444 a8083063 Iustin Pop
445 3ecf6786 Iustin Pop
  @staticmethod
446 3ecf6786 Iustin Pop
  def perspective_node_leave_cluster(params):
447 3ecf6786 Iustin Pop
    """Cleanup after leaving a cluster.
448 3ecf6786 Iustin Pop
449 3ecf6786 Iustin Pop
    """
450 a8083063 Iustin Pop
    return backend.LeaveCluster()
451 a8083063 Iustin Pop
452 3ecf6786 Iustin Pop
  @staticmethod
453 3ecf6786 Iustin Pop
  def perspective_node_volumes(params):
454 3ecf6786 Iustin Pop
    """Query the list of all logical volume groups.
455 3ecf6786 Iustin Pop
456 3ecf6786 Iustin Pop
    """
457 dcb93971 Michael Hanselmann
    return backend.NodeVolumes()
458 dcb93971 Michael Hanselmann
459 a8083063 Iustin Pop
  # cluster --------------------------
460 a8083063 Iustin Pop
461 3ecf6786 Iustin Pop
  @staticmethod
462 3ecf6786 Iustin Pop
  def perspective_version(params):
463 3ecf6786 Iustin Pop
    """Query version information.
464 3ecf6786 Iustin Pop
465 3ecf6786 Iustin Pop
    """
466 a8083063 Iustin Pop
    return constants.PROTOCOL_VERSION
467 a8083063 Iustin Pop
468 3ecf6786 Iustin Pop
  @staticmethod
469 3ecf6786 Iustin Pop
  def perspective_upload_file(params):
470 3ecf6786 Iustin Pop
    """Upload a file.
471 3ecf6786 Iustin Pop
472 3ecf6786 Iustin Pop
    Note that the backend implementation imposes strict rules on which
473 3ecf6786 Iustin Pop
    files are accepted.
474 3ecf6786 Iustin Pop
475 3ecf6786 Iustin Pop
    """
476 a8083063 Iustin Pop
    return backend.UploadFile(*params)
477 a8083063 Iustin Pop
478 4e071d3b Iustin Pop
  @staticmethod
479 4e071d3b Iustin Pop
  def perspective_master_info(params):
480 4e071d3b Iustin Pop
    """Query master information.
481 4e071d3b Iustin Pop
482 4e071d3b Iustin Pop
    """
483 4e071d3b Iustin Pop
    return backend.GetMasterInfo()
484 a8083063 Iustin Pop
485 a8083063 Iustin Pop
  # os -----------------------
486 a8083063 Iustin Pop
487 3ecf6786 Iustin Pop
  @staticmethod
488 3ecf6786 Iustin Pop
  def perspective_os_diagnose(params):
489 3ecf6786 Iustin Pop
    """Query detailed information about existing OSes.
490 3ecf6786 Iustin Pop
491 3ecf6786 Iustin Pop
    """
492 4e679f11 Guido Trotter
    return [os.ToDict() for os in backend.DiagnoseOS()]
493 a8083063 Iustin Pop
494 3ecf6786 Iustin Pop
  @staticmethod
495 3ecf6786 Iustin Pop
  def perspective_os_get(params):
496 3ecf6786 Iustin Pop
    """Query information about a given OS.
497 3ecf6786 Iustin Pop
498 3ecf6786 Iustin Pop
    """
499 a8083063 Iustin Pop
    name = params[0]
500 a8083063 Iustin Pop
    try:
501 dfa96ded Guido Trotter
      os_obj = backend.OSFromDisk(name)
502 a8083063 Iustin Pop
    except errors.InvalidOS, err:
503 dfa96ded Guido Trotter
      os_obj = objects.OS.FromInvalidOS(err)
504 dfa96ded Guido Trotter
    return os_obj.ToDict()
505 a8083063 Iustin Pop
506 a8083063 Iustin Pop
  # hooks -----------------------
507 a8083063 Iustin Pop
508 3ecf6786 Iustin Pop
  @staticmethod
509 3ecf6786 Iustin Pop
  def perspective_hooks_runner(params):
510 3ecf6786 Iustin Pop
    """Run hook scripts.
511 3ecf6786 Iustin Pop
512 3ecf6786 Iustin Pop
    """
513 a8083063 Iustin Pop
    hpath, phase, env = params
514 a8083063 Iustin Pop
    hr = backend.HooksRunner()
515 a8083063 Iustin Pop
    return hr.RunHooks(hpath, phase, env)
516 a8083063 Iustin Pop
517 8d528b7c Iustin Pop
  # iallocator -----------------
518 8d528b7c Iustin Pop
519 8d528b7c Iustin Pop
  @staticmethod
520 8d528b7c Iustin Pop
  def perspective_iallocator_runner(params):
521 8d528b7c Iustin Pop
    """Run an iallocator script.
522 8d528b7c Iustin Pop
523 8d528b7c Iustin Pop
    """
524 8d528b7c Iustin Pop
    name, idata = params
525 8d528b7c Iustin Pop
    iar = backend.IAllocatorRunner()
526 8d528b7c Iustin Pop
    return iar.Run(name, idata)
527 8d528b7c Iustin Pop
528 06009e27 Iustin Pop
  # test -----------------------
529 06009e27 Iustin Pop
530 06009e27 Iustin Pop
  @staticmethod
531 06009e27 Iustin Pop
  def perspective_test_delay(params):
532 06009e27 Iustin Pop
    """Run test delay.
533 06009e27 Iustin Pop
534 06009e27 Iustin Pop
    """
535 06009e27 Iustin Pop
    duration = params[0]
536 06009e27 Iustin Pop
    return utils.TestDelay(duration)
537 06009e27 Iustin Pop
538 4e071d3b Iustin Pop
  # file storage ---------------
539 4e071d3b Iustin Pop
540 a5d7fb43 Manuel Franceschini
  @staticmethod
541 a5d7fb43 Manuel Franceschini
  def perspective_file_storage_dir_create(params):
542 a5d7fb43 Manuel Franceschini
    """Create the file storage directory.
543 a5d7fb43 Manuel Franceschini
544 a5d7fb43 Manuel Franceschini
    """
545 a5d7fb43 Manuel Franceschini
    file_storage_dir = params[0]
546 a5d7fb43 Manuel Franceschini
    return backend.CreateFileStorageDir(file_storage_dir)
547 a5d7fb43 Manuel Franceschini
548 a5d7fb43 Manuel Franceschini
  @staticmethod
549 a5d7fb43 Manuel Franceschini
  def perspective_file_storage_dir_remove(params):
550 a5d7fb43 Manuel Franceschini
    """Remove the file storage directory.
551 a5d7fb43 Manuel Franceschini
552 a5d7fb43 Manuel Franceschini
    """
553 a5d7fb43 Manuel Franceschini
    file_storage_dir = params[0]
554 a5d7fb43 Manuel Franceschini
    return backend.RemoveFileStorageDir(file_storage_dir)
555 a5d7fb43 Manuel Franceschini
556 a5d7fb43 Manuel Franceschini
  @staticmethod
557 a5d7fb43 Manuel Franceschini
  def perspective_file_storage_dir_rename(params):
558 a5d7fb43 Manuel Franceschini
    """Rename the file storage directory.
559 a5d7fb43 Manuel Franceschini
560 a5d7fb43 Manuel Franceschini
    """
561 a5d7fb43 Manuel Franceschini
    old_file_storage_dir = params[0]
562 a5d7fb43 Manuel Franceschini
    new_file_storage_dir = params[1]
563 a5d7fb43 Manuel Franceschini
    return backend.RenameFileStorageDir(old_file_storage_dir,
564 a5d7fb43 Manuel Franceschini
                                        new_file_storage_dir)
565 a5d7fb43 Manuel Franceschini
566 4e071d3b Iustin Pop
  # jobs ------------------------
567 4e071d3b Iustin Pop
568 ca52cdeb Michael Hanselmann
  @staticmethod
569 7f30777b Michael Hanselmann
  @_RequireJobQueueLock
570 ca52cdeb Michael Hanselmann
  def perspective_jobqueue_update(params):
571 ca52cdeb Michael Hanselmann
    """Update job queue.
572 ca52cdeb Michael Hanselmann
573 ca52cdeb Michael Hanselmann
    """
574 ca52cdeb Michael Hanselmann
    (file_name, content) = params
575 7f30777b Michael Hanselmann
    return backend.JobQueueUpdate(file_name, content)
576 ca52cdeb Michael Hanselmann
577 ca52cdeb Michael Hanselmann
  @staticmethod
578 f1f3f45c Michael Hanselmann
  @_RequireJobQueueLock
579 ca52cdeb Michael Hanselmann
  def perspective_jobqueue_purge(params):
580 ca52cdeb Michael Hanselmann
    """Purge job queue.
581 ca52cdeb Michael Hanselmann
582 ca52cdeb Michael Hanselmann
    """
583 ca52cdeb Michael Hanselmann
    return backend.JobQueuePurge()
584 ca52cdeb Michael Hanselmann
585 af5ebcb1 Michael Hanselmann
  @staticmethod
586 af5ebcb1 Michael Hanselmann
  @_RequireJobQueueLock
587 af5ebcb1 Michael Hanselmann
  def perspective_jobqueue_rename(params):
588 af5ebcb1 Michael Hanselmann
    """Rename a job queue file.
589 af5ebcb1 Michael Hanselmann
590 af5ebcb1 Michael Hanselmann
    """
591 af5ebcb1 Michael Hanselmann
    (old, new) = params
592 af5ebcb1 Michael Hanselmann
593 af5ebcb1 Michael Hanselmann
    return backend.JobQueueRename(old, new)
594 af5ebcb1 Michael Hanselmann
595 a8083063 Iustin Pop
596 1df6506c Michael Hanselmann
class NodeDaemonHttpServer(http.HTTPServer):
597 1df6506c Michael Hanselmann
  def __init__(self, server_address):
598 1df6506c Michael Hanselmann
    http.HTTPServer.__init__(self, server_address, NodeDaemonRequestHandler)
599 84b58db2 Michael Hanselmann
    self.noded_pid = os.getpid()
600 84b58db2 Michael Hanselmann
601 84b58db2 Michael Hanselmann
  def serve_forever(self):
602 84b58db2 Michael Hanselmann
    """Handle requests until told to quit."""
603 84b58db2 Michael Hanselmann
    sighandler = utils.SignalHandler([signal.SIGINT, signal.SIGTERM])
604 84b58db2 Michael Hanselmann
    try:
605 84b58db2 Michael Hanselmann
      while not sighandler.called:
606 84b58db2 Michael Hanselmann
        self.handle_request()
607 84b58db2 Michael Hanselmann
      # TODO: There could be children running at this point
608 84b58db2 Michael Hanselmann
    finally:
609 84b58db2 Michael Hanselmann
      sighandler.Reset()
610 1df6506c Michael Hanselmann
611 1df6506c Michael Hanselmann
612 761ce945 Guido Trotter
class ForkingHTTPServer(SocketServer.ForkingMixIn, NodeDaemonHttpServer):
613 761ce945 Guido Trotter
  """Forking HTTP Server.
614 761ce945 Guido Trotter
615 761ce945 Guido Trotter
  This inherits from ForkingMixIn and HTTPServer in order to fork for each
616 761ce945 Guido Trotter
  request we handle. This allows more requests to be handled concurrently.
617 761ce945 Guido Trotter
618 761ce945 Guido Trotter
  """
619 761ce945 Guido Trotter
620 761ce945 Guido Trotter
621 a8083063 Iustin Pop
def ParseOptions():
622 a8083063 Iustin Pop
  """Parse the command line options.
623 a8083063 Iustin Pop
624 a8083063 Iustin Pop
  Returns:
625 a8083063 Iustin Pop
    (options, args) as from OptionParser.parse_args()
626 a8083063 Iustin Pop
627 a8083063 Iustin Pop
  """
628 a8083063 Iustin Pop
  parser = OptionParser(description="Ganeti node daemon",
629 a8083063 Iustin Pop
                        usage="%prog [-f] [-d]",
630 a8083063 Iustin Pop
                        version="%%prog (ganeti) %s" %
631 a8083063 Iustin Pop
                        constants.RELEASE_VERSION)
632 a8083063 Iustin Pop
633 a8083063 Iustin Pop
  parser.add_option("-f", "--foreground", dest="fork",
634 a8083063 Iustin Pop
                    help="Don't detach from the current terminal",
635 a8083063 Iustin Pop
                    default=True, action="store_false")
636 a8083063 Iustin Pop
  parser.add_option("-d", "--debug", dest="debug",
637 a8083063 Iustin Pop
                    help="Enable some debug messages",
638 a8083063 Iustin Pop
                    default=False, action="store_true")
639 a8083063 Iustin Pop
  options, args = parser.parse_args()
640 a8083063 Iustin Pop
  return options, args
641 a8083063 Iustin Pop
642 a8083063 Iustin Pop
643 a8083063 Iustin Pop
def main():
644 3ecf6786 Iustin Pop
  """Main function for the node daemon.
645 3ecf6786 Iustin Pop
646 3ecf6786 Iustin Pop
  """
647 25d6d12a Michael Hanselmann
  global queue_lock
648 25d6d12a Michael Hanselmann
649 a8083063 Iustin Pop
  options, args = ParseOptions()
650 f362096f Iustin Pop
  utils.debug = options.debug
651 a8083063 Iustin Pop
  for fname in (constants.SSL_CERT_FILE,):
652 a8083063 Iustin Pop
    if not os.path.isfile(fname):
653 a8083063 Iustin Pop
      print "config %s not there, will not run." % fname
654 a8083063 Iustin Pop
      sys.exit(5)
655 a8083063 Iustin Pop
656 a8083063 Iustin Pop
  try:
657 a8083063 Iustin Pop
    ss = ssconf.SimpleStore()
658 a8083063 Iustin Pop
    port = ss.GetNodeDaemonPort()
659 a8083063 Iustin Pop
    pwdata = ss.GetNodeDaemonPassword()
660 a8083063 Iustin Pop
  except errors.ConfigurationError, err:
661 a8083063 Iustin Pop
    print "Cluster configuration incomplete: '%s'" % str(err)
662 a8083063 Iustin Pop
    sys.exit(5)
663 a8083063 Iustin Pop
664 195c7f91 Iustin Pop
  # create the various SUB_RUN_DIRS, if not existing, so that we handle the
665 195c7f91 Iustin Pop
  # situation where RUN_DIR is tmpfs
666 195c7f91 Iustin Pop
  for dir_name in constants.SUB_RUN_DIRS:
667 195c7f91 Iustin Pop
    if not os.path.exists(dir_name):
668 195c7f91 Iustin Pop
      try:
669 195c7f91 Iustin Pop
        os.mkdir(dir_name, 0755)
670 195c7f91 Iustin Pop
      except EnvironmentError, err:
671 195c7f91 Iustin Pop
        if err.errno != errno.EEXIST:
672 195c7f91 Iustin Pop
          print ("Node setup wrong, cannot create directory %s: %s" %
673 195c7f91 Iustin Pop
                 (dir_name, err))
674 195c7f91 Iustin Pop
          sys.exit(5)
675 195c7f91 Iustin Pop
    if not os.path.isdir(dir_name):
676 195c7f91 Iustin Pop
      print ("Node setup wrong, %s is not a directory" % dir_name)
677 195c7f91 Iustin Pop
      sys.exit(5)
678 0214b0c0 Iustin Pop
679 a8083063 Iustin Pop
  # become a daemon
680 a8083063 Iustin Pop
  if options.fork:
681 8f765069 Iustin Pop
    utils.Daemonize(logfile=constants.LOG_NODESERVER)
682 a8083063 Iustin Pop
683 99e88451 Iustin Pop
  utils.WritePidFile(constants.NODED_PID)
684 73d927a2 Guido Trotter
685 59f187eb Iustin Pop
  logger.SetupLogging(logfile=constants.LOG_NODESERVER, debug=options.debug,
686 59f187eb Iustin Pop
                      stderr_logging=not options.fork)
687 c89189b1 Iustin Pop
  logging.info("ganeti node daemon startup")
688 a8083063 Iustin Pop
689 25d6d12a Michael Hanselmann
  # Prepare job queue
690 25d6d12a Michael Hanselmann
  queue_lock = jstore.InitAndVerifyQueue(must_lock=False)
691 25d6d12a Michael Hanselmann
692 761ce945 Guido Trotter
  if options.fork:
693 84b58db2 Michael Hanselmann
    server = ForkingHTTPServer(('', port))
694 761ce945 Guido Trotter
  else:
695 84b58db2 Michael Hanselmann
    server = NodeDaemonHttpServer(('', port))
696 a8083063 Iustin Pop
697 73d927a2 Guido Trotter
  try:
698 73d927a2 Guido Trotter
    server.serve_forever()
699 73d927a2 Guido Trotter
  finally:
700 99e88451 Iustin Pop
    utils.RemovePidFile(constants.NODED_PID)
701 73d927a2 Guido Trotter
702 a8083063 Iustin Pop
703 3ecf6786 Iustin Pop
if __name__ == '__main__':
704 a8083063 Iustin Pop
  main()