Statistics
| Branch: | Tag: | Revision:

root / lib / server / masterd.py @ be9150ea

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

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

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

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

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

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

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

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

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

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

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

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

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

255 c1f2901b Iustin Pop
    This involves shutting down the processor threads and the master
256 c1f2901b Iustin Pop
    socket.
257 c1f2901b Iustin Pop

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

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

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

469 39dcf2ef Guido Trotter
  """
470 b459a848 Andrea Spadaccini
  # pylint: disable=W0212
471 7260cfbe Iustin Pop
  # we do want to ensure a singleton here
472 39dcf2ef Guido Trotter
  _instance = None
473 39dcf2ef Guido Trotter
474 39dcf2ef Guido Trotter
  def __init__(self):
475 39dcf2ef Guido Trotter
    """Constructs a new GanetiContext object.
476 39dcf2ef Guido Trotter

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

480 39dcf2ef Guido Trotter
    """
481 39dcf2ef Guido Trotter
    assert self.__class__._instance is None, "double GanetiContext instance"
482 39dcf2ef Guido Trotter
483 9113300d Michael Hanselmann
    # Create global configuration object
484 39dcf2ef Guido Trotter
    self.cfg = config.ConfigWriter()
485 9113300d Michael Hanselmann
486 9113300d Michael Hanselmann
    # Locking manager
487 984f7c32 Guido Trotter
    self.glm = locking.GanetiLockManager(
488 5ae4945a Iustin Pop
      self.cfg.GetNodeList(),
489 5ae4945a Iustin Pop
      self.cfg.GetNodeGroupList(),
490 da4a52a3 Thomas Thrainer
      [inst.name for inst in self.cfg.GetAllInstancesInfo().values()],
491 6c0a75db Dimitris Aragiorgis
      self.cfg.GetNetworkList())
492 39dcf2ef Guido Trotter
493 b2acdbdc Michael Hanselmann
    self.cfg.SetContext(self)
494 b2acdbdc Michael Hanselmann
495 87b3cb26 Michael Hanselmann
    # RPC runner
496 d5ea30e8 Michael Hanselmann
    self.rpc = rpc.RpcRunner(self.cfg, self.glm.AddToLockMonitor)
497 87b3cb26 Michael Hanselmann
498 cb4d3314 Michael Hanselmann
    # Job queue
499 cb4d3314 Michael Hanselmann
    self.jobqueue = jqueue.JobQueue(self)
500 cb4d3314 Michael Hanselmann
501 39dcf2ef Guido Trotter
    # setting this also locks the class against attribute modifications
502 39dcf2ef Guido Trotter
    self.__class__._instance = self
503 39dcf2ef Guido Trotter
504 39dcf2ef Guido Trotter
  def __setattr__(self, name, value):
505 39dcf2ef Guido Trotter
    """Setting GanetiContext attributes is forbidden after initialization.
506 39dcf2ef Guido Trotter

507 39dcf2ef Guido Trotter
    """
508 39dcf2ef Guido Trotter
    assert self.__class__._instance is None, "Attempt to modify Ganeti Context"
509 39dcf2ef Guido Trotter
    object.__setattr__(self, name, value)
510 39dcf2ef Guido Trotter
511 0debfb35 Guido Trotter
  def AddNode(self, node, ec_id):
512 d8470559 Michael Hanselmann
    """Adds a node to the configuration and lock manager.
513 d8470559 Michael Hanselmann

514 d8470559 Michael Hanselmann
    """
515 d8470559 Michael Hanselmann
    # Add it to the configuration
516 0debfb35 Guido Trotter
    self.cfg.AddNode(node, ec_id)
517 d8470559 Michael Hanselmann
518 c36176cc Michael Hanselmann
    # If preseeding fails it'll not be added
519 99aabbed Iustin Pop
    self.jobqueue.AddNode(node)
520 c36176cc Michael Hanselmann
521 d8470559 Michael Hanselmann
    # Add the new node to the Ganeti Lock Manager
522 1c3231aa Thomas Thrainer
    self.glm.add(locking.LEVEL_NODE, node.uuid)
523 1c3231aa Thomas Thrainer
    self.glm.add(locking.LEVEL_NODE_RES, node.uuid)
524 d8470559 Michael Hanselmann
525 d8470559 Michael Hanselmann
  def ReaddNode(self, node):
526 d8470559 Michael Hanselmann
    """Updates a node that's already in the configuration
