Statistics
| Branch: | Tag: | Revision:

root / lib / server / masterd.py @ 8a3c9e8a

History | View | Annotate | Download (23.6 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 a5ce2ea2 Michael Hanselmann
from ganeti import pathutils
62 c1f2901b Iustin Pop
63 c1f2901b Iustin Pop
64 23e50d39 Michael Hanselmann
CLIENT_REQUEST_WORKERS = 16
65 23e50d39 Michael Hanselmann
66 c1f2901b Iustin Pop
EXIT_NOTMASTER = constants.EXIT_NOTMASTER
67 c1f2901b Iustin Pop
EXIT_NODESETUP_ERROR = constants.EXIT_NODESETUP_ERROR
68 ffeffa1d Iustin Pop
69 ffeffa1d Iustin Pop
70 23e50d39 Michael Hanselmann
class ClientRequestWorker(workerpool.BaseWorker):
71 b459a848 Andrea Spadaccini
  # pylint: disable=W0221
72 7e5a6e86 Guido Trotter
  def RunTask(self, server, message, client):
73 23e50d39 Michael Hanselmann
    """Process the request.
74 23e50d39 Michael Hanselmann

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

116 7e5a6e86 Guido Trotter
  """
117 7e5a6e86 Guido Trotter
  _MAX_UNHANDLED = 1
118 e687ec01 Michael Hanselmann
119 7e5a6e86 Guido Trotter
  def __init__(self, server, connected_socket, client_address, family):
120 7e5a6e86 Guido Trotter
    daemon.AsyncTerminatedMessageStream.__init__(self, connected_socket,
121 7e5a6e86 Guido Trotter
                                                 client_address,
122 7e5a6e86 Guido Trotter
                                                 constants.LUXI_EOM,
123 7e5a6e86 Guido Trotter
                                                 family, self._MAX_UNHANDLED)
124 7e5a6e86 Guido Trotter
    self.server = server
125 7e5a6e86 Guido Trotter
126 7e5a6e86 Guido Trotter
  def handle_message(self, message, _):
127 b2e8a4d9 Michael Hanselmann
    self.server.request_workers.AddTask((self.server, message, self))
128 23e50d39 Michael Hanselmann
129 23e50d39 Michael Hanselmann
130 5483fd73 Michael Hanselmann
class _MasterShutdownCheck:
131 5483fd73 Michael Hanselmann
  """Logic for master daemon shutdown.
132 5483fd73 Michael Hanselmann

133 5483fd73 Michael Hanselmann
  """
134 5483fd73 Michael Hanselmann
  #: How long to wait between checks
135 5483fd73 Michael Hanselmann
  _CHECK_INTERVAL = 5.0
136 5483fd73 Michael Hanselmann
137 5483fd73 Michael Hanselmann
  #: How long to wait after all jobs are done (e.g. to give clients time to
138 5483fd73 Michael Hanselmann
  #: retrieve the job status)
139 5483fd73 Michael Hanselmann
  _SHUTDOWN_LINGER = 5.0
140 5483fd73 Michael Hanselmann
141 5483fd73 Michael Hanselmann
  def __init__(self):
142 5483fd73 Michael Hanselmann
    """Initializes this class.
143 5483fd73 Michael Hanselmann

144 5483fd73 Michael Hanselmann
    """
145 5483fd73 Michael Hanselmann
    self._had_active_jobs = None
146 5483fd73 Michael Hanselmann
    self._linger_timeout = None
147 5483fd73 Michael Hanselmann
148 5483fd73 Michael Hanselmann
  def __call__(self, jq_prepare_result):
149 5483fd73 Michael Hanselmann
    """Determines if master daemon is ready for shutdown.
150 5483fd73 Michael Hanselmann

151 5483fd73 Michael Hanselmann
    @param jq_prepare_result: Result of L{jqueue.JobQueue.PrepareShutdown}
152 5483fd73 Michael Hanselmann
    @rtype: None or number
153 5483fd73 Michael Hanselmann
    @return: None if master daemon is ready, timeout if the check must be
154 5483fd73 Michael Hanselmann
             repeated
155 5483fd73 Michael Hanselmann

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

190 cdd7f900 Guido Trotter
  This is the main asynchronous master server. It handles connections to the
191 cdd7f900 Guido Trotter
  master socket.
192 ffeffa1d Iustin Pop

193 ffeffa1d Iustin Pop
  """
194 7e5a6e86 Guido Trotter
  family = socket.AF_UNIX
195 7e5a6e86 Guido Trotter
196 e8a701f6 Michael Hanselmann
  def __init__(self, address, uid, gid):
197 cdd7f900 Guido Trotter
    """MasterServer constructor
198 ce862cd5 Guido Trotter

199 cdd7f900 Guido Trotter
    @param address: the unix socket address to bind the MasterServer to
200 bbfd0568 René Nussbaumer
    @param uid: The uid of the owner of the socket
201 bbfd0568 René Nussbaumer
    @param gid: The gid of the owner of the socket
202 ce862cd5 Guido Trotter

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

232 5483fd73 Michael Hanselmann
    """
233 5483fd73 Michael Hanselmann
    if self._shutdown_check is None:
234 5483fd73 Michael Hanselmann
      self._shutdown_check = _MasterShutdownCheck()
235 5483fd73 Michael Hanselmann
236 5483fd73 Michael Hanselmann
    return self._shutdown_check(self.context.jobqueue.PrepareShutdown())
237 5483fd73 Michael Hanselmann
238 c1f2901b Iustin Pop
  def server_cleanup(self):
239 c1f2901b Iustin Pop
    """Cleanup the server.
240 c1f2901b Iustin Pop

241 c1f2901b Iustin Pop
    This involves shutting down the processor threads and the master
242 c1f2901b Iustin Pop
    socket.
243 c1f2901b Iustin Pop

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

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

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

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

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

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

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

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

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

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

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 05e50653 Michael Hanselmann
  if until is None:
531 a5ce2ea2 Michael Hanselmann
    utils.RemoveFile(pathutils.WATCHER_PAUSEFILE)
532 05e50653 Michael Hanselmann
  else:
533 a5ce2ea2 Michael Hanselmann
    utils.WriteFile(pathutils.WATCHER_PAUSEFILE,
534 05e50653 Michael Hanselmann
                    data="%d\n" % (until, ))
535 05e50653 Michael Hanselmann
536 28b498cd Michael Hanselmann
  return until
537 28b498cd Michael Hanselmann
538 05e50653 Michael Hanselmann
539 e0e916fe Iustin Pop
@rpc.RunWithRPC
540 36205981 Iustin Pop
def CheckAgreement():
541 36205981 Iustin Pop
  """Check the agreement on who is the master.
542 36205981 Iustin Pop

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

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

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

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

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

690 04ccf5e9 Guido Trotter
  """
691 04ccf5e9 Guido Trotter
  # This is safe to do as the pid file guarantees against
692 04ccf5e9 Guido Trotter
  # concurrent execution.
693 a5ce2ea2 Michael Hanselmann
  utils.RemoveFile(pathutils.MASTER_SOCKET)
694 b1b6ea87 Iustin Pop
695 cdd7f900 Guido Trotter
  mainloop = daemon.Mainloop()
696 a5ce2ea2 Michael Hanselmann
  master = MasterServer(pathutils.MASTER_SOCKET, options.uid, options.gid)
697 3ee53f1f Iustin Pop
  return (mainloop, master)
698 3ee53f1f Iustin Pop
699 3ee53f1f Iustin Pop
700 b459a848 Andrea Spadaccini
def ExecMasterd(options, args, prep_data): # pylint: disable=W0613
701 3ee53f1f Iustin Pop
  """Main master daemon function, executed with the PID file held.
702 3ee53f1f Iustin Pop

703 3ee53f1f Iustin Pop
  """
704 3ee53f1f Iustin Pop
  (mainloop, master) = prep_data
705 04ccf5e9 Guido Trotter
  try:
706 15486fa7 Michael Hanselmann
    rpc.Init()
707 4331f6cd Michael Hanselmann
    try:
708 15486fa7 Michael Hanselmann
      master.setup_queue()
709 15486fa7 Michael Hanselmann
      try:
710 5483fd73 Michael Hanselmann
        mainloop.Run(shutdown_wait_fn=master.WaitForShutdown)
711 15486fa7 Michael Hanselmann
      finally:
712 15486fa7 Michael Hanselmann
        master.server_cleanup()
713 4331f6cd Michael Hanselmann
    finally:
714 15486fa7 Michael Hanselmann
      rpc.Shutdown()
715 a4af651e Iustin Pop
  finally:
716 a5ce2ea2 Michael Hanselmann
    utils.RemoveFile(pathutils.MASTER_SOCKET)
717 a4af651e Iustin Pop
718 5483fd73 Michael Hanselmann
  logging.info("Clean master daemon shutdown")
719 5483fd73 Michael Hanselmann
720 ffeffa1d Iustin Pop
721 29d91329 Michael Hanselmann
def Main():
722 04ccf5e9 Guido Trotter
  """Main function"""
723 04ccf5e9 Guido Trotter
  parser = OptionParser(description="Ganeti master daemon",
724 04ccf5e9 Guido Trotter
                        usage="%prog [-f] [-d]",
725 04ccf5e9 Guido Trotter
                        version="%%prog (ganeti) %s" %
726 04ccf5e9 Guido Trotter
                        constants.RELEASE_VERSION)
727 04ccf5e9 Guido Trotter
  parser.add_option("--no-voting", dest="no_voting",
728 04ccf5e9 Guido Trotter
                    help="Do not check that the nodes agree on this node"
729 04ccf5e9 Guido Trotter
                    " being the master and start the daemon unconditionally",
730 04ccf5e9 Guido Trotter
                    default=False, action="store_true")
731 04ccf5e9 Guido Trotter
  parser.add_option("--yes-do-it", dest="yes_do_it",
732 04ccf5e9 Guido Trotter
                    help="Override interactive check for --no-voting",
733 04ccf5e9 Guido Trotter
                    default=False, action="store_true")
734 3ee53f1f Iustin Pop
  daemon.GenericMain(constants.MASTERD, parser, CheckMasterd, PrepMasterd,
735 b42ea9ed Iustin Pop
                     ExecMasterd, multithreaded=True)