Move object-store commands to low-level API

api.object_store.APIv1 now contains the formerly top-level functions
implementing the object-store REST client. This replaces the old-style
ObjectClientv1 that is no longer necessary.

Change-Id: I7d8fea326b214481e7d6b24119bd41777c6aa968
This commit is contained in:
Dean Troyer 2014-09-18 00:54:52 -05:00 committed by Steve Martinelli
parent e3b9b96588
commit 31018bf7c2
13 changed files with 773 additions and 1066 deletions

View File

@ -0,0 +1,381 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
"""Object Store v1 API Library"""
import os
import six
try:
from urllib.parse import urlparse # noqa
except ImportError:
from urlparse import urlparse # noqa
from openstackclient.api import api
class APIv1(api.BaseAPI):
"""Object Store v1 API"""
def __init__(self, **kwargs):
super(APIv1, self).__init__(**kwargs)
def container_create(
self,
container=None,
):
"""Create a container
:param string container:
name of container to create
:returns:
dict of returned headers
"""
response = self.create(container, method='PUT')
url_parts = urlparse(self.endpoint)
data = {
'account': url_parts.path.split('/')[-1],
'container': container,
'x-trans-id': response.headers.get('x-trans-id', None),
}
return data
def container_delete(
self,
container=None,
):
"""Delete a container
:param string container:
name of container to delete
"""
if container:
self.delete(container)
def container_list(
self,
all_data=False,
limit=None,
marker=None,
end_marker=None,
prefix=None,
**params
):
"""Get containers in an account
:param boolean all_data:
if True, return a full listing, else returns a max of
10000 listings
:param integer limit:
query return count limit
:param string marker:
query marker
:param string end_marker:
query end_marker
:param string prefix:
query prefix
:returns:
list of container names
"""
params['format'] = 'json'
if all_data:
data = listing = self.container_list(
limit=limit,
marker=marker,
end_marker=end_marker,
prefix=prefix,
**params
)
while listing:
marker = listing[-1]['name']
listing = self.container_list(
limit=limit,
marker=marker,
end_marker=end_marker,
prefix=prefix,
**params
)
if listing:
data.extend(listing)
return data
if limit:
params['limit'] = limit
if marker:
params['marker'] = marker
if end_marker:
params['end_marker'] = end_marker
if prefix:
params['prefix'] = prefix
return self.list('', **params)
def container_save(
self,
container=None,
):
"""Save all the content from a container
:param string container:
name of container to save
"""
objects = self.object_list(container=container)
for object in objects:
self.object_save(container=container, object=object['name'])
def container_show(
self,
container=None,
):
"""Get container details
:param string container:
name of container to show
:returns:
dict of returned headers
"""
response = self._request('HEAD', container)
data = {
'account': response.headers.get('x-container-meta-owner', None),
'container': container,
'object_count': response.headers.get(
'x-container-object-count',
None,
),
'bytes_used': response.headers.get('x-container-bytes-used', None),
'read_acl': response.headers.get('x-container-read', None),
'write_acl': response.headers.get('x-container-write', None),
'sync_to': response.headers.get('x-container-sync-to', None),
'sync_key': response.headers.get('x-container-sync-key', None),
}
return data
def object_create(
self,
container=None,
object=None,
):
"""Create an object inside a container
:param string container:
name of container to store object
:param string object:
local path to object
:returns:
dict of returned headers
"""
if container is None or object is None:
# TODO(dtroyer): What exception to raise here?
return {}
full_url = "%s/%s" % (container, object)
response = self.create(full_url, method='PUT', data=open(object))
url_parts = urlparse(self.endpoint)
data = {
'account': url_parts.path.split('/')[-1],
'container': container,
'object': object,
'x-trans-id': response.headers.get('X-Trans-Id', None),
'etag': response.headers.get('Etag', None),
}
return data
def object_delete(
self,
container=None,
object=None,
):
"""Delete an object from a container
:param string container:
name of container that stores object
:param string object:
name of object to delete
"""
if container is None or object is None:
return
self.delete("%s/%s" % (container, object))
def object_list(
self,
container=None,
all_data=False,
limit=None,
marker=None,
end_marker=None,
delimiter=None,
prefix=None,
**params
):
"""List objects in a container
:param string container:
container name to get a listing for
:param boolean all_data:
if True, return a full listing, else returns a max of
10000 listings
:param integer limit:
query return count limit
:param string marker:
query marker
:param string end_marker:
query end_marker
:param string prefix:
query prefix
:param string delimiter:
string to delimit the queries on
:returns: a tuple of (response headers, a list of objects) The response
headers will be a dict and all header names will be lowercase.
"""
if container is None or object is None:
return None
if all_data:
data = listing = self.object_list(
container=container,
limit=limit,
marker=marker,
end_marker=end_marker,
prefix=prefix,
delimiter=delimiter,
**params
)
while listing:
if delimiter:
marker = listing[-1].get('name', listing[-1].get('subdir'))
else:
marker = listing[-1]['name']
listing = self.object_list(
container=container,
limit=limit,
marker=marker,
end_marker=end_marker,
prefix=prefix,
delimiter=delimiter,
**params
)
if listing:
data.extend(listing)
return data
params = {}
if limit:
params['limit'] = limit
if marker:
params['marker'] = marker
if end_marker:
params['end_marker'] = end_marker
if prefix:
params['prefix'] = prefix
if delimiter:
params['delimiter'] = delimiter
return self.list(container, **params)
def object_save(
self,
container=None,
object=None,
file=None,
):
"""Save an object stored in a container
:param string container:
name of container that stores object
:param string object:
name of object to save
:param string file:
local name of object
"""
if not file:
file = object
response = self._request(
'GET',
"%s/%s" % (container, object),
stream=True,
)
if response.status_code == 200:
if not os.path.exists(os.path.dirname(file)):
os.makedirs(os.path.dirname(file))
with open(file, 'wb') as f:
for chunk in response.iter_content():
f.write(chunk)
def object_show(
self,
container=None,
object=None,
):
"""Get object details
:param string container:
container name for object to get
:param string object:
name of object to get
:returns:
dict of object properties
"""
if container is None or object is None:
return {}
response = self._request('HEAD', "%s/%s" % (container, object))
data = {
'account': response.headers.get('x-container-meta-owner', None),
'container': container,
'object': object,
'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-'):].lower()] = value
elif key not in (
'content-type',
'content-length',
'last-modified',
'etag',
'date',
'x-object-manifest',
'x-container-meta-owner',
):
data[key.lower()] = value
return data