527 d8470559 Michael Hanselmann

528 d8470559 Michael Hanselmann
    """
529 c36176cc Michael Hanselmann
    # Synchronize the queue again
530 99aabbed Iustin Pop
    self.jobqueue.AddNode(node)
531 d8470559 Michael Hanselmann
532 1c3231aa Thomas Thrainer
  def RemoveNode(self, node):
533 d8470559 Michael Hanselmann
    """Removes a node from the configuration and lock manager.
534 d8470559 Michael Hanselmann

535 d8470559 Michael Hanselmann
    """
536 d8470559 Michael Hanselmann
    # Remove node from configuration
537 1c3231aa Thomas Thrainer
    self.cfg.RemoveNode(node.uuid)
538 d8470559 Michael Hanselmann
539 c36176cc Michael Hanselmann
    # Notify job queue
540 1c3231aa Thomas Thrainer
    self.jobqueue.RemoveNode(node.name)
541 c36176cc Michael Hanselmann
542 d8470559 Michael Hanselmann
    # Remove the node from the Ganeti Lock Manager
543 1c3231aa Thomas Thrainer
    self.glm.remove(locking.LEVEL_NODE, node.uuid)
544 1c3231aa Thomas Thrainer
    self.glm.remove(locking.LEVEL_NODE_RES, node.uuid)
545 d8470559 Michael Hanselmann
546 39dcf2ef Guido Trotter
547 7c4bd156 Michael Hanselmann
def _SetWatcherPause(context, until):
548 05e50653 Michael Hanselmann
  """Creates or removes the watcher pause file.
549 05e50653 Michael Hanselmann

550 7c4bd156 Michael Hanselmann
  @type context: L{GanetiContext}
551 7c4bd156 Michael Hanselmann
  @param context: Global Ganeti context
552 05e50653 Michael Hanselmann
  @type until: None or int
553 05e50653 Michael Hanselmann
  @param until: Unix timestamp saying until when the watcher shouldn't run
554 05e50653 Michael Hanselmann

555 05e50653 Michael Hanselmann
  """
556 7c4bd156 Michael Hanselmann
  node_names = context.cfg.GetNodeList()
557 7c4bd156 Michael Hanselmann
558 05e50653 Michael Hanselmann
  if until is None:
559 7c4bd156 Michael Hanselmann
    logging.info("Received request to no longer pause watcher")
560 05e50653 Michael Hanselmann
  else:
561 7c4bd156 Michael Hanselmann
    if not ht.TNumber(until):
562 7c4bd156 Michael Hanselmann
      raise TypeError("Duration must be numeric")
563 7c4bd156 Michael Hanselmann
564 7c4bd156 Michael Hanselmann
    if until < time.time():
565 7c4bd156 Michael Hanselmann
      raise errors.GenericError("Unable to set pause end time in the past")
566 7c4bd156 Michael Hanselmann
567 7c4bd156 Michael Hanselmann
    logging.info("Received request to pause watcher until %s", until)
568 7c4bd156 Michael Hanselmann
569 7c4bd156 Michael Hanselmann
  result = context.rpc.call_set_watcher_pause(node_names, until)
570 7c4bd156 Michael Hanselmann
571 7c4bd156 Michael Hanselmann
  errmsg = utils.CommaJoin("%s (%s)" % (node_name, nres.fail_msg)
572 7c4bd156 Michael Hanselmann
                           for (node_name, nres) in result.items()
573 7c4bd156 Michael Hanselmann
                           if nres.fail_msg and not nres.offline)
574 7c4bd156 Michael Hanselmann
  if errmsg:
575 7c4bd156 Michael Hanselmann
    raise errors.OpExecError("Watcher pause was set where possible, but failed"
576 7c4bd156 Michael Hanselmann
                             " on the following node(s): %s" % errmsg)
577 05e50653 Michael Hanselmann
578 28b498cd Michael Hanselmann
  return until
579 28b498cd Michael Hanselmann
580 05e50653 Michael Hanselmann
581 e0e916fe Iustin Pop
@rpc.RunWithRPC
582 36205981 Iustin Pop
def CheckAgreement():
583 36205981 Iustin Pop
  """Check the agreement on who is the master.
