xref: /OK3568_Linux_fs/yocto/poky/bitbake/lib/bb/fetch2/s3.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun"""
2*4882a593SmuzhiyunBitBake 'Fetch' implementation for Amazon AWS S3.
3*4882a593Smuzhiyun
4*4882a593SmuzhiyunClass for fetching files from Amazon S3 using the AWS Command Line Interface.
5*4882a593SmuzhiyunThe aws tool must be correctly installed and configured prior to use.
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun"""
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun# Copyright (C) 2017, Andre McCurdy <armccurdy@gmail.com>
10*4882a593Smuzhiyun#
11*4882a593Smuzhiyun# Based in part on bb.fetch2.wget:
12*4882a593Smuzhiyun#    Copyright (C) 2003, 2004  Chris Larson
13*4882a593Smuzhiyun#
14*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only
15*4882a593Smuzhiyun#
16*4882a593Smuzhiyun# Based on functions from the base bb module, Copyright 2003 Holger Schurig
17*4882a593Smuzhiyun
18*4882a593Smuzhiyunimport os
19*4882a593Smuzhiyunimport bb
20*4882a593Smuzhiyunimport urllib.request, urllib.parse, urllib.error
21*4882a593Smuzhiyunimport re
22*4882a593Smuzhiyunfrom bb.fetch2 import FetchMethod
23*4882a593Smuzhiyunfrom bb.fetch2 import FetchError
24*4882a593Smuzhiyunfrom bb.fetch2 import runfetchcmd
25*4882a593Smuzhiyun
26*4882a593Smuzhiyundef convertToBytes(value, unit):
27*4882a593Smuzhiyun    value = float(value)
28*4882a593Smuzhiyun    if (unit == "KiB"):
29*4882a593Smuzhiyun        value = value*1024.0;
30*4882a593Smuzhiyun    elif (unit == "MiB"):
31*4882a593Smuzhiyun        value = value*1024.0*1024.0;
32*4882a593Smuzhiyun    elif (unit == "GiB"):
33*4882a593Smuzhiyun        value = value*1024.0*1024.0*1024.0;
34*4882a593Smuzhiyun    return value
35*4882a593Smuzhiyun
36*4882a593Smuzhiyunclass S3ProgressHandler(bb.progress.LineFilterProgressHandler):
37*4882a593Smuzhiyun    """
38*4882a593Smuzhiyun    Extract progress information from s3 cp output, e.g.:
39*4882a593Smuzhiyun    Completed 5.1 KiB/8.8 GiB (12.0 MiB/s) with 1 file(s) remaining
40*4882a593Smuzhiyun    """
41*4882a593Smuzhiyun    def __init__(self, d):
42*4882a593Smuzhiyun        super(S3ProgressHandler, self).__init__(d)
43*4882a593Smuzhiyun        # Send an initial progress event so the bar gets shown
44*4882a593Smuzhiyun        self._fire_progress(0)
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun    def writeline(self, line):
47*4882a593Smuzhiyun        percs = re.findall(r'^Completed (\d+.{0,1}\d*) (\w+)\/(\d+.{0,1}\d*) (\w+) (\(.+\)) with\s+', line)
48*4882a593Smuzhiyun        if percs:
49*4882a593Smuzhiyun            completed = (percs[-1][0])
50*4882a593Smuzhiyun            completedUnit = (percs[-1][1])
51*4882a593Smuzhiyun            total = (percs[-1][2])
52*4882a593Smuzhiyun            totalUnit = (percs[-1][3])
53*4882a593Smuzhiyun            completed = convertToBytes(completed, completedUnit)
54*4882a593Smuzhiyun            total = convertToBytes(total, totalUnit)
55*4882a593Smuzhiyun            progress = (completed/total)*100.0
56*4882a593Smuzhiyun            rate = percs[-1][4]
57*4882a593Smuzhiyun            self.update(progress, rate)
58*4882a593Smuzhiyun            return False
59*4882a593Smuzhiyun        return True
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun
62*4882a593Smuzhiyunclass S3(FetchMethod):
63*4882a593Smuzhiyun    """Class to fetch urls via 'aws s3'"""
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun    def supports(self, ud, d):
66*4882a593Smuzhiyun        """
67*4882a593Smuzhiyun        Check to see if a given url can be fetched with s3.
68*4882a593Smuzhiyun        """
69*4882a593Smuzhiyun        return ud.type in ['s3']
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun    def recommends_checksum(self, urldata):
72*4882a593Smuzhiyun        return True
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun    def urldata_init(self, ud, d):
75*4882a593Smuzhiyun        if 'downloadfilename' in ud.parm:
76*4882a593Smuzhiyun            ud.basename = ud.parm['downloadfilename']
77*4882a593Smuzhiyun        else:
78*4882a593Smuzhiyun            ud.basename = os.path.basename(ud.path)
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun        ud.localfile = d.expand(urllib.parse.unquote(ud.basename))
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun        ud.basecmd = d.getVar("FETCHCMD_s3") or "/usr/bin/env aws s3"
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun    def download(self, ud, d):
85*4882a593Smuzhiyun        """
86*4882a593Smuzhiyun        Fetch urls
87*4882a593Smuzhiyun        Assumes localpath was called first
88*4882a593Smuzhiyun        """
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun        cmd = '%s cp s3://%s%s %s' % (ud.basecmd, ud.host, ud.path, ud.localpath)
91*4882a593Smuzhiyun        bb.fetch2.check_network_access(d, cmd, ud.url)
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun        progresshandler = S3ProgressHandler(d)
94*4882a593Smuzhiyun        runfetchcmd(cmd, d, False, log=progresshandler)
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun        # Additional sanity checks copied from the wget class (although there
97*4882a593Smuzhiyun        # are no known issues which mean these are required, treat the aws cli
98*4882a593Smuzhiyun        # tool with a little healthy suspicion).
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun        if not os.path.exists(ud.localpath):
101*4882a593Smuzhiyun            raise FetchError("The aws cp command returned success for s3://%s%s but %s doesn't exist?!" % (ud.host, ud.path, ud.localpath))
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun        if os.path.getsize(ud.localpath) == 0:
104*4882a593Smuzhiyun            os.remove(ud.localpath)
105*4882a593Smuzhiyun            raise FetchError("The aws cp command for s3://%s%s resulted in a zero size file?! Deleting and failing since this isn't right." % (ud.host, ud.path))
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun        return True
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun    def checkstatus(self, fetch, ud, d):
110*4882a593Smuzhiyun        """
111*4882a593Smuzhiyun        Check the status of a URL
112*4882a593Smuzhiyun        """
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun        cmd = '%s ls s3://%s%s' % (ud.basecmd, ud.host, ud.path)
115*4882a593Smuzhiyun        bb.fetch2.check_network_access(d, cmd, ud.url)
116*4882a593Smuzhiyun        output = runfetchcmd(cmd, d)
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun        # "aws s3 ls s3://mybucket/foo" will exit with success even if the file
119*4882a593Smuzhiyun        # is not found, so check output of the command to confirm success.
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun        if not output:
122*4882a593Smuzhiyun            raise FetchError("The aws ls command for s3://%s%s gave empty output" % (ud.host, ud.path))
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun        return True
125