d90f113edb757169ce37cd7364d6dfbe09e7fa0c
[csit.git] / resources / tools / presentation / input_data_files.py
1 # Copyright (c) 2018 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
5 #
6 #     http://www.apache.org/licenses/LICENSE-2.0
7 #
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
13
14 """Inputs
15 Download all data.
16 """
17
18 import re
19 import logging
20
21 from os import rename, mkdir
22 from os.path import join
23 from zipfile import ZipFile, is_zipfile, BadZipfile
24 from httplib import responses
25 from requests import get, codes, RequestException, Timeout, TooManyRedirects, \
26     HTTPError, ConnectionError
27
28 from errors import PresentationError
29 from utils import execute_command
30
31 # Chunk size used for file download
32 CHUNK_SIZE = 512
33
34 # Separator used in file names
35 SEPARATOR = "__"
36
37 REGEX_RELEASE = re.compile(r'(\D*)(\d{4}|master)(\D*)')
38
39
40 def _download_file(url, file_name, log):
41     """Download a file with input data.
42
43     :param url: URL to the file to download.
44     :param file_name: Name of file to download.
45     :param log: List of log messages.
46     :type url: str
47     :type file_name: str
48     :type log: list of tuples (severity, msg)
49     :returns: True if the download was successful, otherwise False.
50     :rtype: bool
51     """
52
53     success = False
54     try:
55         log.append(("INFO", "    Connecting to '{0}' ...".format(url)))
56
57         response = get(url, stream=True)
58         code = response.status_code
59
60         log.append(("INFO", "    {0}: {1}".format(code, responses[code])))
61
62         if code != codes["OK"]:
63             return False
64
65         log.append(("INFO", "    Downloading the file '{0}' to '{1}' ...".
66                     format(url, file_name)))
67
68         file_handle = open(file_name, "wb")
69         for chunk in response.iter_content(chunk_size=CHUNK_SIZE):
70             if chunk:
71                 file_handle.write(chunk)
72         file_handle.close()
73         success = True
74     except ConnectionError as err:
75         log.append(("ERROR", "Not possible to connect to '{0}'.".format(url)))
76         log.append(("DEBUG", str(err)))
77     except HTTPError as err:
78         log.append(("ERROR", "Invalid HTTP response from '{0}'.".format(url)))
79         log.append(("DEBUG", str(err)))
80     except TooManyRedirects as err:
81         log.append(("ERROR", "Request exceeded the configured number "
82                              "of maximum re-directions."))
83         log.append(("DEBUG", str(err)))
84     except Timeout as err:
85         log.append(("ERROR", "Request timed out."))
86         log.append(("DEBUG", str(err)))
87     except RequestException as err:
88         log.append(("ERROR", "Unexpected HTTP request exception."))
89         log.append(("DEBUG", str(err)))
90     except (IOError, ValueError, KeyError) as err:
91         log.append(("ERROR", "Download failed."))
92         log.append(("DEBUG", str(err)))
93
94     log.append(("INFO", "    Download finished."))
95     return success
96
97
98 def _unzip_file(spec, build, pid, log):
99     """Unzip downloaded source file.
100
101     :param spec: Specification read form the specification file.
102     :param build: Information about the build.
103     :param log: List of log messages.
104     :type spec: Specification
105     :type build: dict
106     :type log: list of tuples (severity, msg)
107     :returns: True if the download was successful, otherwise False.
108     :rtype: bool
109     """
110
111     data_file = spec.input["extract"]
112     file_name = build["file-name"]
113     directory = spec.environment["paths"]["DIR[WORKING,DATA]"]
114     tmp_dir = join(directory, str(pid))
115     try:
116         mkdir(tmp_dir)
117     except OSError:
118         pass
119     new_name = "{0}{1}{2}".format(file_name.rsplit('.')[-2],
120                                   SEPARATOR,
121                                   data_file.split("/")[-1])
122
123     log.append(("INFO", "    Unzipping: '{0}' from '{1}'.".
124                 format(data_file, file_name)))
125     try:
126         with ZipFile(file_name, 'r') as zip_file:
127             zip_file.extract(data_file, tmp_dir)
128         log.append(("INFO", "    Renaming the file '{0}' to '{1}'".
129                     format(join(tmp_dir, data_file), new_name)))
130         rename(join(tmp_dir, data_file), new_name)
131         build["file-name"] = new_name
132         return True
133     except (BadZipfile, RuntimeError) as err:
134         log.append(("ERROR", "Failed to unzip the file '{0}': {1}.".
135                     format(file_name, str(err))))
136         return False
137     except OSError as err:
138         log.append(("ERROR", "Failed to rename the file '{0}': {1}.".
139                     format(data_file, str(err))))
140         return False
141
142
143 def download_and_unzip_data_file(spec, job, build, pid, log):
144     """Download and unzip a source file.
145
146     :param spec: Specification read form the specification file.
147     :param job: Name of the Jenkins job.
148     :param build: Information about the build.
149     :param pid: PID of the process executing this method.
150     :param log: List of log messages.
151     :type spec: Specification
152     :type job: str
153     :type build: dict
154     :type pid: int
155     :type log: list of tuples (severity, msg)
156     :returns: True if the download was successful, otherwise False.
157     :rtype: bool
158     """
159
160     if job.startswith("csit-"):
161         if spec.input["file-name"].endswith(".zip"):
162             url = spec.environment["urls"]["URL[JENKINS,CSIT]"]
163         elif spec.input["file-name"].endswith(".gz"):
164             url = spec.environment["urls"]["URL[NEXUS,LOG]"]
165         else:
166             log.append(("ERROR", "Not supported file format."))
167             return False
168     elif job.startswith("hc2vpp-"):
169         url = spec.environment["urls"]["URL[JENKINS,HC]"]
170     elif job.startswith("intel-dnv-"):
171         url = spec.environment["urls"]["URL[VIRL,DNV]"]
172     else:
173         raise PresentationError("No url defined for the job '{}'.".
174                                 format(job))
175     file_name = spec.input["file-name"]
176     full_name = spec.input["download-path"]. \
177         format(job=job, build=build["build"], filename=file_name)
178     if not job.startswith("intel-dnv-"):
179         url = "{0}/{1}".format(url, full_name)
180     new_name = join(spec.environment["paths"]["DIR[WORKING,DATA]"],
181                     "{job}{sep}{build}{sep}{name}".
182                     format(job=job, sep=SEPARATOR, build=build["build"],
183                            name=file_name))
184
185     logging.info(new_name)
186
187     # Download the file from the defined source (Jenkins, logs.fd.io):
188     success = _download_file(url, new_name, log)
189
190     logging.info("{}: {}".format(url, success))
191
192     if success and new_name.endswith(".zip"):
193         if not is_zipfile(new_name):
194             success = False
195
196     # If not successful, download from docs.fd.io:
197     if not success:
198         log.append(("INFO", "    Trying to download from https://docs.fd.io:"))
199         release = re.search(REGEX_RELEASE, job).group(2)
200         for rls in (release, "master"):
201             nexus_file_name = "{job}{sep}{build}{sep}{name}". \
202                 format(job=job, sep=SEPARATOR, build=build["build"],
203                        name=file_name)
204             try:
205                 rls = "rls{0}".format(int(rls))
206             except ValueError:
207                 pass
208             url = "{url}/{release}/{dir}/{file}". \
209                 format(url=spec.environment["urls"]["URL[NEXUS]"],
210                        release=rls,
211                        dir=spec.environment["urls"]["DIR[NEXUS]"],
212                        file=nexus_file_name)
213             success = _download_file(url, new_name, log)
214             if success:
215                 break
216
217     if success:
218         build["file-name"] = new_name
219     else:
220         return False
221
222     if spec.input["file-name"].endswith(".gz"):
223         if "docs.fd.io" in url:
224             execute_command("gzip --decompress --keep --force {0}".
225                             format(new_name))
226         else:
227             rename(new_name, new_name[:-3])
228             execute_command("gzip --keep {0}".format(new_name[:-3]))
229         build["file-name"] = new_name[:-3]
230
231     if new_name.endswith(".zip"):
232         if is_zipfile(new_name):
233             return _unzip_file(spec, build, pid, log)
234         else:
235             log.append(("ERROR",
236                         "Zip file '{0}' is corrupted.".format(new_name)))
237             return False
238     else:
239         return True