Statistics
| Branch: | Tag: | Revision:

root / lib / server / masterd.py @ e07f7f7a

History | View | Annotate | Download (23.5 kB)

1 69cf3abd Michael Hanselmann
#
2 ffeffa1d Iustin Pop
#
3 ffeffa1d Iustin Pop
4 f2af0bec Iustin Pop
# Copyright (C) 2006, 2007, 2010, 2011 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 c1f2901b Iustin Pop
61 c1f2901b Iustin Pop
62 23e50d39 Michael Hanselmann
CLIENT_REQUEST_WORKERS = 16
63 23e50d39 Michael Hanselmann
64 c1f2901b Iustin Pop
EXIT_NOTMASTER = constants.EXIT_NOTMASTER
65 c1f2901b Iustin Pop
EXIT_NODESETUP_ERROR = constants.EXIT_NODESETUP_ERROR
66 ffeffa1d Iustin Pop
67 ffeffa1d Iustin Pop
68 23e50d39 Michael Hanselmann
class ClientRequestWorker(workerpool.BaseWorker):
69 b459a848 Andrea Spadaccini
  # pylint: disable=W0221
70 7e5a6e86 Guido Trotter
  def RunTask(self, server, message, client):
71 23e50d39 Michael Hanselmann
    """Process the request.
72 23e50d39 Michael Hanselmann

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

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

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

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

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

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

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

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

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

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

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

239 c1f2901b Iustin Pop
    This involves shutting down the processor threads and the master
240 c1f2901b Iustin Pop
    socket.
241 c1f2901b Iustin Pop

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

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

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

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

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

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

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

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

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

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

522 05e50653 Michael Hanselmann
  @type until: None or int
523 05e50653 Michael Hanselmann
  @param until: Unix timestamp saying until when the watcher shouldn't run
524 05e50653 Michael Hanselmann

525 05e50653 Michael Hanselmann
  """
526 05e50653 Michael Hanselmann
  if until is None:
527 05e50653 Michael Hanselmann
    utils.RemoveFile(constants.WATCHER_PAUSEFILE)
528 05e50653 Michael Hanselmann
  else:
529 05e50653 Michael Hanselmann
    utils.WriteFile(constants.WATCHER_PAUSEFILE,
530 05e50653 Michael Hanselmann
                    data="%d\n" % (until, ))
531 05e50653 Michael Hanselmann
532 28b498cd Michael Hanselmann
  return until
533 28b498cd Michael Hanselmann
534 05e50653 Michael Hanselmann
535 e0e916fe Iustin Pop
@rpc.RunWithRPC
536 36205981 Iustin Pop
def CheckAgreement():
537 36205981 Iustin Pop
  """Check the agreement on who is the master.
538 36205981 Iustin Pop

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

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

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

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

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

683 04ccf5e9 Guido Trotter
  """
684 04ccf5e9 Guido Trotter
  # This is safe to do as the pid file guarantees against
685 04ccf5e9 Guido Trotter
  # concurrent execution.
686 04ccf5e9 Guido Trotter
  utils.RemoveFile(constants.MASTER_SOCKET)
687 b1b6ea87 Iustin Pop
688 cdd7f900 Guido Trotter
  mainloop = daemon.Mainloop()
689 e8a701f6 Michael Hanselmann
  master = MasterServer(constants.MASTER_SOCKET, options.uid, options.gid)
690 3ee53f1f Iustin Pop
  return (mainloop, master)
691 3ee53f1f Iustin Pop
692 3ee53f1f Iustin Pop
693 b459a848 Andrea Spadaccini
def ExecMasterd(options, args, prep_data): # pylint: disable=W0613
694 3ee53f1f Iustin Pop
  """Main master daemon function, executed with the PID file held.
695 3ee53f1f Iustin Pop

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