Fix a bug in command line option parsing code
[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 os
26 import os.path
27 import optparse
28 import sys
29 import logging
30
31 from ganeti import constants
32 from ganeti import errors
33 from ganeti import runtime
34 from ganeti import ssconf
35 from ganeti import utils
36 from ganeti import cli
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 def RecursiveEnsure(path, uid, gid, dir_perm, file_perm):
51   """Ensures permissions recursively down a directory.
52
53   This functions walks the path and sets permissions accordingly.
54
55   @param path: The absolute path to walk
56   @param uid: The uid used as owner
57   @param gid: The gid used as group
58   @param dir_perm: The permission bits set for directories
59   @param file_perm: The permission bits set for files
60
61   """
62   assert os.path.isabs(path), "Path %s is not absolute" % path
63   assert os.path.isdir(path), "Path %s is not a dir" % path
64
65   logging.debug("Recursively processing %s", path)
66
67   for root, dirs, files in os.walk(path):
68     for subdir in dirs:
69       utils.EnforcePermission(os.path.join(root, subdir), dir_perm, uid=uid,
70                               gid=gid)
71
72     for filename in files:
73       utils.EnforcePermission(os.path.join(root, filename), file_perm, uid=uid,
74                               gid=gid)
75
76
77 def EnsureQueueDir(path, mode, uid, gid):
78   """Sets the correct permissions on all job files in the queue.
79
80   @param path: Directory path
81   @param mode: Wanted file mode
82   @param uid: Wanted user ID
83   @param gid: Wanted group ID
84
85   """
86   for filename in utils.ListVisibleFiles(path):
87     if constants.JOB_FILE_RE.match(filename):
88       utils.EnforcePermission(utils.PathJoin(path, filename), mode, uid=uid,
89                               gid=gid)
90
91
92 def ProcessPath(path):
93   """Processes a path component.
94
95   @param path: A tuple of the path component to process
96
97   """
98   (pathname, pathtype, mode, uid, gid) = path[0:5]
99
100   assert pathtype in ALL_TYPES
101
102   if pathtype in (DIR, QUEUE_DIR):
103     # No additional parameters
104     assert len(path[5:]) == 0
105     if pathtype == DIR:
106       utils.MakeDirWithPerm(pathname, mode, uid, gid)
107     elif pathtype == QUEUE_DIR:
108       EnsureQueueDir(pathname, mode, uid, gid)
109   elif pathtype == FILE:
110     (must_exist, ) = path[5:]
111     utils.EnforcePermission(pathname, mode, uid=uid, gid=gid,
112                             must_exist=must_exist)
113
114
115 def GetPaths():
116   """Returns a tuple of path objects to process.
117
118   """
119   getent = runtime.GetEnts()
120   masterd_log = constants.DAEMONS_LOGFILES[constants.MASTERD]
121   noded_log = constants.DAEMONS_LOGFILES[constants.NODED]
122   confd_log = constants.DAEMONS_LOGFILES[constants.CONFD]
123   rapi_log = constants.DAEMONS_LOGFILES[constants.RAPI]
124
125   rapi_dir = os.path.join(constants.DATA_DIR, "rapi")
126
127   paths = [
128     (constants.DATA_DIR, DIR, 0755, getent.masterd_uid,
129      getent.masterd_gid),
130     (constants.CLUSTER_DOMAIN_SECRET_FILE, FILE, 0640,
131      getent.masterd_uid, getent.masterd_gid, False),
132     (constants.CLUSTER_CONF_FILE, FILE, 0640, getent.masterd_uid,
133      getent.confd_gid, False),
134     (constants.CONFD_HMAC_KEY, FILE, 0440, getent.confd_uid,
135      getent.masterd_gid, False),
136     (constants.SSH_KNOWN_HOSTS_FILE, FILE, 0644, getent.masterd_uid,
137      getent.masterd_gid, False),
138     (constants.RAPI_CERT_FILE, FILE, 0440, getent.rapi_uid,
139      getent.masterd_gid, False),
140     (constants.NODED_CERT_FILE, FILE, 0440, getent.masterd_uid,
141      getent.masterd_gid, False),
142     ]
143
144   ss = ssconf.SimpleStore()
145   for ss_path in ss.GetFileList():
146     paths.append((ss_path, FILE, constants.SS_FILE_PERMS,
147                   getent.noded_uid, 0, False))
148
149   paths.extend([
150     (constants.QUEUE_DIR, DIR, 0700, getent.masterd_uid,
151      getent.masterd_gid),
152     (constants.QUEUE_DIR, QUEUE_DIR, 0600, getent.masterd_uid,
153      getent.masterd_gid),
154     (constants.JOB_QUEUE_LOCK_FILE, FILE, 0600,
155      getent.masterd_uid, getent.masterd_gid, False),
156     (constants.JOB_QUEUE_SERIAL_FILE, FILE, 0600,
157      getent.masterd_uid, getent.masterd_gid, False),
158     (constants.JOB_QUEUE_VERSION_FILE, FILE, 0600,
159      getent.masterd_uid, getent.masterd_gid, False),
160     (constants.JOB_QUEUE_ARCHIVE_DIR, DIR, 0700,
161      getent.masterd_uid, getent.masterd_gid),
162     (rapi_dir, DIR, 0750, getent.rapi_uid, getent.masterd_gid),
163     (constants.RAPI_USERS_FILE, FILE, 0640, getent.rapi_uid,
164      getent.masterd_gid, False),
165     (constants.RUN_GANETI_DIR, DIR, 0775, getent.masterd_uid,
166      getent.daemons_gid),
167     (constants.SOCKET_DIR, DIR, 0750, getent.masterd_uid,
168      getent.daemons_gid),
169     (constants.MASTER_SOCKET, FILE, 0770, getent.masterd_uid,
170      getent.daemons_gid, False),
171     (constants.BDEV_CACHE_DIR, DIR, 0755, getent.noded_uid,
172      getent.masterd_gid),
173     (constants.UIDPOOL_LOCKDIR, DIR, 0750, getent.noded_uid,
174      getent.masterd_gid),
175     (constants.DISK_LINKS_DIR, DIR, 0755, getent.noded_uid,
176      getent.masterd_gid),
177     (constants.CRYPTO_KEYS_DIR, DIR, 0700, getent.noded_uid,
178      getent.masterd_gid),
179     (constants.IMPORT_EXPORT_DIR, DIR, 0755, getent.noded_uid,
180      getent.masterd_gid),
181     (constants.LOG_DIR, DIR, 0770, getent.masterd_uid,
182      getent.daemons_gid),
183     (masterd_log, FILE, 0600, getent.masterd_uid, getent.masterd_gid,
184      False),
185     (confd_log, FILE, 0600, getent.confd_uid, getent.masterd_gid, False),
186     (noded_log, FILE, 0600, getent.noded_uid, getent.masterd_gid, False),
187     (rapi_log, FILE, 0600, getent.rapi_uid, getent.masterd_gid, False),
188     (constants.LOG_OS_DIR, DIR, 0750, getent.masterd_uid,
189      getent.daemons_gid),
190     ])
191
192   return tuple(paths)
193
194
195 def SetupLogging(opts):
196   """Configures the logging module.
197
198   """
199   formatter = logging.Formatter("%(asctime)s: %(message)s")
200
201   stderr_handler = logging.StreamHandler()
202   stderr_handler.setFormatter(formatter)
203   if opts.debug:
204     stderr_handler.setLevel(logging.NOTSET)
205   elif opts.verbose:
206     stderr_handler.setLevel(logging.INFO)
207   else:
208     stderr_handler.setLevel(logging.WARNING)
209
210   root_logger = logging.getLogger("")
211   root_logger.setLevel(logging.NOTSET)
212   root_logger.addHandler(stderr_handler)
213
214
215 def ParseOptions():
216   """Parses the options passed to the program.
217
218   @return: Options and arguments
219
220   """
221   program = os.path.basename(sys.argv[0])
222
223   parser = optparse.OptionParser(usage="%%prog [--full-run]",
224                                  prog=program)
225   parser.add_option(cli.DEBUG_OPT)
226   parser.add_option(cli.VERBOSE_OPT)
227   parser.add_option("--full-run", "-f", dest="full_run", action="store_true",
228                     default=False, help=("Make a full run and set permissions"
229                                          " on archived jobs (time consuming)"))
230
231   return parser.parse_args()
232
233
234 def Main():
235   """Main routine.
236
237   """
238   (opts, _) = ParseOptions()
239
240   SetupLogging(opts)
241
242   if opts.full_run:
243     logging.info("Running in full mode")
244
245   getent = runtime.GetEnts()
246
247   try:
248     for path in GetPaths():
249       ProcessPath(path)
250
251     if opts.full_run:
252       RecursiveEnsure(constants.JOB_QUEUE_ARCHIVE_DIR, getent.masterd_uid,
253                       getent.masterd_gid, 0700, 0600)
254   except errors.GenericError, err:
255     logging.error("An error occurred while setting permissions: %s", err)
256     return constants.EXIT_FAILURE
257
258   return constants.EXIT_SUCCESS