Statistics
| Branch: | Tag: | Revision:

root / lib / tools / ensure_dirs.py @ 9849cec7

History | View | Annotate | Download (10 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_ARCHIVE_DIR, DIR, 0700,
231
     getent.masterd_uid, getent.masterd_gid),
232
    (rapi_dir, DIR, 0750, getent.rapi_uid, getent.masterd_gid),
233
    (constants.RAPI_USERS_FILE, FILE, 0640, getent.rapi_uid,
234
     getent.masterd_gid, False),
235
    (constants.RUN_GANETI_DIR, DIR, 0775, getent.masterd_uid,
236
     getent.daemons_gid),
237
    (constants.SOCKET_DIR, DIR, 0750, getent.masterd_uid,
238
     getent.daemons_gid),
239
    (constants.MASTER_SOCKET, FILE, 0770, getent.masterd_uid,
240
     getent.daemons_gid, False),
241
    (constants.BDEV_CACHE_DIR, DIR, 0755, getent.noded_uid,
242
     getent.masterd_gid),
243
    (constants.UIDPOOL_LOCKDIR, DIR, 0750, getent.noded_uid,
244
     getent.masterd_gid),
245
    (constants.DISK_LINKS_DIR, DIR, 0755, getent.noded_uid,
246
     getent.masterd_gid),
247
    (constants.CRYPTO_KEYS_DIR, DIR, 0700, getent.noded_uid,
248
     getent.masterd_gid),
249
    (constants.IMPORT_EXPORT_DIR, DIR, 0755, getent.noded_uid,
250
     getent.masterd_gid),
251
    (constants.LOG_DIR, DIR, 0770, getent.masterd_uid,
252
     getent.daemons_gid),
253
    (masterd_log, FILE, 0600, getent.masterd_uid, getent.masterd_gid,
254
     False),
255
    (confd_log, FILE, 0600, getent.confd_uid, getent.masterd_gid, False),
256
    (noded_log, FILE, 0600, getent.noded_uid, getent.masterd_gid, False),
257
    (rapi_log, FILE, 0600, getent.rapi_uid, getent.masterd_gid, False),
258
    (constants.LOG_OS_DIR, DIR, 0750, getent.masterd_uid,
259
     getent.daemons_gid),
260
    ])
261

    
262
  return tuple(paths)
263

    
264

    
265
def SetupLogging(opts):
266
  """Configures the logging module.
267

268
  """
269
  formatter = logging.Formatter("%(asctime)s: %(message)s")
270

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

    
280
  root_logger = logging.getLogger("")
281
  root_logger.setLevel(logging.NOTSET)
282
  root_logger.addHandler(stderr_handler)
283

    
284

    
285
def ParseOptions():
286
  """Parses the options passed to the program.
287

288
  @return: Options and arguments
289

290
  """
291
  program = os.path.basename(sys.argv[0])
292

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

    
301
  return parser.parse_args()
302

    
303

    
304
def Main():
305
  """Main routine.
306

307
  """
308
  (opts, _) = ParseOptions()
309

    
310
  SetupLogging(opts)
311

    
312
  if opts.full_run:
313
    logging.info("Running in full mode")
314

    
315
  getent = runtime.GetEnts()
316

    
317
  try:
318
    for path in GetPaths():
319
      ProcessPath(path)
320

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

    
328
  return constants.EXIT_SUCCESS