Statistics
| Branch: | Tag: | Revision:

root / lib / server / masterd.py @ 912b2278

History | View | Annotate | Download (24.1 kB)

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

24 ffeffa1d Iustin Pop
Some classes deviates from the standard style guide since the
25 ffeffa1d Iustin Pop
inheritance from parent classes requires it.
26 ffeffa1d Iustin Pop

27 ffeffa1d Iustin Pop
"""
28 ffeffa1d Iustin Pop
29 b459a848 Andrea Spadaccini
# pylint: disable=C0103
30 7260cfbe Iustin Pop
# C0103: Invalid name ganeti-masterd
31 ffeffa1d Iustin Pop
32 bbfd0568 René Nussbaumer
import grp
33 bbfd0568 René Nussbaumer
import os
34 bbfd0568 René Nussbaumer
import pwd
35 c1f2901b Iustin Pop
import sys
36 cdd7f900 Guido Trotter
import socket
37 ffeffa1d Iustin Pop
import time
38 bbfd0568 René Nussbaumer
import tempfile
39 96cb3986 Michael Hanselmann
import logging
40 ffeffa1d Iustin Pop
41 c1f2901b Iustin Pop
from optparse import OptionParser
42 ffeffa1d Iustin Pop
43 39dcf2ef Guido Trotter
from ganeti import config
44 ffeffa1d Iustin Pop
from ganeti import constants
45 04ccf5e9 Guido Trotter
from ganeti import daemon
46 ffeffa1d Iustin Pop
from ganeti import mcpu
47 ffeffa1d Iustin Pop
from ganeti import opcodes
48 ffeffa1d Iustin Pop
from ganeti import jqueue
49 39dcf2ef Guido Trotter
from ganeti import locking
50 ffeffa1d Iustin Pop
from ganeti import luxi
51 ffeffa1d Iustin Pop
from ganeti import utils
52 c1f2901b Iustin Pop
from ganeti import errors
53 c1f2901b Iustin Pop
from ganeti import ssconf
54 23e50d39 Michael Hanselmann
from ganeti import workerpool
55 4869595d Petr Pudlak
import ganeti.rpc.node as rpc
56 912b2278 Petr Pudlak
import ganeti.rpc.client as rpccl
57 d7cdb55d Iustin Pop
from ganeti import bootstrap
58 a744b676 Manuel Franceschini
from ganeti import netutils
59 28b71a76 Michael Hanselmann
from ganeti import objects
60 24d16f76 Michael Hanselmann
from ganeti import query
61 a20e4768 Michael Hanselmann
from ganeti import runtime
62 a5ce2ea2 Michael Hanselmann
from ganeti import pathutils
63 7c4bd156 Michael Hanselmann
from ganeti import ht
64 c1f2901b Iustin Pop
65 effc1b86 Jose A. Lopes
from ganeti.utils import version
66 effc1b86 Jose A. Lopes
67 c1f2901b Iustin Pop
68 23e50d39 Michael Hanselmann
CLIENT_REQUEST_WORKERS = 16
69 23e50d39 Michael Hanselmann
70 c1f2901b Iustin Pop
EXIT_NOTMASTER = constants.EXIT_NOTMASTER
71 c1f2901b Iustin Pop
EXIT_NODESETUP_ERROR = constants.EXIT_NODESETUP_ERROR
72 ffeffa1d Iustin Pop
73 ffeffa1d Iustin Pop
74 4c91d2ad Iustin Pop
def _LogNewJob(status, info, ops):
75 4c91d2ad Iustin Pop
  """Log information about a recently submitted job.
76 4c91d2ad Iustin Pop

77 4c91d2ad Iustin Pop
  """
78 78fcfd43 Michael Hanselmann
  op_summary = utils.CommaJoin(op.Summary() for op in ops)
79 78fcfd43 Michael Hanselmann
80 4c91d2ad Iustin Pop
  if status:
81 78fcfd43 Michael Hanselmann
    logging.info("New job with id %s, summary: %s", info, op_summary)
82 4c91d2ad Iustin Pop
  else:
83 4c91d2ad Iustin Pop
    logging.info("Failed to submit job, reason: '%s', summary: %s",
84 78fcfd43 Michael Hanselmann
                 info, op_summary)
85 4c91d2ad Iustin Pop
86 4c91d2ad Iustin Pop
87 23e50d39 Michael Hanselmann
class ClientRequestWorker(workerpool.BaseWorker):
88 b459a848 Andrea Spadaccini
  # pylint: disable=W0221
89 7e5a6e86 Guido Trotter
  def RunTask(self, server, message, client):
90 23e50d39 Michael Hanselmann
    """Process the request.
91 23e50d39 Michael Hanselmann

92 23e50d39 Michael Hanselmann
    """
93 7e5a6e86 Guido Trotter
    client_ops = ClientOps(server)
94 7e5a6e86 Guido Trotter
95 23e50d39 Michael Hanselmann
    try:
96 912b2278 Petr Pudlak
      (method, args, ver) = rpccl.ParseRequest(message)
97 7e5a6e86 Guido Trotter
    except luxi.ProtocolError, err:
98 7e5a6e86 Guido Trotter
      logging.error("Protocol Error: %s", err)
99 7e5a6e86 Guido Trotter
      client.close_log()
100 7e5a6e86 Guido Trotter
      return
101 7e5a6e86 Guido Trotter
102 7e5a6e86 Guido Trotter
    success = False
103 7e5a6e86 Guido Trotter
    try:
104 e986f20c Michael Hanselmann
      # Verify client's version if there was one in the request
105 effc1b86 Jose A. Lopes
      if ver is not None and ver != constants.LUXI_VERSION:
106 e986f20c Michael Hanselmann
        raise errors.LuxiError("LUXI version mismatch, server %s, request %s" %
107 effc1b86 Jose A. Lopes
                               (constants.LUXI_VERSION, ver))
108 e986f20c Michael Hanselmann
109 7e5a6e86 Guido Trotter
      result = client_ops.handle_request(method, args)
110 7e5a6e86 Guido Trotter
      success = True
111 7e5a6e86 Guido Trotter
    except errors.GenericError, err:
112 7e5a6e86 Guido Trotter
      logging.exception("Unexpected exception")
113 7e5a6e86 Guido Trotter
      success = False
114 7e5a6e86 Guido Trotter
      result = errors.EncodeException(err)
115 7e5a6e86 Guido Trotter
    except:
116 7e5a6e86 Guido Trotter
      logging.exception("Unexpected exception")
117 7e5a6e86 Guido Trotter
      err = sys.exc_info()
118 7e5a6e86 Guido Trotter
      result = "Caught exception: %s" % str(err[1])
119 7e5a6e86 Guido Trotter
120 7e5a6e86 Guido Trotter
    try:
121 912b2278 Petr Pudlak
      reply = rpccl.FormatResponse(success, result)
122 7e5a6e86 Guido Trotter
      client.send_message(reply)
123 7e5a6e86 Guido Trotter
      # awake the main thread so that it can write out the data.
124 7e5a6e86 Guido Trotter
      server.awaker.signal()
125 b459a848 Andrea Spadaccini
    except: # pylint: disable=W0702
126 7e5a6e86 Guido Trotter
      logging.exception("Send error")
127 7e5a6e86 Guido Trotter
      client.close_log()
128 7e5a6e86 Guido Trotter
129 7e5a6e86 Guido Trotter
130 7e5a6e86 Guido Trotter
class MasterClientHandler(daemon.AsyncTerminatedMessageStream):
131 7e5a6e86 Guido Trotter
  """Handler for master peers.
