4 # Copyright (C) 2011 Google Inc.
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.
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.
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
21 """Script to ensure permissions on files/dirs are accurate.
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
43 QUEUE_DIR) = range(1, 4)
45 ALL_TYPES = frozenset([
52 class EnsureError(errors.GenericError):
53 """Top-level error class related to this script.
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.
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)
71 logging.debug("Checking %s", path)
75 fmode = stat.S_IMODE(st[stat.ST_MODE])
77 logging.debug("Changing mode of %s from %#o to %#o", path, fmode, mode)
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:
90 raise EnsureError("Path %s should exist, but does not" % path)
92 raise EnsureError("Error while changing permissions on %s: %s" %
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.
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)
109 logging.debug("Checking directory %s", path)
111 # We don't want to follow symlinks
113 except EnvironmentError, err:
114 if err.errno != errno.ENOENT:
115 raise EnsureError("stat(2) on %s failed: %s" % (path, err))
118 if not stat.S_ISDIR(st[stat.ST_MODE]):
119 raise EnsureError("Path %s is expected to be a directory, but isn't" %
122 _ensure_fn(path, mode, uid=uid, gid=gid)
125 def RecursiveEnsure(path, uid, gid, dir_perm, file_perm):
126 """Ensures permissions recursively down a directory.
128 This functions walks the path and sets permissions accordingly.
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
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
140 logging.debug("Recursively processing %s", path)
142 for root, dirs, files in os.walk(path):
144 EnsurePermission(os.path.join(root, subdir), dir_perm, uid=uid, gid=gid)
146 for filename in files:
147 EnsurePermission(os.path.join(root, filename), file_perm, uid=uid,
151 def EnsureQueueDir(path, mode, uid, gid):
152 """Sets the correct permissions on all job files in the queue.
154 @param path: Directory path
155 @param mode: Wanted file mode
156 @param uid: Wanted user ID
157 @param gid: Wanted group ID
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)
165 def ProcessPath(path):
166 """Processes a path component.
168 @param path: A tuple of the path component to process
171 (pathname, pathtype, mode, uid, gid) = path[0:5]
173 assert pathtype in ALL_TYPES
175 if pathtype in (DIR, QUEUE_DIR):
176 # No additional parameters
177 assert len(path[5:]) == 0
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)
188 """Returns a tuple of path objects to process.
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]
197 rapi_dir = os.path.join(constants.DATA_DIR, "rapi")
200 (constants.DATA_DIR, DIR, 0755, getent.masterd_uid,
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),
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))
222 (constants.QUEUE_DIR, DIR, 0700, getent.masterd_uid,
224 (constants.QUEUE_DIR, QUEUE_DIR, 0600, getent.masterd_uid,
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,
237 (constants.SOCKET_DIR, DIR, 0750, getent.masterd_uid,
239 (constants.MASTER_SOCKET, FILE, 0770, getent.masterd_uid,
240 getent.daemons_gid, False),
241 (constants.BDEV_CACHE_DIR, DIR, 0755, getent.noded_uid,
243 (constants.UIDPOOL_LOCKDIR, DIR, 0750, getent.noded_uid,
245 (constants.DISK_LINKS_DIR, DIR, 0755, getent.noded_uid,
247 (constants.CRYPTO_KEYS_DIR, DIR, 0700, getent.noded_uid,
249 (constants.IMPORT_EXPORT_DIR, DIR, 0755, getent.noded_uid,
251 (constants.LOG_DIR, DIR, 0770, getent.masterd_uid,
253 (masterd_log, FILE, 0600, getent.masterd_uid, getent.masterd_gid,
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,
265 def SetupLogging(opts):
266 """Configures the logging module.
269 formatter = logging.Formatter("%(asctime)s: %(message)s")
271 stderr_handler = logging.StreamHandler()
272 stderr_handler.setFormatter(formatter)
274 stderr_handler.setLevel(logging.NOTSET)
276 stderr_handler.setLevel(logging.INFO)
278 stderr_handler.setLevel(logging.WARNING)
280 root_logger = logging.getLogger("")
281 root_logger.setLevel(logging.NOTSET)
282 root_logger.addHandler(stderr_handler)
286 """Parses the options passed to the program.
288 @return: Options and arguments
291 program = os.path.basename(sys.argv[0])
293 parser = optparse.OptionParser(usage="%%prog [--full-run]",
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)"))
301 return parser.parse_args()
308 (opts, _) = ParseOptions()
313 logging.info("Running in full mode")
315 getent = runtime.GetEnts()
318 for path in GetPaths():
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
328 return constants.EXIT_SUCCESS