Statistics
| Branch: | Tag: | Revision:

root / lib / tools / ensure_dirs.py @ bfe86c76

History | View | Annotate | Download (10.2 kB)

1
#
2
#
3

    
4
# Copyright (C) 2011 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21
"""Script to ensure permissions on files/dirs are accurate.
22

23
"""
24

    
25
import errno
26
import os
27
import os.path
28
import optparse
29
import sys
30
import stat
31
import logging
32

    
33
from ganeti import constants
34
from ganeti import errors
35
from ganeti import runtime
36
from ganeti import ssconf
37
from ganeti import utils
38
from ganeti import cli
39

    
40

    
41
(DIR,
42
 FILE,
43
 QUEUE_DIR) = range(1, 4)
44

    
45
ALL_TYPES = frozenset([
46
  DIR,
47
  FILE,
48
  QUEUE_DIR,
49
  ])
50

    
51

    
52
class EnsureError(errors.GenericError):
53
  """Top-level error class related to this script.
54

55
  """
56

    
57

    
58
def EnsurePermission(path, mode, uid=-1, gid=-1, must_exist=True,
59
                     _chmod_fn=os.chmod, _chown_fn=os.chown, _stat_fn=os.stat):
60
  """Ensures that given path has given mode.
61

62
  @param path: The path to the file
63
  @param mode: The mode of the file
64
  @param uid: The uid of the owner of this file
65
  @param gid: The gid of the owner of this file
66
  @param must_exist: Specifies if non-existance of path will be an error
67
  @param _chmod_fn: chmod function to use (unittest only)
68
  @param _chown_fn: chown function to use (unittest only)
69

70
  """
71
  logging.debug("Checking %s", path)
72
  try:
73
    st = _stat_fn(path)
74

    
75
    fmode = stat.S_IMODE(st[stat.ST_MODE])
76
    if fmode != mode:
77
      logging.debug("Changing mode of %s from %#o to %#o", path, fmode, mode)
78
      _chmod_fn(path, mode)
79

    
80
    if max(uid, gid) > -1:
81
      fuid = st[stat.ST_UID]
82
      fgid = st[stat.ST_GID]
83
      if fuid != uid or fgid != gid:
84
        logging.debug("Changing owner of %s from UID %s/GID %s to"
85
                      " UID %s/GID %s", path, fuid, fgid, uid, gid)
86
        _chown_fn(path, uid, gid)
87
  except EnvironmentError, err:
88
    if err.errno == errno.ENOENT:
89
      if must_exist:
90
        raise EnsureError("Path %s should exist, but does not" % path)
91
    else:
92
      raise EnsureError("Error while changing permissions on %s: %s" %
93
                        (path, err))
94

    
95

    
96
def EnsureDir(path, mode, uid, gid, _lstat_fn=os.lstat, _mkdir_fn=os.mkdir,
97
              _ensure_fn=EnsurePermission):
98
  """Ensures that given path is a dir and has given mode, uid and gid set.
99

100
  @param path: The path to the file
101
  @param mode: The mode of the file
102
  @param uid: The uid of the owner of this file
103
  @param gid: The gid of the owner of this file
104
  @param _lstat_fn: Stat function to use (unittest only)
105
  @param _mkdir_fn: mkdir function to use (unittest only)
106
  @param _ensure_fn: ensure function to use (unittest only)
107

108
  """
109
  logging.debug("Checking directory %s", path)
110
  try:
111
    # We don't want to follow symlinks
112
    st = _lstat_fn(path)
113
  except EnvironmentError, err:
114
    if err.errno != errno.ENOENT:
115
      raise EnsureError("stat(2) on %s failed: %s" % (path, err))
116
    _mkdir_fn(path)
117
  else:
118
    if not stat.S_ISDIR(st[stat.ST_MODE]):
119
      raise EnsureError("Path %s is expected to be a directory, but isn't" %
120
                        path)
121

    
122
  _ensure_fn(path, mode, uid=uid, gid=gid)
123

    
124

    
125
def RecursiveEnsure(path, uid, gid, dir_perm, file_perm):
126
  """Ensures permissions recursively down a directory.
127

128
  This functions walks the path and sets permissions accordingly.
129

130
  @param path: The absolute path to walk
131
  @param uid: The uid used as owner
132
  @param gid: The gid used as group
133
  @param dir_perm: The permission bits set for directories
134
  @param file_perm: The permission bits set for files
135

136
  """
137
  assert os.path.isabs(path), "Path %s is not absolute" % path
