Add object-store show commands

* Add lib.container.show_container() and lib.object.show_object()
* Add container and object show commands

Change-Id: I963d664c55b59739453345f0f353aa2eaf1bf70e
This commit is contained in:
Dean Troyer 2013-08-30 17:55:37 -05:00
parent 74f4e31389
commit ad59b03be6
9 changed files with 368 additions and 17 deletions

@ -17,8 +17,10 @@
import logging import logging
import six
from cliff import lister from cliff import lister
from cliff import show
from openstackclient.common import utils from openstackclient.common import utils
from openstackclient.object.v1.lib import container as lib_container from openstackclient.object.v1.lib import container as lib_container
@ -91,10 +93,35 @@ class ListContainer(lister.Lister):
self.app.client_manager.object.endpoint, self.app.client_manager.object.endpoint,
**kwargs **kwargs
) )
#print "data: %s" % data
return (columns, return (columns,
(utils.get_dict_properties( (utils.get_dict_properties(
s, columns, s, columns,
formatters={}, formatters={},
) for s in data)) ) for s in data))
class ShowContainer(show.ShowOne):
"""Show container information"""
log = logging.getLogger(__name__ + '.ShowContainer')
def get_parser(self, prog_name):
parser = super(ShowContainer, self).get_parser(prog_name)
parser.add_argument(
'container',
metavar='<container>',
help='Container name to display',
)
return parser
def take_action(self, parsed_args):
self.log.debug('take_action(%s)' % parsed_args)
data = lib_container.show_container(
self.app.restapi,
self.app.client_manager.object.endpoint,
parsed_args.container,
)
return zip(*sorted(six.iteritems(data)))

@ -16,6 +16,11 @@
"""Object v1 API library""" """Object v1 API library"""
try:
from urllib.parse import urlparse
except ImportError:
from urlparse import urlparse
def list_containers( def list_containers(
api, api,
@ -75,3 +80,34 @@ def list_containers(
url = "%s?%s" % (object_url, query) url = "%s?%s" % (object_url, query)
response = api.request('GET', url) response = api.request('GET', url)
return response.json() return response.json()
def show_container(
api,
url,
container,
):
"""Get container details
:param api: a restapi object
:param url: endpoint
:param container: name of container to show
:returns: dict of returned headers
"""
object_url = "%s/%s" % (url, container)
url_parts = urlparse(url)
response = api.request('HEAD', object_url)
data = {
'account': url_parts.path.split('/')[-1],
'container': container,
}
data['object_count'] = response.headers.get(
'x-container-object-count', None)
data['bytes_used'] = response.headers.get('x-container-bytes-used', None)
data['read_acl'] = response.headers.get('x-container-read', None)
data['write_acl'] = response.headers.get('x-container-write', None)
data['sync_to'] = response.headers.get('x-container-sync-to', None)
data['sync_key'] = response.headers.get('x-container-sync-key', None)
return data

@ -16,6 +16,13 @@
"""Object v1 API library""" """Object v1 API library"""
import six
try:
from urllib.parse import urlparse
except ImportError:
from urlparse import urlparse
def list_objects( def list_objects(
api, api,
@ -95,3 +102,47 @@ def list_objects(
url = "%s/%s?%s" % (object_url, container, query) url = "%s/%s?%s" % (object_url, container, query)
response = api.request('GET', url) response = api.request('GET', url)
return response.json() return response.json()
def show_object(
api,
url,
container,
obj,
):
"""Get object details
:param api: a restapi object
:param url: endpoint
:param container: container name to get a listing for
:returns: dict of object properties
"""
object_url = "%s/%s/%s" % (url, container, obj)
url_parts = urlparse(url)
response = api.request('HEAD', object_url)
data = {
'account': url_parts.path.split('/')[-1],
'container': container,
'object': obj,
}
#print "data: %s" % data
data['content-type'] = response.headers.get('content-type', None)
if 'content-length' in response.headers:
data['content-length'] = response.headers.get('content-length', None)
if 'last-modified' in response.headers:
data['last-modified'] = response.headers.get('last-modified', None)
if 'etag' in response.headers:
data['etag'] = response.headers.get('etag', None)
if 'x-object-manifest' in response.headers:
data['x-object-manifest'] = response.headers.get(
'x-object-manifest', None)
for key, value in six.iteritems(response.headers):
if key.startswith('x-object-meta-'):
data[key[len('x-object-meta-'):].title()] = value
elif key not in (
'content-type', 'content-length', 'last-modified',
'etag', 'date', 'x-object-manifest'):
data[key.title()] = value
return data

@ -17,8 +17,10 @@
import logging import logging
import six
from cliff import lister from cliff import lister
from cliff import show
from openstackclient.common import utils from openstackclient.common import utils
from openstackclient.object.v1.lib import object as lib_object from openstackclient.object.v1.lib import object as lib_object
@ -116,3 +118,35 @@ class ListObject(lister.Lister):
s, columns, s, columns,
formatters={}, formatters={},
) for s in data)) ) for s in data))
class ShowObject(show.ShowOne):
"""Show object information"""
log = logging.getLogger(__name__ + '.ShowObject')
def get_parser(self, prog_name):
parser = super(ShowObject, self).get_parser(prog_name)
parser.add_argument(
'container',
metavar='<container>',
help='Container name for object to display',
)
parser.add_argument(
'object',
metavar='<object>',
help='Object name to display',
)
return parser
def take_action(self, parsed_args):
self.log.debug('take_action(%s)' % parsed_args)
data = lib_object.show_object(
self.app.restapi,
self.app.client_manager.object.endpoint,
parsed_args.container,
parsed_args.object,
)
return zip(*sorted(six.iteritems(data)))