132 7e5a6e86 Guido Trotter

133 7e5a6e86 Guido Trotter
  """
134 7e5a6e86 Guido Trotter
  _MAX_UNHANDLED = 1
135 e687ec01 Michael Hanselmann
136 7e5a6e86 Guido Trotter
  def __init__(self, server, connected_socket, client_address, family):
137 7e5a6e86 Guido Trotter
    daemon.AsyncTerminatedMessageStream.__init__(self, connected_socket,
138 7e5a6e86 Guido Trotter
                                                 client_address,
139 7e5a6e86 Guido Trotter
                                                 constants.LUXI_EOM,
140 7e5a6e86 Guido Trotter
                                                 family, self._MAX_UNHANDLED)
141 7e5a6e86 Guido Trotter
    self.server = server
142 7e5a6e86 Guido Trotter
143 7e5a6e86 Guido Trotter
  def handle_message(self, message, _):
144 b2e8a4d9 Michael Hanselmann
    self.server.request_workers.AddTask((self.server, message, self))
145 23e50d39 Michael Hanselmann
146 23e50d39 Michael Hanselmann
147 5483fd73 Michael Hanselmann
class _MasterShutdownCheck:
148 5483fd73 Michael Hanselmann
  """Logic for master daemon shutdown.
149 5483fd73 Michael Hanselmann

150 5483fd73 Michael Hanselmann
  """
151 5483fd73 Michael Hanselmann
  #: How long to wait between checks
152 5483fd73 Michael Hanselmann
  _CHECK_INTERVAL = 5.0
153 5483fd73 Michael Hanselmann
154 5483fd73 Michael Hanselmann
  #: How long to wait after all jobs are done (e.g. to give clients time to
155 5483fd73 Michael Hanselmann
  #: retrieve the job status)
156 5483fd73 Michael Hanselmann
  _SHUTDOWN_LINGER = 5.0
157 5483fd73 Michael Hanselmann
158 5483fd73 Michael Hanselmann
  def __init__(self):
159 5483fd73 Michael Hanselmann
    """Initializes this class.
160 5483fd73 Michael Hanselmann

161 5483fd73 Michael Hanselmann
    """
162 5483fd73 Michael Hanselmann
    self._had_active_jobs = None
163 5483fd73 Michael Hanselmann
    self._linger_timeout = None
164 5483fd73 Michael Hanselmann
165 5483fd73 Michael Hanselmann
  def __call__(self, jq_prepare_result):
166 5483fd73 Michael Hanselmann
    """Determines if master daemon is ready for shutdown.
167 5483fd73 Michael Hanselmann

168 5483fd73 Michael Hanselmann
    @param jq_prepare_result: Result of L{jqueue.JobQueue.PrepareShutdown}
169 5483fd73 Michael Hanselmann
    @rtype: None or number
170 5483fd73 Michael Hanselmann
    @return: None if master daemon is ready, timeout if the check must be
171 5483fd73 Michael Hanselmann
             repeated
172 5483fd73 Michael Hanselmann

173 5483fd73 Michael Hanselmann
    """
174 5483fd73 Michael Hanselmann
    if jq_prepare_result:
175 5483fd73 Michael Hanselmann
      # Check again shortly
176 5483fd73 Michael Hanselmann
      logging.info("Job queue has been notified for shutdown but is still"
177 5483fd73 Michael Hanselmann
                   " busy; next check in %s seconds", self._CHECK_INTERVAL)
178 5483fd73 Michael Hanselmann
      self._had_active_jobs = True
179 5483fd73 Michael Hanselmann
      return self._CHECK_INTERVAL
180 5483fd73 Michael Hanselmann
181 5483fd73 Michael Hanselmann
    if not self._had_active_jobs:
182 5483fd73 Michael Hanselmann
      # Can shut down as there were no active jobs on the first check
183 5483fd73 Michael Hanselmann
      return None
184 5483fd73 Michael Hanselmann
185 5483fd73 Michael Hanselmann
    # No jobs are running anymore, but maybe some clients want to collect some
186 5483fd73 Michael Hanselmann
    # information. Give them a short amount of time.
187 5483fd73 Michael Hanselmann
    if self._linger_timeout is None:
188 5483fd73 Michael Hanselmann
      self._linger_timeout = utils.RunningTimeout(self._SHUTDOWN_LINGER, True)
189 5483fd73 Michael Hanselmann
190 5483fd73 Michael Hanselmann
    remaining = self._linger_timeout.Remaining()
191 5483fd73 Michael Hanselmann
192 5483fd73 Michael Hanselmann
    logging.info("Job queue no longer busy; shutting down master daemon"
193 5483fd73 Michael Hanselmann
                 " in %s seconds", remaining)
194 5483fd73 Michael Hanselmann
195 5483fd73 Michael Hanselmann
    # TODO: Should the master daemon socket be closed at this point? Doing so
196 5483fd73 Michael Hanselmann
    # wouldn't affect existing connections.
197 5483fd73 Michael Hanselmann
198 5483fd73 Michael Hanselmann
    if remaining < 0:
199 5483fd73 Michael Hanselmann
      return None
200 5483fd73 Michael Hanselmann
    else:
201 5483fd73 Michael Hanselmann
      return remaining
202 5483fd73 Michael Hanselmann
203 5483fd73 Michael Hanselmann
204 cdd7f900 Guido Trotter
class MasterServer(daemon.AsyncStreamServer):
205 cdd7f900 Guido Trotter
  """Master Server.
206 ffeffa1d Iustin Pop

207 cdd7f900 Guido Trotter
  This is the main asynchronous master server. It handles connections to the
208 cdd7f900 Guido Trotter
  master socket.
209 ffeffa1d Iustin Pop

210 ffeffa1d Iustin Pop
  """
211 7e5a6e86 Guido Trotter
  family = socket.AF_UNIX
212 7e5a6e86 Guido Trotter
213 e8a701f6 Michael Hanselmann
  def __init__(self, address, uid, gid):
214 cdd7f900 Guido Trotter
    """MasterServer constructor
215 ce862cd5 Guido Trotter

216 cdd7f900 Guido Trotter
    @param address: the unix socket address to bind the MasterServer to
217 bbfd0568 René Nussbaumer
    @param uid: The uid of the owner of the socket
