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