root / lib / storage / filestorage.py @ 178ad717
History | View | Annotate | Download (12.9 kB)
1 | 820bade9 | Helga Velroyen | #
|
---|---|---|---|
2 | 820bade9 | Helga Velroyen | #
|
3 | 820bade9 | Helga Velroyen | |
4 | 820bade9 | Helga Velroyen | # Copyright (C) 2013 Google Inc.
|
5 | 820bade9 | Helga Velroyen | #
|
6 | 820bade9 | Helga Velroyen | # This program is free software; you can redistribute it and/or modify
|
7 | 820bade9 | Helga Velroyen | # it under the terms of the GNU General Public License as published by
|
8 | 820bade9 | Helga Velroyen | # the Free Software Foundation; either version 2 of the License, or
|
9 | 820bade9 | Helga Velroyen | # (at your option) any later version.
|
10 | 820bade9 | Helga Velroyen | #
|
11 | 820bade9 | Helga Velroyen | # This program is distributed in the hope that it will be useful, but
|
12 | 820bade9 | Helga Velroyen | # WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 | 820bade9 | Helga Velroyen | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14 | 820bade9 | Helga Velroyen | # General Public License for more details.
|
15 | 820bade9 | Helga Velroyen | #
|
16 | 820bade9 | Helga Velroyen | # You should have received a copy of the GNU General Public License
|
17 | 820bade9 | Helga Velroyen | # along with this program; if not, write to the Free Software
|
18 | 820bade9 | Helga Velroyen | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
19 | 820bade9 | Helga Velroyen | # 02110-1301, USA.
|
20 | 820bade9 | Helga Velroyen | |
21 | 820bade9 | Helga Velroyen | |
22 | 2656b017 | Santi Raffa | """Filesystem-based access functions and disk templates.
|
23 | 820bade9 | Helga Velroyen |
|
24 | 820bade9 | Helga Velroyen | """
|
25 | 820bade9 | Helga Velroyen | |
26 | 13a6c760 | Helga Velroyen | import logging |
27 | 2656b017 | Santi Raffa | import errno |
28 | 5d94c034 | Helga Velroyen | import os |
29 | 820bade9 | Helga Velroyen | |
30 | 13a6c760 | Helga Velroyen | from ganeti import compat |
31 | 13669ecd | Helga Velroyen | from ganeti import constants |
32 | 5d94c034 | Helga Velroyen | from ganeti import errors |
33 | 13a6c760 | Helga Velroyen | from ganeti import pathutils |
34 | 13a6c760 | Helga Velroyen | from ganeti import utils |
35 | 267520ba | Santi Raffa | from ganeti.utils import io |
36 | 2656b017 | Santi Raffa | from ganeti.storage import base |
37 | 2656b017 | Santi Raffa | |
38 | 2656b017 | Santi Raffa | |
39 | 267520ba | Santi Raffa | class FileDeviceHelper(object): |
40 | 267520ba | Santi Raffa | |
41 | 267520ba | Santi Raffa | @classmethod
|
42 | 267520ba | Santi Raffa | def CreateFile(cls, path, size, create_folders=False, |
43 | 267520ba | Santi Raffa | _file_path_acceptance_fn=None):
|
44 | 267520ba | Santi Raffa | """Create a new file and its file device helper.
|
45 | 267520ba | Santi Raffa |
|
46 | 267520ba | Santi Raffa | @param size: the size in MiBs the file should be truncated to.
|
47 | 267520ba | Santi Raffa | @param create_folders: create the directories for the path if necessary
|
48 | 267520ba | Santi Raffa | (using L{ganeti.utils.io.Makedirs})
|
49 | 267520ba | Santi Raffa |
|
50 | 267520ba | Santi Raffa | @rtype: FileDeviceHelper
|
51 | 267520ba | Santi Raffa | @return: The FileDeviceHelper object representing the object.
|
52 | 267520ba | Santi Raffa | @raise errors.FileStoragePathError: if the file path is disallowed by policy
|
53 | 267520ba | Santi Raffa |
|
54 | 267520ba | Santi Raffa | """
|
55 | 267520ba | Santi Raffa | |
56 | 267520ba | Santi Raffa | if not _file_path_acceptance_fn: |
57 | 267520ba | Santi Raffa | _file_path_acceptance_fn = CheckFileStoragePathAcceptance |
58 | 267520ba | Santi Raffa | _file_path_acceptance_fn(path) |
59 | 267520ba | Santi Raffa | |
60 | 267520ba | Santi Raffa | if create_folders:
|
61 | 267520ba | Santi Raffa | folder = os.path.dirname(path) |
62 | 267520ba | Santi Raffa | io.Makedirs(folder) |
63 | 267520ba | Santi Raffa | |
64 | 267520ba | Santi Raffa | try:
|
65 | 267520ba | Santi Raffa | fd = os.open(path, os.O_RDWR | os.O_CREAT | os.O_EXCL) |
66 | 267520ba | Santi Raffa | f = os.fdopen(fd, "w")
|
67 | 267520ba | Santi Raffa | f.truncate(size * 1024 * 1024) |
68 | 267520ba | Santi Raffa | f.close() |
69 | 267520ba | Santi Raffa | except EnvironmentError as err: |
70 | 267520ba | Santi Raffa | base.ThrowError("%s: can't create: %s", path, str(err)) |
71 | 267520ba | Santi Raffa | |
72 | 267520ba | Santi Raffa | return FileDeviceHelper(path,
|
73 | 267520ba | Santi Raffa | _file_path_acceptance_fn=_file_path_acceptance_fn) |
74 | 267520ba | Santi Raffa | |
75 | 267520ba | Santi Raffa | def __init__(self, path, _file_path_acceptance_fn=None): |
76 | 267520ba | Santi Raffa | """Create a new file device helper.
|
77 | 267520ba | Santi Raffa |
|
78 | 267520ba | Santi Raffa | @raise errors.FileStoragePathError: if the file path is disallowed by policy
|
79 | 267520ba | Santi Raffa |
|
80 | 267520ba | Santi Raffa | """
|
81 | 267520ba | Santi Raffa | if not _file_path_acceptance_fn: |
82 | 267520ba | Santi Raffa | _file_path_acceptance_fn = CheckFileStoragePathAcceptance |
83 | 267520ba | Santi Raffa | _file_path_acceptance_fn(path) |
84 | 267520ba | Santi Raffa | |
85 | 267520ba | Santi Raffa | self.path = path
|
86 | 267520ba | Santi Raffa | |
87 | 267520ba | Santi Raffa | def Exists(self, assert_exists=None): |
88 | 267520ba | Santi Raffa | """Check for the existence of the given file.
|
89 | 267520ba | Santi Raffa |
|
90 | 267520ba | Santi Raffa | @param assert_exists: creates an assertion on the result value:
|
91 | 267520ba | Santi Raffa | - if true, raise errors.BlockDeviceError if the file doesn't exist
|
92 | 267520ba | Santi Raffa | - if false, raise errors.BlockDeviceError if the file does exist
|
93 | 267520ba | Santi Raffa | @rtype: boolean
|
94 | 267520ba | Santi Raffa | @return: True if the file exists
|
95 | 267520ba | Santi Raffa |
|
96 | 267520ba | Santi Raffa | """
|
97 | 267520ba | Santi Raffa | |
98 | 267520ba | Santi Raffa | exists = os.path.isfile(self.path)
|
99 | 267520ba | Santi Raffa | |
100 | 267520ba | Santi Raffa | if not exists and assert_exists is True: |
101 | 267520ba | Santi Raffa | raise base.ThrowError("%s: No such file", self.path) |
102 | 267520ba | Santi Raffa | if exists and assert_exists is False: |
103 | 267520ba | Santi Raffa | raise base.ThrowError("%s: File exists", self.path) |
104 | 267520ba | Santi Raffa | |
105 | 267520ba | Santi Raffa | return exists
|
106 | 267520ba | Santi Raffa | |
107 | 267520ba | Santi Raffa | def Remove(self): |
108 | 267520ba | Santi Raffa | """Remove the file backing the block device.
|
109 | 267520ba | Santi Raffa |
|
110 | 267520ba | Santi Raffa | @rtype: boolean
|
111 | 267520ba | Santi Raffa | @return: True if the removal was successful
|
112 | 267520ba | Santi Raffa |
|
113 | 267520ba | Santi Raffa | """
|
114 | 267520ba | Santi Raffa | try:
|
115 | 267520ba | Santi Raffa | os.remove(self.path)
|
116 | 267520ba | Santi Raffa | return True |
117 | 267520ba | Santi Raffa | except OSError as err: |
118 | 267520ba | Santi Raffa | if err.errno != errno.ENOENT:
|
119 | 267520ba | Santi Raffa | base.ThrowError("%s: can't remove: %s", self.path, err) |
120 | 267520ba | Santi Raffa | return False |
121 | 267520ba | Santi Raffa | |
122 | 267520ba | Santi Raffa | def Size(self): |
123 | 267520ba | Santi Raffa | """Return the actual disk size in bytes.
|
124 | 267520ba | Santi Raffa |
|
125 | 267520ba | Santi Raffa | @rtype: int
|
126 | 267520ba | Santi Raffa | @return: The file size in bytes.
|
127 | 267520ba | Santi Raffa |
|
128 | 267520ba | Santi Raffa | """
|
129 | 267520ba | Santi Raffa | self.Exists(assert_exists=True) |
130 | 267520ba | Santi Raffa | try:
|
131 | 267520ba | Santi Raffa | return os.stat(self.path).st_size |
132 | 267520ba | Santi Raffa | except OSError as err: |
133 | 267520ba | Santi Raffa | base.ThrowError("%s: can't stat: %s", self.path, err) |
134 | 267520ba | Santi Raffa | |
135 | 267520ba | Santi Raffa | def Grow(self, amount, dryrun, backingstore, _excl_stor): |
136 | 267520ba | Santi Raffa | """Grow the file
|
137 | 267520ba | Santi Raffa |
|
138 | 267520ba | Santi Raffa | @param amount: the amount (in mebibytes) to grow by.
|
139 | 267520ba | Santi Raffa |
|
140 | 267520ba | Santi Raffa | """
|
141 | 267520ba | Santi Raffa | # Check that the file exists
|
142 | 267520ba | Santi Raffa | self.Exists(assert_exists=True) |
143 | 267520ba | Santi Raffa | |
144 | 267520ba | Santi Raffa | if amount < 0: |
145 | 267520ba | Santi Raffa | base.ThrowError("%s: can't grow by negative amount", self.path) |
146 | 267520ba | Santi Raffa | |
147 | 267520ba | Santi Raffa | if dryrun:
|
148 | 267520ba | Santi Raffa | return
|
149 | 267520ba | Santi Raffa | if not backingstore: |
150 | 267520ba | Santi Raffa | return
|
151 | 267520ba | Santi Raffa | |
152 | 267520ba | Santi Raffa | current_size = self.Size()
|
153 | 267520ba | Santi Raffa | new_size = current_size + amount * 1024 * 1024 |
154 | 267520ba | Santi Raffa | try:
|
155 | 267520ba | Santi Raffa | f = open(self.path, "a+") |
156 | 267520ba | Santi Raffa | f.truncate(new_size) |
157 | 267520ba | Santi Raffa | f.close() |
158 | 267520ba | Santi Raffa | except EnvironmentError, err: |
159 | 267520ba | Santi Raffa | base.ThrowError("%s: can't grow: ", self.path, str(err)) |
160 | 267520ba | Santi Raffa | |
161 | 267520ba | Santi Raffa | |
162 | 2656b017 | Santi Raffa | class FileStorage(base.BlockDev): |
163 | 2656b017 | Santi Raffa | """File device.
|
164 | 2656b017 | Santi Raffa |
|
165 | 2656b017 | Santi Raffa | This class represents a file storage backend device.
|
166 | 2656b017 | Santi Raffa |
|
167 | 2656b017 | Santi Raffa | The unique_id for the file device is a (file_driver, file_path) tuple.
|
168 | 2656b017 | Santi Raffa |
|
169 | 2656b017 | Santi Raffa | """
|
170 | 2656b017 | Santi Raffa | def __init__(self, unique_id, children, size, params, dyn_params): |
171 | 2656b017 | Santi Raffa | """Initalizes a file device backend.
|
172 | 2656b017 | Santi Raffa |
|
173 | 2656b017 | Santi Raffa | """
|
174 | 2656b017 | Santi Raffa | if children:
|
175 | 2656b017 | Santi Raffa | raise errors.BlockDeviceError("Invalid setup for file device") |
176 | 2656b017 | Santi Raffa | super(FileStorage, self).__init__(unique_id, children, size, params, |
177 | 2656b017 | Santi Raffa | dyn_params) |
178 | 2656b017 | Santi Raffa | if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2: |
179 | 2656b017 | Santi Raffa | raise ValueError("Invalid configuration data %s" % str(unique_id)) |
180 | 2656b017 | Santi Raffa | self.driver = unique_id[0] |
181 | 2656b017 | Santi Raffa | self.dev_path = unique_id[1] |
182 | 267520ba | Santi Raffa | self.file = FileDeviceHelper(self.dev_path) |
183 | 2656b017 | Santi Raffa | self.Attach()
|
184 | 2656b017 | Santi Raffa | |
185 | 2656b017 | Santi Raffa | def Assemble(self): |
186 | 2656b017 | Santi Raffa | """Assemble the device.
|
187 | 2656b017 | Santi Raffa |
|
188 | 2656b017 | Santi Raffa | Checks whether the file device exists, raises BlockDeviceError otherwise.
|
189 | 2656b017 | Santi Raffa |
|
190 | 2656b017 | Santi Raffa | """
|
191 | 267520ba | Santi Raffa | self.file.Exists(assert_exists=True) |
192 | 2656b017 | Santi Raffa | |
193 | 2656b017 | Santi Raffa | def Shutdown(self): |
194 | 2656b017 | Santi Raffa | """Shutdown the device.
|
195 | 2656b017 | Santi Raffa |
|
196 | 2656b017 | Santi Raffa | This is a no-op for the file type, as we don't deactivate
|
197 | 2656b017 | Santi Raffa | the file on shutdown.
|
198 | 2656b017 | Santi Raffa |
|
199 | 2656b017 | Santi Raffa | """
|
200 | 2656b017 | Santi Raffa | pass
|
201 | 2656b017 | Santi Raffa | |
202 | 2656b017 | Santi Raffa | def Open(self, force=False): |
203 | 2656b017 | Santi Raffa | """Make the device ready for I/O.
|
204 | 2656b017 | Santi Raffa |
|
205 | 2656b017 | Santi Raffa | This is a no-op for the file type.
|
206 | 2656b017 | Santi Raffa |
|
207 | 2656b017 | Santi Raffa | """
|
208 | 2656b017 | Santi Raffa | pass
|
209 | 2656b017 | Santi Raffa | |
210 | 2656b017 | Santi Raffa | def Close(self): |
211 | 2656b017 | Santi Raffa | """Notifies that the device will no longer be used for I/O.
|
212 | 2656b017 | Santi Raffa |
|
213 | 2656b017 | Santi Raffa | This is a no-op for the file type.
|
214 | 2656b017 | Santi Raffa |
|
215 | 2656b017 | Santi Raffa | """
|
216 | 2656b017 | Santi Raffa | pass
|
217 | 2656b017 | Santi Raffa | |
218 | 2656b017 | Santi Raffa | def Remove(self): |
219 | 2656b017 | Santi Raffa | """Remove the file backing the block device.
|
220 | 2656b017 | Santi Raffa |
|
221 | 2656b017 | Santi Raffa | @rtype: boolean
|
222 | 2656b017 | Santi Raffa | @return: True if the removal was successful
|
223 | 2656b017 | Santi Raffa |
|
224 | 2656b017 | Santi Raffa | """
|
225 | 267520ba | Santi Raffa | return self.file.Remove() |
226 | 2656b017 | Santi Raffa | |
227 | 2656b017 | Santi Raffa | def Rename(self, new_id): |
228 | 2656b017 | Santi Raffa | """Renames the file.
|
229 | 2656b017 | Santi Raffa |
|
230 | 2656b017 | Santi Raffa | """
|
231 | 2656b017 | Santi Raffa | # TODO: implement rename for file-based storage
|
232 | 2656b017 | Santi Raffa | base.ThrowError("Rename is not supported for file-based storage")
|
233 | 2656b017 | Santi Raffa | |
234 | 2656b017 | Santi Raffa | def Grow(self, amount, dryrun, backingstore, excl_stor): |
235 | 2656b017 | Santi Raffa | """Grow the file
|
236 | 2656b017 | Santi Raffa |
|
237 | 2656b017 | Santi Raffa | @param amount: the amount (in mebibytes) to grow with
|
238 | 2656b017 | Santi Raffa |
|
239 | 2656b017 | Santi Raffa | """
|
240 | 2656b017 | Santi Raffa | if not backingstore: |
241 | 2656b017 | Santi Raffa | return
|
242 | 2656b017 | Santi Raffa | if dryrun:
|
243 | 2656b017 | Santi Raffa | return
|
244 | 267520ba | Santi Raffa | self.file.Grow(amount, dryrun, backingstore, excl_stor)
|
245 | 2656b017 | Santi Raffa | |
246 | 2656b017 | Santi Raffa | def Attach(self): |
247 | 2656b017 | Santi Raffa | """Attach to an existing file.
|
248 | 2656b017 | Santi Raffa |
|
249 | 2656b017 | Santi Raffa | Check if this file already exists.
|
250 | 2656b017 | Santi Raffa |
|
251 | 2656b017 | Santi Raffa | @rtype: boolean
|
252 | 2656b017 | Santi Raffa | @return: True if file exists
|
253 | 2656b017 | Santi Raffa |
|
254 | 2656b017 | Santi Raffa | """
|
255 | 267520ba | Santi Raffa | self.attached = self.file.Exists() |
256 | 2656b017 | Santi Raffa | return self.attached |
257 | 2656b017 | Santi Raffa | |
258 | 2656b017 | Santi Raffa | def GetActualSize(self): |
259 | 2656b017 | Santi Raffa | """Return the actual disk size.
|
260 | 2656b017 | Santi Raffa |
|
261 | 2656b017 | Santi Raffa | @note: the device needs to be active when this is called
|
262 | 2656b017 | Santi Raffa |
|
263 | 2656b017 | Santi Raffa | """
|
264 | 267520ba | Santi Raffa | return self.file.Size() |
265 | 2656b017 | Santi Raffa | |
266 | 2656b017 | Santi Raffa | @classmethod
|
267 | 2656b017 | Santi Raffa | def Create(cls, unique_id, children, size, spindles, params, excl_stor, |
268 | 2656b017 | Santi Raffa | dyn_params): |
269 | 2656b017 | Santi Raffa | """Create a new file.
|
270 | 2656b017 | Santi Raffa |
|
271 | 2656b017 | Santi Raffa | @type size: int
|
272 | 2656b017 | Santi Raffa | @param size: the size of file in MiB
|
273 | 2656b017 | Santi Raffa |
|
274 | 2656b017 | Santi Raffa | @rtype: L{bdev.FileStorage}
|
275 | 2656b017 | Santi Raffa | @return: an instance of FileStorage
|
276 | 2656b017 | Santi Raffa |
|
277 | 2656b017 | Santi Raffa | """
|
278 | 2656b017 | Santi Raffa | if excl_stor:
|
279 | 2656b017 | Santi Raffa | raise errors.ProgrammerError("FileStorage device requested with" |
280 | 2656b017 | Santi Raffa | " exclusive_storage")
|
281 | 2656b017 | Santi Raffa | if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2: |
282 | 2656b017 | Santi Raffa | raise ValueError("Invalid configuration data %s" % str(unique_id)) |
283 | 2656b017 | Santi Raffa | |
284 | 2656b017 | Santi Raffa | dev_path = unique_id[1]
|
285 | 2656b017 | Santi Raffa | |
286 | 267520ba | Santi Raffa | FileDeviceHelper.CreateFile(dev_path, size) |
287 | 2656b017 | Santi Raffa | return FileStorage(unique_id, children, size, params, dyn_params)
|
288 | 820bade9 | Helga Velroyen | |
289 | 820bade9 | Helga Velroyen | |
290 | e798d484 | Helga Velroyen | def GetFileStorageSpaceInfo(path): |
291 | 820bade9 | Helga Velroyen | """Retrieves the free and total space of the device where the file is
|
292 | 820bade9 | Helga Velroyen | located.
|
293 | 820bade9 | Helga Velroyen |
|
294 | 820bade9 | Helga Velroyen | @type path: string
|
295 | 820bade9 | Helga Velroyen | @param path: Path of the file whose embracing device's capacity is
|
296 | 820bade9 | Helga Velroyen | reported.
|
297 | 5d94c034 | Helga Velroyen | @return: a dictionary containing 'vg_size' and 'vg_free' given in MebiBytes
|
298 | 5d94c034 | Helga Velroyen |
|
299 | 820bade9 | Helga Velroyen | """
|
300 | 5d94c034 | Helga Velroyen | try:
|
301 | 5d94c034 | Helga Velroyen | result = os.statvfs(path) |
302 | 5d94c034 | Helga Velroyen | free = (result.f_frsize * result.f_bavail) / (1024 * 1024) |
303 | 5d94c034 | Helga Velroyen | size = (result.f_frsize * result.f_blocks) / (1024 * 1024) |
304 | 13669ecd | Helga Velroyen | return {"type": constants.ST_FILE, |
305 | 13669ecd | Helga Velroyen | "name": path,
|
306 | 32389d91 | Helga Velroyen | "storage_size": size,
|
307 | 32389d91 | Helga Velroyen | "storage_free": free}
|
308 | 5d94c034 | Helga Velroyen | except OSError, e: |
309 | 5d94c034 | Helga Velroyen | raise errors.CommandError("Failed to retrieve file system information about" |
310 | 5d94c034 | Helga Velroyen | " path: %s - %s" % (path, e.strerror))
|
311 | 13a6c760 | Helga Velroyen | |
312 | 13a6c760 | Helga Velroyen | |
313 | 13a6c760 | Helga Velroyen | def _GetForbiddenFileStoragePaths(): |
314 | 13a6c760 | Helga Velroyen | """Builds a list of path prefixes which shouldn't be used for file storage.
|
315 | 13a6c760 | Helga Velroyen |
|
316 | 13a6c760 | Helga Velroyen | @rtype: frozenset
|
317 | 13a6c760 | Helga Velroyen |
|
318 | 13a6c760 | Helga Velroyen | """
|
319 | 13a6c760 | Helga Velroyen | paths = set([
|
320 | 13a6c760 | Helga Velroyen | "/boot",
|
321 | 13a6c760 | Helga Velroyen | "/dev",
|
322 | 13a6c760 | Helga Velroyen | "/etc",
|
323 | 13a6c760 | Helga Velroyen | "/home",
|
324 | 13a6c760 | Helga Velroyen | "/proc",
|
325 | 13a6c760 | Helga Velroyen | "/root",
|
326 | 13a6c760 | Helga Velroyen | "/sys",
|
327 | 13a6c760 | Helga Velroyen | ]) |
328 | 13a6c760 | Helga Velroyen | |
329 | 13a6c760 | Helga Velroyen | for prefix in ["", "/usr", "/usr/local"]: |
330 | 13a6c760 | Helga Velroyen | paths.update(map(lambda s: "%s/%s" % (prefix, s), |
331 | 13a6c760 | Helga Velroyen | ["bin", "lib", "lib32", "lib64", "sbin"])) |
332 | 13a6c760 | Helga Velroyen | |
333 | 13a6c760 | Helga Velroyen | return compat.UniqueFrozenset(map(os.path.normpath, paths)) |
334 | 13a6c760 | Helga Velroyen | |
335 | 13a6c760 | Helga Velroyen | |
336 | 13a6c760 | Helga Velroyen | def _ComputeWrongFileStoragePaths(paths, |
337 | 13a6c760 | Helga Velroyen | _forbidden=_GetForbiddenFileStoragePaths()): |
338 | 13a6c760 | Helga Velroyen | """Cross-checks a list of paths for prefixes considered bad.
|
339 | 13a6c760 | Helga Velroyen |
|
340 | 13a6c760 | Helga Velroyen | Some paths, e.g. "/bin", should not be used for file storage.
|
341 | 13a6c760 | Helga Velroyen |
|
342 | 13a6c760 | Helga Velroyen | @type paths: list
|
343 | 13a6c760 | Helga Velroyen | @param paths: List of paths to be checked
|
344 | 13a6c760 | Helga Velroyen | @rtype: list
|
345 | 13a6c760 | Helga Velroyen | @return: Sorted list of paths for which the user should be warned
|
346 | 13a6c760 | Helga Velroyen |
|
347 | 13a6c760 | Helga Velroyen | """
|
348 | 13a6c760 | Helga Velroyen | def _Check(path): |
349 | 13a6c760 | Helga Velroyen | return (not os.path.isabs(path) or |
350 | 13a6c760 | Helga Velroyen | path in _forbidden or |
351 | 13a6c760 | Helga Velroyen | filter(lambda p: utils.IsBelowDir(p, path), _forbidden)) |
352 | 13a6c760 | Helga Velroyen | |
353 | 13a6c760 | Helga Velroyen | return utils.NiceSort(filter(_Check, map(os.path.normpath, paths))) |
354 | 13a6c760 | Helga Velroyen | |
355 | 13a6c760 | Helga Velroyen | |
356 | 13a6c760 | Helga Velroyen | def ComputeWrongFileStoragePaths(_filename=pathutils.FILE_STORAGE_PATHS_FILE): |
357 | 13a6c760 | Helga Velroyen | """Returns a list of file storage paths whose prefix is considered bad.
|
358 | 13a6c760 | Helga Velroyen |
|
359 | 13a6c760 | Helga Velroyen | See L{_ComputeWrongFileStoragePaths}.
|
360 | 13a6c760 | Helga Velroyen |
|
361 | 13a6c760 | Helga Velroyen | """
|
362 | 13a6c760 | Helga Velroyen | return _ComputeWrongFileStoragePaths(_LoadAllowedFileStoragePaths(_filename))
|
363 | 13a6c760 | Helga Velroyen | |
364 | 13a6c760 | Helga Velroyen | |
365 | 9c1c3c19 | Helga Velroyen | def _CheckFileStoragePath(path, allowed, exact_match_ok=False): |
366 | 13a6c760 | Helga Velroyen | """Checks if a path is in a list of allowed paths for file storage.
|
367 | 13a6c760 | Helga Velroyen |
|
368 | 13a6c760 | Helga Velroyen | @type path: string
|
369 | 13a6c760 | Helga Velroyen | @param path: Path to check
|
370 | 13a6c760 | Helga Velroyen | @type allowed: list
|
371 | 13a6c760 | Helga Velroyen | @param allowed: List of allowed paths
|
372 | 9c1c3c19 | Helga Velroyen | @type exact_match_ok: bool
|
373 | 9c1c3c19 | Helga Velroyen | @param exact_match_ok: whether or not it is okay when the path is exactly
|
374 | 9c1c3c19 | Helga Velroyen | equal to an allowed path and not a subdir of it
|
375 | 13a6c760 | Helga Velroyen | @raise errors.FileStoragePathError: If the path is not allowed
|
376 | 13a6c760 | Helga Velroyen |
|
377 | 13a6c760 | Helga Velroyen | """
|
378 | 13a6c760 | Helga Velroyen | if not os.path.isabs(path): |
379 | 13a6c760 | Helga Velroyen | raise errors.FileStoragePathError("File storage path must be absolute," |
380 | 13a6c760 | Helga Velroyen | " got '%s'" % path)
|
381 | 13a6c760 | Helga Velroyen | |
382 | 13a6c760 | Helga Velroyen | for i in allowed: |
383 | 13a6c760 | Helga Velroyen | if not os.path.isabs(i): |
384 | 13a6c760 | Helga Velroyen | logging.info("Ignoring relative path '%s' for file storage", i)
|
385 | 13a6c760 | Helga Velroyen | continue
|
386 | 13a6c760 | Helga Velroyen | |
387 | 9c1c3c19 | Helga Velroyen | if exact_match_ok:
|
388 | 9c1c3c19 | Helga Velroyen | if os.path.normpath(i) == os.path.normpath(path):
|
389 | 9c1c3c19 | Helga Velroyen | break
|
390 | 9c1c3c19 | Helga Velroyen | |
391 | 13a6c760 | Helga Velroyen | if utils.IsBelowDir(i, path):
|
392 | 13a6c760 | Helga Velroyen | break
|
393 | 13a6c760 | Helga Velroyen | else:
|
394 | 13a6c760 | Helga Velroyen | raise errors.FileStoragePathError("Path '%s' is not acceptable for file" |
395 | 13a6c760 | Helga Velroyen | " storage" % path)
|
396 | 13a6c760 | Helga Velroyen | |
397 | 13a6c760 | Helga Velroyen | |
398 | 13a6c760 | Helga Velroyen | def _LoadAllowedFileStoragePaths(filename): |
399 | 13a6c760 | Helga Velroyen | """Loads file containing allowed file storage paths.
|
400 | 13a6c760 | Helga Velroyen |
|
401 | 13a6c760 | Helga Velroyen | @rtype: list
|
402 | 13a6c760 | Helga Velroyen | @return: List of allowed paths (can be an empty list)
|
403 | 13a6c760 | Helga Velroyen |
|
404 | 13a6c760 | Helga Velroyen | """
|
405 | 13a6c760 | Helga Velroyen | try:
|
406 | 13a6c760 | Helga Velroyen | contents = utils.ReadFile(filename) |
407 | 13a6c760 | Helga Velroyen | except EnvironmentError: |
408 | 13a6c760 | Helga Velroyen | return []
|
409 | 13a6c760 | Helga Velroyen | else:
|
410 | 13a6c760 | Helga Velroyen | return utils.FilterEmptyLinesAndComments(contents)
|
411 | 13a6c760 | Helga Velroyen | |
412 | 13a6c760 | Helga Velroyen | |
413 | 13a6c760 | Helga Velroyen | def CheckFileStoragePathAcceptance( |
414 | 9c1c3c19 | Helga Velroyen | path, _filename=pathutils.FILE_STORAGE_PATHS_FILE, |
415 | 9c1c3c19 | Helga Velroyen | exact_match_ok=False):
|
416 | 13a6c760 | Helga Velroyen | """Checks if a path is allowed for file storage.
|
417 | 13a6c760 | Helga Velroyen |
|
418 | 13a6c760 | Helga Velroyen | @type path: string
|
419 | 13a6c760 | Helga Velroyen | @param path: Path to check
|
420 | 13a6c760 | Helga Velroyen | @raise errors.FileStoragePathError: If the path is not allowed
|
421 | 13a6c760 | Helga Velroyen |
|
422 | 13a6c760 | Helga Velroyen | """
|
423 | 13a6c760 | Helga Velroyen | allowed = _LoadAllowedFileStoragePaths(_filename) |
424 | f3ebe73e | Helga Velroyen | if not allowed: |
425 | f3ebe73e | Helga Velroyen | raise errors.FileStoragePathError("No paths are valid or path file '%s'" |
426 | f3ebe73e | Helga Velroyen | " was not accessible." % _filename)
|
427 | 13a6c760 | Helga Velroyen | |
428 | 13a6c760 | Helga Velroyen | if _ComputeWrongFileStoragePaths([path]):
|
429 | 13a6c760 | Helga Velroyen | raise errors.FileStoragePathError("Path '%s' uses a forbidden prefix" % |
430 | 13a6c760 | Helga Velroyen | path) |
431 | 13a6c760 | Helga Velroyen | |
432 | 9c1c3c19 | Helga Velroyen | _CheckFileStoragePath(path, allowed, exact_match_ok=exact_match_ok) |
433 | 9c1c3c19 | Helga Velroyen | |
434 | 9c1c3c19 | Helga Velroyen | |
435 | 9c1c3c19 | Helga Velroyen | def _CheckFileStoragePathExistance(path): |
436 | 9c1c3c19 | Helga Velroyen | """Checks whether the given path is usable on the file system.
|
437 | 9c1c3c19 | Helga Velroyen |
|
438 | 9c1c3c19 | Helga Velroyen | This checks wether the path is existing, a directory and writable.
|
439 | 9c1c3c19 | Helga Velroyen |
|
440 | 9c1c3c19 | Helga Velroyen | @type path: string
|
441 | 9c1c3c19 | Helga Velroyen | @param path: path to check
|
442 | 9c1c3c19 | Helga Velroyen |
|
443 | 9c1c3c19 | Helga Velroyen | """
|
444 | 9c1c3c19 | Helga Velroyen | if not os.path.isdir(path): |
445 | f3ebe73e | Helga Velroyen | raise errors.FileStoragePathError("Path '%s' is not existing or not a" |
446 | 9c1c3c19 | Helga Velroyen | " directory." % path)
|
447 | 9c1c3c19 | Helga Velroyen | if not os.access(path, os.W_OK): |
448 | 9c1c3c19 | Helga Velroyen | raise errors.FileStoragePathError("Path '%s' is not writable" % path) |
449 | 9c1c3c19 | Helga Velroyen | |
450 | 9c1c3c19 | Helga Velroyen | |
451 | 9c1c3c19 | Helga Velroyen | def CheckFileStoragePath( |
452 | 9c1c3c19 | Helga Velroyen | path, _allowed_paths_file=pathutils.FILE_STORAGE_PATHS_FILE): |
453 | 9c1c3c19 | Helga Velroyen | """Checks whether the path exists and is acceptable to use.
|
454 | 9c1c3c19 | Helga Velroyen |
|
455 | 4b322a76 | Helga Velroyen | Can be used for any file-based storage, for example shared-file storage.
|
456 | 4b322a76 | Helga Velroyen |
|
457 | 9c1c3c19 | Helga Velroyen | @type path: string
|
458 | 9c1c3c19 | Helga Velroyen | @param path: path to check
|
459 | 9c1c3c19 | Helga Velroyen | @rtype: string
|
460 | 9c1c3c19 | Helga Velroyen | @returns: error message if the path is not ready to use
|
461 | 9c1c3c19 | Helga Velroyen |
|
462 | 9c1c3c19 | Helga Velroyen | """
|
463 | 9c1c3c19 | Helga Velroyen | try:
|
464 | 9c1c3c19 | Helga Velroyen | CheckFileStoragePathAcceptance(path, _filename=_allowed_paths_file, |
465 | 9c1c3c19 | Helga Velroyen | exact_match_ok=True)
|
466 | 2650ea6b | Helga Velroyen | except errors.FileStoragePathError as e: |
467 | 2650ea6b | Helga Velroyen | return str(e) |
468 | 9c1c3c19 | Helga Velroyen | if not os.path.isdir(path): |
469 | 9c1c3c19 | Helga Velroyen | return "Path '%s' is not exisiting or not a directory." % path |
470 | 9c1c3c19 | Helga Velroyen | if not os.access(path, os.W_OK): |
471 | 9c1c3c19 | Helga Velroyen | return "Path '%s' is not writable" % path |