CSIT-1351: Add Denverton results to report
[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 job.startswith("intel-dnv-"):
179         full_name = ""
180     url = "{0}/{1}".format(url, full_name)
181     new_name = join(spec.environment["paths"]["DIR[WORKING,DATA]"],
182                     "{job}{sep}{build}{sep}{name}".
183                     format(job=job, sep=SEPARATOR, build=build["build"],
184                            name=file_name))
185
186     logging.info(new_name)
187
188     # Download the file from the defined source (Jenkins, logs.fd.io):
189     success = _download_file(url, new_name, log)
190
191     logging.info("{}: {}".format(url, success))
192
193     if success and new_name.endswith(".zip"):
194         if not is_zipfile(new_name):
195             success = False
196
197     # If not successful, download from docs.fd.io:
198     if not success:
199         log.append(("INFO", "    Trying to download from https://docs.fd.io:"))
200         release = re.search(REGEX_RELEASE, job).group(2)
201         for rls in (release, "master"):
202             nexus_file_name = "{job}{sep}{build}{sep}{name}". \
203                 format(job=job, sep=SEPARATOR, build=build["build"],
204                        name=file_name)
205             try:
206                 rls = "rls{0}".format(int(rls))
207             except ValueError:
208                 pass
209             url = "{url}/{release}/{dir}/{file}". \
210                 format(url=spec.environment["urls"]["URL[NEXUS]"],
211                        release=rls,
212                        dir=spec.environment["urls"]["DIR[NEXUS]"],
213                        file=nexus_file_name)
214             success = _download_file(url, new_name, log)
215             if success:
216                 break
217
218     if success:
219         build["file-name"] = new_name
220     else:
221         return False
222
223     if spec.input["file-name"].endswith(".gz"):
224         if "docs.fd.io" in url:
225             execute_command("gzip --decompress --keep --force {0}".
226                             format(new_name))
227         else:
228             rename(new_name, new_name[:-3])
229             execute_command("gzip --keep {0}".format(new_name[:-3]))
230         build["file-name"] = new_name[:-3]
231
232     if new_name.endswith(".zip"):
233         if is_zipfile(new_name):
234             return _unzip_file(spec, build, pid, log)
235         else:
236             log.append(("ERROR",
237                         "Zip file '{0}' is corrupted.".format(new_name)))
238             return False
239     else:
240         return True