OLD | NEW |
| (Empty) |
1 from cStringIO import StringIO | |
2 import urllib | |
3 import urlparse | |
4 | |
5 from twisted.web.client import getPage | |
6 from twisted.web.error import Error | |
7 | |
8 from juju.errors import FileNotFound, ProviderError | |
9 from juju.providers.common.utils import convert_unknown_error | |
10 from juju.providers.orchestra.digestauth import ( | |
11 DigestAuthenticator, get_page_auth) | |
12 | |
13 | |
14 def _convert_error(failure, method, url, errors): | |
15 if failure.check(Error): | |
16 status = failure.value.status | |
17 error = errors.get(int(status)) | |
18 if error: | |
19 raise error | |
20 raise ProviderError( | |
21 "Unexpected HTTP %s trying to %s %s" % (status, method, url)) | |
22 return convert_unknown_error(failure) | |
23 | |
24 | |
25 class FileStorage(object): | |
26 """A WebDAV-backed :class:`FileStorage` abstraction""" | |
27 | |
28 def __init__(self, config): | |
29 fallback_url = "http://%(orchestra-server)s/webdav" % config | |
30 self._base_url = config.get("storage-url", fallback_url) | |
31 self._auth = DigestAuthenticator( | |
32 config.get("storage-user", config["orchestra-user"]), | |
33 config.get("storage-pass", config["orchestra-pass"])) | |
34 | |
35 def get_url(self, name): | |
36 """Return a URL that can be used to access a stored file. | |
37 | |
38 :param unicode name: the file path for which to provide a URL | |
39 | |
40 :return: a URL | |
41 :rtype: str | |
42 """ | |
43 url = u"/".join((self._base_url, name)) | |
44 # query and fragment are irrelevant to our purposes | |
45 scheme, netloc, path = urlparse.urlsplit(url)[:3] | |
46 return urlparse.urlunsplit(( | |
47 str(scheme), | |
48 netloc.encode("idna"), | |
49 urllib.quote(path.encode("utf-8")), | |
50 "", "")) | |
51 | |
52 def get(self, name): | |
53 """Get a file object from the Orchestra WebDAV server. | |
54 | |
55 :param unicode name: path to for the desired file | |
56 | |
57 :return: an open file object | |
58 :rtype: :class:`twisted.internet.defer.Deferred` | |
59 | |
60 :raises: :exc:`juju.errors.FileNotFound` if the file doesn't exist | |
61 """ | |
62 url = self.get_url(name) | |
63 d = getPage(url) | |
64 d.addCallback(StringIO) | |
65 d.addErrback(_convert_error, "GET", url, {404: FileNotFound(url)}) | |
66 return d | |
67 | |
68 def put(self, name, file_object): | |
69 """Upload a file to WebDAV. | |
70 | |
71 :param unicode remote_path: path on which to store the content | |
72 | |
73 :param file_object: open file object containing the content | |
74 | |
75 :rtype: :class:`twisted.internet.defer.Deferred` | |
76 """ | |
77 url = self.get_url(name) | |
78 postdata = file_object.read() | |
79 d = get_page_auth(url, self._auth, method="PUT", postdata=postdata) | |
80 d.addCallback(lambda _: True) | |
81 d.addErrback(_convert_error, "PUT", url, {401: ProviderError( | |
82 "The supplied storage credentials were not accepted by the " | |
83 "server")}) | |
84 return d | |
OLD | NEW |