Statistics
| Branch: | Tag: | Revision:

root / lib / tools / ensure_dirs.py @ 98dfcaff

History | View | Annotate | Download (10.1 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.NODED_CERT_FILE, FILE, 0440, getent.masterd_uid,
213
     getent.masterd_gid, False),
214
    ]
215

    
216
  ss = ssconf.SimpleStore()
217
  for ss_path in ss.GetFileList():
218
    paths.append((ss_path, FILE, constants.SS_FILE_PERMS,
219
                  getent.noded_uid, 0, False))
220

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

    
264
  return tuple(paths)
265

    
266

    
267
def SetupLogging(opts):
268
  """Configures the logging module.
269

270
  """
271
  formatter = logging.Formatter("%(asctime)s: %(message)s")
272

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

    
282
  root_logger = logging.getLogger("")
283
  root_logger.setLevel(logging.NOTSET)
284
  root_logger.addHandler(stderr_handler)
285

    
286

    
287
def ParseOptions():
288
  """Parses the options passed to the program.
289

290
  @return: Options and arguments
291

292
  """
293
  program = os.path.basename(sys.argv[0])
294

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

    
303
  return parser.parse_args()
304

    
305

    
306
def Main():
307
  """Main routine.
308

309
  """
310
  (opts, _) = ParseOptions()
311

    
312
  SetupLogging(opts)
313

    
314
  if opts.full_run:
315
    logging.info("Running in full mode")
316

    
317
  getent = runtime.GetEnts()
318

    
319
  try:
320
    for path in GetPaths():
321
      ProcessPath(path)
322

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

    
330
  return constants.EXIT_SUCCESS