218 bbfd0568 René Nussbaumer
    @param gid: The gid of the owner of the socket
219 ce862cd5 Guido Trotter

220 ce862cd5 Guido Trotter
    """
221 bbfd0568 René Nussbaumer
    temp_name = tempfile.mktemp(dir=os.path.dirname(address))
222 7e5a6e86 Guido Trotter
    daemon.AsyncStreamServer.__init__(self, self.family, temp_name)
223 bbfd0568 René Nussbaumer
    os.chmod(temp_name, 0770)
224 bbfd0568 René Nussbaumer
    os.chown(temp_name, uid, gid)
225 bbfd0568 René Nussbaumer
    os.rename(temp_name, address)
226 bbfd0568 René Nussbaumer
227 7e5a6e86 Guido Trotter
    self.awaker = daemon.AsyncAwaker()
228 50a3fbb2 Michael Hanselmann
229 50a3fbb2 Michael Hanselmann
    # We'll only start threads once we've forked.
230 9113300d Michael Hanselmann
    self.context = None
231 23e50d39 Michael Hanselmann
    self.request_workers = None
232 50a3fbb2 Michael Hanselmann
233 5483fd73 Michael Hanselmann
    self._shutdown_check = None
234 5483fd73 Michael Hanselmann
235 cdd7f900 Guido Trotter
  def handle_connection(self, connected_socket, client_address):
236 7e5a6e86 Guido Trotter
    # TODO: add connection count and limit the number of open connections to a
237 7e5a6e86 Guido Trotter
    # maximum number to avoid breaking for lack of file descriptors or memory.
238 7e5a6e86 Guido Trotter
    MasterClientHandler(self, connected_socket, client_address, self.family)
239 cdd7f900 Guido Trotter
240 50a3fbb2 Michael Hanselmann
  def setup_queue(self):
241 9113300d Michael Hanselmann
    self.context = GanetiContext()
242 89e2b4d2 Michael Hanselmann
    self.request_workers = workerpool.WorkerPool("ClientReq",
243 89e2b4d2 Michael Hanselmann
                                                 CLIENT_REQUEST_WORKERS,
244 23e50d39 Michael Hanselmann
                                                 ClientRequestWorker)
245 ffeffa1d Iustin Pop
246 5483fd73 Michael Hanselmann
  def WaitForShutdown(self):
247 5483fd73 Michael Hanselmann
    """Prepares server for shutdown.
248 5483fd73 Michael Hanselmann

249 5483fd73 Michael Hanselmann
    """
250 5483fd73 Michael Hanselmann
    if self._shutdown_check is None:
251 5483fd73 Michael Hanselmann
      self._shutdown_check = _MasterShutdownCheck()
252 5483fd73 Michael Hanselmann
253 5483fd73 Michael Hanselmann
    return self._shutdown_check(self.context.jobqueue.PrepareShutdown())
254 5483fd73 Michael Hanselmann
255 c1f2901b Iustin Pop
  def server_cleanup(self):
256 c1f2901b Iustin Pop
    """Cleanup the server.
257 c1f2901b Iustin Pop

258 c1f2901b Iustin Pop
    This involves shutting down the processor threads and the master
259 c1f2901b Iustin Pop
    socket.
260 c1f2901b Iustin Pop

