1#!/usr/bin/env python3
2#
3#   Copyright 2018 - The Android Open Source Project
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 implied.
14#   See the License for the specific language governing permissions and
15#   limitations under the License.
16
17import os
18from acts.controllers.fuchsia_lib.ssh import FuchsiaSSHError
19
20
21def http_file_download_by_curl(fd,
22                               url,
23                               out_path='/tmp/',
24                               curl_loc='/bin/curl',
25                               remove_file_after_check=True,
26                               timeout=3600,
27                               limit_rate=None,
28                               additional_args=None,
29                               retry=3):
30    """Download http file by ssh curl.
31
32    Args:
33        fd: Fuchsia Device Object.
34        url: The url that file to be downloaded from.
35        out_path: Optional. Where to download file to.
36            out_path is /tmp by default.
37        curl_loc: Location of curl binary on fd.
38        remove_file_after_check: Whether to remove the downloaded file after
39            check.
40        timeout: timeout for file download to complete.
41        limit_rate: download rate in bps. None, if do not apply rate limit.
42        additional_args: Any additional args for curl.
43        retry: the retry request times provided in curl command.
44    """
45    file_directory, file_name = _generate_file_directory_and_file_name(
46        url, out_path)
47    file_path = os.path.join(file_directory, file_name)
48    curl_cmd = curl_loc
49    if limit_rate:
50        curl_cmd += f' --limit-rate {limit_rate}'
51    if retry:
52        curl_cmd += f' --retry {retry}'
53    if additional_args:
54        curl_cmd += f' {additional_args}'
55    curl_cmd += f' --url {url} > {file_path}'
56
57    fd.log.info(f'Download {url} to {file_path} by ssh command {curl_cmd}')
58    try:
59        fd.ssh.run(curl_cmd, timeout_sec=timeout)
60        if _check_file_existence(fd, file_path):
61            fd.log.info(f'{url} is downloaded to {file_path} successfully')
62            return True
63
64        fd.log.warning(f'Fail to download {url}')
65        return False
66    except FuchsiaSSHError as e:
67        fd.log.warning(f'Command "{curl_cmd}" failed with error {e}')
68        return False
69    except Exception as e:
70        fd.log.error(f'Download {url} failed with unexpected exception {e}')
71        return False
72    finally:
73        if remove_file_after_check:
74            fd.log.info(f'Remove the downloaded file {file_path}')
75            try:
76                fd.ssh.run(f'rm {file_path}')
77            except FuchsiaSSHError:
78                pass
79
80
81def _generate_file_directory_and_file_name(url, out_path):
82    """Splits the file from the url and specifies the appropriate location of
83       where to store the downloaded file.
84
85    Args:
86        url: A url to the file that is going to be downloaded.
87        out_path: The location of where to store the file that is downloaded.
88
89    Returns:
90        file_directory: The directory of where to store the downloaded file.
91        file_name: The name of the file that is being downloaded.
92    """
93    file_name = url.split('/')[-1]
94    if not out_path:
95        file_directory = '/tmp/'
96    elif not out_path.endswith('/'):
97        file_directory, file_name = os.path.split(out_path)
98    else:
99        file_directory = out_path
100    return file_directory, file_name
101
102
103def _check_file_existence(fd, file_path):
104    """Check file existence by file_path. If expected_file_size
105       is provided, then also check if the file meet the file size requirement.
106
107    Args:
108        fd: A fuchsia device
109        file_path: Where to store the file on the fuchsia device.
110    """
111    try:
112        result = fd.ssh.run(f'ls -al "{file_path}"')
113        fd.log.debug(f'File {file_path} exists.')
114        return True
115    except FuchsiaSSHError as e:
116        if 'No such file or directory' in e.result.stderr:
117            fd.log.debug(f'File {file_path} does not exist.')
118            return False
119        raise e
120