support part-num in python swiftClient

Change-Id: Ib3b076581eb0440da1071ea2cb53a90315f55775
This commit is contained in:
indianwhocodes 2023-11-27 18:35:41 -08:00
parent fa9718deee
commit 61e3709dc5
4 changed files with 98 additions and 4 deletions

View File

@ -159,6 +159,14 @@ def stat_object(conn, options, container, obj):
query_string = None
if options.get('version_id') is not None:
query_string = 'version-id=%s' % options['version_id']
if options.get('part_number') is not None:
query_string = 'part-number=%s' % options['part_number']
elif (options.get('version_id') is not None and options.get('part_number')
is not None):
query_string = 'version-id=%s&part-number=%s' % (
options['version_id'], options['part_number']
)
headers = conn.head_object(container, obj, headers=req_headers,
query_string=query_string)
items = []

View File

@ -1228,6 +1228,14 @@ class SwiftService:
if options.get('version_id') is not None:
get_args['query_string'] = (
'version-id=%s' % options['version_id'])
if options.get('part_number') is not None:
get_args['query_string'] = (
'part-number=%s' % options['part_number'])
elif options.get('version_id') is not None and \
options.get('part_number') is not None:
get_args['query_string'] = (
'version-id=%s&part-number=%s' % (
options['version_id'], options['part_number']))
if options['skip_identical']:
# Assume the file is a large object; if we're wrong, the query
# string is ignored and the If-None-Match header will trigger

View File

