Support last modified on listing containers
For now, last modified timestamp is supported only on object listing. (i.e. GET container) For example: GET container with json format results in like as: [{"hash": "d41d8cd98f00b204e9800998ecf8427e", "last_modified": "2015-06-10T04:58:23.460230", "bytes": 0, "name": "object", "content_type": "application/octet-stream"}] However, container listing (i.e. GET account) shows just a dict consists of ("name", "bytes", "name") for each container. For example: GET accounts with json format result in like as: [{"count": 0, "bytes": 0, "name": "container"}] This patch is for supporting last_modified key in the container listing results as well as object listing like as: [{"count": 0, "bytes": 0, "name": "container", "last_modified": "2015-06-10T04:58:23.460230"}] This patch is changing just output for listing. The original timestamp to show the last modified is already in container table of account.db as a "put_timestamp" column. Note that this patch *DOESN'T* change the put_timestamp semantics. i.e. the last_modified timestamp will be changed only at both PUT container and POST container. (PUT object doesn't affect the timestamp) Note that the tuple format of returning value from swift.account.backend.AccountBroker.list_containers is now (name, object_count, bytes_used, put_timestamp, 0) * put_timestamp is added * Original discussion was in working session at Vancouver Summit. Etherpads are around here: https://etherpad.openstack.org/p/liberty-swift-contributors-meetup https://etherpad.openstack.org/p/liberty-container-listing-update DocImpact Change-Id: Iba0503916f1481a20c59ae9136436f40183e4c5b
This commit is contained in:
parent
436374e66c
commit
652276fea6
@ -2,11 +2,13 @@
|
||||
{
|
||||
"count": 0,
|
||||
"bytes": 0,
|
||||
"name": "janeausten"
|
||||
"name": "janeausten",
|
||||
"last_modified": "2013-11-19T20:08:13.283452"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"bytes": 14,
|
||||
"name": "marktwain"
|
||||
"name": "marktwain",
|
||||
"last_modified": "2016-04-29T16:23:50.460230"
|
||||
}
|
||||
]
|
||||
|
@ -4,10 +4,12 @@
|
||||
<name>janeausten</name>
|
||||
<count>0</count>
|
||||
<bytes>0</bytes>
|
||||
<last_modified>2013-11-19T20:08:13.283452</last_modified>
|
||||
</container>
|
||||
<container>
|
||||
<name>marktwain</name>
|
||||
<count>1</count>
|
||||
<bytes>14</bytes>
|
||||
<last_modified>2016-04-29T16:23:50.460230</last_modified>
|
||||
</container>
|
||||
</account>
|
||||
|
@ -379,7 +379,8 @@ class AccountBroker(DatabaseBroker):
|
||||
:param delimiter: delimiter for query
|
||||
:param reverse: reverse the result order.
|
||||
|
||||
:returns: list of tuples of (name, object_count, bytes_used, 0)
|
||||
:returns: list of tuples of (name, object_count, bytes_used,
|
||||
put_timestamp, 0)
|
||||
"""
|
||||
delim_force_gte = False
|
||||
(marker, end_marker, prefix, delimiter) = utf8encode(
|
||||
@ -397,7 +398,7 @@ class AccountBroker(DatabaseBroker):
|
||||
results = []
|
||||
while len(results) < limit:
|
||||
query = """
|
||||
SELECT name, object_count, bytes_used, 0
|
||||
SELECT name, object_count, bytes_used, put_timestamp, 0
|
||||
FROM container
|
||||
WHERE """
|
||||
query_args = []
|
||||
@ -459,7 +460,7 @@ class AccountBroker(DatabaseBroker):
|
||||
delim_force_gte = True
|
||||
dir_name = name[:end + 1]
|
||||
if dir_name != orig_marker:
|
||||
results.append([dir_name, 0, 0, 1])
|
||||
results.append([dir_name, 0, 0, '0', 1])
|
||||
curs.close()
|
||||
break
|
||||
results.append(row)
|
||||
|
@ -268,7 +268,7 @@ class AccountReaper(Daemon):
|
||||
if not containers:
|
||||
break
|
||||
try:
|
||||
for (container, _junk, _junk, _junk) in containers:
|
||||
for (container, _junk, _junk, _junk, _junk) in containers:
|
||||
this_shard = int(md5(container).hexdigest(), 16) % \
|
||||
len(nodes)
|
||||
if container_shard not in (this_shard, None):
|
||||
|
@ -81,24 +81,30 @@ def account_listing_response(account, req, response_content_type, broker=None,
|
||||
prefix, delimiter, reverse)
|
||||
if response_content_type == 'application/json':
|
||||
data = []
|
||||
for (name, object_count, bytes_used, is_subdir) in account_list:
|
||||
for (name, object_count, bytes_used, put_timestamp, is_subdir) \
|
||||
in account_list:
|
||||
if is_subdir:
|
||||
data.append({'subdir': name})
|
||||
else:
|
||||
data.append({'name': name, 'count': object_count,
|
||||
'bytes': bytes_used})
|
||||
data.append(
|
||||
{'name': name, 'count': object_count,
|
||||
'bytes': bytes_used,
|
||||
'last_modified': Timestamp(put_timestamp).isoformat})
|
||||
account_list = json.dumps(data)
|
||||
elif response_content_type.endswith('/xml'):
|
||||
output_list = ['<?xml version="1.0" encoding="UTF-8"?>',
|
||||
'<account name=%s>' % saxutils.quoteattr(account)]
|
||||
for (name, object_count, bytes_used, is_subdir) in account_list:
|
||||
for (name, object_count, bytes_used, put_timestamp, is_subdir) \
|
||||
in account_list:
|
||||
if is_subdir:
|
||||
output_list.append(
|
||||
'<subdir name=%s />' % saxutils.quoteattr(name))
|
||||
else:
|
||||
item = '<container><name>%s</name><count>%s</count>' \
|
||||
'<bytes>%s</bytes></container>' % \
|
||||
(saxutils.escape(name), object_count, bytes_used)
|
||||
'<bytes>%s</bytes><last_modified>%s</last_modified>' \
|
||||
'</container>' % \
|
||||
(saxutils.escape(name), object_count,
|
||||
bytes_used, Timestamp(put_timestamp).isoformat)
|
||||
output_list.append(item)
|
||||
output_list.append('</account>')
|
||||
account_list = '\n'.join(output_list)
|
||||
|
@ -439,7 +439,7 @@ class Account(Base):
|
||||
tree = minidom.parseString(self.conn.response.read())
|
||||
for x in tree.getElementsByTagName('container'):
|
||||
cont = {}
|
||||
for key in ['name', 'count', 'bytes']:
|
||||
for key in ['name', 'count', 'bytes', 'last_modified']:
|
||||
cont[key] = x.getElementsByTagName(key)[0].\
|
||||
childNodes[0].nodeValue
|
||||
conts.append(cont)
|
||||
|
@ -28,6 +28,7 @@ from copy import deepcopy
|
||||
import eventlet
|
||||
from unittest2 import SkipTest
|
||||
from swift.common.http import is_success, is_client_error
|
||||
from email.utils import parsedate
|
||||
|
||||
from test.functional import normalized_urls, load_constraint, cluster_info
|
||||
from test.functional import check_response, retry
|
||||
@ -299,6 +300,33 @@ class TestAccount(Base):
|
||||
results = [r for r in results if r in expected]
|
||||
self.assertEqual(expected, results)
|
||||
|
||||
def testContainerListingLastModified(self):
|
||||
expected = {}
|
||||
for container in self.env.containers:
|
||||
res = container.info()
|
||||
expected[container.name] = time.mktime(
|
||||
parsedate(res['last_modified']))
|
||||
|
||||
for format_type in ['json', 'xml']:
|
||||
actual = {}
|
||||
containers = self.env.account.containers(
|
||||
parms={'format': format_type})
|
||||
if isinstance(containers[0], dict):
|
||||
for container in containers:
|
||||
self.assertIn('name', container) # sanity
|
||||
self.assertIn('last_modified', container) # sanity
|
||||
# ceil by hand (wants easier way!)
|
||||
datetime_str, micro_sec_str = \
|
||||
container['last_modified'].split('.')
|
||||
timestamp = time.mktime(
|
||||
time.strptime(datetime_str,
|
||||
"%Y-%m-%dT%H:%M:%S"))
|
||||
if int(micro_sec_str):
|
||||
timestamp += 1
|
||||
actual[container['name']] = timestamp
|
||||
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def testInvalidAuthToken(self):
|
||||
hdrs = {'X-Auth-Token': 'bogus_auth_token'}
|
||||
self.assertRaises(ResponseError, self.env.account.info, hdrs=hdrs)
|
||||
|
@ -1225,18 +1225,19 @@ class TestAccountBrokerBeforeSPI(TestAccountBroker):
|
||||
'from container table!')
|
||||
|
||||
# manually insert an existing row to avoid migration
|
||||
timestamp = Timestamp(time()).internal
|
||||
with broker.get() as conn:
|
||||
conn.execute('''
|
||||
INSERT INTO container (name, put_timestamp,
|
||||
delete_timestamp, object_count, bytes_used,
|
||||
deleted)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
''', ('test_name', Timestamp(time()).internal, 0, 1, 2, 0))
|
||||
''', ('test_name', timestamp, 0, 1, 2, 0))
|
||||
conn.commit()
|
||||
|
||||
# make sure we can iter containers without the migration
|
||||
for c in broker.list_containers_iter(1, None, None, None, None):
|
||||
self.assertEqual(c, ('test_name', 1, 2, 0))
|
||||
self.assertEqual(c, ('test_name', 1, 2, timestamp, 0))
|
||||
|
||||
# stats table is mysteriously empty...
|
||||
stats = broker.get_policy_stats()
|
||||
@ -1363,6 +1364,7 @@ class TestAccountBrokerBeforeSPI(TestAccountBroker):
|
||||
new_broker = AccountBroker(os.path.join(tempdir, 'new_account.db'),
|
||||
account='a')
|
||||
new_broker.initialize(next(ts).internal)
|
||||
timestamp = next(ts).internal
|
||||
|
||||
# manually insert an existing row to avoid migration for old database
|
||||
with old_broker.get() as conn:
|
||||
@ -1371,7 +1373,7 @@ class TestAccountBrokerBeforeSPI(TestAccountBroker):
|
||||
delete_timestamp, object_count, bytes_used,
|
||||
deleted)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
''', ('test_name', next(ts).internal, 0, 1, 2, 0))
|
||||
''', ('test_name', timestamp, 0, 1, 2, 0))
|
||||
conn.commit()
|
||||
|
||||
# get replication info and rows form old database
|
||||
@ -1384,7 +1386,7 @@ class TestAccountBrokerBeforeSPI(TestAccountBroker):
|
||||
# make sure "test_name" container in new database
|
||||
self.assertEqual(new_broker.get_info()['container_count'], 1)
|
||||
for c in new_broker.list_containers_iter(1, None, None, None, None):
|
||||
self.assertEqual(c, ('test_name', 1, 2, 0))
|
||||
self.assertEqual(c, ('test_name', 1, 2, timestamp, 0))
|
||||
|
||||
# full migration successful
|
||||
with new_broker.get() as conn:
|
||||
|
@ -87,7 +87,7 @@ class FakeAccountBroker(object):
|
||||
|
||||
def list_containers_iter(self, *args):
|
||||
for cont in self.containers:
|
||||
yield cont, None, None, None
|
||||
yield cont, None, None, None, None
|
||||
|
||||
def is_status_deleted(self):
|
||||
return True
|
||||
@ -749,7 +749,7 @@ class TestReaper(unittest.TestCase):
|
||||
if container in self.containers_yielded:
|
||||
continue
|
||||
|
||||
yield container, None, None, None
|
||||
yield container, None, None, None, None
|
||||
self.containers_yielded.append(container)
|
||||
|
||||
def fake_reap_container(self, account, account_partition,
|
||||
|
@ -34,7 +34,7 @@ from swift.common.swob import (Request, WsgiBytesIO, HTTPNoContent)
|
||||
from swift.common import constraints
|
||||
from swift.account.server import AccountController
|
||||
from swift.common.utils import (normalize_timestamp, replication, public,
|
||||
mkdirs, storage_directory)
|
||||
mkdirs, storage_directory, Timestamp)
|
||||
from swift.common.request_helpers import get_sys_meta_prefix
|
||||
from test.unit import patch_policies, debug_logger
|
||||
from swift.common.storage_policy import StoragePolicy, POLICIES
|
||||
@ -847,18 +847,21 @@ class TestAccountController(unittest.TestCase):
|
||||
self.assertEqual(resp.charset, 'utf-8')
|
||||
|
||||
def test_GET_with_containers_json(self):
|
||||
put_timestamps = {}
|
||||
req = Request.blank('/sda1/p/a', environ={'REQUEST_METHOD': 'PUT',
|
||||
'HTTP_X_TIMESTAMP': '0'})
|
||||
req.get_response(self.controller)
|
||||
put_timestamps['c1'] = normalize_timestamp(1)
|
||||
req = Request.blank('/sda1/p/a/c1', environ={'REQUEST_METHOD': 'PUT'},
|
||||
headers={'X-Put-Timestamp': '1',
|
||||
headers={'X-Put-Timestamp': put_timestamps['c1'],
|
||||
'X-Delete-Timestamp': '0',
|
||||
'X-Object-Count': '0',
|
||||
'X-Bytes-Used': '0',
|
||||
'X-Timestamp': normalize_timestamp(0)})
|
||||
req.get_response(self.controller)
|
||||
put_timestamps['c2'] = normalize_timestamp(2)
|
||||
req = Request.blank('/sda1/p/a/c2', environ={'REQUEST_METHOD': 'PUT'},
|
||||
headers={'X-Put-Timestamp': '2',
|
||||
headers={'X-Put-Timestamp': put_timestamps['c2'],
|
||||
'X-Delete-Timestamp': '0',
|
||||
'X-Object-Count': '0',
|
||||
'X-Bytes-Used': '0',
|
||||
@ -868,18 +871,23 @@ class TestAccountController(unittest.TestCase):
|
||||
environ={'REQUEST_METHOD': 'GET'})
|
||||
resp = req.get_response(self.controller)
|
||||
self.assertEqual(resp.status_int, 200)
|
||||
self.assertEqual(json.loads(resp.body),
|
||||
[{'count': 0, 'bytes': 0, 'name': 'c1'},
|
||||
{'count': 0, 'bytes': 0, 'name': 'c2'}])
|
||||
self.assertEqual(
|
||||
json.loads(resp.body),
|
||||
[{'count': 0, 'bytes': 0, 'name': 'c1',
|
||||
'last_modified': Timestamp(put_timestamps['c1']).isoformat},
|
||||
{'count': 0, 'bytes': 0, 'name': 'c2',
|
||||
'last_modified': Timestamp(put_timestamps['c2']).isoformat}])
|
||||
put_timestamps['c1'] = normalize_timestamp(3)
|
||||
req = Request.blank('/sda1/p/a/c1', environ={'REQUEST_METHOD': 'PUT'},
|
||||
headers={'X-Put-Timestamp': '1',
|
||||
headers={'X-Put-Timestamp': put_timestamps['c1'],
|
||||
'X-Delete-Timestamp': '0',
|
||||
'X-Object-Count': '1',
|
||||
'X-Bytes-Used': '2',
|
||||
'X-Timestamp': normalize_timestamp(0)})
|
||||
req.get_response(self.controller)
|
||||
put_timestamps['c2'] = normalize_timestamp(4)
|
||||
req = Request.blank('/sda1/p/a/c2', environ={'REQUEST_METHOD': 'PUT'},
|
||||
headers={'X-Put-Timestamp': '2',
|
||||
headers={'X-Put-Timestamp': put_timestamps['c2'],
|
||||
'X-Delete-Timestamp': '0',
|
||||
'X-Object-Count': '3',
|
||||
'X-Bytes-Used': '4',
|
||||
@ -889,25 +897,31 @@ class TestAccountController(unittest.TestCase):
|
||||
environ={'REQUEST_METHOD': 'GET'})
|
||||
resp = req.get_response(self.controller)
|
||||
self.assertEqual(resp.status_int, 200)
|
||||
self.assertEqual(json.loads(resp.body),
|
||||
[{'count': 1, 'bytes': 2, 'name': 'c1'},
|
||||
{'count': 3, 'bytes': 4, 'name': 'c2'}])
|
||||
self.assertEqual(
|
||||
json.loads(resp.body),
|
||||
[{'count': 1, 'bytes': 2, 'name': 'c1',
|
||||
'last_modified': Timestamp(put_timestamps['c1']).isoformat},
|
||||
{'count': 3, 'bytes': 4, 'name': 'c2',
|
||||
'last_modified': Timestamp(put_timestamps['c2']).isoformat}])
|
||||
self.assertEqual(resp.content_type, 'application/json')
|
||||
self.assertEqual(resp.charset, 'utf-8')
|
||||
|
||||
def test_GET_with_containers_xml(self):
|
||||
put_timestamps = {}
|
||||
req = Request.blank('/sda1/p/a', environ={'REQUEST_METHOD': 'PUT',
|
||||
'HTTP_X_TIMESTAMP': '0'})
|
||||
req.get_response(self.controller)
|
||||
put_timestamps['c1'] = normalize_timestamp(1)
|
||||
req = Request.blank('/sda1/p/a/c1', environ={'REQUEST_METHOD': 'PUT'},
|
||||
headers={'X-Put-Timestamp': '1',
|
||||
headers={'X-Put-Timestamp': put_timestamps['c1'],
|
||||
'X-Delete-Timestamp': '0',
|
||||
'X-Object-Count': '0',
|
||||
'X-Bytes-Used': '0',
|
||||
'X-Timestamp': normalize_timestamp(0)})
|
||||
req.get_response(self.controller)
|
||||
put_timestamps['c2'] = normalize_timestamp(2)
|
||||
req = Request.blank('/sda1/p/a/c2', environ={'REQUEST_METHOD': 'PUT'},
|
||||
headers={'X-Put-Timestamp': '2',
|
||||
headers={'X-Put-Timestamp': put_timestamps['c2'],
|
||||
'X-Delete-Timestamp': '0',
|
||||
'X-Object-Count': '0',
|
||||
'X-Bytes-Used': '0',
|
||||
@ -926,24 +940,30 @@ class TestAccountController(unittest.TestCase):
|
||||
self.assertEqual(listing[0].nodeName, 'container')
|
||||
container = [n for n in listing[0].childNodes if n.nodeName != '#text']
|
||||
self.assertEqual(sorted([n.nodeName for n in container]),
|
||||
['bytes', 'count', 'name'])
|
||||
['bytes', 'count', 'last_modified', 'name'])
|
||||
node = [n for n in container if n.nodeName == 'name'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue, 'c1')
|
||||
node = [n for n in container if n.nodeName == 'count'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue, '0')
|
||||
node = [n for n in container if n.nodeName == 'bytes'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue, '0')
|
||||
node = [n for n in container if n.nodeName == 'last_modified'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue,
|
||||
Timestamp(put_timestamps['c1']).isoformat)
|
||||
self.assertEqual(listing[-1].nodeName, 'container')
|
||||
container = \
|
||||
[n for n in listing[-1].childNodes if n.nodeName != '#text']
|
||||
self.assertEqual(sorted([n.nodeName for n in container]),
|
||||
['bytes', 'count', 'name'])
|
||||
['bytes', 'count', 'last_modified', 'name'])
|
||||
node = [n for n in container if n.nodeName == 'name'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue, 'c2')
|
||||
node = [n for n in container if n.nodeName == 'count'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue, '0')
|
||||
node = [n for n in container if n.nodeName == 'bytes'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue, '0')
|
||||
node = [n for n in container if n.nodeName == 'last_modified'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue,
|
||||
Timestamp(put_timestamps['c2']).isoformat)
|
||||
req = Request.blank('/sda1/p/a/c1', environ={'REQUEST_METHOD': 'PUT'},
|
||||
headers={'X-Put-Timestamp': '1',
|
||||
'X-Delete-Timestamp': '0',
|
||||
@ -970,24 +990,30 @@ class TestAccountController(unittest.TestCase):
|
||||
self.assertEqual(listing[0].nodeName, 'container')
|
||||
container = [n for n in listing[0].childNodes if n.nodeName != '#text']
|
||||
self.assertEqual(sorted([n.nodeName for n in container]),
|
||||
['bytes', 'count', 'name'])
|
||||
['bytes', 'count', 'last_modified', 'name'])
|
||||
node = [n for n in container if n.nodeName == 'name'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue, 'c1')
|
||||
node = [n for n in container if n.nodeName == 'count'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue, '1')
|
||||
node = [n for n in container if n.nodeName == 'bytes'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue, '2')
|
||||
node = [n for n in container if n.nodeName == 'last_modified'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue,
|
||||
Timestamp(put_timestamps['c1']).isoformat)
|
||||
self.assertEqual(listing[-1].nodeName, 'container')
|
||||
container = [
|
||||
n for n in listing[-1].childNodes if n.nodeName != '#text']
|
||||
self.assertEqual(sorted([n.nodeName for n in container]),
|
||||
['bytes', 'count', 'name'])
|
||||
['bytes', 'count', 'last_modified', 'name'])
|
||||
node = [n for n in container if n.nodeName == 'name'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue, 'c2')
|
||||
node = [n for n in container if n.nodeName == 'count'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue, '3')
|
||||
node = [n for n in container if n.nodeName == 'bytes'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue, '4')
|
||||
node = [n for n in container if n.nodeName == 'last_modified'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue,
|
||||
Timestamp(put_timestamps['c2']).isoformat)
|
||||
self.assertEqual(resp.charset, 'utf-8')
|
||||
|
||||
def test_GET_xml_escapes_account_name(self):
|
||||
@ -1054,15 +1080,16 @@ class TestAccountController(unittest.TestCase):
|
||||
req = Request.blank('/sda1/p/a', environ={'REQUEST_METHOD': 'PUT',
|
||||
'HTTP_X_TIMESTAMP': '0'})
|
||||
req.get_response(self.controller)
|
||||
put_timestamp = normalize_timestamp(0)
|
||||
for c in range(5):
|
||||
req = Request.blank(
|
||||
'/sda1/p/a/c%d' % c,
|
||||
environ={'REQUEST_METHOD': 'PUT'},
|
||||
headers={'X-Put-Timestamp': str(c + 1),
|
||||
headers={'X-Put-Timestamp': put_timestamp,
|
||||
'X-Delete-Timestamp': '0',
|
||||
'X-Object-Count': '2',
|
||||
'X-Bytes-Used': '3',
|
||||
'X-Timestamp': normalize_timestamp(0)})
|
||||
'X-Timestamp': put_timestamp})
|
||||
req.get_response(self.controller)
|
||||
req = Request.blank('/sda1/p/a?limit=3',
|
||||
environ={'REQUEST_METHOD': 'GET'})
|
||||
@ -1079,31 +1106,38 @@ class TestAccountController(unittest.TestCase):
|
||||
req = Request.blank('/sda1/p/a', environ={'REQUEST_METHOD': 'PUT',
|
||||
'HTTP_X_TIMESTAMP': '0'})
|
||||
req.get_response(self.controller)
|
||||
put_timestamp = normalize_timestamp(0)
|
||||
for c in range(5):
|
||||
req = Request.blank(
|
||||
'/sda1/p/a/c%d' % c,
|
||||
environ={'REQUEST_METHOD': 'PUT'},
|
||||
headers={'X-Put-Timestamp': str(c + 1),
|
||||
headers={'X-Put-Timestamp': put_timestamp,
|
||||
'X-Delete-Timestamp': '0',
|
||||
'X-Object-Count': '2',
|
||||
'X-Bytes-Used': '3',
|
||||
'X-Timestamp': normalize_timestamp(0)})
|
||||
'X-Timestamp': put_timestamp})
|
||||
req.get_response(self.controller)
|
||||
req = Request.blank('/sda1/p/a?limit=3&format=json',
|
||||
environ={'REQUEST_METHOD': 'GET'})
|
||||
resp = req.get_response(self.controller)
|
||||
self.assertEqual(resp.status_int, 200)
|
||||
self.assertEqual(json.loads(resp.body),
|
||||
[{'count': 2, 'bytes': 3, 'name': 'c0'},
|
||||
{'count': 2, 'bytes': 3, 'name': 'c1'},
|
||||
{'count': 2, 'bytes': 3, 'name': 'c2'}])
|
||||
timestamp_str = Timestamp(put_timestamp).isoformat
|
||||
expected = [{'count': 2, 'bytes': 3, 'name': 'c0',
|
||||
'last_modified': timestamp_str},
|
||||
{'count': 2, 'bytes': 3, 'name': 'c1',
|
||||
'last_modified': timestamp_str},
|
||||
{'count': 2, 'bytes': 3, 'name': 'c2',
|
||||
'last_modified': timestamp_str}]
|
||||
self.assertEqual(json.loads(resp.body), expected)
|
||||
req = Request.blank('/sda1/p/a?limit=3&marker=c2&format=json',
|
||||
environ={'REQUEST_METHOD': 'GET'})
|
||||
resp = req.get_response(self.controller)
|
||||
self.assertEqual(resp.status_int, 200)
|
||||
self.assertEqual(json.loads(resp.body),
|
||||
[{'count': 2, 'bytes': 3, 'name': 'c3'},
|
||||
{'count': 2, 'bytes': 3, 'name': 'c4'}])
|
||||
expected = [{'count': 2, 'bytes': 3, 'name': 'c3',
|
||||
'last_modified': timestamp_str},
|
||||
{'count': 2, 'bytes': 3, 'name': 'c4',
|
||||
'last_modified': timestamp_str}]
|
||||
self.assertEqual(json.loads(resp.body), expected)
|
||||
|
||||
def test_GET_limit_marker_xml(self):
|
||||
req = Request.blank('/sda1/p/a', environ={'REQUEST_METHOD': 'PUT',
|
||||
@ -1131,24 +1165,31 @@ class TestAccountController(unittest.TestCase):
|
||||
self.assertEqual(listing[0].nodeName, 'container')
|
||||
container = [n for n in listing[0].childNodes if n.nodeName != '#text']
|
||||
self.assertEqual(sorted([n.nodeName for n in container]),
|
||||
['bytes', 'count', 'name'])
|
||||
['bytes', 'count', 'last_modified', 'name'])
|
||||
node = [n for n in container if n.nodeName == 'name'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue, 'c0')
|
||||
node = [n for n in container if n.nodeName == 'count'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue, '2')
|
||||
node = [n for n in container if n.nodeName == 'bytes'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue, '3')
|
||||
node = [n for n in container if n.nodeName == 'last_modified'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue,
|
||||
Timestamp('1').isoformat)
|
||||
self.assertEqual(listing[-1].nodeName, 'container')
|
||||
container = [
|
||||
n for n in listing[-1].childNodes if n.nodeName != '#text']
|
||||
self.assertEqual(sorted([n.nodeName for n in container]),
|
||||
['bytes', 'count', 'name'])
|
||||
['bytes', 'count', 'last_modified', 'name'])
|
||||
node = [n for n in container if n.nodeName == 'name'][0]
|
||||
node = [n for n in container if n.nodeName == 'name'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue, 'c2')
|
||||
node = [n for n in container if n.nodeName == 'count'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue, '2')
|
||||
node = [n for n in container if n.nodeName == 'bytes'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue, '3')
|
||||
node = [n for n in container if n.nodeName == 'last_modified'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue,
|
||||
Timestamp('3').isoformat)
|
||||
req = Request.blank('/sda1/p/a?limit=3&marker=c2&format=xml',
|
||||
environ={'REQUEST_METHOD': 'GET'})
|
||||
resp = req.get_response(self.controller)
|
||||
@ -1161,18 +1202,21 @@ class TestAccountController(unittest.TestCase):
|
||||
self.assertEqual(listing[0].nodeName, 'container')
|
||||
container = [n for n in listing[0].childNodes if n.nodeName != '#text']
|
||||
self.assertEqual(sorted([n.nodeName for n in container]),
|
||||
['bytes', 'count', 'name'])
|
||||
['bytes', 'count', 'last_modified', 'name'])
|
||||
node = [n for n in container if n.nodeName == 'name'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue, 'c3')
|
||||
node = [n for n in container if n.nodeName == 'count'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue, '2')
|
||||
node = [n for n in container if n.nodeName == 'bytes'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue, '3')
|
||||
node = [n for n in container if n.nodeName == 'last_modified'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue,
|
||||
Timestamp('4').isoformat)
|
||||
self.assertEqual(listing[-1].nodeName, 'container')
|
||||
container = [
|
||||
n for n in listing[-1].childNodes if n.nodeName != '#text']
|
||||
self.assertEqual(sorted([n.nodeName for n in container]),
|
||||
['bytes', 'count', 'name'])
|
||||
['bytes', 'count', 'last_modified', 'name'])
|
||||
node = [n for n in container if n.nodeName == 'name'][0]
|
||||
self.assertEqual(node.firstChild.nodeValue, 'c4')
|
||||
node = [n for n in container if n.nodeName == 'count'][0]
|
||||
|
Loading…
Reference in New Issue
Block a user