Statistics
| Branch: | Tag: | Revision:

root / lib / server / masterd.py @ 31d3b918

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 9ba38706 Petr Pudlak
import ganeti.rpc.errors as rpcerr
52 ffeffa1d Iustin Pop
from ganeti import utils
53 c1f2901b Iustin Pop
from ganeti import errors
54 c1f2901b Iustin Pop
from ganeti import ssconf
55 23e50d39 Michael Hanselmann
from ganeti import workerpool
56 4869595d Petr Pudlak
import ganeti.rpc.node as rpc
57 912b2278 Petr Pudlak
import ganeti.rpc.client as rpccl
58 d7cdb55d Iustin Pop
from ganeti import bootstrap
59 a744b676 Manuel Franceschini
from ganeti import netutils
60 28b71a76 Michael Hanselmann
from ganeti import objects
61 24d16f76 Michael Hanselmann
from ganeti import query
62 a20e4768 Michael Hanselmann
from ganeti import runtime
63 a5ce2ea2 Michael Hanselmann
from ganeti import pathutils
64 7c4bd156 Michael Hanselmann
from ganeti import ht
65 c1f2901b Iustin Pop
66 effc1b86 Jose A. Lopes
from ganeti.utils import version
67 effc1b86 Jose A. Lopes
68 c1f2901b Iustin Pop
69 23e50d39 Michael Hanselmann
CLIENT_REQUEST_WORKERS = 16
70 23e50d39 Michael Hanselmann
71 c1f2901b Iustin Pop
EXIT_NOTMASTER = constants.EXIT_NOTMASTER
72 c1f2901b Iustin Pop
EXIT_NODESETUP_ERROR = constants.EXIT_NODESETUP_ERROR
73 ffeffa1d Iustin Pop
74 ffeffa1d Iustin Pop
75 4c91d2ad Iustin Pop
def _LogNewJob(status, info, ops):
76 4c91d2ad Iustin Pop
  """Log information about a recently submitted job.
77 4c91d2ad Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

456 39dcf2ef Guido Trotter
    """
457 39dcf2ef Guido Trotter
    assert self.__class__._instance is None, "double GanetiContext instance"
458 39dcf2ef Guido Trotter
459 9113300d Michael Hanselmann
    # Create global configuration object
460 39dcf2ef Guido Trotter
    self.cfg = config.ConfigWriter()
461 9113300d Michael Hanselmann
462 9113300d Michael Hanselmann
    # Locking manager
463 984f7c32 Guido Trotter
    self.glm = locking.GanetiLockManager(
464 5ae4945a Iustin Pop
      self.cfg.GetNodeList(),
465 5ae4945a Iustin Pop
      self.cfg.GetNodeGroupList(),
466 da4a52a3 Thomas Thrainer
      [inst.name for inst in self.cfg.GetAllInstancesInfo().values()],
467 6c0a75db Dimitris Aragiorgis
      self.cfg.GetNetworkList())
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 1c3231aa Thomas Thrainer
    self.glm.add(locking.LEVEL_NODE, node.uuid)
499 1c3231aa Thomas Thrainer
    self.glm.add(locking.LEVEL_NODE_RES, node.uuid)
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 1c3231aa Thomas Thrainer
  def RemoveNode(self, node):
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 1c3231aa Thomas Thrainer
    self.cfg.RemoveNode(node.uuid)
514 d8470559 Michael Hanselmann
515 c36176cc Michael Hanselmann
    # Notify job queue
516 1c3231aa Thomas Thrainer
    self.jobqueue.RemoveNode(node.name)
517 c36176cc Michael Hanselmann
518 d8470559 Michael Hanselmann
    # Remove the node from the Ganeti Lock Manager
519 1c3231aa Thomas Thrainer
    self.glm.remove(locking.LEVEL_NODE, node.uuid)
520 1c3231aa Thomas Thrainer
    self.glm.remove(locking.LEVEL_NODE_RES, node.uuid)
521 d8470559 Michael Hanselmann
522 39dcf2ef Guido Trotter
523 7c4bd156 Michael Hanselmann
def _SetWatcherPause(context, until):
524 05e50653 Michael Hanselmann
  """Creates or removes the watcher pause file.
525 05e50653 Michael Hanselmann

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

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

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

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

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

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

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

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

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