s3_cmd_bin 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +3 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +28 -0
  5. data/Rakefile +1 -0
  6. data/lib/s3_cmd_bin/version.rb +3 -0
  7. data/lib/s3_cmd_bin.rb +15 -0
  8. data/resources/ChangeLog +1462 -0
  9. data/resources/INSTALL +97 -0
  10. data/resources/LICENSE +339 -0
  11. data/resources/MANIFEST.in +2 -0
  12. data/resources/Makefile +4 -0
  13. data/resources/NEWS +234 -0
  14. data/resources/README +342 -0
  15. data/resources/S3/ACL.py +224 -0
  16. data/resources/S3/ACL.pyc +0 -0
  17. data/resources/S3/AccessLog.py +92 -0
  18. data/resources/S3/AccessLog.pyc +0 -0
  19. data/resources/S3/BidirMap.py +42 -0
  20. data/resources/S3/BidirMap.pyc +0 -0
  21. data/resources/S3/CloudFront.py +773 -0
  22. data/resources/S3/CloudFront.pyc +0 -0
  23. data/resources/S3/Config.py +294 -0
  24. data/resources/S3/Config.pyc +0 -0
  25. data/resources/S3/ConnMan.py +71 -0
  26. data/resources/S3/ConnMan.pyc +0 -0
  27. data/resources/S3/Exceptions.py +88 -0
  28. data/resources/S3/Exceptions.pyc +0 -0
  29. data/resources/S3/FileDict.py +53 -0
  30. data/resources/S3/FileDict.pyc +0 -0
  31. data/resources/S3/FileLists.py +517 -0
  32. data/resources/S3/FileLists.pyc +0 -0
  33. data/resources/S3/HashCache.py +53 -0
  34. data/resources/S3/HashCache.pyc +0 -0
  35. data/resources/S3/MultiPart.py +137 -0
  36. data/resources/S3/MultiPart.pyc +0 -0
  37. data/resources/S3/PkgInfo.py +14 -0
  38. data/resources/S3/PkgInfo.pyc +0 -0
  39. data/resources/S3/Progress.py +173 -0
  40. data/resources/S3/Progress.pyc +0 -0
  41. data/resources/S3/S3.py +979 -0
  42. data/resources/S3/S3.pyc +0 -0
  43. data/resources/S3/S3Uri.py +223 -0
  44. data/resources/S3/S3Uri.pyc +0 -0
  45. data/resources/S3/SimpleDB.py +178 -0
  46. data/resources/S3/SortedDict.py +66 -0
  47. data/resources/S3/SortedDict.pyc +0 -0
  48. data/resources/S3/Utils.py +462 -0
  49. data/resources/S3/Utils.pyc +0 -0
  50. data/resources/S3/__init__.py +0 -0
  51. data/resources/S3/__init__.pyc +0 -0
  52. data/resources/TODO +52 -0
  53. data/resources/artwork/AtomicClockRadio.ttf +0 -0
  54. data/resources/artwork/TypeRa.ttf +0 -0
  55. data/resources/artwork/site-top-full-size.xcf +0 -0
  56. data/resources/artwork/site-top-label-download.png +0 -0
  57. data/resources/artwork/site-top-label-s3cmd.png +0 -0
  58. data/resources/artwork/site-top-label-s3sync.png +0 -0
  59. data/resources/artwork/site-top-s3tools-logo.png +0 -0
  60. data/resources/artwork/site-top.jpg +0 -0
  61. data/resources/artwork/site-top.png +0 -0
  62. data/resources/artwork/site-top.xcf +0 -0
  63. data/resources/format-manpage.pl +196 -0
  64. data/resources/magic +63 -0
  65. data/resources/run-tests.py +537 -0
  66. data/resources/s3cmd +2116 -0
  67. data/resources/s3cmd.1 +435 -0
  68. data/resources/s3db +55 -0
  69. data/resources/setup.cfg +2 -0
  70. data/resources/setup.py +80 -0
  71. data/resources/testsuite.tar.gz +0 -0
  72. data/resources/upload-to-sf.sh +7 -0
  73. data/s3_cmd_bin.gemspec +23 -0
  74. metadata +152 -0