584 36205981 Iustin Pop

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

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

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

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

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

733 04ccf5e9 Guido Trotter
  """
734 04ccf5e9 Guido Trotter
  # This is safe to do as the pid file guarantees against
735 04ccf5e9 Guido Trotter
  # concurrent execution.
736 a5ce2ea2 Michael Hanselmann
  utils.RemoveFile(pathutils.MASTER_SOCKET)
737 b1b6ea87 Iustin Pop
738 cdd7f900 Guido Trotter
  mainloop = daemon.Mainloop()
739 a5ce2ea2 Michael Hanselmann
  master = MasterServer(pathutils.MASTER_SOCKET, options.uid, options.gid)
740 3ee53f1f Iustin Pop
  return (mainloop, master)
741 3ee53f1f Iustin Pop
742 3ee53f1f Iustin Pop
743 b459a848 Andrea Spadaccini
def ExecMasterd(options, args, prep_data): # pylint: disable=W0613
744 3ee53f1f Iustin Pop
  """Main master daemon function, executed with the PID file held.
745 3ee53f1f Iustin Pop

746 3ee53f1f Iustin Pop
  """
747 3ee53f1f Iustin Pop
  (mainloop, master) = prep_data
748 04ccf5e9 Guido Trotter
  try:
749 15486fa7 Michael Hanselmann
    rpc.Init()
750 4331f6cd Michael Hanselmann
    try:
751 15486fa7 Michael Hanselmann
      master.setup_queue()
752 15486fa7 Michael Hanselmann
      try:
753 5483fd73 Michael Hanselmann
        mainloop.Run(shutdown_wait_fn=master.WaitForShutdown)
754 15486fa7 Michael Hanselmann
      finally:
755 15486fa7 Michael Hanselmann
        master.server_cleanup()
756 4331f6cd Michael Hanselmann
    finally:
757 15486fa7 Michael Hanselmann
      rpc.Shutdown()
758 a4af651e Iustin Pop
  finally:
759 a5ce2ea2 Michael Hanselmann
    utils.RemoveFile(pathutils.MASTER_SOCKET)
760 a4af651e Iustin Pop
761 5483fd73 Michael Hanselmann
  logging.info("Clean master daemon shutdown")
762 5483fd73 Michael Hanselmann
763 ffeffa1d Iustin Pop
764 29d91329 Michael Hanselmann
def Main():
765 04ccf5e9 Guido Trotter
  """Main function"""
766 04ccf5e9 Guido Trotter
  parser = OptionParser(description="Ganeti master daemon",
767 04ccf5e9 Guido Trotter
                        usage="%prog [-f] [-d]",
768 04ccf5e9 Guido Trotter
                        version="%%prog (ganeti) %s" %
769 04ccf5e9 Guido Trotter
                        constants.RELEASE_VERSION)
770 04ccf5e9 Guido Trotter
  parser.add_option("--no-voting", dest="no_voting",
771 04ccf5e9 Guido Trotter
                    help="Do not check that the nodes agree on this node"
772 04ccf5e9 Guido Trotter
                    " being the master and start the daemon unconditionally",
773 04ccf5e9 Guido Trotter
                    default=False, action="store_true")
774 04ccf5e9 Guido Trotter
  parser.add_option("--yes-do-it", dest="yes_do_it",
775 04ccf5e9 Guido Trotter
                    help="Override interactive check for --no-voting",
776 04ccf5e9 Guido Trotter
                    default=False, action="store_true")
777 3ee53f1f Iustin Pop
  daemon.GenericMain(constants.MASTERD, parser, CheckMasterd, PrepMasterd,
778 b42ea9ed Iustin Pop
                     ExecMasterd, multithreaded=True)