138
  assert os.path.isdir(path), "Path %s is not a dir" % path
139

    
140
  logging.debug("Recursively processing %s", path)
141

    
142
  for root, dirs, files in os.walk(path):
143
    for subdir in dirs:
144
      EnsurePermission(os.path.join(root, subdir), dir_perm, uid=uid, gid=gid)
145

    
146
    for filename in files:
147
      EnsurePermission(os.path.join(root, filename), file_perm, uid=uid,
148
                       gid=gid)
149

    
150

    
151
def EnsureQueueDir(path, mode, uid, gid):
152
  """Sets the correct permissions on all job files in the queue.
153

154
  @param path: Directory path
155
  @param mode: Wanted file mode
156
  @param uid: Wanted user ID
157
  @param gid: Wanted group ID
158

159
  """
160
  for filename in utils.ListVisibleFiles(path):
161
    if constants.JOB_FILE_RE.match(filename):
162
      EnsurePermission(utils.PathJoin(path, filename), mode, uid=uid, gid=gid)
163

    
164

    
165
def ProcessPath(path):
166
  """Processes a path component.
167

168
  @param path: A tuple of the path component to process
169

170
  """
171
  (pathname, pathtype, mode, uid, gid) = path[0:5]
172

    
173
  assert pathtype in ALL_TYPES
174

    
175
  if pathtype in (DIR, QUEUE_DIR):
176
    # No additional parameters
177
    assert len(path[5:]) == 0
178
    if pathtype == DIR:
179
      EnsureDir(pathname, mode, uid, gid)
180
    elif pathtype == QUEUE_DIR:
181
      EnsureQueueDir(pathname, mode, uid, gid)
182
  elif pathtype == FILE:
183
    (must_exist, ) = path[5:]
184
    EnsurePermission(pathname, mode, uid=uid, gid=gid, must_exist=must_exist)
185

    
186

    
187
def GetPaths():
188
  """Returns a tuple of path objects to process.
189

190
  """
191
  getent = runtime.GetEnts()
192
  masterd_log = constants.DAEMONS_LOGFILES[constants.MASTERD]
193
  noded_log = constants.DAEMONS_LOGFILES[constants.NODED]
194
  confd_log = constants.DAEMONS_LOGFILES[constants.CONFD]
195
  rapi_log = constants.DAEMONS_LOGFILES[constants.RAPI]
196

    
197
  rapi_dir = os.path.join(constants.DATA_DIR, "rapi")
198

    
199
  paths = [
200
    (constants.DATA_DIR, DIR, 0755, getent.masterd_uid,
201
     getent.masterd_gid),
202
    (constants.CLUSTER_DOMAIN_SECRET_FILE, FILE, 0640,
203
     getent.masterd_uid, getent.masterd_gid, False),
204
    (constants.CLUSTER_CONF_FILE, FILE, 0640, getent.masterd_uid,
205
     getent.confd_gid, False),
206
    (constants.CONFD_HMAC_KEY, FILE, 0440, getent.confd_uid,
207
     getent.masterd_gid, False),
208
    (constants.SSH_KNOWN_HOSTS_FILE, FILE, 0644, getent.masterd_uid,
209
     getent.masterd_gid, False),
210
    (constants.RAPI_CERT_FILE, FILE, 0440, getent.rapi_uid,
211
     getent.masterd_gid, False),
212
    (constants.SPICE_CERT_FILE, FILE, 0440, getent.noded_uid,
213
     getent.masterd_gid, False),
214
    (constants.SPICE_CACERT_FILE, FILE, 0440, getent.noded_uid,
215
     getent.masterd_gid, False),
216
    (constants.NODED_CERT_FILE, FILE, 0440, getent.masterd_uid,
217
     getent.masterd_gid, False),
218
    ]
219

    
220
  ss = ssconf.SimpleStore()
221
  for ss_path in ss.GetFileList():
222
    paths.append((ss_path, FILE, constants.SS_FILE_PERMS,
223
                  getent.noded_uid, 0, False))