@ -313,3 +313,49 @@ class TestContainerList(TestObject):
(object_fakes.container_name_3, ), (object_fakes.container_name_3, ),
) )
self.assertEqual(tuple(data), datalist) self.assertEqual(tuple(data), datalist)
@mock.patch(
'openstackclient.object.v1.container.lib_container.show_container'
)
class TestContainerShow(TestObject):
def setUp(self):
super(TestContainerShow, self).setUp()
# Get the command object to test
self.cmd = container.ShowContainer(self.app, None)
def test_container_show(self, c_mock):
c_mock.return_value = copy.deepcopy(object_fakes.CONTAINER)
arglist = [
object_fakes.container_name,
]
verifylist = [
('container', object_fakes.container_name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
}
# lib.container.show_container(api, url, container)
c_mock.assert_called_with(
self.app.restapi,
AUTH_URL,
object_fakes.container_name,
**kwargs
)
collist = ('bytes', 'count', 'name')
self.assertEqual(columns, collist)
datalist = (
object_fakes.container_bytes,
object_fakes.container_count,
object_fakes.container_name,
)
self.assertEqual(data, datalist)

@ -360,3 +360,54 @@ class TestObjectList(TestObject):
(object_fakes.object_name_2, ), (object_fakes.object_name_2, ),
) )
self.assertEqual(tuple(data), datalist) self.assertEqual(tuple(data), datalist)
@mock.patch(
'openstackclient.object.v1.object.lib_object.show_object'
)
class TestObjectShow(TestObject):
def setUp(self):
super(TestObjectShow, self).setUp()
# Get the command object to test
self.cmd = obj.ShowObject(self.app, None)
def test_object_show(self, c_mock):
c_mock.return_value = copy.deepcopy(object_fakes.OBJECT)
arglist = [
object_fakes.container_name,
object_fakes.object_name_1,
]
verifylist = [
('container', object_fakes.container_name),
('object', object_fakes.object_name_1),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
}
# lib.container.show_container(api, url, container)
c_mock.assert_called_with(
self.app.restapi,
AUTH_URL,
object_fakes.container_name,
object_fakes.object_name_1,
**kwargs
)
collist = ('bytes', 'content_type', 'hash', 'last_modified', 'name')
self.assertEqual(columns, collist)
datalist = (
object_fakes.object_bytes_1,
object_fakes.object_content_type_1,
object_fakes.object_hash_1,
object_fakes.object_modified_1,
object_fakes.object_name_1,
)
self.assertEqual(data, datalist)