@@ -0,0 +1,137 @@
1
+ ## Amazon S3 Multipart upload support
2
+ ## Author: Jerome Leclanche <jerome.leclanche@gmail.com>
3
+ ## License: GPL Version 2
4
+
5
+ import os
6
+ from stat import ST_SIZE
7
+ from logging import debug, info, warning, error
8
+ from Utils import getTextFromXml, formatSize, unicodise
9
+ from Exceptions import S3UploadError
10
+
11
+ class MultiPartUpload(object):
12
+
13
+ MIN_CHUNK_SIZE_MB = 5 # 5MB
14
+ MAX_CHUNK_SIZE_MB = 5120 # 5GB
15
+ MAX_FILE_SIZE = 42949672960 # 5TB
16
+
17
+ def __init__(self, s3, file, uri, headers_baseline = {}):
18
+ self.s3 = s3
19
+ self.file = file
20
+ self.uri = uri
21
+ self.parts = {}
22
+ self.headers_baseline = headers_baseline
23
+ self.upload_id = self.initiate_multipart_upload()
24
+
25
+ def initiate_multipart_upload(self):
26
+ """
27
+ Begin a multipart upload
28
+ http://docs.amazonwebservices.com/AmazonS3/latest/API/index.html?mpUploadInitiate.html
29
+ """
30
+ request = self.s3.create_request("OBJECT_POST", uri = self.uri, headers = self.headers_baseline, extra = "?uploads")
31
+ response = self.s3.send_request(request)
32
+ data = response["data"]
33
+ self.upload_id = getTextFromXml(data, "UploadId")
34
+ return self.upload_id
35
+
36
+ def upload_all_parts(self):
37
+ """
38
+ Execute a full multipart upload on a file
39
+ Returns the seq/etag dict
40
+ TODO use num_processes to thread it
41
+ """
42
+ if not self.upload_id:
43
+ raise RuntimeError("Attempting to use a multipart upload that has not been initiated.")
44
+
45
+ self.chunk_size = self.s3.config.multipart_chunk_size_mb * 1024 * 1024
46
+
47
+ if self.file.name != "<stdin>":
48
+ size_left = file_size = os.stat(self.file.name)[ST_SIZE]
49
+ nr_parts = file_size / self.chunk_size + (file_size % self.chunk_size and 1)
50
+ debug("MultiPart: Uploading %s in %d parts" % (self.file.name, nr_parts))
51
+ else:
52
+ debug("MultiPart: Uploading from %s" % (self.file.name))
53
+
54
+ seq = 1
55
+ if self.file.name != "<stdin>":
56
+ while size_left > 0:
57
+ offset = self.chunk_size * (seq - 1)
58
+ current_chunk_size = min(file_size - offset, self.chunk_size)
59
+ size_left -= current_chunk_size
60
+ labels = {
61
+ 'source' : unicodise(self.file.name),
62
+ 'destination' : unicodise(self.uri.uri()),
63
+ 'extra' : "[part %d of %d, %s]" % (seq, nr_parts, "%d%sB" % formatSize(current_chunk_size, human_readable = True))
64
+ }
65
+ try:
66
+ self.upload_part(seq, offset, current_chunk_size, labels)
67
+ except:
68
+ error(u"Upload of '%s' part %d failed. Aborting multipart upload." % (self.file.name, seq))
69
+ self.abort_upload()
70
+ raise
71
+ seq += 1
72
+ else:
73
+ while True:
74
+ buffer = self.file.read(self.chunk_size)
75
+ offset = self.chunk_size * (seq - 1)
76
+ current_chunk_size = len(buffer)
77
+ labels = {
78
+ 'source' : unicodise(self.file.name),
79
+ 'destination' : unicodise(self.uri.uri()),
80
+ 'extra' : "[part %d, %s]" % (seq, "%d%sB" % formatSize(current_chunk_size, human_readable = True))
81
+ }
82
+ if len(buffer) == 0: # EOF
83
+ break
84
+ try:
85
+ self.upload_part(seq, offset, current_chunk_size, labels, buffer)
86
+ except:
87
+ error(u"Upload of '%s' part %d failed. Aborting multipart upload." % (self.file.name, seq))
88
+ self.abort_upload()
89
+ raise
90
+ seq += 1
91
+
92
+ debug("MultiPart: Upload finished: %d parts", seq - 1)
93
+
94
+ def upload_part(self, seq, offset, chunk_size, labels, buffer = ''):
95
+ """
96
+ Upload a file chunk
97
+ http://docs.amazonwebservices.com/AmazonS3/latest/API/index.html?mpUploadUploadPart.html
98
+ """
99
+ # TODO implement Content-MD5
100
+ debug("Uploading part %i of %r (%s bytes)" % (seq, self.upload_id, chunk_size))
101
+ headers = { "content-length": chunk_size }
102
+ query_string = "?partNumber=%i&uploadId=%s" % (seq, self.upload_id)
103
+ request = self.s3.create_request("OBJECT_PUT", uri = self.uri, headers = headers, extra = query_string)
104
+ response = self.s3.send_file(request, self.file, labels, buffer, offset = offset, chunk_size = chunk_size)
105
+ self.parts[seq] = response["headers"]["etag"]
106
+ return response
107
+
108
+ def complete_multipart_upload(self):
109
+ """
110
+ Finish a multipart upload
111
+ http://docs.amazonwebservices.com/AmazonS3/latest/API/index.html?mpUploadComplete.html
112
+ """
113
+ debug("MultiPart: Completing upload: %s" % self.upload_id)
114
+
115
+ parts_xml = []
116
+ part_xml = "<Part><PartNumber>%i</PartNumber><ETag>%s</ETag></Part>"
117
+ for seq, etag in self.parts.items():
118
+ parts_xml.append(part_xml % (seq, etag))
119
+ body = "<CompleteMultipartUpload>%s</CompleteMultipartUpload>" % ("".join(parts_xml))
120
+
121
+ headers = { "content-length": len(body) }
122
+ request = self.s3.create_request("OBJECT_POST", uri = self.uri, headers = headers, extra = "?uploadId=%s" % (self.upload_id))
123
+ response = self.s3.send_request(request, body = body)
124
+
125
+ return response
126
+
127
+ def abort_upload(self):
128
+ """
129
+ Abort multipart upload
130
+ http://docs.amazonwebservices.com/AmazonS3/latest/API/index.html?mpUploadAbort.html
131
+ """
132
+ debug("MultiPart: Aborting upload: %s" % self.upload_id)
133
+ request = self.s3.create_request("OBJECT_DELETE", uri = self.uri, extra = "?uploadId=%s" % (self.upload_id))
134
+ response = self.s3.send_request(request)
135
+ return response
136
+
137
+ # vim:et:ts=4:sts=4:ai
Binary file
@@ -0,0 +1,14 @@
1
+ package = "s3cmd"
2
+ version = "1.5.0-alpha3"
3
+ url = "http://s3tools.org"
4
+ license = "GPL version 2"
5
+ short_description = "Command line tool for managing Amazon S3 and CloudFront services"
6
+ long_description = """
7
+ S3cmd lets you copy files from/to Amazon S3
8
+ (Simple Storage Service) using a simple to use
9
+ command line client. Supports rsync-like backup,
10
+ GPG encryption, and more. Also supports management
11
+ of Amazon's CloudFront content delivery network.
12
+ """
13
+
14
+ # vim:et:ts=4:sts=4:ai
Binary file
@@ -0,0 +1,173 @@
1
+ ## Amazon S3 manager
2
+ ## Author: Michal Ludvig <michal@logix.cz>
3
+ ## http://www.logix.cz/michal
4
+ ## License: GPL Version 2
5
+
6
+ import sys
7
+ import datetime
8
+ import time
9
+ import Utils
10
+
11
+ class Progress(object):
12
+ _stdout = sys.stdout
13
+ _last_display = 0
14
+
15
+ def __init__(self, labels, total_size):
16
+ self._stdout = sys.stdout
17
+ self.new_file(labels, total_size)
18
+
19
+ def new_file(self, labels, total_size):
20
+ self.labels = labels
21
+ self.total_size = total_size
22
+ # Set initial_position to something in the
23
+ # case we're not counting from 0. For instance
24
+ # when appending to a partially downloaded file.
25
+ # Setting initial_position will let the speed
26
+ # be computed right.
27
+ self.initial_position = 0
28
+ self.current_position = self.initial_position
29
+ self.time_start = datetime.datetime.now()
30
+ self.time_last = self.time_start
31
+ self.time_current = self.time_start
32
+
33
+ self.display(new_file = True)
34
+
35
+ def update(self, current_position = -1, delta_position = -1):
36
+ self.time_last = self.time_current
37
+ self.time_current = datetime.datetime.now()
38
+ if current_position > -1:
39
+ self.current_position = current_position
40
+ elif delta_position > -1:
41
+ self.current_position += delta_position
42
+ #else:
43
+ # no update, just call display()
44
+ self.display()
45
+
46
+ def done(self, message):
47
+ self.display(done_message = message)
48
+
49
+ def output_labels(self):
50
+ self._stdout.write(u"%(source)s -> %(destination)s %(extra)s\n" % self.labels)
51
+ self._stdout.flush()
52
+
53
+ def _display_needed(self):
54
+ # We only need to update the display every so often.
55
+ if time.time() - self._last_display > 1:
56
+ self._last_display = time.time()
57
+ return True
58
+ return False
59
+
60
+ def display(self, new_file = False, done_message = None):
61
+ """
62
+ display(new_file = False[/True], done = False[/True])
63
+
64
+ Override this method to provide a nicer output.
65
+ """
66
+ if new_file:
67
+ self.output_labels()
68
+ self.last_milestone = 0
69
+ return
70
+
71
+ if self.current_position == self.total_size:
72
+ print_size = Utils.formatSize(self.current_position, True)
73
+ if print_size[1] != "": print_size[1] += "B"
74
+ timedelta = self.time_current - self.time_start
75
+ sec_elapsed = timedelta.days * 86400 + timedelta.seconds + float(timedelta.microseconds)/1000000.0
76
+ print_speed = Utils.formatSize((self.current_position - self.initial_position) / sec_elapsed, True, True)
77
+ self._stdout.write("100%% %s%s in %.2fs (%.2f %sB/s)\n" %
78
+ (print_size[0], print_size[1], sec_elapsed, print_speed[0], print_speed[1]))
79
+ self._stdout.flush()
80
+ return
81
+
82
+ rel_position = selfself.current_position * 100 / self.total_size
83
+ if rel_position >= self.last_milestone:
84
+ self.last_milestone = (int(rel_position) / 5) * 5
85
+ self._stdout.write("%d%% ", self.last_milestone)
86
+ self._stdout.flush()
87
+ return
88
+
89
+ class ProgressANSI(Progress):
90
+ ## http://en.wikipedia.org/wiki/ANSI_escape_code
91
+ SCI = '\x1b['
92
+ ANSI_hide_cursor = SCI + "?25l"
93
+ ANSI_show_cursor = SCI + "?25h"
94
+ ANSI_save_cursor_pos = SCI + "s"
95
+ ANSI_restore_cursor_pos = SCI + "u"
96
+ ANSI_move_cursor_to_column = SCI + "%uG"
97
+ ANSI_erase_to_eol = SCI + "0K"
98
+ ANSI_erase_current_line = SCI + "2K"
99
+
100
+ def display(self, new_file = False, done_message = None):
101
+ """
102
+ display(new_file = False[/True], done_message = None)
103
+ """
104
+ if new_file:
105
+ self.output_labels()
106
+ self._stdout.write(self.ANSI_save_cursor_pos)
107
+ self._stdout.flush()
108
+ return
109
+
110
+ # Only display progress every so often
111
+ if not (new_file or done_message) and not self._display_needed():
112
+ return
113
+
114
+ timedelta = self.time_current - self.time_start
115
+ sec_elapsed = timedelta.days * 86400 + timedelta.seconds + float(timedelta.microseconds)/1000000.0
116
+ if (sec_elapsed > 0):
117
+ print_speed = Utils.formatSize((self.current_position - self.initial_position) / sec_elapsed, True, True)
118
+ else:
119
+ print_speed = (0, "")
120
+ self._stdout.write(self.ANSI_restore_cursor_pos)
121
+ self._stdout.write(self.ANSI_erase_to_eol)
122
+ self._stdout.write("%(current)s of %(total)s %(percent)3d%% in %(elapsed)ds %(speed).2f %(speed_coeff)sB/s" % {
123
+ "current" : str(self.current_position).rjust(len(str(self.total_size))),
124
+ "total" : self.total_size,
125
+ "percent" : self.total_size and (self.current_position * 100 / self.total_size) or 0,
126
+ "elapsed" : sec_elapsed,
127
+ "speed" : print_speed[0],
128
+ "speed_coeff" : print_speed[1]
129
+ })
130
+
131
+ if done_message:
132
+ self._stdout.write(" %s\n" % done_message)
133
+
134
+ self._stdout.flush()
135
+
136
+ class ProgressCR(Progress):
137
+ ## Uses CR char (Carriage Return) just like other progress bars do.
138
+ CR_char = chr(13)
139
+
140
+ def display(self, new_file = False, done_message = None):
141
+ """
142
+ display(new_file = False[/True], done_message = None)
143
+ """
144
+ if new_file:
145
+ self.output_labels()
146
+ return
147
+
148
+ # Only display progress every so often
149
+ if not (new_file or done_message) and not self._display_needed():
150
+ return
151
+
152
+ timedelta = self.time_current - self.time_start
153
+ sec_elapsed = timedelta.days * 86400 + timedelta.seconds + float(timedelta.microseconds)/1000000.0
154
+ if (sec_elapsed > 0):
155
+ print_speed = Utils.formatSize((self.current_position - self.initial_position) / sec_elapsed, True, True)
156
+ else:
157
+ print_speed = (0, "")
158
+ self._stdout.write(self.CR_char)
159
+ output = " %(current)s of %(total)s %(percent)3d%% in %(elapsed)4ds %(speed)7.2f %(speed_coeff)sB/s" % {
160
+ "current" : str(self.current_position).rjust(len(str(self.total_size))),
161
+ "total" : self.total_size,
162
+ "percent" : self.total_size and (self.current_position * 100 / self.total_size) or 0,
163
+ "elapsed" : sec_elapsed,
164
+ "speed" : print_speed[0],
165
+ "speed_coeff" : print_speed[1]
166
+ }
167
+ self._stdout.write(output)
168
+ if done_message:
169
+ self._stdout.write(" %s\n" % done_message)
170
+
171
+ self._stdout.flush()
172
+
173
+ # vim:et:ts=4:sts=4:ai
Binary file