261 c1f2901b Iustin Pop
    """
262 50a3fbb2 Michael Hanselmann
    try:
263 cdd7f900 Guido Trotter
      self.close()
264 50a3fbb2 Michael Hanselmann
    finally:
265 23e50d39 Michael Hanselmann
      if self.request_workers:
266 36088c4c Michael Hanselmann
        self.request_workers.TerminateWorkers()
267 9113300d Michael Hanselmann
      if self.context:
268 9113300d Michael Hanselmann
        self.context.jobqueue.Shutdown()
269 ffeffa1d Iustin Pop
270 ffeffa1d Iustin Pop
271 ffeffa1d Iustin Pop
class ClientOps:
272 ffeffa1d Iustin Pop
  """Class holding high-level client operations."""
273 ffeffa1d Iustin Pop
  def __init__(self, server):
274 ffeffa1d Iustin Pop
    self.server = server
275 ffeffa1d Iustin Pop
276 b459a848 Andrea Spadaccini
  def handle_request(self, method, args): # pylint: disable=R0911
277 e07f7f7a Michael Hanselmann
    context = self.server.context
278 e07f7f7a Michael Hanselmann
    queue = context.jobqueue
279 0bbe448c Michael Hanselmann
280 0bbe448c Michael Hanselmann
    # TODO: Parameter validation
281 a629ecb9 Iustin Pop
    if not isinstance(args, (tuple, list)):
282 a629ecb9 Iustin Pop
      logging.info("Received invalid arguments of type '%s'", type(args))
283 a629ecb9 Iustin Pop
      raise ValueError("Invalid arguments type '%s'" % type(args))
284 0bbe448c Michael Hanselmann
285 64d7e30f Klaus Aehlig
    if method not in luxi.REQ_ALL:
286 64d7e30f Klaus Aehlig
      logging.info("Received invalid request '%s'", method)
287 64d7e30f Klaus Aehlig
      raise ValueError("Invalid operation '%s'" % method)
288 64d7e30f Klaus Aehlig
289 7260cfbe Iustin Pop
    # TODO: Rewrite to not exit in each 'if/elif' branch
290 7260cfbe Iustin Pop
291 0bbe448c Michael Hanselmann
    if method == luxi.REQ_SUBMIT_JOB:
292 4c91d2ad Iustin Pop
      logging.info("Receiving new job")
293 734a2a7c René Nussbaumer
      (job_def, ) = args
294 734a2a7c René Nussbaumer
      ops = [opcodes.OpCode.LoadOpCode(state) for state in job_def]
295 4c91d2ad Iustin Pop
      job_id = queue.SubmitJob(ops)
296 4c91d2ad Iustin Pop
      _LogNewJob(True, job_id, ops)
297 346c3037 Klaus Aehlig
      return job_id
298 346c3037 Klaus Aehlig
299 d9d1e541 Klaus Aehlig
    elif method == luxi.REQ_PICKUP_JOB:
300 d9d1e541 Klaus Aehlig
      logging.info("Picking up new job from queue")
301 d9d1e541 Klaus Aehlig
      (job_id, ) = args
302 d9d1e541 Klaus Aehlig
      queue.PickupJob(job_id)
303 d9d1e541 Klaus Aehlig
304 346c3037 Klaus Aehlig
    elif method == luxi.REQ_SUBMIT_JOB_TO_DRAINED_QUEUE:
305 346c3037 Klaus Aehlig
      logging.info("Forcefully receiving new job")
306 346c3037 Klaus Aehlig
      (job_def, ) = args
307 346c3037 Klaus Aehlig
      ops = [opcodes.OpCode.LoadOpCode(state) for state in job_def]
308 346c3037 Klaus Aehlig
      job_id = queue.SubmitJobToDrainedQueue(ops)
309 346c3037 Klaus Aehlig
      _LogNewJob(True, job_id, ops)
310 4c91d2ad Iustin Pop
      return job_id
311 ffeffa1d Iustin Pop
312 1b5b1c49 René Nussbaumer
    elif method == luxi.REQ_SUBMIT_MANY_JOBS:
313 4c91d2ad Iustin Pop
      logging.info("Receiving multiple jobs")
314 734a2a7c René Nussbaumer
      (job_defs, ) = args
315 2971c913 Iustin Pop
      jobs = []
316 734a2a7c René Nussbaumer
      for ops in job_defs:
317 2971c913 Iustin Pop
        jobs.append([opcodes.OpCode.LoadOpCode(state) for state in ops])
318 4c91d2ad Iustin Pop
      job_ids = queue.SubmitManyJobs(jobs)
319 4c91d2ad Iustin Pop
      for ((status, job_id), ops) in zip(job_ids, jobs):
320 4c91d2ad Iustin Pop
        _LogNewJob(status, job_id, ops)
321 4c91d2ad Iustin Pop
      return job_ids
322 2971c913 Iustin Pop
323 0bbe448c Michael Hanselmann
    elif method == luxi.REQ_CANCEL_JOB:
324 a629ecb9 Iustin Pop
      (job_id, ) = args
325 e566ddbd Iustin Pop
      logging.info("Received job cancel request for %s", job_id)
326 0bbe448c Michael Hanselmann
      return queue.CancelJob(job_id)
327 ffeffa1d Iustin Pop
328 f63ffb37 Michael Hanselmann
    elif method == luxi.REQ_CHANGE_JOB_PRIORITY:
329 f63ffb37 Michael Hanselmann
      (job_id, priority) = args
330 f63ffb37 Michael Hanselmann
      logging.info("Received request to change priority for job %s to %s",
331 f63ffb37 Michael Hanselmann
                   job_id, priority)
332 f63ffb37 Michael Hanselmann
      return queue.ChangeJobPriority(job_id, priority)
333 f63ffb37 Michael Hanselmann
334 0bbe448c Michael Hanselmann
    elif method == luxi.REQ_ARCHIVE_JOB:
335 a629ecb9 Iustin Pop
      (job_id, ) = args
336 e566ddbd Iustin Pop
      logging.info("Received job archive request for %s", job_id)
337 0bbe448c Michael Hanselmann
      return queue.ArchiveJob(job_id)
338 0bbe448c Michael Hanselmann
339 83c046a2 Iustin Pop
    elif method == luxi.REQ_AUTO_ARCHIVE_JOBS:
340 f8ad5591 Michael Hanselmann
      (age, timeout) = args
341 e566ddbd Iustin Pop
      logging.info("Received job autoarchive request for age %s, timeout %s",
342 e566ddbd Iustin Pop
                   age, timeout)
343 f8ad5591 Michael Hanselmann
      return queue.AutoArchiveJobs(age, timeout)
344 07cd723a Iustin Pop
345 dfe57c22 Michael Hanselmann
    elif method == luxi.REQ_WAIT_FOR_JOB_CHANGE:
346 5c735209 Iustin Pop
      (job_id, fields, prev_job_info, prev_log_serial, timeout) = args
347 e566ddbd Iustin Pop
      logging.info("Received job poll request for %s", job_id)
348 6c5a7090 Michael Hanselmann
      return queue.WaitForJobChanges(job_id, fields, prev_job_info,
349 5c735209 Iustin Pop
                                     prev_log_serial, timeout)
350 dfe57c22 Michael Hanselmann
351 28b71a76 Michael Hanselmann
    elif method == luxi.REQ_QUERY:
352 a629ecb9 Iustin Pop
      (what, fields, qfilter) = args
353 28b71a76 Michael Hanselmann
354 b4b3266b Michael Hanselmann
      if what in constants.QR_VIA_OP:
355 b4b3266b Michael Hanselmann
        result = self._Query(opcodes.OpQuery(what=what, fields=fields,
356 b4b3266b Michael Hanselmann
                                             qfilter=qfilter))
357 b4b3266b Michael Hanselmann
      elif what == constants.QR_LOCK:
358 b4b3266b Michael Hanselmann
        if qfilter is not None:
359 2cfbc784 Iustin Pop
          raise errors.OpPrereqError("Lock queries can't be filtered",
360 2cfbc784 Iustin Pop
                                     errors.ECODE_INVAL)
361 b4b3266b Michael Hanselmann
        return context.glm.QueryLocks(fields)
362 b4b3266b Michael Hanselmann
      elif what == constants.QR_JOB:
363 b4b3266b Michael Hanselmann
        return queue.QueryJobs(fields, qfilter)
364 b4b3266b Michael Hanselmann
      elif what in constants.QR_VIA_LUXI:
365 a9532fb0 Helga Velroyen
        luxi_client = runtime.GetClient(query=True)
366 a9532fb0 Helga Velroyen
        result = luxi_client.Query(what, fields, qfilter).ToDict()
367 28b71a76 Michael Hanselmann
      else:
368 b4b3266b Michael Hanselmann
        raise errors.OpPrereqError("Resource type '%s' unknown" % what,
369 28b71a76 Michael Hanselmann
                                   errors.ECODE_INVAL)
370 28b71a76 Michael Hanselmann
371 28b71a76 Michael Hanselmann
      return result
372 28b71a76 Michael Hanselmann
373 28b71a76 Michael Hanselmann
    elif method == luxi.REQ_QUERY_FIELDS:
374 a629ecb9 Iustin Pop
      (what, fields) = args
375 a629ecb9 Iustin Pop
      req = objects.QueryFieldsRequest(what=what, fields=fields)
376 28b71a76 Michael Hanselmann
377 c1391810 Michael Hanselmann
      try:
378 c1391810 Michael Hanselmann
        fielddefs = query.ALL_FIELDS[req.what]
379 c1391810 Michael Hanselmann
      except KeyError:
380 28b71a76 Michael Hanselmann
        raise errors.OpPrereqError("Resource type '%s' unknown" % req.what,
381 28b71a76 Michael Hanselmann
                                   errors.ECODE_INVAL)
382 28b71a76 Michael Hanselmann
383 c1391810 Michael Hanselmann
      return query.QueryFields(fielddefs, req.fields)
384 28b71a76 Michael Hanselmann
385 0bbe448c Michael Hanselmann
    elif method == luxi.REQ_QUERY_JOBS:
386 0bbe448c Michael Hanselmann
      (job_ids, fields) = args
387 e566ddbd Iustin Pop
      if isinstance(job_ids, (tuple, list)) and job_ids:
388 1f864b60 Iustin Pop
        msg = utils.CommaJoin(job_ids)
389 e566ddbd Iustin Pop
      else:
390 e566ddbd Iustin Pop
        msg = str(job_ids)
391 e566ddbd Iustin Pop
      logging.info("Received job query request for %s", msg)
392 e07f7f7a Michael Hanselmann
      return queue.OldStyleQueryJobs(job_ids, fields)
393 0bbe448c Michael Hanselmann
394 ae5849b5 Michael Hanselmann
    elif method == luxi.REQ_QUERY_CONFIG_VALUES:
395 a629ecb9 Iustin Pop
      (fields, ) = args
396 e566ddbd Iustin Pop
      logging.info("Received config values query request for %s", fields)
397 2f093ea0 Iustin Pop
      op = opcodes.OpClusterConfigQuery(output_fields=fields)
398 ae5849b5 Michael Hanselmann
      return self._Query(op)
399 ae5849b5 Michael Hanselmann
400 66baeccc Iustin Pop
    elif method == luxi.REQ_QUERY_CLUSTER_INFO:
401 e566ddbd Iustin Pop
      logging.info("Received cluster info query request")
402 a2f7ab92 Iustin Pop
      op = opcodes.OpClusterQuery()
403 66baeccc Iustin Pop
      return self._Query(op)
404 66baeccc Iustin Pop
405 7699c3af Iustin Pop
    elif method == luxi.REQ_QUERY_TAGS:
406 a629ecb9 Iustin Pop
      (kind, name) = args
407 7699c3af Iustin Pop
      logging.info("Received tags query request")
408 cfdf561d Michael Hanselmann
      op = opcodes.OpTagsGet(kind=kind, name=name, use_locking=False)
409 7699c3af Iustin Pop
      return self._Query(op)
410 7699c3af Iustin Pop
411 83c046a2 Iustin Pop
    elif method == luxi.REQ_SET_DRAIN_FLAG:
412 a629ecb9 Iustin Pop
      (drain_flag, ) = args
413 e566ddbd Iustin Pop
      logging.info("Received queue drain flag change request to %s",
414 e566ddbd Iustin Pop
                   drain_flag)
415 3ccafd0e Iustin Pop
      return queue.SetDrainFlag(drain_flag)
416 3ccafd0e Iustin Pop
417 05e50653 Michael Hanselmann
    elif method == luxi.REQ_SET_WATCHER_PAUSE:
418 05e50653 Michael Hanselmann
      (until, ) = args
419 05e50653 Michael Hanselmann
420 7c4bd156 Michael Hanselmann
      return _SetWatcherPause(context, until)
421 05e50653 Michael Hanselmann
422 0bbe448c Michael Hanselmann
    else:
423 64d7e30f Klaus Aehlig
      logging.critical("Request '%s' in luxi.REQ_ALL, but not known", method)
424 64d7e30f Klaus Aehlig
      raise errors.ProgrammerError("Operation '%s' in luxi.REQ_ALL,"
425 64d7e30f Klaus Aehlig
                                   " but not implemented" % method)
426 ffeffa1d Iustin Pop
427 ee6c7b94 Michael Hanselmann
  def _Query(self, op):
428 ee6c7b94 Michael Hanselmann
    """Runs the specified opcode and returns the result.