@ -236,6 +236,7 @@ st_download_options = '''[--all] [--marker <marker>] [--prefix <prefix>]
[--container-threads <threads>] [--no-download]
[--skip-identical] [--remove-prefix]
[--version-id <version_id>]
[--part-number <part_number>]
[--header <header:value>] [--no-shuffle]
[<container> [<object>] [...]]
'''
@ -282,6 +283,8 @@ Optional arguments:
sides.
--version-id <version-id>
Download specific version of a versioned object.
--part-number <part-number>
Download a specific part of a static large object.
--ignore-checksum Turn off checksum validation for downloads.
--no-shuffle By default, when downloading a complete account or
container, download order is randomised in order to
@ -346,6 +349,9 @@ def st_download(parser, args, output_manager, return_parser=False):
parser.add_argument(
'--version-id', action='store', default=None,
help='Download a specific version of a versioned object')
parser.add_argument(
'--part-number', action='store', default=None,
help='Download a specific version of a versioned object')
parser.add_argument(
'--ignore-checksum', action='store_false', dest='checksum',
default=True, help='Turn off checksum validation for downloads.')
@ -375,12 +381,19 @@ def st_download(parser, args, output_manager, return_parser=False):
if options['out_file'] and len(args) != 2:
exit('-o option only allowed for single file downloads')
elif options['out_file'] and options['part_number'] and len(args) != 4:
exit('--part-number only allowed for single file downloads')
if not options['prefix']:
options['remove_prefix'] = False
if options['out_directory'] and len(args) == 2:
exit('Please use -o option for single file downloads and renames')
if options['out_directory'] and len(args) == 4 and options['part_number']:
exit('Please use -o option for single file downloads and renames')
if options['part_number'] and len(args) < 4:
exit('--part-number option only allowed for object downloads')
if (not args and not options['yes_all']) or (args and options['yes_all']):
output_manager.error('Usage: %s download %s\n%s', BASENAME,
@ -677,6 +690,7 @@ def st_list(parser, args, output_manager, return_parser=False):
st_stat_options = '''[--lh] [--header <header:value>]
[--version-id <version_id>]
[--part-number <part_number>]
[<container> [<object>]]
'''
@ -692,6 +706,8 @@ Optional arguments:
ls -lh.
--version-id <version-id>
Report stat of specific version of a versioned object.
--part-number <part-number>
Report a specific part of a static large object.
-H, --header <header:value>
Adds a custom request header to use for stat.
'''.strip('\n')
@ -704,6 +720,9 @@ def st_stat(parser, args, output_manager, return_parser=False):
parser.add_argument(
'--version-id', action='store', default=None,
help='Report stat of a specific version of a versioned object')
parser.add_argument(
'--part-number', action='store', default=None,
help='Report a specific part of a static large object')
parser.add_argument(
'-H', '--header', action='append', dest='header',
default=[],
@ -717,6 +736,8 @@ def st_stat(parser, args, output_manager, return_parser=False):
args = args[1:]
if options['version_id'] and len(args) < 2:
exit('--version-id option only allowed for object stats')
if options['part_number'] and len(args) < 2:
exit('--part-number option only allowed for object stats')
with SwiftService(options=options) as swift:
try:
@ -756,7 +777,7 @@ def st_stat(parser, args, output_manager, return_parser=False):
items, headers, output_manager
)
else:
raise stat_result["error"]
raise (stat_result["error"])
else:
output_manager.error(
'Usage: %s stat %s\n%s', BASENAME,
@ -866,8 +887,7 @@ def st_post(parser, args, output_manager, return_parser=False):
else:
result = swift.post(container=container)
if not result["success"]:
raise result["error"]
raise (result["error"])
except SwiftError as e:
output_manager.error_with_txn_id(e)
@ -1518,7 +1538,8 @@ def st_tempurl(parser, args, thread_manager, return_parser=False):
thread_manager.print_msg(url)
st_bash_completion_help = '''Retrieve command specific flags used by bash_completion.
st_bash_completion_help = '''Retrieve command specific flags used by
bash_completion.
Optional positional arguments:
<command> Swift client command to filter the flags by.

View File

@ -313,6 +313,30 @@ class TestShell(unittest.TestCase):
query_string='version-id=1')],
connection.return_value.head_object.mock_calls)
@mock.patch('swiftclient.service.Connection')
def test_stat_part_number(self, connection):
argv = ["", "stat", "--part-number", "3"]
with self.assertRaises(SystemExit) as caught:
swiftclient.shell.main(argv)
self.assertEqual(str(caught.exception),
"--part-number option only allowed for "
"object stats")
argv = ["", "stat", "--part-number", "8", "container"]
with self.assertRaises(SystemExit) as caught:
swiftclient.shell.main(argv)
self.assertEqual(str(caught.exception),
"--part-number option only allowed for "
"object stats")
argv = ["", "stat", "--part-number", "20", "container", "object"]
connection.return_value.head_object.return_value = {}
with CaptureOutput():
swiftclient.shell.main(argv)
self.assertEqual([mock.call('container', 'object', headers={},
query_string='part-number=20')],
connection.return_value.head_object.mock_calls)
@mock.patch('swiftclient.service.Connection')
def test_stat_object(self, connection):
return_headers = {
@ -699,6 +723,39 @@ class TestShell(unittest.TestCase):
response_dict={})],
connection.return_value.get_object.mock_calls)
@mock.patch('swiftclient.service.Connection')
def test_download_part_number(self, connection):
argv = ["", "download", "--part-number", "3"]
with self.assertRaises(SystemExit) as caught:
swiftclient.shell.main(argv)
self.assertEqual(str(caught.exception),
"--part-number option only allowed for "
"object downloads")
argv = ["", "download", "--part-number", "12", "container"]
with self.assertRaises(SystemExit) as caught:
swiftclient.shell.main(argv)
self.assertEqual(str(caught.exception),
"--part-number option only allowed for "
"object downloads")
argv = ["", "download", " --part-number", "7", "container", "object"]
connection.return_value.head_object.return_value = {}
connection.return_value.get_object.return_value = {}, ''
connection.return_value.attempts = 0
with CaptureOutput():
swiftclient.shell.main(argv)
self.assertEqual([mock.call(' --part-number', '7',
headers={}, response_dict={},
resp_chunk_size=65536),
mock.call(' --part-number', 'container',
headers={}, response_dict={},
resp_chunk_size=65536),
mock.call(' --part-number', 'object',
headers={}, response_dict={},
resp_chunk_size=65536)],
connection.return_value.get_object.mock_calls)
@mock.patch('swiftclient.service.makedirs')
@mock.patch('swiftclient.service.Connection')
def test_download(self, connection, makedirs):