View File

@ -17,6 +17,7 @@
import logging
from openstackclient.api import object_store_v1
from openstackclient.common import utils
LOG = logging.getLogger(__name__)
@ -30,7 +31,7 @@ API_VERSIONS = {
def make_client(instance):
"""Returns an object service client."""
"""Returns an object-store API client."""
object_client = utils.get_client_class(
API_NAME,
@ -42,9 +43,11 @@ def make_client(instance):
endpoint = instance._url
else:
endpoint = instance.get_endpoint_for_service_type("object-store")
client = object_client(
client = object_store_v1.APIv1(
session=instance.session,
service_type='object-store',
endpoint=endpoint,
token=instance._token,
)
return client
@ -61,16 +64,3 @@ def build_option_parser(parser):
DEFAULT_OBJECT_API_VERSION +
' (Env: OS_OBJECT_API_VERSION)')
return parser
class ObjectClientv1(object):
def __init__(
self,
endpoint_type='publicURL',
endpoint=None,
token=None,
):
self.endpoint_type = endpoint_type
self.endpoint = endpoint
self.token = token

View File

@ -24,7 +24,6 @@ from cliff import lister
from cliff import show
from openstackclient.common import utils
from openstackclient.object.v1.lib import container as lib_container
class CreateContainer(lister.Lister):
@ -47,10 +46,8 @@ class CreateContainer(lister.Lister):
results = []
for container in parsed_args.containers:
data = lib_container.create_container(
self.app.client_manager.session,
self.app.client_manager.object_store.endpoint,
container,
data = self.app.client_manager.object_store.container_create(
container=container,
)
results.append(data)
@ -81,10 +78,8 @@ class DeleteContainer(command.Command):
self.log.debug('take_action(%s)', parsed_args)
for container in parsed_args.containers:
lib_container.delete_container(
self.app.client_manager.session,
self.app.client_manager.object_store.endpoint,
container,
self.app.client_manager.object_store.container_delete(
container=container,
)
@ -150,9 +145,7 @@ class ListContainer(lister.Lister):
if parsed_args.all:
kwargs['full_listing'] = True
data = lib_container.list_containers(
self.app.client_manager.session,
self.app.client_manager.object_store.endpoint,
data = self.app.client_manager.object_store.container_list(
**kwargs
)
@ -180,10 +173,8 @@ class SaveContainer(command.Command):
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
lib_container.save_container(
self.app.client_manager.session,
self.app.client_manager.object_store.endpoint,
parsed_args.container
self.app.client_manager.object_store.container_save(
container=parsed_args.container,
)
@ -204,10 +195,8 @@ class ShowContainer(show.ShowOne):
def take_action(self, parsed_args):
self.log.debug('take_action(%s)', parsed_args)
data = lib_container.show_container(
self.app.client_manager.session,
self.app.client_manager.object_store.endpoint,
parsed_args.container,
data = self.app.client_manager.object_store.container_show(
container=parsed_args.container,
)
return zip(*sorted(six.iteritems(data)))

View File

@ -1,170 +0,0 @@
# Copyright 2010-2012 OpenStack Foundation
# Copyright 2013 Nebula Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
"""Object v1 API library"""
try:
from urllib.parse import urlparse # noqa
except ImportError:
from urlparse import urlparse # noqa
from openstackclient.object.v1.lib import object as object_lib
def create_container(
session,
url,
container,
):
"""Create a container
:param session: an authenticated keystoneclient.session.Session object
:param url: endpoint
:param container: name of container to create
:returns: dict of returned headers
"""
response = session.put("%s/%s" % (url, container))
url_parts = urlparse(url)
data = {
'account': url_parts.path.split('/')[-1],
'container': container,
'x-trans-id': response.headers.get('x-trans-id', None),
}
return data
def delete_container(
session,
url,
container,
):
"""Delete a container
:param session: an authenticated keystoneclient.session.Session object
:param url: endpoint
:param container: name of container to delete
"""
session.delete("%s/%s" % (url, container))
def list_containers(
session,
url,
marker=None,
limit=None,
end_marker=None,
prefix=None,
full_listing=False,
):
"""Get containers in an account
:param session: an authenticated keystoneclient.session.Session object
:param url: endpoint
:param marker: marker query
:param limit: limit query
:param end_marker: end_marker query
:param prefix: prefix query
:param full_listing: if True, return a full listing, else returns a max
of 10000 listings
:returns: list of containers
"""
if full_listing:
data = listing = list_containers(
session,
url,
marker,
limit,
end_marker,
prefix,
)
while listing:
marker = listing[-1]['name']
listing = list_containers(
session,
url,
marker,
limit,
end_marker,
prefix,
)
if listing:
data.extend(listing)
return data
params = {
'format': 'json',
}
if marker:
params['marker'] = marker
if limit:
params['limit'] = limit
if end_marker:
params['end_marker'] = end_marker
if prefix:
params['prefix'] = prefix
return session.get(url, params=params).json()
def save_container(
session,
url,
container
):
"""Save all the content from a container
:param session: an authenticated keystoneclient.session.Session object
:param url: endpoint
:param container: name of container to save
"""
objects = object_lib.list_objects(session, url, container)
for object in objects:
object_lib.save_object(session, url, container, object['name'])
def show_container(
session,
url,
container,
):
"""Get container details
:param session: an authenticated keystoneclient.session.Session object
:param url: endpoint
:param container: name of container to show
:returns: dict of returned headers
"""
response = session.head("%s/%s" % (url, container))
data = {
'account': response.headers.get('x-container-meta-owner', None),
'container': container,
'object_count': response.headers.get(
'x-container-object-count',
None,
),
'bytes_used': response.headers.get('x-container-bytes-used', None),
'read_acl': response.headers.get('x-container-read', None),
'write_acl': response.headers.get('x-container-write', None),
'sync_to': response.headers.get('x-container-sync-to', None),
'sync_key': response.headers.get('x-container-sync-key', None),
}
return data

View File

@ -1,221 +0,0 @@
# Copyright 2010-2012 OpenStack Foundation
# Copyright 2013 Nebula Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
"""Object v1 API library"""
import os
import six
try:
from urllib.parse import urlparse # noqa
except ImportError:
from urlparse import urlparse # noqa
def create_object(
session,
url,
container,
object,
):
"""Create an object, upload it to a container
:param session: an authenticated keystoneclient.session.Session object
:param url: endpoint
:param container: name of container to store object
:param object: local path to object
:returns: dict of returned headers
"""
full_url = "%s/%s/%s" % (url, container, object)
response = session.put(full_url, data=open(object))
url_parts = urlparse(url)
data = {
'account': url_parts.path.split('/')[-1],
'container': container,
'object': object,
'x-trans-id': response.headers.get('X-Trans-Id', None),
'etag': response.headers.get('Etag', None),
}
return data
def delete_object(
session,
url,
container,
object,
):
"""Delete an object stored in a container
:param session: an authenticated keystoneclient.session.Session object
:param url: endpoint
:param container: name of container that stores object
:param container: name of object to delete
"""
session.delete("%s/%s/%s" % (url, container, object))
def list_objects(
session,
url,
container,
marker=None,
limit=None,
end_marker=None,
delimiter=None,
prefix=None,
path=None,
full_listing=False,
):
"""Get objects in a container
:param session: an authenticated keystoneclient.session.Session object
:param url: endpoint
:param container: container name to get a listing for
:param marker: marker query
:param limit: limit query
:param end_marker: marker query
:param delimiter: string to delimit the queries on
:param prefix: prefix query
:param path: path query (equivalent: "delimiter=/" and "prefix=path/")
:param full_listing: if True, return a full listing, else returns a max
of 10000 listings
:returns: a tuple of (response headers, a list of objects) The response
headers will be a dict and all header names will be lowercase.
"""
if full_listing:
data = listing = list_objects(
session,
url,
container,
marker,
limit,
end_marker,
delimiter,
prefix,
path,
)
while listing:
if delimiter:
marker = listing[-1].get('name', listing[-1].get('subdir'))
else:
marker = listing[-1]['name']
listing = list_objects(
session,
url,
container,
marker,
limit,
end_marker,
delimiter,
prefix,
path,
)
if listing:
data.extend(listing)
return data
params = {
'format': 'json',
}
if marker:
params['marker'] = marker
if limit:
params['limit'] = limit
if end_marker:
params['end_marker'] = end_marker
if delimiter:
params['delimiter'] = delimiter
if prefix:
params['prefix'] = prefix
if path:
params['path'] = path
requrl = "%s/%s" % (url, container)
return session.get(requrl, params=params).json()
def save_object(
session,
url,
container,
obj,
file=None
):
"""Save an object stored in a container
:param session: an authenticated keystoneclient.session.Session object
:param url: endpoint
:param container: name of container that stores object
:param object: name of object to save
:param file: local name of object
"""
if not file:
file = obj
response = session.get("%s/%s/%s" % (url, container, obj), stream=True)
if response.status_code == 200:
if not os.path.exists(os.path.dirname(file)):
os.makedirs(os.path.dirname(file))
with open(file, 'wb') as f:
for chunk in response.iter_content():
f.write(chunk)
def show_object(
session,
url,
container,
obj,
):
"""Get object details
:param session: an authenticated keystoneclient.session.Session object
:param url: endpoint
:param container: container name to get a listing for
:returns: dict of object properties
"""
response = session.head("%s/%s/%s" % (url, container, obj))
data = {
'account': response.headers.get('x-container-meta-owner', None),
'container': container,
'object': obj,
'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-'):].lower()] = value
elif key not in (
'content-type', 'content-length', 'last-modified',
'etag', 'date', 'x-object-manifest', 'x-container-meta-owner'):
data[key.lower()] = value
return data

View File

@ -24,7 +24,6 @@ from cliff import lister
from cliff import show
from openstackclient.common import utils
from openstackclient.object.v1.lib import object as lib_object
class CreateObject(lister.Lister):
@ -52,11 +51,9 @@ class CreateObject(lister.Lister):
results = []
for obj in parsed_args.objects:
data = lib_object.create_object(
self.app.client_manager.session,
self.app.client_manager.object_store.endpoint,
parsed_args.container,
obj,
data = self.app.client_manager.object_store.object_create(
container=parsed_args.container,
object=obj,
)
results.append(data)
@ -92,12 +89,9 @@ class DeleteObject(command.Command):
self.log.debug('take_action(%s)', parsed_args)
for obj in parsed_args.objects:
lib_object.delete_object(
self.app.restapi,
self.app.client_manager.session,
self.app.client_manager.object_store.endpoint,
parsed_args.container,
obj,
self.app.client_manager.object_store.object_delete(
container=parsed_args.container,
object=obj,
)
@ -181,10 +175,8 @@ class ListObject(lister.Lister):
if parsed_args.all:
kwargs['full_listing'] = True
data = lib_object.list_objects(
self.app.client_manager.session,
self.app.client_manager.object_store.endpoint,
parsed_args.container,
data = self.app.client_manager.object_store.object_list(
container=parsed_args.container,
**kwargs
)
@ -222,12 +214,10 @@ class SaveObject(command.Command):
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
lib_object.save_object(
self.app.client_manager.session,
self.app.client_manager.object_store.endpoint,
parsed_args.container,
parsed_args.object,
parsed_args.file,
self.app.client_manager.object_store.object_save(
container=parsed_args.container,
object=parsed_args.object,
file=parsed_args.file,
)
@ -253,11 +243,9 @@ class ShowObject(show.ShowOne):
def take_action(self, parsed_args):
self.log.debug('take_action(%s)', parsed_args)
data = lib_object.show_object(
self.app.client_manager.session,
self.app.client_manager.object_store.endpoint,
parsed_args.container,
parsed_args.object,
data = self.app.client_manager.object_store.object_show(
container=parsed_args.container,
object=parsed_args.object,
)
return zip(*sorted(six.iteritems(data)))

View File

@ -0,0 +1,326 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
"""Object Store v1 API Library Tests"""
from requests_mock.contrib import fixture
from keystoneclient import session
from openstackclient.api import object_store_v1 as object_store
from openstackclient.tests import utils
FAKE_ACCOUNT = 'q12we34r'
FAKE_AUTH = '11223344556677889900'
FAKE_URL = 'http://gopher.com/v1/' + FAKE_ACCOUNT
FAKE_CONTAINER = 'rainbarrel'
FAKE_OBJECT = 'spigot'
LIST_CONTAINER_RESP = [
'qaz',
'fred',
]
LIST_OBJECT_RESP = [
{'name': 'fred', 'bytes': 1234, 'content_type': 'text'},
{'name': 'wilma', 'bytes': 5678, 'content_type': 'text'},
]
class TestObjectAPIv1(utils.TestCase):
def setUp(self):
super(TestObjectAPIv1, self).setUp()
sess = session.Session()
self.api = object_store.APIv1(session=sess, endpoint=FAKE_URL)
self.requests_mock = self.useFixture(fixture.Fixture())
class TestContainer(TestObjectAPIv1):
def setUp(self):
super(TestContainer, self).setUp()
def test_container_create(self):
headers = {
'x-trans-id': '1qaz2wsx',
}
self.requests_mock.register_uri(
'PUT',
FAKE_URL + '/qaz',
headers=headers,
status_code=201,
)
ret = self.api.container_create(container='qaz')
data = {
'account': FAKE_ACCOUNT,
'container': 'qaz',
'x-trans-id': '1qaz2wsx',
}
self.assertEqual(data, ret)
def test_container_delete(self):
self.requests_mock.register_uri(
'DELETE',
FAKE_URL + '/qaz',
status_code=204,
)
ret = self.api.container_delete(container='qaz')
self.assertIsNone(ret)
def test_container_list_no_options(self):
self.requests_mock.register_uri(
'GET',
FAKE_URL,
json=LIST_CONTAINER_RESP,
status_code=200,
)
ret = self.api.container_list()
self.assertEqual(LIST_CONTAINER_RESP, ret)
def test_container_list_prefix(self):
self.requests_mock.register_uri(
'GET',
FAKE_URL + '?prefix=foo%2f&format=json',
json=LIST_CONTAINER_RESP,
status_code=200,
)
ret = self.api.container_list(
prefix='foo/',
)
self.assertEqual(LIST_CONTAINER_RESP, ret)
def test_container_list_marker_limit_end(self):
self.requests_mock.register_uri(
'GET',
FAKE_URL + '?marker=next&limit=2&end_marker=stop&format=json',
json=LIST_CONTAINER_RESP,
status_code=200,
)
ret = self.api.container_list(
marker='next',
limit=2,
end_marker='stop',
)
self.assertEqual(LIST_CONTAINER_RESP, ret)
# def test_container_list_full_listing(self):
# sess = self.app.client_manager.session
#
# def side_effect(*args, **kwargs):
# rv = sess.get().json.return_value
# sess.get().json.return_value = []
# sess.get().json.side_effect = None
# return rv
#
# resp = [{'name': 'is-name'}]
# sess.get().json.return_value = resp
# sess.get().json.side_effect = side_effect
#
# data = lib_container.list_containers(
# self.app.client_manager.session,
# fake_url,
# full_listing=True,
# )
#
# # Check expected values
# sess.get.assert_called_with(
# fake_url,
# params={
# 'format': 'json',
# 'marker': 'is-name',
# }
# )
# self.assertEqual(resp, data)
def test_container_show(self):
headers = {
'X-Container-Meta-Owner': FAKE_ACCOUNT,
'x-container-object-count': '1',
'x-container-bytes-used': '577',
}
resp = {
'account': FAKE_ACCOUNT,
'container': 'qaz',
'object_count': '1',
'bytes_used': '577',
'read_acl': None,
'write_acl': None,
'sync_to': None,
'sync_key': None,
}
self.requests_mock.register_uri(
'HEAD',
FAKE_URL + '/qaz',
headers=headers,
status_code=204,
)
ret = self.api.container_show(container='qaz')
self.assertEqual(resp, ret)
class TestObject(TestObjectAPIv1):
def setUp(self):
super(TestObject, self).setUp()
def test_object_create(self):
headers = {
'etag': 'youreit',
'x-trans-id': '1qaz2wsx',
}
self.requests_mock.register_uri(
'PUT',
FAKE_URL + '/qaz/requirements.txt',
headers=headers,
status_code=201,
)
ret = self.api.object_create(
container='qaz',
object='requirements.txt',
)
data = {
'account': FAKE_ACCOUNT,
'container': 'qaz',
'object': 'requirements.txt',
'etag': 'youreit',
'x-trans-id': '1qaz2wsx',
}
self.assertEqual(data, ret)
def test_object_delete(self):
self.requests_mock.register_uri(
'DELETE',
FAKE_URL + '/qaz/wsx',
status_code=204,
)
ret = self.api.object_delete(
container='qaz',
object='wsx',
)
self.assertIsNone(ret)
def test_object_list_no_options(self):
self.requests_mock.register_uri(
'GET',
FAKE_URL + '/qaz',
json=LIST_OBJECT_RESP,
status_code=200,
)
ret = self.api.object_list(container='qaz')
self.assertEqual(LIST_OBJECT_RESP, ret)
def test_object_list_delimiter(self):
self.requests_mock.register_uri(
'GET',
FAKE_URL + '/qaz?delimiter=%7C',
json=LIST_OBJECT_RESP,
status_code=200,
)
ret = self.api.object_list(
container='qaz',
delimiter='|',
)
self.assertEqual(LIST_OBJECT_RESP, ret)
def test_object_list_prefix(self):
self.requests_mock.register_uri(
'GET',
FAKE_URL + '/qaz?prefix=foo%2f',
json=LIST_OBJECT_RESP,
status_code=200,
)
ret = self.api.object_list(
container='qaz',
prefix='foo/',
)
self.assertEqual(LIST_OBJECT_RESP, ret)
def test_object_list_marker_limit_end(self):
self.requests_mock.register_uri(
'GET',
FAKE_URL + '/qaz?marker=next&limit=2&end_marker=stop',
json=LIST_CONTAINER_RESP,
status_code=200,
)
ret = self.api.object_list(
container='qaz',
marker='next',
limit=2,
end_marker='stop',
)
self.assertEqual(LIST_CONTAINER_RESP, ret)
# def test_list_objects_full_listing(self):
# sess = self.app.client_manager.session
#
# def side_effect(*args, **kwargs):
# rv = sess.get().json.return_value
# sess.get().json.return_value = []
# sess.get().json.side_effect = None
# return rv
#
# resp = [{'name': 'is-name'}]
# sess.get().json.return_value = resp
# sess.get().json.side_effect = side_effect
#
# data = lib_object.list_objects(
# sess,
# fake_url,
# fake_container,
# full_listing=True,
# )
#
# # Check expected values
# sess.get.assert_called_with(
# fake_url + '/' + fake_container,
# params={
# 'format': 'json',
# 'marker': 'is-name',
# }
# )
# self.assertEqual(resp, data)
def test_object_show(self):
headers = {
'content-type': 'text/alpha',
'content-length': '577',
'last-modified': '20130101',
'etag': 'qaz',
'x-container-meta-owner': FAKE_ACCOUNT,
'x-object-meta-wife': 'Wilma',
'x-tra-header': 'yabba-dabba-do',
}
resp = {
'account': FAKE_ACCOUNT,
'container': 'qaz',
'object': FAKE_OBJECT,
'content-type': 'text/alpha',
'content-length': '577',
'last-modified': '20130101',
'etag': 'qaz',
'wife': 'Wilma',
'x-tra-header': 'yabba-dabba-do',
}
self.requests_mock.register_uri(
'HEAD',
FAKE_URL + '/qaz/' + FAKE_OBJECT,
headers=headers,
status_code=204,
)
ret = self.api.object_show(
container='qaz',
object=FAKE_OBJECT,
)
self.assertEqual(resp, ret)

View File

@ -1,207 +0,0 @@
# Copyright 2013 Nebula Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
"""Test Object API library module"""
import mock
from openstackclient.object.v1.lib import container as lib_container
from openstackclient.tests import fakes
from openstackclient.tests.object.v1 import fakes as object_fakes
fake_account = 'q12we34r'
fake_auth = '11223344556677889900'
fake_url = 'http://gopher.com/v1/' + fake_account
fake_container = 'rainbarrel'
class FakeClient(object):
def __init__(self, endpoint=None, **kwargs):
self.endpoint = fake_url
self.token = fake_auth
class TestContainer(object_fakes.TestObjectv1):
def setUp(self):
super(TestContainer, self).setUp()
self.app.client_manager.session = mock.MagicMock()
class TestContainerList(TestContainer):
def test_container_list_no_options(self):
resp = [{'name': 'is-name'}]
self.app.client_manager.session.get().json.return_value = resp
data = lib_container.list_containers(
self.app.client_manager.session,
fake_url,
)
# Check expected values
self.app.client_manager.session.get.assert_called_with(
fake_url,
params={
'format': 'json',
}
)
self.assertEqual(resp, data)
def test_container_list_marker(self):
resp = [{'name': 'is-name'}]
self.app.client_manager.session.get().json.return_value = resp
data = lib_container.list_containers(
self.app.client_manager.session,
fake_url,
marker='next',
)
# Check expected values
self.app.client_manager.session.get.assert_called_with(
fake_url,
params={
'format': 'json',
'marker': 'next',
}
)
self.assertEqual(resp, data)
def test_container_list_limit(self):
resp = [{'name': 'is-name'}]
self.app.client_manager.session.get().json.return_value = resp
data = lib_container.list_containers(
self.app.client_manager.session,
fake_url,
limit=5,
)
# Check expected values
self.app.client_manager.session.get.assert_called_with(
fake_url,
params={
'format': 'json',
'limit': 5,
}
)
self.assertEqual(resp, data)
def test_container_list_end_marker(self):
resp = [{'name': 'is-name'}]
self.app.client_manager.session.get().json.return_value = resp
data = lib_container.list_containers(
self.app.client_manager.session,
fake_url,
end_marker='last',
)
# Check expected values
self.app.client_manager.session.get.assert_called_with(
fake_url,
params={
'format': 'json',
'end_marker': 'last',
}
)
self.assertEqual(resp, data)
def test_container_list_prefix(self):
resp = [{'name': 'is-name'}]
self.app.client_manager.session.get().json.return_value = resp
data = lib_container.list_containers(
self.app.client_manager.session,
fake_url,
prefix='foo/',
)
# Check expected values
self.app.client_manager.session.get.assert_called_with(
fake_url,
params={
'format': 'json',
'prefix': 'foo/',
}
)
self.assertEqual(resp, data)
def test_container_list_full_listing(self):
sess = self.app.client_manager.session
def side_effect(*args, **kwargs):
rv = sess.get().json.return_value
sess.get().json.return_value = []
sess.get().json.side_effect = None
return rv
resp = [{'name': 'is-name'}]
sess.get().json.return_value = resp
sess.get().json.side_effect = side_effect
data = lib_container.list_containers(
self.app.client_manager.session,
fake_url,
full_listing=True,
)
# Check expected values
sess.get.assert_called_with(
fake_url,
params={
'format': 'json',
'marker': 'is-name',
}
)
self.assertEqual(resp, data)
class TestContainerShow(TestContainer):
def test_container_show_no_options(self):
resp = {
'X-Container-Meta-Owner': fake_account,
'x-container-object-count': 1,
'x-container-bytes-used': 577,
}
self.app.client_manager.session.head.return_value = \
fakes.FakeResponse(headers=resp)
data = lib_container.show_container(
self.app.client_manager.session,
fake_url,
'is-name',
)
# Check expected values
self.app.client_manager.session.head.assert_called_with(
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_expected, data)

View File

@ -1,295 +0,0 @@
# Copyright 2013 Nebula Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
"""Test Object API library module"""
import mock
from openstackclient.object.v1.lib import object as lib_object
from openstackclient.tests import fakes
from openstackclient.tests.object.v1 import fakes as object_fakes
fake_account = 'q12we34r'
fake_auth = '11223344556677889900'
fake_url = 'http://gopher.com/v1/' + fake_account
fake_container = 'rainbarrel'
fake_object = 'raindrop'
class FakeClient(object):
def __init__(self, endpoint=None, **kwargs):
self.endpoint = fake_url
self.token = fake_auth
class TestObject(object_fakes.TestObjectv1):
def setUp(self):
super(TestObject, self).setUp()
self.app.client_manager.session = mock.MagicMock()
class TestObjectListObjects(TestObject):
def test_list_objects_no_options(self):
resp = [{'name': 'is-name'}]
self.app.client_manager.session.get().json.return_value = resp
data = lib_object.list_objects(
self.app.client_manager.session,
fake_url,
fake_container,
)
# Check expected values
self.app.client_manager.session.get.assert_called_with(
fake_url + '/' + fake_container,
params={
'format': 'json',
}
)
self.assertEqual(resp, data)
def test_list_objects_marker(self):
resp = [{'name': 'is-name'}]
self.app.client_manager.session.get().json.return_value = resp
data = lib_object.list_objects(
self.app.client_manager.session,
fake_url,
fake_container,
marker='next',
)
# Check expected values
self.app.client_manager.session.get.assert_called_with(
fake_url + '/' + fake_container,
params={
'format': 'json',
'marker': 'next',
}
)
self.assertEqual(resp, data)
def test_list_objects_limit(self):
resp = [{'name': 'is-name'}]
self.app.client_manager.session.get().json.return_value = resp
data = lib_object.list_objects(
self.app.client_manager.session,
fake_url,
fake_container,
limit=5,
)
# Check expected values
self.app.client_manager.session.get.assert_called_with(
fake_url + '/' + fake_container,
params={
'format': 'json',
'limit': 5,
}
)
self.assertEqual(resp, data)
def test_list_objects_end_marker(self):
resp = [{'name': 'is-name'}]
self.app.client_manager.session.get().json.return_value = resp
data = lib_object.list_objects(
self.app.client_manager.session,
fake_url,
fake_container,
end_marker='last',
)
# Check expected values
self.app.client_manager.session.get.assert_called_with(
fake_url + '/' + fake_container,
params={
'format': 'json',
'end_marker': 'last',
}
)
self.assertEqual(resp, data)
def test_list_objects_delimiter(self):
resp = [{'name': 'is-name'}]
self.app.client_manager.session.get().json.return_value = resp
data = lib_object.list_objects(
self.app.client_manager.session,
fake_url,
fake_container,
delimiter='|',
)
# Check expected values
# NOTE(dtroyer): requests handles the URL encoding and we're
# mocking that so use the otherwise-not-legal
# pipe '|' char in the response.
self.app.client_manager.session.get.assert_called_with(
fake_url + '/' + fake_container,
params={
'format': 'json',
'delimiter': '|',
}
)
self.assertEqual(resp, data)
def test_list_objects_prefix(self):
resp = [{'name': 'is-name'}]
self.app.client_manager.session.get().json.return_value = resp
data = lib_object.list_objects(
self.app.client_manager.session,
fake_url,
fake_container,
prefix='foo/',
)
# Check expected values
self.app.client_manager.session.get.assert_called_with(
fake_url + '/' + fake_container,
params={
'format': 'json',
'prefix': 'foo/',
}
)
self.assertEqual(resp, data)
def test_list_objects_path(self):
resp = [{'name': 'is-name'}]
self.app.client_manager.session.get().json.return_value = resp
data = lib_object.list_objects(
self.app.client_manager.session,
fake_url,
fake_container,
path='next',
)
# Check expected values
self.app.client_manager.session.get.assert_called_with(
fake_url + '/' + fake_container,
params={
'format': 'json',
'path': 'next',
}
)
self.assertEqual(resp, data)
def test_list_objects_full_listing(self):
sess = self.app.client_manager.session
def side_effect(*args, **kwargs):
rv = sess.get().json.return_value
sess.get().json.return_value = []
sess.get().json.side_effect = None
return rv
resp = [{'name': 'is-name'}]
sess.get().json.return_value = resp
sess.get().json.side_effect = side_effect
data = lib_object.list_objects(
sess,
fake_url,
fake_container,
full_listing=True,
)
# Check expected values
sess.get.assert_called_with(
fake_url + '/' + fake_container,
params={
'format': 'json',
'marker': 'is-name',
}
)
self.assertEqual(resp, data)
class TestObjectShowObjects(TestObject):
def test_object_show_no_options(self):
resp = {
'content-type': 'text/alpha',
'x-container-meta-owner': fake_account,
}
self.app.client_manager.session.head.return_value = \
fakes.FakeResponse(headers=resp)
data = lib_object.show_object(
self.app.client_manager.session,
fake_url,
fake_container,
fake_object,
)
# Check expected values
self.app.client_manager.session.head.assert_called_with(
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_expected, data)
def test_object_show_all_options(self):
resp = {
'content-type': 'text/alpha',
'content-length': 577,
'last-modified': '20130101',
'etag': 'qaz',
'x-container-meta-owner': fake_account,
'x-object-manifest': None,
'x-object-meta-wife': 'Wilma',
'x-tra-header': 'yabba-dabba-do',
}
self.app.client_manager.session.head.return_value = \
fakes.FakeResponse(headers=resp)
data = lib_object.show_object(
self.app.client_manager.session,
fake_url,
fake_container,
fake_object,
)
# Check expected values
self.app.client_manager.session.head.assert_called_with(
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_expected, data)

View File

@ -16,6 +16,7 @@
import copy
import mock
from openstackclient.api import object_store_v1 as object_store
from openstackclient.object.v1 import container
from openstackclient.tests.object.v1 import fakes as object_fakes
@ -30,28 +31,20 @@ class FakeClient(object):
self.token = AUTH_TOKEN
class TestObject(object_fakes.TestObjectv1):
class TestContainer(object_fakes.TestObjectv1):
def setUp(self):
super(TestObject, self).setUp()
class TestObjectClient(TestObject):
def test_make_client(self):
self.assertEqual(
self.app.client_manager.object_store.endpoint,
AUTH_URL,
)
self.assertEqual(
self.app.client_manager.object_store.token,
AUTH_TOKEN,
super(TestContainer, self).setUp()
self.app.client_manager.object_store = object_store.APIv1(
session=mock.Mock(),
service_type="object-store",
)
self.api = self.app.client_manager.object_store
@mock.patch(
'openstackclient.object.v1.container.lib_container.list_containers'
'openstackclient.api.object_store_v1.APIv1.container_list'
)
class TestContainerList(TestObject):
class TestContainerList(TestContainer):
def setUp(self):
super(TestContainerList, self).setUp()
@ -77,8 +70,6 @@ class TestContainerList(TestObject):
kwargs = {
}
c_mock.assert_called_with(
self.app.client_manager.session,
AUTH_URL,
**kwargs
)
@ -113,8 +104,6 @@ class TestContainerList(TestObject):
'prefix': 'bit',
}
c_mock.assert_called_with(
self.app.client_manager.session,
AUTH_URL,
**kwargs
)
@ -134,43 +123,10 @@ class TestContainerList(TestObject):
arglist = [
'--marker', object_fakes.container_name,
]
verifylist = [
('marker', 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 = {
'marker': object_fakes.container_name,
}
c_mock.assert_called_with(
self.app.client_manager.session,
AUTH_URL,
**kwargs
)
collist = ('Name',)
self.assertEqual(columns, collist)
datalist = (
(object_fakes.container_name, ),
(object_fakes.container_name_3, ),
)
self.assertEqual(tuple(data), datalist)
def test_object_list_containers_end_marker(self, c_mock):
c_mock.return_value = [
copy.deepcopy(object_fakes.CONTAINER),
copy.deepcopy(object_fakes.CONTAINER_3),
]
arglist = [
'--end-marker', object_fakes.container_name_3,
]
verifylist = [
('marker', object_fakes.container_name),
('end_marker', object_fakes.container_name_3),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@ -180,11 +136,10 @@ class TestContainerList(TestObject):
# Set expected values
kwargs = {
'marker': object_fakes.container_name,
'end_marker': object_fakes.container_name_3,
}
c_mock.assert_called_with(
self.app.client_manager.session,
AUTH_URL,
**kwargs
)
@ -218,8 +173,6 @@ class TestContainerList(TestObject):
'limit': 2,
}
c_mock.assert_called_with(
self.app.client_manager.session,
AUTH_URL,
**kwargs
)
@ -252,8 +205,6 @@ class TestContainerList(TestObject):
kwargs = {
}
c_mock.assert_called_with(
self.app.client_manager.session,
AUTH_URL,
**kwargs
)
@ -296,8 +247,6 @@ class TestContainerList(TestObject):
'full_listing': True,
}
c_mock.assert_called_with(
self.app.client_manager.session,
AUTH_URL,
**kwargs
)
@ -312,9 +261,9 @@ class TestContainerList(TestObject):
@mock.patch(
'openstackclient.object.v1.container.lib_container.show_container'
'openstackclient.api.object_store_v1.APIv1.container_show'
)
class TestContainerShow(TestObject):
class TestContainerShow(TestContainer):
def setUp(self):
super(TestContainerShow, self).setUp()
@ -341,9 +290,7 @@ class TestContainerShow(TestObject):
}
# lib.container.show_container(api, url, container)
c_mock.assert_called_with(
self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name,
container=object_fakes.container_name,
**kwargs
)

View File

@ -16,6 +16,7 @@ import copy
from requests_mock.contrib import fixture
from keystoneclient import session
from openstackclient.api import object_store_v1 as object_store
from openstackclient.object.v1 import container
from openstackclient.tests.object.v1 import fakes as object_fakes
@ -29,7 +30,10 @@ class TestObjectAll(object_fakes.TestObjectv1):
self.requests_mock = self.useFixture(fixture.Fixture())
# TODO(dtroyer): move this to object_fakes.TestObjectv1
self.app.client_manager.object_store.endpoint = object_fakes.ENDPOINT
self.app.client_manager.object_store = object_store.APIv1(
session=self.app.client_manager.session,
endpoint=object_fakes.ENDPOINT,
)
class TestContainerCreate(TestObjectAll):

View File

@ -16,6 +16,7 @@
import copy
import mock
from openstackclient.api import object_store_v1 as object_store
from openstackclient.object.v1 import object as obj
from openstackclient.tests.object.v1 import fakes as object_fakes
@ -27,23 +28,15 @@ AUTH_URL = "http://0.0.0.0"
class TestObject(object_fakes.TestObjectv1):
def setUp(self):
super(TestObject, self).setUp()
class TestObjectClient(TestObject):
def test_make_client(self):
self.assertEqual(
self.app.client_manager.object_store.endpoint,
AUTH_URL,
)
self.assertEqual(
self.app.client_manager.object_store.token,
AUTH_TOKEN,
self.app.client_manager.object_store = object_store.APIv1(
session=mock.Mock(),
service_type="object-store",
)
self.api = self.app.client_manager.object_store
@mock.patch(
'openstackclient.object.v1.object.lib_object.list_objects'
'openstackclient.api.object_store_v1.APIv1.object_list'
)
class TestObjectList(TestObject):
@ -71,9 +64,7 @@ class TestObjectList(TestObject):
columns, data = self.cmd.take_action(parsed_args)
o_mock.assert_called_with(
self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name,
container=object_fakes.container_name,
)
collist = ('Name',)
@ -107,9 +98,7 @@ class TestObjectList(TestObject):
'prefix': 'floppy',
}
o_mock.assert_called_with(
self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name_2,
container=object_fakes.container_name_2,
**kwargs
)
@ -143,9 +132,7 @@ class TestObjectList(TestObject):
'delimiter': '=',
}
o_mock.assert_called_with(
self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name_2,
container=object_fakes.container_name_2,
**kwargs
)
@ -179,9 +166,7 @@ class TestObjectList(TestObject):
'marker': object_fakes.object_name_2,
}
o_mock.assert_called_with(
self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name_2,
container=object_fakes.container_name_2,
**kwargs
)
@ -215,9 +200,7 @@ class TestObjectList(TestObject):
'end_marker': object_fakes.object_name_2,
}
o_mock.assert_called_with(
self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name_2,
container=object_fakes.container_name_2,
**kwargs
)
@ -251,9 +234,7 @@ class TestObjectList(TestObject):
'limit': 2,
}
o_mock.assert_called_with(
self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name_2,
container=object_fakes.container_name_2,
**kwargs
)
@ -287,9 +268,7 @@ class TestObjectList(TestObject):
kwargs = {
}
o_mock.assert_called_with(
self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name,
container=object_fakes.container_name,
**kwargs
)
@ -337,9 +316,7 @@ class TestObjectList(TestObject):
'full_listing': True,
}
o_mock.assert_called_with(
self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name,
container=object_fakes.container_name,
**kwargs
)
@ -353,7 +330,7 @@ class TestObjectList(TestObject):
@mock.patch(
'openstackclient.object.v1.object.lib_object.show_object'
'openstackclient.api.object_store_v1.APIv1.object_show'
)
class TestObjectShow(TestObject):
@ -384,10 +361,8 @@ class TestObjectShow(TestObject):
}
# lib.container.show_container(api, url, container)
c_mock.assert_called_with(
self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name,
object_fakes.object_name_1,
container=object_fakes.container_name,
object=object_fakes.object_name_1,
**kwargs
)