429 ee6c7b94 Michael Hanselmann

430 ee6c7b94 Michael Hanselmann
    """
431 adfa97e3 Guido Trotter
    # Queries don't have a job id
432 dc4bdf73 Michael Hanselmann
    proc = mcpu.Processor(self.server.context, None, enable_locks=False)
433 26d3fd2f Michael Hanselmann
434 26d3fd2f Michael Hanselmann
    # TODO: Executing an opcode using locks will acquire them in blocking mode.
435 26d3fd2f Michael Hanselmann
    # Consider using a timeout for retries.
436 031a3e57 Michael Hanselmann
    return proc.ExecOpCode(op, None)
437 ee6c7b94 Michael Hanselmann
438 ffeffa1d Iustin Pop
439 39dcf2ef Guido Trotter
class GanetiContext(object):
440 39dcf2ef Guido Trotter
  """Context common to all ganeti threads.
441 39dcf2ef Guido Trotter

442 39dcf2ef Guido Trotter
  This class creates and holds common objects shared by all threads.
443 39dcf2ef Guido Trotter

444 39dcf2ef Guido Trotter
  """
445 b459a848 Andrea Spadaccini
  # pylint: disable=W0212
446 7260cfbe Iustin Pop
  # we do want to ensure a singleton here
447 39dcf2ef Guido Trotter
  _instance = None
448 39dcf2ef Guido Trotter
449 39dcf2ef Guido Trotter
  def __init__(self):
450 39dcf2ef Guido Trotter
    """Constructs a new GanetiContext object.
451 39dcf2ef Guido Trotter

452 39dcf2ef Guido Trotter
    There should be only a GanetiContext object at any time, so this
453 39dcf2ef Guido Trotter
    function raises an error if this is not the case.
454 39dcf2ef Guido Trotter

455 39dcf2ef Guido Trotter
    """
456 39dcf2ef Guido Trotter
    assert self.__class__._instance is None, "double GanetiContext instance"
457 39dcf2ef Guido Trotter
458 9113300d Michael Hanselmann
    # Create global configuration object
459 39dcf2ef Guido Trotter
    self.cfg = config.ConfigWriter()
460 9113300d Michael Hanselmann
461 9113300d Michael Hanselmann
    # Locking manager
462 984f7c32 Guido Trotter
    self.glm = locking.GanetiLockManager(
463 5ae4945a Iustin Pop
      self.cfg.GetNodeList(),
464 5ae4945a Iustin Pop
      self.cfg.GetNodeGroupList(),
465 da4a52a3 Thomas Thrainer
      [inst.name for inst in self.cfg.GetAllInstancesInfo().values()],
466 6c0a75db Dimitris Aragiorgis
      self.cfg.GetNetworkList())
467 39dcf2ef Guido Trotter
468 b2acdbdc Michael Hanselmann
    self.cfg.SetContext(self)
469 b2acdbdc Michael Hanselmann
470 87b3cb26 Michael Hanselmann
    # RPC runner
471 d5ea30e8 Michael Hanselmann
    self.rpc = rpc.RpcRunner(self.cfg, self.glm.AddToLockMonitor)
472 87b3cb26 Michael Hanselmann
473 cb4d3314 Michael Hanselmann
    # Job queue
474 cb4d3314 Michael Hanselmann
    self.jobqueue = jqueue.JobQueue(self)
475 cb4d3314 Michael Hanselmann
476 39dcf2ef Guido Trotter
    # setting this also locks the class against attribute modifications
477 39dcf2ef Guido Trotter
    self.__class__._instance = self
478 39dcf2ef Guido Trotter
479 39dcf2ef Guido Trotter
  def __setattr__(self, name, value):
480 39dcf2ef Guido Trotter
    """Setting GanetiContext attributes is forbidden after initialization.
481 39dcf2ef Guido Trotter

