ensure-dirs: Set permissions on job files in queue
[ganeti-local] / lib / tools / ensure_dirs.py
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
32 from ganeti import constants
33 from ganeti import errors
34 from ganeti import runtime
35 from ganeti import ssconf
36 from ganeti import utils
37
38
39 (DIR,
40  FILE,
41  QUEUE_DIR) = range(1, 4)
42
43 ALL_TYPES = frozenset([
44   DIR,
45   FILE,
46   QUEUE_DIR,
47   ])
48
49
50 class EnsureError(errors.GenericError):
51   """Top-level error class related to this script.
52
53   """
54
55
56 def EnsurePermission(path, mode, uid=-1, gid=-1, must_exist=True,
57                      _chmod_fn=os.chmod, _chown_fn=os.chown):
58   """Ensures that given path has given mode.
59
60   @param path: The path to the file
61   @param mode: The mode of the file
62   @param uid: The uid of the owner of this file
63   @param gid: The gid of the owner of this file
64   @param must_exist: Specifies if non-existance of path will be an error
65   @param _chmod_fn: chmod function to use (unittest only)
66   @param _chown_fn: chown function to use (unittest only)
67
68   """
69   try:
70     _chmod_fn(path, mode)
71
72     if max(uid, gid) > -1:
73       _chown_fn(path, uid, gid)
74   except EnvironmentError, err:
75     if err.errno == errno.ENOENT:
76       if must_exist:
77         raise EnsureError("Path %s does not exists, but should" % path)
78     else:
79       raise EnsureError("Error while changing permission on %s: %s" %
80                         (path, err))
81
82
83 def EnsureDir(path, mode, uid, gid, _stat_fn=os.lstat, _mkdir_fn=os.mkdir,
84               _ensure_fn=EnsurePermission):
85   """Ensures that given path is a dir and has given mode, uid and gid set.
86
87   @param path: The path to the file
88   @param mode: The mode of the file
89   @param uid: The uid of the owner of this file
90   @param gid: The gid of the owner of this file
91   @param _stat_fn: Stat function to use (unittest only)
92   @param _mkdir_fn: mkdir function to use (unittest only)
93   @param _ensure_fn: ensure function to use (unittest only)
94
95   """
96   try:
97     # We don't want to follow symlinks
98     st_mode = _stat_fn(path)[stat.ST_MODE]
99
100     if not stat.S_ISDIR(st_mode):
101       raise EnsureError("Path %s is expected to be a directory, but it's not" %
102                         path)
103   except EnvironmentError, err:
104     if err.errno == errno.ENOENT:
105       _mkdir_fn(path)
106     else:
107       raise EnsureError("Error while do a stat() on %s: %s" % (path, err))
108
109   _ensure_fn(path, mode, uid=uid, gid=gid)
110
111
112 def RecursiveEnsure(path, uid, gid, dir_perm, file_perm):
113   """Ensures permissions recursively down a directory.
114
115   This functions walks the path and sets permissions accordingly.
116
117   @param path: The absolute path to walk
118   @param uid: The uid used as owner
119   @param gid: The gid used as group
120   @param dir_perm: The permission bits set for directories
121   @param file_perm: The permission bits set for files
122
123   """
124   assert os.path.isabs(path), "Path %s is not absolute" % path
125   assert os.path.isdir(path), "Path %s is not a dir" % path
126
127   for root, dirs, files in os.walk(path):
128     for subdir in dirs:
129       EnsurePermission(os.path.join(root, subdir), dir_perm, uid=uid, gid=gid)
130
131     for filename in files:
132       EnsurePermission(os.path.join(root, filename), file_perm, uid=uid,
133                        gid=gid)
134
135
136 def EnsureQueueDir(path, mode, uid, gid):
137   """Sets the correct permissions on all job files in the queue.
138
139   @param path: Directory path
140   @param mode: Wanted file mode
141   @param uid: Wanted user ID
142   @param gid: Wanted group ID
143
144   """
145   for filename in utils.ListVisibleFiles(path):
146     if constants.JOB_FILE_RE.match(filename):
147       EnsurePermission(utils.PathJoin(path, filename), mode, uid=uid, gid=gid)
148
149
150 def ProcessPath(path):
151   """Processes a path component.
152
153   @param path: A tuple of the path component to process
154
155   """
156   (pathname, pathtype, mode, uid, gid) = path[0:5]
157
158   assert pathtype in ALL_TYPES
159
160   if pathtype in (DIR, QUEUE_DIR):
161     # No additional parameters
162     assert len(path[5:]) == 0
163     if pathtype == DIR:
164       EnsureDir(pathname, mode, uid, gid)
165     elif pathtype == QUEUE_DIR:
166       EnsureQueueDir(pathname, mode, uid, gid)
167   elif pathtype == FILE:
168     (must_exist, ) = path[5:]
169     EnsurePermission(pathname, mode, uid=uid, gid=gid, must_exist=must_exist)
170
171
172 def GetPaths():
173   """Returns a tuple of path objects to process.
174
175   """
176   getent = runtime.GetEnts()
177   masterd_log = constants.DAEMONS_LOGFILES[constants.MASTERD]
178   noded_log = constants.DAEMONS_LOGFILES[constants.NODED]
179   confd_log = constants.DAEMONS_LOGFILES[constants.CONFD]
180   rapi_log = constants.DAEMONS_LOGFILES[constants.RAPI]
181
182   rapi_dir = os.path.join(constants.DATA_DIR, "rapi")
183
184   paths = [
185     (constants.DATA_DIR, DIR, 0755, getent.masterd_uid,
186      getent.masterd_gid),
187     (constants.CLUSTER_DOMAIN_SECRET_FILE, FILE, 0640,
188      getent.masterd_uid, getent.masterd_gid, False),
189     (constants.CLUSTER_CONF_FILE, FILE, 0640, getent.masterd_uid,
190      getent.confd_gid, False),
191     (constants.CONFD_HMAC_KEY, FILE, 0440, getent.confd_uid,
192      getent.masterd_gid, False),
193     (constants.SSH_KNOWN_HOSTS_FILE, FILE, 0644, getent.masterd_uid,
194      getent.masterd_gid, False),
195     (constants.RAPI_CERT_FILE, FILE, 0440, getent.rapi_uid,
196      getent.masterd_gid, False),
197     (constants.NODED_CERT_FILE, FILE, 0440, getent.masterd_uid,
198      getent.masterd_gid, False),
199     ]
200
201   ss = ssconf.SimpleStore()
202   for ss_path in ss.GetFileList():
203     paths.append((ss_path, FILE, constants.SS_FILE_PERMS,
204                   getent.noded_uid, 0, False))
205
206   paths.extend([
207     (constants.QUEUE_DIR, DIR, 0700, getent.masterd_uid,
208      getent.masterd_gid),
209     (constants.QUEUE_DIR, QUEUE_DIR, 0600, getent.masterd_uid,
210      getent.masterd_gid),
211     (constants.JOB_QUEUE_LOCK_FILE, FILE, 0600,
212      getent.masterd_uid, getent.masterd_gid, False),
213     (constants.JOB_QUEUE_SERIAL_FILE, FILE, 0600,
214      getent.masterd_uid, getent.masterd_gid, False),
215     (constants.JOB_QUEUE_ARCHIVE_DIR, DIR, 0700,
216      getent.masterd_uid, getent.masterd_gid),
217     (rapi_dir, DIR, 0750, getent.rapi_uid, getent.masterd_gid),
218     (constants.RAPI_USERS_FILE, FILE, 0640, getent.rapi_uid,
219      getent.masterd_gid, False),
220     (constants.RUN_GANETI_DIR, DIR, 0775, getent.masterd_uid,
221      getent.daemons_gid),
222     (constants.SOCKET_DIR, DIR, 0750, getent.masterd_uid,
223      getent.daemons_gid),
224     (constants.MASTER_SOCKET, FILE, 0770, getent.masterd_uid,
225      getent.daemons_gid, False),
226     (constants.BDEV_CACHE_DIR, DIR, 0755, getent.noded_uid,
227      getent.masterd_gid),
228     (constants.UIDPOOL_LOCKDIR, DIR, 0750, getent.noded_uid,
229      getent.masterd_gid),
230     (constants.DISK_LINKS_DIR, DIR, 0755, getent.noded_uid,
231      getent.masterd_gid),
232     (constants.CRYPTO_KEYS_DIR, DIR, 0700, getent.noded_uid,
233      getent.masterd_gid),
234     (constants.IMPORT_EXPORT_DIR, DIR, 0755, getent.noded_uid,
235      getent.masterd_gid),
236     (constants.LOG_DIR, DIR, 0770, getent.masterd_uid,
237      getent.daemons_gid),
238     (masterd_log, FILE, 0600, getent.masterd_uid, getent.masterd_gid,
239      False),
240     (confd_log, FILE, 0600, getent.confd_uid, getent.masterd_gid, False),
241     (noded_log, FILE, 0600, getent.noded_uid, getent.masterd_gid, False),
242     (rapi_log, FILE, 0600, getent.rapi_uid, getent.masterd_gid, False),
243     (constants.LOG_OS_DIR, DIR, 0750, getent.masterd_uid,
244      getent.daemons_gid),
245     ])
246
247   return tuple(paths)
248
249
250 def ParseOptions():
251   """Parses the options passed to the program.
252
253   @return: Options and arguments
254
255   """
256   program = os.path.basename(sys.argv[0])
257
258   parser = optparse.OptionParser(usage="%%prog [--full-run]",
259                                  prog=program)
260   parser.add_option("--full-run", "-f", dest="full_run", action="store_true",
261                     default=False, help=("Make a full run and collect"
262                                          " additional files (time consuming)"))
263
264   return parser.parse_args()
265
266
267 def Main():
268   """Main routine.
269
270   """
271   getent = runtime.GetEnts()
272   (opts, _) = ParseOptions()
273
274   try:
275     for path in GetPaths():
276       ProcessPath(path)
277
278     if opts.full_run:
279       RecursiveEnsure(constants.JOB_QUEUE_ARCHIVE_DIR, getent.masterd_uid,
280                       getent.masterd_gid, 0700, 0600)
281   except EnsureError, err:
282     print >> sys.stderr, "An error occurred while ensure permissions:", err
283     return constants.EXIT_FAILURE
284
285   return constants.EXIT_SUCCESS