Statistics
| Branch: | Tag: | Revision:

root / lib / server / masterd.py @ 4c91d2ad

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 b1b6ea87 Iustin Pop
from ganeti import 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 c1f2901b Iustin Pop
62 c1f2901b Iustin Pop
63 23e50d39 Michael Hanselmann
CLIENT_REQUEST_WORKERS = 16
64 23e50d39 Michael Hanselmann
65 c1f2901b Iustin Pop
EXIT_NOTMASTER = constants.EXIT_NOTMASTER
66 c1f2901b Iustin Pop
EXIT_NODESETUP_ERROR = constants.EXIT_NODESETUP_ERROR
67 ffeffa1d Iustin Pop
68 ffeffa1d Iustin Pop
69 4c91d2ad Iustin Pop
def _LogNewJob(status, info, ops):
70 4c91d2ad Iustin Pop
  """Log information about a recently submitted job.
71 4c91d2ad Iustin Pop

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

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

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

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

155 5483fd73 Michael Hanselmann
    """
156 5483fd73 Michael Hanselmann
    self._had_active_jobs = None
157 5483fd73 Michael Hanselmann
    self._linger_timeout = None
158 5483fd73 Michael Hanselmann
159 5483fd73 Michael Hanselmann
  def __call__(self, jq_prepare_result):
160 5483fd73 Michael Hanselmann
    """Determines if master daemon is ready for shutdown.
161 5483fd73 Michael Hanselmann

162 5483fd73 Michael Hanselmann
    @param jq_prepare_result: Result of L{jqueue.JobQueue.PrepareShutdown}
163 5483fd73 Michael Hanselmann
    @rtype: None or number
164 5483fd73 Michael Hanselmann
    @return: None if master daemon is ready, timeout if the check must be
165 5483fd73 Michael Hanselmann
             repeated
166 5483fd73 Michael Hanselmann

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

201 cdd7f900 Guido Trotter
  This is the main asynchronous master server. It handles connections to the
202 cdd7f900 Guido Trotter
  master socket.
203 ffeffa1d Iustin Pop

204 ffeffa1d Iustin Pop
  """
205 7e5a6e86 Guido Trotter
  family = socket.AF_UNIX
206 7e5a6e86 Guido Trotter
207 e8a701f6 Michael Hanselmann
  def __init__(self, address, uid, gid):
208 cdd7f900 Guido Trotter
    """MasterServer constructor
209 ce862cd5 Guido Trotter

210 cdd7f900 Guido Trotter
    @param address: the unix socket address to bind the MasterServer to
211 bbfd0568 René Nussbaumer
    @param uid: The uid of the owner of the socket
212 bbfd0568 René Nussbaumer
    @param gid: The gid of the owner of the socket
213 ce862cd5 Guido Trotter

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

243 5483fd73 Michael Hanselmann
    """
244 5483fd73 Michael Hanselmann
    if self._shutdown_check is None:
245 5483fd73 Michael Hanselmann
      self._shutdown_check = _MasterShutdownCheck()
246 5483fd73 Michael Hanselmann
247 5483fd73 Michael Hanselmann
    return self._shutdown_check(self.context.jobqueue.PrepareShutdown())
248 5483fd73 Michael Hanselmann
249 c1f2901b Iustin Pop
  def server_cleanup(self):
250 c1f2901b Iustin Pop
    """Cleanup the server.
251 c1f2901b Iustin Pop

252 c1f2901b Iustin Pop
    This involves shutting down the processor threads and the master
253 c1f2901b Iustin Pop
    socket.
254 c1f2901b Iustin Pop

255 c1f2901b Iustin Pop
    """
256 50a3fbb2 Michael Hanselmann
    try:
257 cdd7f900 Guido Trotter
      self.close()
258 50a3fbb2 Michael Hanselmann
    finally:
259 23e50d39 Michael Hanselmann
      if self.request_workers:
260 36088c4c Michael Hanselmann
        self.request_workers.TerminateWorkers()
261 9113300d Michael Hanselmann
      if self.context:
262 9113300d Michael Hanselmann
        self.context.jobqueue.Shutdown()
263 ffeffa1d Iustin Pop
264 ffeffa1d Iustin Pop
265 ffeffa1d Iustin Pop
class ClientOps:
266 ffeffa1d Iustin Pop
  """Class holding high-level client operations."""
267 ffeffa1d Iustin Pop
  def __init__(self, server):