482 39dcf2ef Guido Trotter
    """
483 39dcf2ef Guido Trotter
    assert self.__class__._instance is None, "Attempt to modify Ganeti Context"
484 39dcf2ef Guido Trotter
    object.__setattr__(self, name, value)
485 39dcf2ef Guido Trotter
486 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
487 d8470559 Michael Hanselmann
    """Adds a node to the configuration and lock manager.
488 d8470559 Michael Hanselmann

489 d8470559 Michael Hanselmann
    """
490 d8470559 Michael Hanselmann
    # Add it to the configuration
491 0debfb35 Guido Trotter
    self.cfg.AddNode(node, ec_id)
492 d8470559 Michael Hanselmann
493 c36176cc Michael Hanselmann
    # If preseeding fails it'll not be added
494 99aabbed Iustin Pop
    self.jobqueue.AddNode(node)
495 c36176cc Michael Hanselmann
496 d8470559 Michael Hanselmann
    # Add the new node to the Ganeti Lock Manager
497 1c3231aa Thomas Thrainer
    self.glm.add(locking.LEVEL_NODE, node.uuid)
498 1c3231aa Thomas Thrainer
    self.glm.add(locking.LEVEL_NODE_RES, node.uuid)
499 d8470559 Michael Hanselmann
500 d8470559 Michael Hanselmann
  def ReaddNode(self, node):
501 d8470559 Michael Hanselmann
    """Updates a node that's already in the configuration
502 d8470559 Michael Hanselmann

503 d8470559 Michael Hanselmann
    """
504 c36176cc Michael Hanselmann
    # Synchronize the queue again
505 99aabbed Iustin Pop
    self.jobqueue.AddNode(node)
506 d8470559 Michael Hanselmann
507 1c3231aa Thomas Thrainer
  def RemoveNode(self, node):
508 d8470559 Michael Hanselmann
    """Removes a node from the configuration and lock manager.
509 d8470559 Michael Hanselmann

510 d8470559 Michael Hanselmann
    """
511 d8470559 Michael Hanselmann
    # Remove node from configuration
512 1c3231aa Thomas Thrainer
    self.cfg.RemoveNode(node.uuid)
513 d8470559 Michael Hanselmann
514 c36176cc Michael Hanselmann
    # Notify job queue
515 1c3231aa Thomas Thrainer
    self.jobqueue.RemoveNode(node.name)
516 c36176cc Michael Hanselmann
517 d8470559 Michael Hanselmann
    # Remove the node from the Ganeti Lock Manager
518 1c3231aa Thomas Thrainer
    self.glm.remove(locking.LEVEL_NODE, node.uuid)
519 1c3231aa Thomas Thrainer
    self.glm.remove(locking.LEVEL_NODE_RES, node.uuid)
520 d8470559 Michael Hanselmann
521 39dcf2ef Guido Trotter
522 7c4bd156 Michael Hanselmann
def _SetWatcherPause(context, until):
523 05e50653 Michael Hanselmann
  """Creates or removes the watcher pause file.
524 05e50653 Michael Hanselmann

525 7c4bd156 Michael Hanselmann
  @type context: L{GanetiContext}
526 7c4bd156 Michael Hanselmann
  @param context: Global Ganeti context
527 05e50653 Michael Hanselmann
  @type until: None or int
528 05e50653 Michael Hanselmann
  @param until: Unix timestamp saying until when the watcher shouldn't run
529 05e50653 Michael Hanselmann

530 05e50653 Michael Hanselmann
  """
531 7c4bd156 Michael Hanselmann
  node_names = context.cfg.GetNodeList()
532 7c4bd156 Michael Hanselmann
533 05e50653 Michael Hanselmann
  if until is None:
534 7c4bd156 Michael Hanselmann
    logging.info("Received request to no longer pause watcher")
535 05e50653 Michael Hanselmann
  else:
536 7c4bd156 Michael Hanselmann
    if not ht.TNumber(until):
537 7c4bd156 Michael Hanselmann
      raise TypeError("Duration must be numeric")
538 7c4bd156 Michael Hanselmann
539 7c4bd156 Michael Hanselmann
    if until < time.time():
540 7c4bd156 Michael Hanselmann
      raise errors.GenericError("Unable to set pause end time in the past")
541 7c4bd156 Michael Hanselmann
542 7c4bd156 Michael Hanselmann
    logging.info("Received request to pause watcher until %s", until)
543 7c4bd156 Michael Hanselmann
544 7c4bd156 Michael Hanselmann
  result = context.rpc.call_set_watcher_pause(node_names, until)
545 7c4bd156 Michael Hanselmann
546 7c4bd156 Michael Hanselmann
  errmsg = utils.CommaJoin("%s (%s)" % (node_name, nres.fail_msg)
547 7c4bd156 Michael Hanselmann
                           for (node_name, nres) in result.items()
548 7c4bd156 Michael Hanselmann
                           if nres.fail_msg and not nres.offline)
549 7c4bd156 Michael Hanselmann
  if errmsg:
550 7c4bd156 Michael Hanselmann
    raise errors.OpExecError("Watcher pause was set where possible, but failed"
551 7c4bd156 Michael Hanselmann
                             " on the following node(s): %s" % errmsg)
552 05e50653 Michael Hanselmann
553 28b498cd Michael Hanselmann
  return until
554 28b498cd Michael Hanselmann
555 05e50653 Michael Hanselmann
556 e0e916fe Iustin Pop
@rpc.RunWithRPC
557 36205981 Iustin Pop
def CheckAgreement():
558 36205981 Iustin Pop
  """Check the agreement on who is the master.
559 36205981 Iustin Pop

560 36205981 Iustin Pop
  The function uses a very simple algorithm: we must get more positive
561 36205981 Iustin Pop
  than negative answers. Since in most of the cases we are the master,
562 36205981 Iustin Pop
  we'll use our own config file for getting the node list. In the
563 36205981 Iustin Pop
  future we could collect the current node list from our (possibly
564 36205981 Iustin Pop
  obsolete) known nodes.
565 36205981 Iustin Pop

566 d7cdb55d Iustin Pop
  In order to account for cold-start of all nodes, we retry for up to
567 d7cdb55d Iustin Pop
  a minute until we get a real answer as the top-voted one. If the
568 d7cdb55d Iustin Pop
  nodes are more out-of-sync, for now manual startup of the master
569 d7cdb55d Iustin Pop
  should be attempted.
570 d7cdb55d Iustin Pop

571 d7cdb55d Iustin Pop
  Note that for a even number of nodes cluster, we need at least half
572 d7cdb55d Iustin Pop
  of the nodes (beside ourselves) to vote for us. This creates a
573 d7cdb55d Iustin Pop
  problem on two-node clusters, since in this case we require the
574 d7cdb55d Iustin Pop
  other node to be up too to confirm our status.
575 d7cdb55d Iustin Pop