224

    
225
  paths.extend([
226
    (constants.QUEUE_DIR, DIR, 0700, getent.masterd_uid,
227
     getent.masterd_gid),
228
    (constants.QUEUE_DIR, QUEUE_DIR, 0600, getent.masterd_uid,
229
     getent.masterd_gid),
230
    (constants.JOB_QUEUE_LOCK_FILE, FILE, 0600,
231
     getent.masterd_uid, getent.masterd_gid, False),
232
    (constants.JOB_QUEUE_SERIAL_FILE, FILE, 0600,
233
     getent.masterd_uid, getent.masterd_gid, False),
234
    (constants.JOB_QUEUE_ARCHIVE_DIR, DIR, 0700,
235
     getent.masterd_uid, getent.masterd_gid),
236
    (rapi_dir, DIR, 0750, getent.rapi_uid, getent.masterd_gid),
237
    (constants.RAPI_USERS_FILE, FILE, 0640, getent.rapi_uid,
238
     getent.masterd_gid, False),
239
    (constants.RUN_GANETI_DIR, DIR, 0775, getent.masterd_uid,
240
     getent.daemons_gid),
241
    (constants.SOCKET_DIR, DIR, 0750, getent.masterd_uid,
242
     getent.daemons_gid),
243
    (constants.MASTER_SOCKET, FILE, 0770, getent.masterd_uid,
244
     getent.daemons_gid, False),
245
    (constants.BDEV_CACHE_DIR, DIR, 0755, getent.noded_uid,
246
     getent.masterd_gid),
247
    (constants.UIDPOOL_LOCKDIR, DIR, 0750, getent.noded_uid,
248
     getent.masterd_gid),
249
    (constants.DISK_LINKS_DIR, DIR, 0755, getent.noded_uid,
250
     getent.masterd_gid),
251
    (constants.CRYPTO_KEYS_DIR, DIR, 0700, getent.noded_uid,
252
     getent.masterd_gid),
253
    (constants.IMPORT_EXPORT_DIR, DIR, 0755, getent.noded_uid,
254
     getent.masterd_gid),
255
    (constants.LOG_DIR, DIR, 0770, getent.masterd_uid,
256
     getent.daemons_gid),
257
    (masterd_log, FILE, 0600, getent.masterd_uid, getent.masterd_gid,
258
     False),
259
    (confd_log, FILE, 0600, getent.confd_uid, getent.masterd_gid, False),
260
    (noded_log, FILE, 0600, getent.noded_uid, getent.masterd_gid, False),
261
    (rapi_log, FILE, 0600, getent.rapi_uid, getent.masterd_gid, False),
262
    (constants.LOG_OS_DIR, DIR, 0750, getent.masterd_uid,
263
     getent.daemons_gid),
264
    ])
265

    
266
  return tuple(paths)
267

    
268

    
269
def SetupLogging(opts):
270
  """Configures the logging module.
271

272
  """
273
  formatter = logging.Formatter("%(asctime)s: %(message)s")
274

    
275
  stderr_handler = logging.StreamHandler()
276
  stderr_handler.setFormatter(formatter)
277
  if opts.debug:
278
    stderr_handler.setLevel(logging.NOTSET)
279
  elif opts.verbose:
280
    stderr_handler.setLevel(logging.INFO)
281
  else:
282
    stderr_handler.setLevel(logging.WARNING)
283

    
284
  root_logger = logging.getLogger("")
285
  root_logger.setLevel(logging.NOTSET)
286
  root_logger.addHandler(stderr_handler)
287

    
288

    
289
def ParseOptions():
290
  """Parses the options passed to the program.
291

292
  @return: Options and arguments
293

294
  """
295
  program = os.path.basename(sys.argv[0])
296

    
297
  parser = optparse.OptionParser(usage="%%prog [--full-run]",
298
                                 prog=program)
299
  parser.add_option(cli.DEBUG_OPT)
300
  parser.add_option(cli.VERBOSE_OPT)
301
  parser.add_option("--full-run", "-f", dest="full_run", action="store_true",
302
                    default=False, help=("Make a full run and set permissions"
303
                                         " on archived jobs (time consuming)"))
304

    
305
  return parser.parse_args()
306

    
307

    
308
def Main():
309
  """Main routine.
310

311
  """
312
  (opts, _) = ParseOptions()
313

    
314
  SetupLogging(opts)
315

    
316
  if opts.full_run:
317
    logging.info("Running in full mode")
318

    
319
  getent = runtime.GetEnts()
320

    
321
  try:
322
    for path in GetPaths():
323
      ProcessPath(path)
324

    
325
    if opts.full_run:
326
      RecursiveEnsure(constants.JOB_QUEUE_ARCHIVE_DIR, getent.masterd_uid,
327
                      getent.masterd_gid, 0700, 0600)
328
  except EnsureError, err:
329
    logging.error("An error occurred while setting permissions: %s", err)
330
    return constants.EXIT_FAILURE
331

    
332
  return constants.EXIT_SUCCESS