268 ffeffa1d Iustin Pop
    self.server = server
269 ffeffa1d Iustin Pop
270 b459a848 Andrea Spadaccini
  def handle_request(self, method, args): # pylint: disable=R0911
271 e07f7f7a Michael Hanselmann
    context = self.server.context
272 e07f7f7a Michael Hanselmann
    queue = context.jobqueue
273 0bbe448c Michael Hanselmann
274 0bbe448c Michael Hanselmann
    # TODO: Parameter validation
275 a629ecb9 Iustin Pop
    if not isinstance(args, (tuple, list)):
276 a629ecb9 Iustin Pop
      logging.info("Received invalid arguments of type '%s'", type(args))
277 a629ecb9 Iustin Pop
      raise ValueError("Invalid arguments type '%s'" % type(args))
278 0bbe448c Michael Hanselmann
279 7260cfbe Iustin Pop
    # TODO: Rewrite to not exit in each 'if/elif' branch
280 7260cfbe Iustin Pop
281 0bbe448c Michael Hanselmann
    if method == luxi.REQ_SUBMIT_JOB:
282 4c91d2ad Iustin Pop
      logging.info("Receiving new job")
283 734a2a7c René Nussbaumer
      (job_def, ) = args
284 734a2a7c René Nussbaumer
      ops = [opcodes.OpCode.LoadOpCode(state) for state in job_def]
285 4c91d2ad Iustin Pop
      job_id = queue.SubmitJob(ops)
286 4c91d2ad Iustin Pop
      _LogNewJob(True, job_id, ops)
287 4c91d2ad Iustin Pop
      return job_id
288 ffeffa1d Iustin Pop
289 1b5b1c49 René Nussbaumer
    elif method == luxi.REQ_SUBMIT_MANY_JOBS:
290 4c91d2ad Iustin Pop
      logging.info("Receiving multiple jobs")
291 734a2a7c René Nussbaumer
      (job_defs, ) = args
292 2971c913 Iustin Pop
      jobs = []
293 734a2a7c René Nussbaumer
      for ops in job_defs:
294 2971c913 Iustin Pop
        jobs.append([opcodes.OpCode.LoadOpCode(state) for state in ops])
295 4c91d2ad Iustin Pop
      job_ids = queue.SubmitManyJobs(jobs)
296 4c91d2ad Iustin Pop
      for ((status, job_id), ops) in zip(job_ids, jobs):
297 4c91d2ad Iustin Pop
        _LogNewJob(status, job_id, ops)
298 4c91d2ad Iustin Pop
      return job_ids
299 2971c913 Iustin Pop
300 0bbe448c Michael Hanselmann
    elif method == luxi.REQ_CANCEL_JOB:
301 a629ecb9 Iustin Pop
      (job_id, ) = args
302 e566ddbd Iustin Pop
      logging.info("Received job cancel request for %s", job_id)
303 0bbe448c Michael Hanselmann
      return queue.CancelJob(job_id)
304 ffeffa1d Iustin Pop
305 0bbe448c Michael Hanselmann
    elif method == luxi.REQ_ARCHIVE_JOB:
306 a629ecb9 Iustin Pop
      (job_id, ) = args
307 e566ddbd Iustin Pop
      logging.info("Received job archive request for %s", job_id)
308 0bbe448c Michael Hanselmann
      return queue.ArchiveJob(job_id)
309 0bbe448c Michael Hanselmann
310 83c046a2 Iustin Pop
    elif method == luxi.REQ_AUTO_ARCHIVE_JOBS:
311 f8ad5591 Michael Hanselmann
      (age, timeout) = args
312 e566ddbd Iustin Pop
      logging.info("Received job autoarchive request for age %s, timeout %s",
313 e566ddbd Iustin Pop
                   age, timeout)
314 f8ad5591 Michael Hanselmann
      return queue.AutoArchiveJobs(age, timeout)
315 07cd723a Iustin Pop
316 dfe57c22 Michael Hanselmann
    elif method == luxi.REQ_WAIT_FOR_JOB_CHANGE:
317 5c735209 Iustin Pop
      (job_id, fields, prev_job_info, prev_log_serial, timeout) = args
318 e566ddbd Iustin Pop
      logging.info("Received job poll request for %s", job_id)
319 6c5a7090 Michael Hanselmann
      return queue.WaitForJobChanges(job_id, fields, prev_job_info,
320 5c735209 Iustin Pop
                                     prev_log_serial, timeout)
