Statistics
| Branch: | Tag: | Revision:

root / lib / server / masterd.py @ 4869595d

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 d7cdb55d Iustin Pop
from ganeti import bootstrap
57 a744b676 Manuel Franceschini
from ganeti import netutils
58 28b71a76 Michael Hanselmann
from ganeti import objects
59 24d16f76 Michael Hanselmann
from ganeti import query
60 a20e4768 Michael Hanselmann
from ganeti import runtime
61 a5ce2ea2 Michael Hanselmann
from ganeti import pathutils
62 7c4bd156 Michael Hanselmann
from ganeti import ht
63 c1f2901b Iustin Pop
64 effc1b86 Jose A. Lopes
from ganeti.utils import version
65 effc1b86 Jose A. Lopes
66 c1f2901b Iustin Pop
67 23e50d39 Michael Hanselmann
CLIENT_REQUEST_WORKERS = 16
68 23e50d39 Michael Hanselmann
69 c1f2901b Iustin Pop
EXIT_NOTMASTER = constants.EXIT_NOTMASTER
70 c1f2901b Iustin Pop
EXIT_NODESETUP_ERROR = constants.EXIT_NODESETUP_ERROR
71 ffeffa1d Iustin Pop
72 ffeffa1d Iustin Pop
73 4c91d2ad Iustin Pop
def _LogNewJob(status, info, ops):
74 4c91d2ad Iustin Pop
  """Log information about a recently submitted job.
75 4c91d2ad Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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