@ -15,19 +15,17 @@
"""Test Object API library module""" """Test Object API library module"""
from __future__ import unicode_literals
import mock import mock
from openstackclient.object.v1.lib import container as lib_container from openstackclient.object.v1.lib import container as lib_container
from openstackclient.tests.common import test_restapi as restapi from openstackclient.tests.common import test_restapi as restapi
from openstackclient.tests import fakes from openstackclient.tests import fakes
from openstackclient.tests import utils from openstackclient.tests import utils
fake_account = 'q12we34r'
fake_auth = '11223344556677889900' fake_auth = '11223344556677889900'
fake_url = 'http://gopher.com' fake_url = 'http://gopher.com/v1/' + fake_account
fake_container = 'rainbarrel' fake_container = 'rainbarrel'
@ -38,18 +36,18 @@ class FakeClient(object):
self.token = fake_auth self.token = fake_auth
class TestObject(utils.TestCommand): class TestContainer(utils.TestCommand):
def setUp(self): def setUp(self):
super(TestObject, self).setUp() super(TestContainer, self).setUp()
self.app.client_manager = fakes.FakeClientManager() self.app.client_manager = fakes.FakeClientManager()
self.app.client_manager.object = FakeClient() self.app.client_manager.object = FakeClient()
self.app.restapi = mock.MagicMock() self.app.restapi = mock.MagicMock()
class TestObjectListContainers(TestObject): class TestContainerList(TestContainer):
def test_list_containers_no_options(self): def test_container_list_no_options(self):
resp = [{'name': 'is-name'}] resp = [{'name': 'is-name'}]
self.app.restapi.request.return_value = restapi.FakeResponse(data=resp) self.app.restapi.request.return_value = restapi.FakeResponse(data=resp)
@ -65,7 +63,7 @@ class TestObjectListContainers(TestObject):
) )
self.assertEqual(data, resp) self.assertEqual(data, resp)
def test_list_containers_marker(self): def test_container_list_marker(self):
resp = [{'name': 'is-name'}] resp = [{'name': 'is-name'}]
self.app.restapi.request.return_value = restapi.FakeResponse(data=resp) self.app.restapi.request.return_value = restapi.FakeResponse(data=resp)
@ -82,7 +80,7 @@ class TestObjectListContainers(TestObject):
) )
self.assertEqual(data, resp) self.assertEqual(data, resp)
def test_list_containers_limit(self): def test_container_list_limit(self):
resp = [{'name': 'is-name'}] resp = [{'name': 'is-name'}]
self.app.restapi.request.return_value = restapi.FakeResponse(data=resp) self.app.restapi.request.return_value = restapi.FakeResponse(data=resp)
@ -99,7 +97,7 @@ class TestObjectListContainers(TestObject):
) )
self.assertEqual(data, resp) self.assertEqual(data, resp)
def test_list_containers_end_marker(self): def test_container_list_end_marker(self):
resp = [{'name': 'is-name'}] resp = [{'name': 'is-name'}]
self.app.restapi.request.return_value = restapi.FakeResponse(data=resp) self.app.restapi.request.return_value = restapi.FakeResponse(data=resp)
@ -116,7 +114,7 @@ class TestObjectListContainers(TestObject):
) )
self.assertEqual(data, resp) self.assertEqual(data, resp)
def test_list_containers_prefix(self): def test_container_list_prefix(self):
resp = [{'name': 'is-name'}] resp = [{'name': 'is-name'}]
self.app.restapi.request.return_value = restapi.FakeResponse(data=resp) self.app.restapi.request.return_value = restapi.FakeResponse(data=resp)
@ -133,7 +131,7 @@ class TestObjectListContainers(TestObject):
) )
self.assertEqual(data, resp) self.assertEqual(data, resp)
def test_list_containers_full_listing(self): def test_container_list_full_listing(self):
def side_effect(*args, **kwargs): def side_effect(*args, **kwargs):
rv = self.app.restapi.request.return_value rv = self.app.restapi.request.return_value
@ -159,3 +157,38 @@ class TestObjectListContainers(TestObject):
fake_url + '?format=json&marker=is-name', fake_url + '?format=json&marker=is-name',
) )
self.assertEqual(data, resp) self.assertEqual(data, resp)
class TestContainerShow(TestContainer):
def test_container_show_no_options(self):
resp = {
'x-container-object-count': 1,
'x-container-bytes-used': 577,
}
self.app.restapi.request.return_value = \
restapi.FakeResponse(headers=resp)
data = lib_container.show_container(
self.app.restapi,
self.app.client_manager.object.endpoint,
'is-name',
)
# Check expected values
self.app.restapi.request.assert_called_with(
'HEAD',
fake_url + '/is-name',
)
data_expected = {
'account': fake_account,
'container': 'is-name',
'object_count': 1,
'bytes_used': 577,
'read_acl': None,
'write_acl': None,
'sync_to': None,
'sync_key': None,
}
self.assertEqual(data, data_expected)