321 dfe57c22 Michael Hanselmann
322 28b71a76 Michael Hanselmann
    elif method == luxi.REQ_QUERY:
323 a629ecb9 Iustin Pop
      (what, fields, qfilter) = args
324 28b71a76 Michael Hanselmann
325 b4b3266b Michael Hanselmann
      if what in constants.QR_VIA_OP:
326 b4b3266b Michael Hanselmann
        result = self._Query(opcodes.OpQuery(what=what, fields=fields,
327 b4b3266b Michael Hanselmann
                                             qfilter=qfilter))
328 b4b3266b Michael Hanselmann
      elif what == constants.QR_LOCK:
329 b4b3266b Michael Hanselmann
        if qfilter is not None:
330 24d16f76 Michael Hanselmann
          raise errors.OpPrereqError("Lock queries can't be filtered")
331 b4b3266b Michael Hanselmann
        return context.glm.QueryLocks(fields)
332 b4b3266b Michael Hanselmann
      elif what == constants.QR_JOB:
333 b4b3266b Michael Hanselmann
        return queue.QueryJobs(fields, qfilter)
334 b4b3266b Michael Hanselmann
      elif what in constants.QR_VIA_LUXI:
335 28b71a76 Michael Hanselmann
        raise NotImplementedError
336 28b71a76 Michael Hanselmann
      else:
337 b4b3266b Michael Hanselmann
        raise errors.OpPrereqError("Resource type '%s' unknown" % what,
338 28b71a76 Michael Hanselmann
                                   errors.ECODE_INVAL)
339 28b71a76 Michael Hanselmann
340 28b71a76 Michael Hanselmann
      return result
341 28b71a76 Michael Hanselmann
342 28b71a76 Michael Hanselmann
    elif method == luxi.REQ_QUERY_FIELDS:
343 a629ecb9 Iustin Pop
      (what, fields) = args
344 a629ecb9 Iustin Pop
      req = objects.QueryFieldsRequest(what=what, fields=fields)
345 28b71a76 Michael Hanselmann
346 c1391810 Michael Hanselmann
      try:
347 c1391810 Michael Hanselmann
        fielddefs = query.ALL_FIELDS[req.what]
348 c1391810 Michael Hanselmann
      except KeyError:
349 28b71a76 Michael Hanselmann
        raise errors.OpPrereqError("Resource type '%s' unknown" % req.what,
350 28b71a76 Michael Hanselmann
                                   errors.ECODE_INVAL)
351 28b71a76 Michael Hanselmann
352 c1391810 Michael Hanselmann
      return query.QueryFields(fielddefs, req.fields)
353 28b71a76 Michael Hanselmann
354 0bbe448c Michael Hanselmann
    elif method == luxi.REQ_QUERY_JOBS:
355 0bbe448c Michael Hanselmann
      (job_ids, fields) = args
356 e566ddbd Iustin Pop
      if isinstance(job_ids, (tuple, list)) and job_ids:
357 1f864b60 Iustin Pop
        msg = utils.CommaJoin(job_ids)
358 e566ddbd Iustin Pop
      else:
359 e566ddbd Iustin Pop
        msg = str(job_ids)
360 e566ddbd Iustin Pop
      logging.info("Received job query request for %s", msg)
361 e07f7f7a Michael Hanselmann
      return queue.OldStyleQueryJobs(job_ids, fields)
362 0bbe448c Michael Hanselmann
363 ee6c7b94 Michael Hanselmann
    elif method == luxi.REQ_QUERY_INSTANCES:
364 ec79568d Iustin Pop
      (names, fields, use_locking) = args
365 e566ddbd Iustin Pop
      logging.info("Received instance query request for %s", names)
366 77921a95 Iustin Pop
      if use_locking:
367 debac808 Iustin Pop
        raise errors.OpPrereqError("Sync queries are not allowed",
368 debac808 Iustin Pop
                                   errors.ECODE_INVAL)
369 f2af0bec Iustin Pop
      op = opcodes.OpInstanceQuery(names=names, output_fields=fields,
370 f2af0bec Iustin Pop
                                   use_locking=use_locking)
371 ee6c7b94 Michael Hanselmann
      return self._Query(op)
372 ee6c7b94 Michael Hanselmann
373 02f7fe54 Michael Hanselmann
    elif method == luxi.REQ_QUERY_NODES:
