1 |
|
# -*- coding: utf-8 -*- |
2 |
|
|
3 |
|
# Copyright 2018 Telefonica S.A. |
4 |
|
# |
5 |
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
6 |
|
# you may not use this file except in compliance with the License. |
7 |
|
# You may obtain a copy of the License at |
8 |
|
# |
9 |
|
# http://www.apache.org/licenses/LICENSE-2.0 |
10 |
|
# |
11 |
|
# Unless required by applicable law or agreed to in writing, software |
12 |
|
# distributed under the License is distributed on an "AS IS" BASIS, |
13 |
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
14 |
|
# implied. |
15 |
|
# See the License for the specific language governing permissions and |
16 |
|
# limitations under the License. |
17 |
|
|
18 |
1 |
from http import HTTPStatus |
19 |
1 |
import logging |
20 |
1 |
import os |
21 |
1 |
from shutil import rmtree |
22 |
1 |
import tarfile |
23 |
1 |
import zipfile |
24 |
|
|
25 |
1 |
from osm_common.fsbase import FsBase, FsException |
26 |
|
|
27 |
1 |
__author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>" |
28 |
|
|
29 |
|
|
30 |
1 |
class FsLocal(FsBase): |
31 |
1 |
def __init__(self, logger_name="fs", lock=False): |
32 |
1 |
super().__init__(logger_name, lock) |
33 |
1 |
self.path = None |
34 |
|
|
35 |
1 |
def get_params(self): |
36 |
0 |
return {"fs": "local", "path": self.path} |
37 |
|
|
38 |
1 |
def fs_connect(self, config): |
39 |
0 |
try: |
40 |
0 |
if "logger_name" in config: |
41 |
0 |
self.logger = logging.getLogger(config["logger_name"]) |
42 |
0 |
self.path = config["path"] |
43 |
0 |
if not self.path.endswith("/"): |
44 |
0 |
self.path += "/" |
45 |
0 |
if not os.path.exists(self.path): |
46 |
0 |
raise FsException( |
47 |
|
"Invalid configuration param at '[storage]': path '{}' does not exist".format( |
48 |
|
config["path"] |
49 |
|
) |
50 |
|
) |
51 |
0 |
except FsException: |
52 |
0 |
raise |
53 |
0 |
except Exception as e: # TODO refine |
54 |
0 |
raise FsException(str(e)) |
55 |
|
|
56 |
1 |
def fs_disconnect(self): |
57 |
0 |
pass # TODO |
58 |
|
|
59 |
1 |
def mkdir(self, folder): |
60 |
|
""" |
61 |
|
Creates a folder or parent object location |
62 |
|
:param folder: |
63 |
|
:return: None or raises and exception |
64 |
|
""" |
65 |
0 |
try: |
66 |
0 |
os.mkdir(self.path + folder) |
67 |
0 |
except FileExistsError: # make it idempotent |
68 |
0 |
pass |
69 |
0 |
except Exception as e: |
70 |
0 |
raise FsException(str(e), http_code=HTTPStatus.INTERNAL_SERVER_ERROR) |
71 |
|
|
72 |
1 |
def dir_rename(self, src, dst): |
73 |
|
""" |
74 |
|
Rename one directory name. If dst exist, it replaces (deletes) existing directory |
75 |
|
:param src: source directory |
76 |
|
:param dst: destination directory |
77 |
|
:return: None or raises and exception |
78 |
|
""" |
79 |
0 |
try: |
80 |
0 |
if os.path.exists(self.path + dst): |
81 |
0 |
rmtree(self.path + dst) |
82 |
|
|
83 |
0 |
os.rename(self.path + src, self.path + dst) |
84 |
|
|
85 |
0 |
except Exception as e: |
86 |
0 |
raise FsException(str(e), http_code=HTTPStatus.INTERNAL_SERVER_ERROR) |
87 |
|
|
88 |
1 |
def file_exists(self, storage, mode=None): |
89 |
|
""" |
90 |
|
Indicates if "storage" file exist |
91 |
|
:param storage: can be a str or a str list |
92 |
|
:param mode: can be 'file' exist as a regular file; 'dir' exists as a directory or; 'None' just exists |
93 |
|
:return: True, False |
94 |
|
""" |
95 |
0 |
if isinstance(storage, str): |
96 |
0 |
f = storage |
97 |
|
else: |
98 |
0 |
f = "/".join(storage) |
99 |
0 |
if os.path.exists(self.path + f): |
100 |
0 |
if not mode: |
101 |
0 |
return True |
102 |
0 |
if mode == "file" and os.path.isfile(self.path + f): |
103 |
0 |
return True |
104 |
0 |
if mode == "dir" and os.path.isdir(self.path + f): |
105 |
0 |
return True |
106 |
0 |
return False |
107 |
|
|
108 |
1 |
def file_size(self, storage): |
109 |
|
""" |
110 |
|
return file size |
111 |
|
:param storage: can be a str or a str list |
112 |
|
:return: file size |
113 |
|
""" |
114 |
0 |
if isinstance(storage, str): |
115 |
0 |
f = storage |
116 |
|
else: |
117 |
0 |
f = "/".join(storage) |
118 |
0 |
return os.path.getsize(self.path + f) |
119 |
|
|
120 |
1 |
def file_extract(self, compressed_object, path): |
121 |
|
""" |
122 |
|
extract a tar file |
123 |
|
:param compressed_object: object of type tar or zip |
124 |
|
:param path: can be a str or a str list, or a tar object where to extract the tar_object |
125 |
|
:return: None |
126 |
|
""" |
127 |
0 |
if isinstance(path, str): |
128 |
0 |
f = self.path + path |
129 |
|
else: |
130 |
0 |
f = self.path + "/".join(path) |
131 |
|
|
132 |
0 |
if type(compressed_object) is tarfile.TarFile: |
133 |
0 |
compressed_object.extractall(path=f) |
134 |
0 |
elif ( |
135 |
|
type(compressed_object) is zipfile.ZipFile |
136 |
|
): # Just a check to know if this works with both tar and zip |
137 |
0 |
compressed_object.extractall(path=f) |
138 |
|
|
139 |
1 |
def file_open(self, storage, mode): |
140 |
|
""" |
141 |
|
Open a file |
142 |
|
:param storage: can be a str or list of str |
143 |
|
:param mode: file mode |
144 |
|
:return: file object |
145 |
|
""" |
146 |
0 |
try: |
147 |
0 |
if isinstance(storage, str): |
148 |
0 |
f = storage |
149 |
|
else: |
150 |
0 |
f = "/".join(storage) |
151 |
0 |
return open(self.path + f, mode) |
152 |
0 |
except FileNotFoundError: |
153 |
0 |
raise FsException( |
154 |
|
"File {} does not exist".format(f), http_code=HTTPStatus.NOT_FOUND |
155 |
|
) |
156 |
0 |
except IOError: |
157 |
0 |
raise FsException( |
158 |
|
"File {} cannot be opened".format(f), http_code=HTTPStatus.BAD_REQUEST |
159 |
|
) |
160 |
|
|
161 |
1 |
def dir_ls(self, storage): |
162 |
|
""" |
163 |
|
return folder content |
164 |
|
:param storage: can be a str or list of str |
165 |
|
:return: folder content |
166 |
|
""" |
167 |
0 |
try: |
168 |
0 |
if isinstance(storage, str): |
169 |
0 |
f = storage |
170 |
|
else: |
171 |
0 |
f = "/".join(storage) |
172 |
0 |
return os.listdir(self.path + f) |
173 |
0 |
except NotADirectoryError: |
174 |
0 |
raise FsException( |
175 |
|
"File {} does not exist".format(f), http_code=HTTPStatus.NOT_FOUND |
176 |
|
) |
177 |
0 |
except IOError: |
178 |
0 |
raise FsException( |
179 |
|
"File {} cannot be opened".format(f), http_code=HTTPStatus.BAD_REQUEST |
180 |
|
) |
181 |
|
|
182 |
1 |
def file_delete(self, storage, ignore_non_exist=False): |
183 |
|
""" |
184 |
|
Delete storage content recursively |
185 |
|
:param storage: can be a str or list of str |
186 |
|
:param ignore_non_exist: not raise exception if storage does not exist |
187 |
|
:return: None |
188 |
|
""" |
189 |
0 |
try: |
190 |
0 |
if isinstance(storage, str): |
191 |
0 |
f = self.path + storage |
192 |
|
else: |
193 |
0 |
f = self.path + "/".join(storage) |
194 |
0 |
if os.path.exists(f): |
195 |
0 |
rmtree(f) |
196 |
0 |
elif not ignore_non_exist: |
197 |
0 |
raise FsException( |
198 |
|
"File {} does not exist".format(storage), |
199 |
|
http_code=HTTPStatus.NOT_FOUND, |
200 |
|
) |
201 |
0 |
except (IOError, PermissionError) as e: |
202 |
0 |
raise FsException( |
203 |
|
"File {} cannot be deleted: {}".format(f, e), |
204 |
|
http_code=HTTPStatus.INTERNAL_SERVER_ERROR, |
205 |
|
) |
206 |
|
|
207 |
1 |
def sync(self, from_path=None): |
208 |
0 |
pass # Not needed in fslocal |
209 |
|
|
210 |
1 |
def reverse_sync(self, from_path): |
211 |
0 |
pass # Not needed in fslocal |