@ -15,8 +15,6 @@
"""Test Object API library module""" """Test Object API library module"""
from __future__ import unicode_literals
import mock import mock
from openstackclient.object.v1.lib import object as lib_object from openstackclient.object.v1.lib import object as lib_object
@ -25,10 +23,12 @@ from openstackclient.tests import fakes
from openstackclient.tests import utils from openstackclient.tests import utils
fake_account = 'q12we34r'
fake_auth = '11223344556677889900' fake_auth = '11223344556677889900'
fake_url = 'http://gopher.com' fake_url = 'http://gopher.com/v1/' + fake_account
fake_container = 'rainbarrel' fake_container = 'rainbarrel'
fake_object = 'raindrop'
class FakeClient(object): class FakeClient(object):
@ -203,3 +203,74 @@ class TestObjectListObjects(TestObject):
fake_url + '/' + fake_container + '?format=json&marker=is-name', fake_url + '/' + fake_container + '?format=json&marker=is-name',
) )
self.assertEqual(data, resp) self.assertEqual(data, resp)
class TestObjectShowObjects(TestObject):
def test_object_show_no_options(self):
resp = {
'content-type': 'text/alpha',
}
self.app.restapi.request.return_value = \
restapi.FakeResponse(headers=resp)
data = lib_object.show_object(
self.app.restapi,
self.app.client_manager.object.endpoint,
fake_container,
fake_object,
)
# Check expected values
self.app.restapi.request.assert_called_with(
'HEAD',
fake_url + '/%s/%s' % (fake_container, fake_object),
)
data_expected = {
'account': fake_account,
'container': fake_container,
'object': fake_object,
'content-type': 'text/alpha',
}
self.assertEqual(data, data_expected)
def test_object_show_all_options(self):
resp = {
'content-type': 'text/alpha',
'content-length': 577,
'last-modified': '20130101',
'etag': 'qaz',
'x-object-manifest': None,
'x-object-meta-wife': 'Wilma',
'x-tra-header': 'yabba-dabba-do',
}
self.app.restapi.request.return_value = \
restapi.FakeResponse(headers=resp)
data = lib_object.show_object(
self.app.restapi,
self.app.client_manager.object.endpoint,
fake_container,
fake_object,
)
# Check expected values
self.app.restapi.request.assert_called_with(
'HEAD',
fake_url + '/%s/%s' % (fake_container, fake_object),
)
data_expected = {
'account': fake_account,
'container': fake_container,
'object': fake_object,
'content-type': 'text/alpha',
'content-length': 577,
'last-modified': '20130101',
'etag': 'qaz',
'x-object-manifest': None,
'Wife': 'Wilma',
'X-Tra-Header': 'yabba-dabba-do',
}
self.assertEqual(data, data_expected)

@ -241,7 +241,9 @@ openstack.image.v2 =
openstack.object_store.v1 = openstack.object_store.v1 =
container_list = openstackclient.object.v1.container:ListContainer container_list = openstackclient.object.v1.container:ListContainer
container_show = openstackclient.object.v1.container:ShowContainer
object_list = openstackclient.object.v1.object:ListObject object_list = openstackclient.object.v1.object:ListObject
object_show = openstackclient.object.v1.object:ShowObject
openstack.volume.v1 = openstack.volume.v1 =
snapshot_create = openstackclient.volume.v1.snapshot:CreateSnapshot snapshot_create = openstackclient.volume.v1.snapshot:CreateSnapshot