~free.ekanayaka/landscape-client/lucid-1.4.4-0ubuntu0.10.04

« back to all changes in this revision

Viewing changes to landscape/lib/fetch.py

  • Committer: Bazaar Package Importer
  • Author(s): Free Ekanayaka
  • Date: 2009-12-16 10:50:05 UTC
  • mfrom: (1.1.12 upstream)
  • Revision ID: james.westby@ubuntu.com-20091216105005-svplwdorkgz6vja7
Tags: 1.4.0-0ubuntu0.10.04.0
* New upstream release with several bug fixes:
  - Fix landscape daemons fail to start when too many groups are
    available (LP: #456124)
  - Fix landscape programs wake up far too much. (LP: #340843)
  - Fix Package manager fails with 'no such table: task' (LP #465846)
  - Fix test suite leaving temporary files around (LP #476418)
  - Fix the 1hr long wait for user data to be uploaded following a
    resynchronisation (LP #369000)

* Add support for Ubuntu release upgrades:
  - Add helper function to fetch many files at once (LP: #450629)
  - Handle release-upgrade messages in the packagemanager
    plugin (LP: #455217)
  - Add a release-upgrader task handler (LP: #462543)
  - Support upgrade-tool environment variables (LP: #463321)

* Add initial support for Smart package locking:
  - Detect and report changes about Smart package locks (#488108)

* Packaging fixes:
  - Turn unnecessary Pre-Depends on python-gobject into a regular Depends
  - If it's empty, remove /etc/landscape upon purge

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import os
 
2
import sys
 
3
 
1
4
from optparse import OptionParser
2
5
from StringIO import StringIO
3
 
import sys
4
6
 
5
7
import pycurl
6
8
 
7
9
from twisted.internet.threads import deferToThread
 
10
from twisted.internet.defer import DeferredList
 
11
 
8
12
 
9
13
class FetchError(Exception):
10
14
    pass
11
15
 
 
16
 
12
17
class HTTPCodeError(FetchError):
13
18
 
14
19
    def __init__(self, http_code, body):
23
28
 
24
29
 
25
30
class PyCurlError(FetchError):
 
31
 
26
32
    def __init__(self, error_code, message):
27
33
        self.error_code = error_code
28
 
        self.message = message
 
34
        self._message = message
29
35
 
30
36
    def __str__(self):
31
37
        return "Error %d: %s" % (self.error_code, self.message)
34
40
        return "<PyCurlError args=(%d, '%s')>" % (self.error_code,
35
41
                                                  self.message)
36
42
 
 
43
    @property
 
44
    def message(self):
 
45
        return self._message
 
46
 
 
47
 
37
48
def fetch(url, post=False, data="", headers={}, cainfo=None, curl=None,
38
49
          connect_timeout=30, total_timeout=600):
39
50
    """Retrieve a URL and return the content.
65
76
        curl.setopt(pycurl.HTTPHEADER,
66
77
                    ["%s: %s" % pair for pair in sorted(headers.iteritems())])
67
78
 
68
 
    curl.setopt(pycurl.URL, url)
 
79
    curl.setopt(pycurl.URL, str(url))
69
80
    curl.setopt(pycurl.FOLLOWLOCATION, True)
70
81
    curl.setopt(pycurl.MAXREDIRS, 5)
71
82
    curl.setopt(pycurl.CONNECTTIMEOUT, connect_timeout)
99
110
 
100
111
 
101
112
def fetch_async(*args, **kwargs):
 
113
    """Retrieve a URL asynchronously.
 
114
 
 
115
    @return: A C{Deferred} resulting in the URL content.
 
116
    """
102
117
    return deferToThread(fetch, *args, **kwargs)
103
118
 
104
119
 
 
120
def fetch_many_async(urls, callback=None, errback=None, **kwargs):
 
121
    """
 
122
    Retrieve a list of URLs asynchronously.
 
123
 
 
124
    @param callback: Optionally, a function that will be fired one time for
 
125
        each successful URL, and will be passed its content and the URL itself.
 
126
    @param errback: Optionally, a function that will be fired one time for each
 
127
        failing URL, and will be passed the failure and the URL itself.
 
128
    @return: A C{DeferredList} whose callback chain will be fired as soon as
 
129
        all downloads have terminated. If an error occurs, the errback chain
 
130
        of the C{DeferredList} will be fired immediatly.
 
131
    """
 
132
    results = []
 
133
    for url in urls:
 
134
        result = fetch_async(url, **kwargs)
 
135
        if callback:
 
136
            result.addCallback(callback, url)
 
137
        if errback:
 
138
            result.addErrback(errback, url)
 
139
        results.append(result)
 
140
    return DeferredList(results, fireOnOneErrback=True, consumeErrors=True)
 
141
 
 
142
 
 
143
def url_to_filename(url, directory=None):
 
144
    """Return the last component of the given C{url}.
 
145
 
 
146
    @param url: The URL to get the filename from.
 
147
    @param directory: Optionally a path to prepend to the returned filename.
 
148
 
 
149
    @note: Any trailing slash in the C{url} will be removed
 
150
    """
 
151
    filename = url.rstrip("/").split("/")[-1]
 
152
    if directory is not None:
 
153
        filename = os.path.join(directory, filename)
 
154
    return filename
 
155
 
 
156
 
 
157
def fetch_to_files(urls, directory, logger=None, **kwargs):
 
158
    """
 
159
    Retrieve a list of URLs and save their content as files in a directory.
 
160
 
 
161
    @param urls: The list URLs to fetch.
 
162
    @param directory: The directory to save the files to, the name of the file
 
163
        will equal the last fragment of the URL.
 
164
    @param logger: Optional function to be used to log errors for failed URLs.
 
165
    """
 
166
 
 
167
    def write(data, url):
 
168
        filename = url_to_filename(url, directory=directory)
 
169
        fd = open(filename, "w")
 
170
        fd.write(data)
 
171
        fd.close()
 
172
 
 
173
    def log_error(failure, url):
 
174
        if logger:
 
175
            logger("Couldn't fetch file from %s (%s)" % (
 
176
                url, str(failure.value)))
 
177
        return failure
 
178
 
 
179
    return fetch_many_async(urls, callback=write, errback=log_error, **kwargs)
 
180
 
 
181
 
105
182
if __name__ == "__main__":
106
183
    test(sys.argv[1:])