Includes recursive option.
Robert Palmer authored on 2013/12/30 13:45:32... | ... |
@@ -490,6 +490,18 @@ class S3(object): |
490 | 490 |
request = self.create_request("OBJECT_DELETE", uri = uri) |
491 | 491 |
response = self.send_request(request) |
492 | 492 |
return response |
493 |
+ |
|
494 |
+ def object_restore(self, uri): |
|
495 |
+ if uri.type != "s3": |
|
496 |
+ raise ValueError("Expected URI type 's3', got '%s'" % uri.type) |
|
497 |
+ body = '<RestoreRequest xmlns="http://s3.amazonaws.com/doc/2006-3-01">' |
|
498 |
+ body += (' <Days>%s</Days>' % self.config.restore_days) |
|
499 |
+ body += '</RestoreRequest>' |
|
500 |
+ request = self.create_request("OBJECT_POST", uri = uri, extra = "?restore") |
|
501 |
+ debug("About to send request '%s' with body '%s'" % (request, body)) |
|
502 |
+ response = self.send_request(request, body) |
|
503 |
+ debug("Received response '%s'" % (response)) |
|
504 |
+ return response |
|
493 | 505 |
|
494 | 506 |
def object_copy(self, src_uri, dst_uri, extra_headers = None): |
495 | 507 |
if src_uri.type != "s3": |
... | ... |
@@ -538,6 +538,40 @@ def subcmd_object_del_uri(uri_str, recursive = None): |
538 | 538 |
item = remote_list[key] |
539 | 539 |
response = s3.object_delete(S3Uri(item['object_uri_str'])) |
540 | 540 |
output(u"File %s deleted" % item['object_uri_str']) |
541 |
+ |
|
542 |
+def cmd_object_restore(args): |
|
543 |
+ s3 = S3(cfg) |
|
544 |
+ |
|
545 |
+ if cfg.restore_days < 1: |
|
546 |
+ raise ParameterError("You must restore a file for 1 or more days") |
|
547 |
+ |
|
548 |
+ remote_list = fetch_remote_list(args, require_attribs = False, recursive = cfg.recursive) |
|
549 |
+ remote_list, exclude_list = filter_exclude_include(remote_list) |
|
550 |
+ |
|
551 |
+ remote_count = len(remote_list) |
|
552 |
+ |
|
553 |
+ info(u"Summary: Restoring %d remote files for %d days" % (remote_count, cfg.restore_days)) |
|
554 |
+ |
|
555 |
+ if cfg.dry_run: |
|
556 |
+ for key in exclude_list: |
|
557 |
+ output(u"exclude: %s" % unicodise(key)) |
|
558 |
+ for key in remote_list: |
|
559 |
+ output(u"restore: %s" % remote_list[key]['object_uri_str']) |
|
560 |
+ |
|
561 |
+ warning(u"Exiting now because of --dry-run") |
|
562 |
+ return |
|
563 |
+ |
|
564 |
+ for key in remote_list: |
|
565 |
+ item = remote_list[key] |
|
566 |
+ |
|
567 |
+ uri = S3Uri(item['object_uri_str']) |
|
568 |
+ if not item['object_uri_str'].endswith("/"): |
|
569 |
+ output(u"Restoring obj: %s" % item['object_uri_str']) |
|
570 |
+ response = s3.object_restore(S3Uri(item['object_uri_str'])) |
|
571 |
+ output(u"File %s restored" % item['object_uri_str']) |
|
572 |
+ else: |
|
573 |
+ debug(u"Skipping directory since only files may be restored") |
|
574 |
+ |
|
541 | 575 |
|
542 | 576 |
def subcmd_cp_mv(args, process_fce, action_str, message): |
543 | 577 |
if len(args) < 2: |
... | ... |
@@ -1752,6 +1786,7 @@ def get_commands_list(): |
1752 | 1752 |
{"cmd":"get", "label":"Get file from bucket", "param":"s3://BUCKET/OBJECT LOCAL_FILE", "func":cmd_object_get, "argc":1}, |
1753 | 1753 |
{"cmd":"del", "label":"Delete file from bucket", "param":"s3://BUCKET/OBJECT", "func":cmd_object_del, "argc":1}, |
1754 | 1754 |
#{"cmd":"mkdir", "label":"Make a virtual S3 directory", "param":"s3://BUCKET/path/to/dir", "func":cmd_mkdir, "argc":1}, |
1755 |
+ {"cmd":"restore", "label":"Restore file from Glacier storage", "param":"s3://BUCKET/OBJECT", "func":cmd_object_restore, "argc":1}, |
|
1755 | 1756 |
{"cmd":"sync", "label":"Synchronize a directory tree to S3", "param":"LOCAL_DIR s3://BUCKET[/PREFIX] or s3://BUCKET[/PREFIX] LOCAL_DIR", "func":cmd_sync, "argc":2}, |
1756 | 1757 |
{"cmd":"du", "label":"Disk usage by buckets", "param":"[s3://BUCKET[/PREFIX]]", "func":cmd_du, "argc":0}, |
1757 | 1758 |
{"cmd":"info", "label":"Get various information about Buckets or Files", "param":"s3://BUCKET[/OBJECT]", "func":cmd_info, "argc":1}, |
... | ... |
@@ -1918,6 +1953,8 @@ def main(): |
1918 | 1918 |
optparser.add_option( "--acl-grant", dest="acl_grants", type="s3acl", action="append", metavar="PERMISSION:EMAIL or USER_CANONICAL_ID", help="Grant stated permission to a given amazon user. Permission is one of: read, write, read_acp, write_acp, full_control, all") |
1919 | 1919 |
optparser.add_option( "--acl-revoke", dest="acl_revokes", type="s3acl", action="append", metavar="PERMISSION:USER_CANONICAL_ID", help="Revoke stated permission for a given amazon user. Permission is one of: read, write, read_acp, wr ite_acp, full_control, all") |
1920 | 1920 |
|
1921 |
+ optparser.add_option("-D", "--restore-days", dest="restore_days", action="store", help="Number of days to keep restored file available (only for 'restore' command).", metavar="NUM") |
|
1922 |
+ |
|
1921 | 1923 |
optparser.add_option( "--delete-removed", dest="delete_removed", action="store_true", help="Delete remote objects with no corresponding local file [sync]") |
1922 | 1924 |
optparser.add_option( "--no-delete-removed", dest="delete_removed", action="store_false", help="Don't delete remote objects.") |
1923 | 1925 |
optparser.add_option( "--delete-after", dest="delete_after", action="store_true", help="Perform deletes after new uploads [sync]") |