576 36205981 Iustin Pop
  """
577 b705c7a6 Manuel Franceschini
  myself = netutils.Hostname.GetSysName()
578 36205981 Iustin Pop
  #temp instantiation of a config writer, used only to get the node list
579 36205981 Iustin Pop
  cfg = config.ConfigWriter()
580 1c3231aa Thomas Thrainer
  node_names = cfg.GetNodeNames(cfg.GetNodeList())
581 36205981 Iustin Pop
  del cfg
582 d7cdb55d Iustin Pop
  retries = 6
583 d7cdb55d Iustin Pop
  while retries > 0:
584 1c3231aa Thomas Thrainer
    votes = bootstrap.GatherMasterVotes(node_names)
585 d7cdb55d Iustin Pop
    if not votes:
586 d7cdb55d Iustin Pop
      # empty node list, this is a one node cluster
587 d7cdb55d Iustin Pop
      return True
588 d7cdb55d Iustin Pop
    if votes[0][0] is None:
589 d7cdb55d Iustin Pop
      retries -= 1
590 d7cdb55d Iustin Pop
      time.sleep(10)
591 36205981 Iustin Pop
      continue
592 d7cdb55d Iustin Pop
    break
593 d7cdb55d Iustin Pop
  if retries == 0:
594 e09fdcfa Iustin Pop
    logging.critical("Cluster inconsistent, most of the nodes didn't answer"
595 e09fdcfa Iustin Pop
                     " after multiple retries. Aborting startup")
596 d8f5a37d Iustin Pop
    logging.critical("Use the --no-voting option if you understand what"
597 d8f5a37d Iustin Pop
                     " effects it has on the cluster state")
598 e09fdcfa Iustin Pop
    return False
599 d7cdb55d Iustin Pop
  # here a real node is at the top of the list
600 d7cdb55d Iustin Pop
  all_votes = sum(item[1] for item in votes)
601 d7cdb55d Iustin Pop
  top_node, top_votes = votes[0]
602 8a20c732 Michael Hanselmann
603 d7cdb55d Iustin Pop
  result = False
604 d7cdb55d Iustin Pop
  if top_node != myself:
605 d7cdb55d Iustin Pop
    logging.critical("It seems we are not the master (top-voted node"
606 bbe19c17 Iustin Pop
                     " is %s with %d out of %d votes)", top_node, top_votes,
607 bbe19c17 Iustin Pop
                     all_votes)
608 d7cdb55d Iustin Pop
  elif top_votes < all_votes - top_votes:
609 36205981 Iustin Pop
    logging.critical("It seems we are not the master (%d votes for,"
610 d7cdb55d Iustin Pop
                     " %d votes against)", top_votes, all_votes - top_votes)
611 d7cdb55d Iustin Pop
  else:
612 d7cdb55d Iustin Pop
    result = True
613 d7cdb55d Iustin Pop
614 d7cdb55d Iustin Pop
  return result
615 36205981 Iustin Pop
616 6c948699 Michael Hanselmann
617 340f4757 Iustin Pop
@rpc.RunWithRPC
618 340f4757 Iustin Pop
def ActivateMasterIP():
619 340f4757 Iustin Pop
  # activate ip
620 8da2bd43 Andrea Spadaccini
  cfg = config.ConfigWriter()
621 f9d20654 Andrea Spadaccini
  master_params = cfg.GetMasterNetworkParameters()
622 57c7bc57 Andrea Spadaccini
  ems = cfg.GetUseExternalMipScript()
623 8da2bd43 Andrea Spadaccini
  runner = rpc.BootstrapRunner()
624 1c3231aa Thomas Thrainer
  # we use the node name, as the configuration is only available here yet
625 1c3231aa Thomas Thrainer
  result = runner.call_node_activate_master_ip(
626 1c3231aa Thomas Thrainer
             cfg.GetNodeName(master_params.uuid), master_params, ems)
627 8da2bd43 Andrea Spadaccini
628 340f4757 Iustin Pop
  msg = result.fail_msg
629 340f4757 Iustin Pop
  if msg:
630 340f4757 Iustin Pop
    logging.error("Can't activate master IP address: %s", msg)
631 340f4757 Iustin Pop
632 340f4757 Iustin Pop
633 ed0efaa5 Michael Hanselmann
def CheckMasterd(options, args):
634 ed0efaa5 Michael Hanselmann
  """Initial checks whether to run or exit with a failure.
635 ed0efaa5 Michael Hanselmann