374 ec79568d Iustin Pop
      (names, fields, use_locking) = args
375 e566ddbd Iustin Pop
      logging.info("Received node query request for %s", names)
376 77921a95 Iustin Pop
      if use_locking:
377 debac808 Iustin Pop
        raise errors.OpPrereqError("Sync queries are not allowed",
378 debac808 Iustin Pop
                                   errors.ECODE_INVAL)
379 2237687b Iustin Pop
      op = opcodes.OpNodeQuery(names=names, output_fields=fields,
380 2237687b Iustin Pop
                               use_locking=use_locking)
381 02f7fe54 Michael Hanselmann
      return self._Query(op)
382 02f7fe54 Michael Hanselmann
383 a79ef2a5 Adeodato Simo
    elif method == luxi.REQ_QUERY_GROUPS:
384 a79ef2a5 Adeodato Simo
      (names, fields, use_locking) = args
385 a79ef2a5 Adeodato Simo
      logging.info("Received group query request for %s", names)
386 a79ef2a5 Adeodato Simo
      if use_locking:
387 a79ef2a5 Adeodato Simo
        raise errors.OpPrereqError("Sync queries are not allowed",
388 a79ef2a5 Adeodato Simo
                                   errors.ECODE_INVAL)
389 d4d654bd Iustin Pop
      op = opcodes.OpGroupQuery(names=names, output_fields=fields)
390 a79ef2a5 Adeodato Simo
      return self._Query(op)
391 a79ef2a5 Adeodato Simo
392 32f93223 Michael Hanselmann
    elif method == luxi.REQ_QUERY_EXPORTS:
393 a629ecb9 Iustin Pop
      (nodes, use_locking) = args
394 77921a95 Iustin Pop
      if use_locking:
395 debac808 Iustin Pop
        raise errors.OpPrereqError("Sync queries are not allowed",
396 debac808 Iustin Pop
                                   errors.ECODE_INVAL)
397 e566ddbd Iustin Pop
      logging.info("Received exports query request")
398 7ca2d4d8 Iustin Pop
      op = opcodes.OpBackupQuery(nodes=nodes, use_locking=use_locking)
399 32f93223 Michael Hanselmann
      return self._Query(op)
400 32f93223 Michael Hanselmann
401 ae5849b5 Michael Hanselmann
    elif method == luxi.REQ_QUERY_CONFIG_VALUES:
402 a629ecb9 Iustin Pop
      (fields, ) = args
403 e566ddbd Iustin Pop
      logging.info("Received config values query request for %s", fields)
404 2f093ea0 Iustin Pop
      op = opcodes.OpClusterConfigQuery(output_fields=fields)
405 ae5849b5 Michael Hanselmann
      return self._Query(op)
406 ae5849b5 Michael Hanselmann
407 66baeccc Iustin Pop
    elif method == luxi.REQ_QUERY_CLUSTER_INFO:
408 e566ddbd Iustin Pop
      logging.info("Received cluster info query request")
409 a2f7ab92 Iustin Pop
      op = opcodes.OpClusterQuery()
410 66baeccc Iustin Pop
      return self._Query(op)
411 66baeccc Iustin Pop
412 7699c3af Iustin Pop
    elif method == luxi.REQ_QUERY_TAGS:
413 a629ecb9 Iustin Pop
      (kind, name) = args
414 7699c3af Iustin Pop
      logging.info("Received tags query request")
415 cfdf561d Michael Hanselmann
      op = opcodes.OpTagsGet(kind=kind, name=name, use_locking=False)
416 7699c3af Iustin Pop
      return self._Query(op)
417 7699c3af Iustin Pop
418 83c046a2 Iustin Pop
    elif method == luxi.REQ_SET_DRAIN_FLAG:
419 a629ecb9 Iustin Pop
      (drain_flag, ) = args
420 e566ddbd Iustin Pop
      logging.info("Received queue drain flag change request to %s",
421 e566ddbd Iustin Pop
                   drain_flag)
422 3ccafd0e Iustin Pop
      return queue.SetDrainFlag(drain_flag)
423 3ccafd0e Iustin Pop
424 05e50653 Michael Hanselmann
    elif method == luxi.REQ_SET_WATCHER_PAUSE:
425 05e50653 Michael Hanselmann
      (until, ) = args
426 05e50653 Michael Hanselmann
427 05e50653 Michael Hanselmann
      if until is None:
428 05e50653 Michael Hanselmann
        logging.info("Received request to no longer pause the watcher")
429 05e50653 Michael Hanselmann
      else:
430 05e50653 Michael Hanselmann
        if not isinstance(until, (int, float)):
431 05e50653 Michael Hanselmann
          raise TypeError("Duration must be an integer or float")
432 05e50653 Michael Hanselmann
433 05e50653 Michael Hanselmann
        if until < time.time():
434 05e50653 Michael Hanselmann
          raise errors.GenericError("Unable to set pause end time in the past")
435 05e50653 Michael Hanselmann
436 05e50653 Michael Hanselmann
        logging.info("Received request to pause the watcher until %s", until)
437 05e50653 Michael Hanselmann
438 05e50653 Michael Hanselmann
      return _SetWatcherPause(until)
439 05e50653 Michael Hanselmann
440 0bbe448c Michael Hanselmann
    else:
441 e566ddbd Iustin Pop
      logging.info("Received invalid request '%s'", method)
442 e566ddbd Iustin Pop
      raise ValueError("Invalid operation '%s'" % method)
443 ffeffa1d Iustin Pop
444 ee6c7b94 Michael Hanselmann
  def _Query(self, op):
445 ee6c7b94 Michael Hanselmann
    """Runs the specified opcode and returns the result.
446 ee6c7b94 Michael Hanselmann

447 ee6c7b94 Michael Hanselmann
    """
448 adfa97e3 Guido Trotter
    # Queries don't have a job id
449 dc4bdf73 Michael Hanselmann
    proc = mcpu.Processor(self.server.context, None, enable_locks=False)
450 26d3fd2f Michael Hanselmann
451 26d3fd2f Michael Hanselmann
    # TODO: Executing an opcode using locks will acquire them in blocking mode.
452 26d3fd2f Michael Hanselmann
    # Consider using a timeout for retries.
453 031a3e57 Michael Hanselmann
    return proc.ExecOpCode(op, None)
454 ee6c7b94 Michael Hanselmann
455 ffeffa1d Iustin Pop
456 39dcf2ef Guido Trotter
class GanetiContext(object):
457 39dcf2ef Guido Trotter
  """Context common to all ganeti threads.
458 39dcf2ef Guido Trotter

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

461 39dcf2ef Guido Trotter
  """
462 b459a848 Andrea Spadaccini
  # pylint: disable=W0212
463 7260cfbe Iustin Pop
  # we do want to ensure a singleton here
464 39dcf2ef Guido Trotter
  _instance = None
465 39dcf2ef Guido Trotter
466 39dcf2ef Guido Trotter
  def __init__(self):
467 39dcf2ef Guido Trotter
    """Constructs a new GanetiContext object.
468 39dcf2ef Guido Trotter

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

472 39dcf2ef Guido Trotter
    """
473 39dcf2ef Guido Trotter
    assert self.__class__._instance is None, "double GanetiContext instance"
474 39dcf2ef Guido Trotter
475 9113300d Michael Hanselmann
    # Create global configuration object
476 39dcf2ef Guido Trotter
    self.cfg = config.ConfigWriter()
477 9113300d Michael Hanselmann
478 9113300d Michael Hanselmann
    # Locking manager
479 984f7c32 Guido Trotter
    self.glm = locking.GanetiLockManager(
480 39dcf2ef Guido Trotter
                self.cfg.GetNodeList(),
481 819ca990 Guido Trotter
                self.cfg.GetNodeGroupList(),
482 39dcf2ef Guido Trotter
                self.cfg.GetInstanceList())
483 39dcf2ef Guido Trotter
484 b2acdbdc Michael Hanselmann
    self.cfg.SetContext(self)
485 b2acdbdc Michael Hanselmann
486 87b3cb26 Michael Hanselmann
    # RPC runner
487 d5ea30e8 Michael Hanselmann
    self.rpc = rpc.RpcRunner(self.cfg, self.glm.AddToLockMonitor)
488 87b3cb26 Michael Hanselmann
489 cb4d3314 Michael Hanselmann
    # Job queue
490 cb4d3314 Michael Hanselmann
    self.jobqueue = jqueue.JobQueue(self)
491 cb4d3314 Michael Hanselmann
492 39dcf2ef Guido Trotter
    # setting this also locks the class against attribute modifications
493 39dcf2ef Guido Trotter
    self.__class__._instance = self
