Enable parallel execution and output of tox env
[osm/common.git] / osm_common / fslocal.py
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 from http import HTTPStatus
19 import logging
20 import os
21 from shutil import rmtree
22 import tarfile
23 import zipfile
24
25 from osm_common.fsbase import FsBase, FsException
26
27 __author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
28
29
30 class FsLocal(FsBase):
31 def __init__(self, logger_name="fs", lock=False):
32 super().__init__(logger_name, lock)
33 self.path = None
34
35 def get_params(self):
36 return {"fs": "local", "path": self.path}
37
38 def fs_connect(self, config):
39 try:
40 if "logger_name" in config:
41 self.logger = logging.getLogger(config["logger_name"])
42 self.path = config["path"]
43 if not self.path.endswith("/"):
44 self.path += "/"
45 if not os.path.exists(self.path):
46 raise FsException(
47 "Invalid configuration param at '[storage]': path '{}' does not exist".format(
48 config["path"]
49 )
50 )
51 except FsException:
52 raise
53 except Exception as e: # TODO refine
54 raise FsException(str(e))
55
56 def fs_disconnect(self):
57 pass # TODO
58
59 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 try:
66 os.mkdir(self.path + folder)
67 except FileExistsError: # make it idempotent
68 pass
69 except Exception as e:
70 raise FsException(str(e), http_code=HTTPStatus.INTERNAL_SERVER_ERROR)
71
72 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 try:
80 if os.path.exists(self.path + dst):
81 rmtree(self.path + dst)
82
83 os.rename(self.path + src, self.path + dst)
84
85 except Exception as e:
86 raise FsException(str(e), http_code=HTTPStatus.INTERNAL_SERVER_ERROR)
87
88 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 if isinstance(storage, str):
96 f = storage
97 else:
98 f = "/".join(storage)
99 if os.path.exists(self.path + f):
100 if not mode:
101 return True
102 if mode == "file" and os.path.isfile(self.path + f):
103 return True
104 if mode == "dir" and os.path.isdir(self.path + f):
105 return True
106 return False
107
108 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 if isinstance(storage, str):
115 f = storage
116 else:
117 f = "/".join(storage)
118 return os.path.getsize(self.path + f)
119
120 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 if isinstance(path, str):
128 f = self.path + path
129 else:
130 f = self.path + "/".join(path)
131
132 if type(compressed_object) is tarfile.TarFile:
133 compressed_object.extractall(path=f)
134 elif (
135 type(compressed_object) is zipfile.ZipFile
136 ): # Just a check to know if this works with both tar and zip
137 compressed_object.extractall(path=f)
138
139 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 try:
147 if isinstance(storage, str):
148 f = storage
149 else:
150 f = "/".join(storage)
151 return open(self.path + f, mode)
152 except FileNotFoundError:
153 raise FsException(
154 "File {} does not exist".format(f), http_code=HTTPStatus.NOT_FOUND
155 )
156 except IOError:
157 raise FsException(
158 "File {} cannot be opened".format(f), http_code=HTTPStatus.BAD_REQUEST
159 )
160
161 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 try:
168 if isinstance(storage, str):
169 f = storage
170 else:
171 f = "/".join(storage)
172 return os.listdir(self.path + f)
173 except NotADirectoryError:
174 raise FsException(
175 "File {} does not exist".format(f), http_code=HTTPStatus.NOT_FOUND
176 )
177 except IOError:
178 raise FsException(
179 "File {} cannot be opened".format(f), http_code=HTTPStatus.BAD_REQUEST
180 )
181
182 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 try:
190 if isinstance(storage, str):
191 f = self.path + storage
192 else:
193 f = self.path + "/".join(storage)
194 if os.path.exists(f):
195 rmtree(f)
196 elif not ignore_non_exist:
197 raise FsException(
198 "File {} does not exist".format(storage),
199 http_code=HTTPStatus.NOT_FOUND,
200 )
201 except (IOError, PermissionError) as e:
202 raise FsException(
203 "File {} cannot be deleted: {}".format(f, e),
204 http_code=HTTPStatus.INTERNAL_SERVER_ERROR,
205 )
206
207 def sync(self, from_path=None):
208 pass # Not needed in fslocal
209
210 def reverse_sync(self, from_path):
211 pass # Not needed in fslocal