636 ed0efaa5 Michael Hanselmann
  """
637 f93427cd Iustin Pop
  if args: # masterd doesn't take any arguments
638 f93427cd Iustin Pop
    print >> sys.stderr, ("Usage: %s [-f] [-d]" % sys.argv[0])
639 f93427cd Iustin Pop
    sys.exit(constants.EXIT_FAILURE)
640 f93427cd Iustin Pop
641 ed0efaa5 Michael Hanselmann
  ssconf.CheckMaster(options.debug)
642 ed0efaa5 Michael Hanselmann
643 bbfd0568 René Nussbaumer
  try:
644 bbfd0568 René Nussbaumer
    options.uid = pwd.getpwnam(constants.MASTERD_USER).pw_uid
645 bbfd0568 René Nussbaumer
    options.gid = grp.getgrnam(constants.DAEMONS_GROUP).gr_gid
646 bbfd0568 René Nussbaumer
  except KeyError:
647 bbfd0568 René Nussbaumer
    print >> sys.stderr, ("User or group not existing on system: %s:%s" %
648 bbfd0568 René Nussbaumer
                          (constants.MASTERD_USER, constants.DAEMONS_GROUP))
649 bbfd0568 René Nussbaumer
    sys.exit(constants.EXIT_FAILURE)
650 bbfd0568 René Nussbaumer
651 a20e4768 Michael Hanselmann
  # Determine static runtime architecture information
652 a20e4768 Michael Hanselmann
  runtime.InitArchInfo()
653 a20e4768 Michael Hanselmann
654 4b63dc7a Iustin Pop
  # Check the configuration is sane before anything else
655 4b63dc7a Iustin Pop
  try:
656 4b63dc7a Iustin Pop
    config.ConfigWriter()
657 4b63dc7a Iustin Pop
  except errors.ConfigVersionMismatch, err:
658 effc1b86 Jose A. Lopes
    v1 = "%s.%s.%s" % version.SplitVersion(err.args[0])
659 effc1b86 Jose A. Lopes
    v2 = "%s.%s.%s" % version.SplitVersion(err.args[1])
660 4b63dc7a Iustin Pop
    print >> sys.stderr,  \
661 4b63dc7a Iustin Pop
        ("Configuration version mismatch. The current Ganeti software"
662 4b63dc7a Iustin Pop
         " expects version %s, but the on-disk configuration file has"
663 4b63dc7a Iustin Pop
         " version %s. This is likely the result of upgrading the"
664 4b63dc7a Iustin Pop
         " software without running the upgrade procedure. Please contact"
665 4b63dc7a Iustin Pop
         " your cluster administrator or complete the upgrade using the"
666 4b63dc7a Iustin Pop
         " cfgupgrade utility, after reading the upgrade notes." %
667 4b63dc7a Iustin Pop
         (v1, v2))
668 4b63dc7a Iustin Pop
    sys.exit(constants.EXIT_FAILURE)
669 4b63dc7a Iustin Pop
  except errors.ConfigurationError, err:
670 4b63dc7a Iustin Pop
    print >> sys.stderr, \
671 4b63dc7a Iustin Pop
        ("Configuration error while opening the configuration file: %s\n"
672 4b63dc7a Iustin Pop
         "This might be caused by an incomplete software upgrade or"
673 4b63dc7a Iustin Pop
         " by a corrupted configuration file. Until the problem is fixed"
674 4b63dc7a Iustin Pop
         " the master daemon cannot start." % str(err))
675 4b63dc7a Iustin Pop
    sys.exit(constants.EXIT_FAILURE)
676 bbfd0568 René Nussbaumer
677 ed0efaa5 Michael Hanselmann
  # If CheckMaster didn't fail we believe we are the master, but we have to
678 ed0efaa5 Michael Hanselmann
  # confirm with the other nodes.
679 ed0efaa5 Michael Hanselmann
  if options.no_voting:
680 675e2bf5 Iustin Pop
    if not options.yes_do_it:
681 675e2bf5 Iustin Pop
      sys.stdout.write("The 'no voting' option has been selected.\n")
682 675e2bf5 Iustin Pop
      sys.stdout.write("This is dangerous, please confirm by"
683 675e2bf5 Iustin Pop
                       " typing uppercase 'yes': ")
684 675e2bf5 Iustin Pop
      sys.stdout.flush()
685 ed0efaa5 Michael Hanselmann
686 675e2bf5 Iustin Pop
      confirmation = sys.stdin.readline().strip()
687 675e2bf5 Iustin Pop
      if confirmation != "YES":
688 675e2bf5 Iustin Pop
        print >> sys.stderr, "Aborting."
689 675e2bf5 Iustin Pop
        sys.exit(constants.EXIT_FAILURE)
690 ed0efaa5 Michael Hanselmann
691 675e2bf5 Iustin Pop
  else:
692 675e2bf5 Iustin Pop
    # CheckAgreement uses RPC and threads, hence it needs to be run in
693 675e2bf5 Iustin Pop
    # a separate process before we call utils.Daemonize in the current
694 675e2bf5 Iustin Pop
    # process.
695 675e2bf5 Iustin Pop
    if not utils.RunInSeparateProcess(CheckAgreement):
696 ed0efaa5 Michael Hanselmann
      sys.exit(constants.EXIT_FAILURE)
697 ed0efaa5 Michael Hanselmann
698 340f4757 Iustin Pop
  # ActivateMasterIP also uses RPC/threads, so we run it again via a
699 340f4757 Iustin Pop
  # separate process.
700 340f4757 Iustin Pop
701 340f4757 Iustin Pop
  # TODO: decide whether failure to activate the master IP is a fatal error
702 340f4757 Iustin Pop
  utils.RunInSeparateProcess(ActivateMasterIP)
703 340f4757 Iustin Pop
704 ed0efaa5 Michael Hanselmann
705 3ee53f1f Iustin Pop
def PrepMasterd(options, _):
706 3ee53f1f Iustin Pop
  """Prep master daemon function, executed with the PID file held.
707 3b316acb Iustin Pop

708 04ccf5e9 Guido Trotter
  """
709 04ccf5e9 Guido Trotter
  # This is safe to do as the pid file guarantees against
710 04ccf5e9 Guido Trotter
  # concurrent execution.
711 a5ce2ea2 Michael Hanselmann
  utils.RemoveFile(pathutils.MASTER_SOCKET)
712 b1b6ea87 Iustin Pop
713 cdd7f900 Guido Trotter
  mainloop = daemon.Mainloop()
714 a5ce2ea2 Michael Hanselmann
  master = MasterServer(pathutils.MASTER_SOCKET, options.uid, options.gid)
715 3ee53f1f Iustin Pop
  return (mainloop, master)
716 3ee53f1f Iustin Pop
717 3ee53f1f Iustin Pop
718 b459a848 Andrea Spadaccini
def ExecMasterd(options, args, prep_data): # pylint: disable=W0613
719 3ee53f1f Iustin Pop
  """Main master daemon function, executed with the PID file held.
720 3ee53f1f Iustin Pop

721 3ee53f1f Iustin Pop
  """
722 3ee53f1f Iustin Pop
  (mainloop, master) = prep_data
723 04ccf5e9 Guido Trotter
  try:
724 15486fa7 Michael Hanselmann
    rpc.Init()
725 4331f6cd Michael Hanselmann
    try:
726 15486fa7 Michael Hanselmann
      master.setup_queue()
727 15486fa7 Michael Hanselmann
      try:
728 5483fd73 Michael Hanselmann
        mainloop.Run(shutdown_wait_fn=master.WaitForShutdown)
729 15486fa7 Michael Hanselmann
      finally:
730 15486fa7 Michael Hanselmann
        master.server_cleanup()
731 4331f6cd Michael Hanselmann
    finally:
732 15486fa7 Michael Hanselmann
      rpc.Shutdown()
733 a4af651e Iustin Pop
  finally:
734 a5ce2ea2 Michael Hanselmann
    utils.RemoveFile(pathutils.MASTER_SOCKET)
735 a4af651e Iustin Pop
736 5483fd73 Michael Hanselmann
  logging.info("Clean master daemon shutdown")
737 5483fd73 Michael Hanselmann
738 ffeffa1d Iustin Pop
739 29d91329 Michael Hanselmann
def Main():
740 04ccf5e9 Guido Trotter
  """Main function"""
741 04ccf5e9 Guido Trotter
  parser = OptionParser(description="Ganeti master daemon",
742 04ccf5e9 Guido Trotter
                        usage="%prog [-f] [-d]",
743 04ccf5e9 Guido Trotter
                        version="%%prog (ganeti) %s" %
744 04ccf5e9 Guido Trotter
                        constants.RELEASE_VERSION)
745 04ccf5e9 Guido Trotter
  parser.add_option("--no-voting", dest="no_voting",
746 04ccf5e9 Guido Trotter
                    help="Do not check that the nodes agree on this node"
747 04ccf5e9 Guido Trotter
                    " being the master and start the daemon unconditionally",
748 04ccf5e9 Guido Trotter
                    default=False, action="store_true")
749 04ccf5e9 Guido Trotter
  parser.add_option("--yes-do-it", dest="yes_do_it",
750 04ccf5e9 Guido Trotter
                    help="Override interactive check for --no-voting",
751 04ccf5e9 Guido Trotter
                    default=False, action="store_true")
752 3ee53f1f Iustin Pop
  daemon.GenericMain(constants.MASTERD, parser, CheckMasterd, PrepMasterd,
753 b42ea9ed Iustin Pop
                     ExecMasterd, multithreaded=True)