494 39dcf2ef Guido Trotter
495 39dcf2ef Guido Trotter
  def __setattr__(self, name, value):
496 39dcf2ef Guido Trotter
    """Setting GanetiContext attributes is forbidden after initialization.
497 39dcf2ef Guido Trotter

498 39dcf2ef Guido Trotter
    """
499 39dcf2ef Guido Trotter
    assert self.__class__._instance is None, "Attempt to modify Ganeti Context"
500 39dcf2ef Guido Trotter
    object.__setattr__(self, name, value)
501 39dcf2ef Guido Trotter
502 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
503 d8470559 Michael Hanselmann
    """Adds a node to the configuration and lock manager.
504 d8470559 Michael Hanselmann

505 d8470559 Michael Hanselmann
    """
506 d8470559 Michael Hanselmann
    # Add it to the configuration
507 0debfb35 Guido Trotter
    self.cfg.AddNode(node, ec_id)
508 d8470559 Michael Hanselmann
509 c36176cc Michael Hanselmann
    # If preseeding fails it'll not be added
510 99aabbed Iustin Pop
    self.jobqueue.AddNode(node)
511 c36176cc Michael Hanselmann
512 d8470559 Michael Hanselmann
    # Add the new node to the Ganeti Lock Manager
513 d8470559 Michael Hanselmann
    self.glm.add(locking.LEVEL_NODE, node.name)
514 4e070776 Michael Hanselmann
    self.glm.add(locking.LEVEL_NODE_RES, node.name)
515 d8470559 Michael Hanselmann
516 d8470559 Michael Hanselmann
  def ReaddNode(self, node):
517 d8470559 Michael Hanselmann
    """Updates a node that's already in the configuration
518 d8470559 Michael Hanselmann

519 d8470559 Michael Hanselmann
    """
520 c36176cc Michael Hanselmann
    # Synchronize the queue again
521 99aabbed Iustin Pop
    self.jobqueue.AddNode(node)
522 d8470559 Michael Hanselmann
523 d8470559 Michael Hanselmann
  def RemoveNode(self, name):
524 d8470559 Michael Hanselmann
    """Removes a node from the configuration and lock manager.
525 d8470559 Michael Hanselmann

526 d8470559 Michael Hanselmann
    """
527 d8470559 Michael Hanselmann
    # Remove node from configuration
528 d8470559 Michael Hanselmann
    self.cfg.RemoveNode(name)
529 d8470559 Michael Hanselmann
530 c36176cc Michael Hanselmann
    # Notify job queue
531 c36176cc Michael Hanselmann
    self.jobqueue.RemoveNode(name)
532 c36176cc Michael Hanselmann
533 d8470559 Michael Hanselmann
    # Remove the node from the Ganeti Lock Manager
534 d8470559 Michael Hanselmann
    self.glm.remove(locking.LEVEL_NODE, name)
535 4e070776 Michael Hanselmann
    self.glm.remove(locking.LEVEL_NODE_RES, name)
536 d8470559 Michael Hanselmann
537 39dcf2ef Guido Trotter
538 05e50653 Michael Hanselmann
def _SetWatcherPause(until):
539 05e50653 Michael Hanselmann
  """Creates or removes the watcher pause file.
540 05e50653 Michael Hanselmann

541 05e50653 Michael Hanselmann
  @type until: None or int
542 05e50653 Michael Hanselmann
  @param until: Unix timestamp saying until when the watcher shouldn't run
543 05e50653 Michael Hanselmann

544 05e50653 Michael Hanselmann
  """
545 05e50653 Michael Hanselmann
  if until is None:
546 05e50653 Michael Hanselmann
    utils.RemoveFile(constants.WATCHER_PAUSEFILE)
547 05e50653 Michael Hanselmann
  else:
548 05e50653 Michael Hanselmann
    utils.WriteFile(constants.WATCHER_PAUSEFILE,
549 05e50653 Michael Hanselmann
                    data="%d\n" % (until, ))
550 05e50653 Michael Hanselmann
551 28b498cd Michael Hanselmann
  return until
552 28b498cd Michael Hanselmann
553 05e50653 Michael Hanselmann
554 e0e916fe Iustin Pop
@rpc.RunWithRPC
555 36205981 Iustin Pop
def CheckAgreement():
556 36205981 Iustin Pop
  """Check the agreement on who is the master.
557 36205981 Iustin Pop

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

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

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

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

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

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

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