diff --git a/openstack/object_store/__init__.py b/openstack/object_store/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/openstack/object_store/object_store_service.py b/openstack/object_store/object_store_service.py new file mode 100644 index 000000000..0757e7ad3 --- /dev/null +++ b/openstack/object_store/object_store_service.py @@ -0,0 +1,21 @@ +# 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. + +from openstack.auth import service_filter + + +class ObjectStoreService(service_filter.ServiceFilter): + """The object store service.""" + + def __init__(self): + """Create an object store service.""" + super(ObjectStoreService, self).__init__(service_type='object-store') diff --git a/openstack/object_store/v1/__init__.py b/openstack/object_store/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/openstack/object_store/v1/container.py b/openstack/object_store/v1/container.py new file mode 100644 index 000000000..1457300c4 --- /dev/null +++ b/openstack/object_store/v1/container.py @@ -0,0 +1,59 @@ +# 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. + +from openstack.object_store import object_store_service +from openstack import resource + + +class Container(resource.Resource): + base_path = "/" + service = object_store_service.ObjectStoreService() + + allow_create = True + allow_retrieve = True + allow_update = True + allow_delete = True + allow_list = True + allow_head = True + + # Account data (from headers when id=None) + timestamp = resource.prop("x-timestamp") + account_bytes_used = resource.prop("x-account-bytes-used") + account_container_count = resource.prop("x-account-container-count") + account_object_count = resource.prop("x-account-object-count") + meta_temp_url_key = resource.prop("x-account-meta-temp-url-key") + meta_temp_url_key_2 = resource.prop("x-account-meta-temp-url-key-2") + + # Container data (from list when id=None) + name = resource.prop("name") + count = resource.prop("count") + bytes = resource.prop("bytes") + + # Container metadata (when id=name) + object_count = resource.prop("x-container-object-count") + bytes_used = resource.prop("x-container-bytes-used") + + # Optional Container metadata (from head when id=name) + can_read = resource.prop("x-container-read") + can_write = resource.prop("x-container-write") + sync_to = resource.prop("x-container-sync-to") + sync_key = resource.prop("x-container-sync-key") + versions_location = resource.prop("x-versions-location") + + @property + def id(self): + try: + val = self.name + except AttributeError: + val = None + return val diff --git a/openstack/tests/object_store/__init__.py b/openstack/tests/object_store/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/openstack/tests/object_store/test_object_store_service.py b/openstack/tests/object_store/test_object_store_service.py new file mode 100644 index 000000000..b607bfaa4 --- /dev/null +++ b/openstack/tests/object_store/test_object_store_service.py @@ -0,0 +1,25 @@ +# 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. + +import testtools + +from openstack.object_store import object_store_service + + +class TestObjectStoreService(testtools.TestCase): + + def test_service(self): + sot = object_store_service.ObjectStoreService() + self.assertEqual('object-store', sot.service_type) + self.assertEqual('public', sot.visibility) + self.assertIsNone(sot.region) + self.assertIsNone(sot.service_name) diff --git a/openstack/tests/object_store/v1/__init__.py b/openstack/tests/object_store/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/openstack/tests/object_store/v1/test_container.py b/openstack/tests/object_store/v1/test_container.py new file mode 100644 index 000000000..dd45f8c87 --- /dev/null +++ b/openstack/tests/object_store/v1/test_container.py @@ -0,0 +1,143 @@ +# 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. + +import mock +import testtools + +from openstack.object_store.v1 import container + + +ACCOUNT_EXAMPLE = { + 'content-length': '0', + 'accept-ranges': 'bytes', + 'x-timestamp': '1403312946.78273', + 'id': 'tx4272aa0d6e1e4f8f971f8-0053b84f54', + 'date': 'Sat, 05 Jul 2014 19:17:40 GMT', + 'x-account-bytes-used': '12345', + 'x-account-container-count': '678', + 'content-type': 'text/plain; charset=utf-8', + 'x-account-object-count': '98765' +} + +CONT_EXAMPLE = { + "count": 999, + "bytes": 12345, + "name": "mycontainer" +} + +HEAD_EXAMPLE = { + 'content-length': '346', + 'x-container-object-count': '2', + 'accept-ranges': 'bytes', + 'id': 'tx1878fdc50f9b4978a3fdc-0053c31462', + 'date': 'Sun, 13 Jul 2014 23:21:06 GMT', + 'x-timestamp': '1403312946.79252', + 'x-container-read': 'read-settings', + 'x-container-write': 'write-settings', + 'x-container-sync-to': 'sync-to', + 'x-container-sync-key': 'sync-key', + 'x-container-bytes-used': '630666', + 'x-versions-location': 'versions-location', + 'content-type': 'application/json; charset=utf-8' +} + +LIST_EXAMPLE = [ + { + "count": 999, + "bytes": 12345, + "name": "container1" + }, + { + "count": 888, + "bytes": 54321, + "name": "container2" + } +] + + +class TestAccount(testtools.TestCase): + + def test_make_it(self): + sot = container.Container(ACCOUNT_EXAMPLE) + self.assertIsNone(sot.id) + self.assertEqual(ACCOUNT_EXAMPLE['x-timestamp'], sot.timestamp) + self.assertEqual(ACCOUNT_EXAMPLE['x-account-bytes-used'], + sot.account_bytes_used) + self.assertEqual(ACCOUNT_EXAMPLE['x-account-container-count'], + sot.account_container_count) + self.assertEqual(ACCOUNT_EXAMPLE['x-account-object-count'], + sot.account_object_count) + + +class TestContainer(testtools.TestCase): + + def test_basic(self): + sot = container.Container(CONT_EXAMPLE) + self.assertIsNone(sot.resources_key) + self.assertEqual('/', sot.base_path) + self.assertEqual('object-store', sot.service.service_type) + self.assertTrue(sot.allow_update) + self.assertTrue(sot.allow_create) + self.assertTrue(sot.allow_retrieve) + self.assertTrue(sot.allow_delete) + self.assertTrue(sot.allow_list) + self.assertTrue(sot.allow_head) + + def test_make_it(self): + sot = container.Container(CONT_EXAMPLE) + self.assertEqual(CONT_EXAMPLE['name'], sot.id) + self.assertEqual(CONT_EXAMPLE['name'], sot.name) + self.assertEqual(CONT_EXAMPLE['count'], sot.count) + self.assertEqual(CONT_EXAMPLE['bytes'], sot.bytes) + + def test_create_and_head(self): + sot = container.Container(CONT_EXAMPLE) + + # Update container with HEAD data + sot._attrs.update(HEAD_EXAMPLE) + + # Attributes from create + self.assertEqual(CONT_EXAMPLE['name'], sot.id) + self.assertEqual(CONT_EXAMPLE['name'], sot.name) + self.assertEqual(CONT_EXAMPLE['count'], sot.count) + self.assertEqual(CONT_EXAMPLE['bytes'], sot.bytes) + + # Attributes from header + self.assertEqual(HEAD_EXAMPLE['x-container-object-count'], + sot.object_count) + self.assertEqual(HEAD_EXAMPLE['x-container-bytes-used'], + sot.bytes_used) + self.assertEqual(HEAD_EXAMPLE['x-container-read'], + sot.can_read) + self.assertEqual(HEAD_EXAMPLE['x-container-write'], + sot.can_write) + self.assertEqual(HEAD_EXAMPLE['x-container-sync-to'], + sot.sync_to) + self.assertEqual(HEAD_EXAMPLE['x-container-sync-key'], + sot.sync_key) + self.assertEqual(HEAD_EXAMPLE['x-versions-location'], + sot.versions_location) + + @mock.patch("openstack.resource.Resource.list") + def test_list(self, fake_list): + fake_val = [container.Container.existing(**ex) for ex in LIST_EXAMPLE] + fake_list.return_value = fake_val + + # Since the list method is mocked out, just pass None for the session. + response = container.Container.list(None) + + self.assertTrue(len(response), len(LIST_EXAMPLE)) + for item in range(len(response)): + self.assertEqual(type(response[item]), container.Container) + self.assertEqual(response[item].name, LIST_EXAMPLE[item]["name"]) + self.assertEqual(response[item].count, LIST_EXAMPLE[item]["count"]) + self.assertEqual(response[item].bytes, LIST_EXAMPLE[item]["bytes"])