Skip to content

Commit b0d727d

Browse files
author
Mridula Joshi
committed
Extending stores-detail API
This patch extends the functionality of the API ``GET /v2/info/stores/detail`` to expose store details of other stores. Currently the ``stores-detail`` API exposes store details of RBD backend. Implements: blueprint expanding-stores-detail Change-Id: I0c7bbc315b56dc0e40397b4ed8a68d1668203c44
1 parent f40fdba commit b0d727d

File tree

6 files changed

+210
-23
lines changed

6 files changed

+210
-23
lines changed

api-ref/source/v2/samples/stores-list-detail-response.json

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,41 @@
66
"description": "More expensive store with data redundancy",
77
"default": true,
88
"properties": {
9-
"pool": "pool1"
9+
"pool": "pool1",
10+
"chunk_size": 65536,
11+
"thin_provisioning": false
1012
}
1113
},
1214
{
1315
"id":"cheap",
1416
"type": "file",
1517
"description": "Less expensive store for seldom-used images",
16-
"properties": {}
18+
"properties": {
19+
"datadir": "fdir",
20+
"chunk_size": 65536,
21+
"thin_provisioning": false
22+
}
23+
},
24+
{
25+
"id":"fast",
26+
"type": "cinder",
27+
"description": "Reasonably-priced fast store",
28+
"properties": {
29+
"volume_type": "volume1",
30+
"use_multipath": false
31+
}
32+
},
33+
{
34+
"id":"slow",
35+
"type": "swift",
36+
"description": "Entry-level store balancing price and speed",
37+
"properties": {
38+
"container": "container1",
39+
"large_object_size": 52428,
40+
"large_object_chunk_size": 204800
41+
}
1742
}
43+
44+
1845
]
1946
}

glance/api/v2/discovery.py

Lines changed: 65 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -77,24 +77,79 @@ def get_stores(self, req):
7777

7878
return {'stores': backends}
7979

80+
@staticmethod
81+
def _get_rbd_properties(store_detail):
82+
return {
83+
'chunk_size': store_detail.chunk_size,
84+
'pool': store_detail.pool,
85+
'thin_provisioning': store_detail.thin_provisioning
86+
}
87+
88+
@staticmethod
89+
def _get_file_properties(store_detail):
90+
return {
91+
'data_dir': store_detail.datadir,
92+
'chunk_size': store_detail.chunk_size,
93+
'thin_provisioning': store_detail.thin_provisioning
94+
}
95+
96+
@staticmethod
97+
def _get_cinder_properties(store_detail):
98+
return {
99+
'volume_type': store_detail.store_conf.cinder_volume_type,
100+
'use_multipath': store_detail.store_conf.cinder_use_multipath
101+
}
102+
103+
@staticmethod
104+
def _get_swift_properties(store_detail):
105+
return {
106+
'container': store_detail.container,
107+
'large_object_size': store_detail.large_object_size,
108+
'large_object_chunk_size': store_detail.large_object_chunk_size
109+
}
110+
111+
@staticmethod
112+
def _get_s3_properties(store_detail):
113+
return {
114+
's3_store_large_object_size':
115+
store_detail.s3_store_large_object_size,
116+
's3_store_large_object_chunk_size':
117+
store_detail.s3_store_large_object_chunk_size,
118+
's3_store_thread_pools':
119+
store_detail.s3_store_thread_pools
120+
}
121+
122+
@staticmethod
123+
def _get_http_properties(store_detail):
124+
# NOTE(mrjoshi): Thre are no useful properties
125+
# to be exposed.
126+
return {}
127+
80128
def get_stores_detail(self, req):
81129
enabled_backends = CONF.enabled_backends
82130
stores = self.get_stores(req).get('stores')
83131
try:
84132
api_policy.DiscoveryAPIPolicy(
85133
req.context,
86134
enforcer=self.policy).stores_info_detail()
135+
136+
store_mapper = {
137+
'rbd': self._get_rbd_properties,
138+
'file': self._get_file_properties,
139+
'cinder': self._get_cinder_properties,
140+
'swift': self._get_swift_properties,
141+
's3': self._get_s3_properties,
142+
'http': self._get_http_properties
143+
}
144+
87145
for store in stores:
88-
store['type'] = enabled_backends[store['id']]
89-
store['properties'] = {}
90-
if store['type'] == 'rbd':
91-
store_detail = g_store.get_store_from_store_identifier(
92-
store['id'])
93-
store['properties'] = {'chunk_size':
94-
store_detail.chunk_size,
95-
'pool': store_detail.pool,
96-
'thin_provisioning':
97-
store_detail.thin_provisioning}
146+
store_type = enabled_backends[store['id']]
147+
store['type'] = store_type
148+
store_detail = g_store.get_store_from_store_identifier(
149+
store['id'])
150+
store['properties'] = store_mapper.get(store_type)(
151+
store_detail)
152+
98153
except exception.Forbidden as e:
99154
LOG.debug("User not permitted to view details")
100155
raise webob.exc.HTTPForbidden(explanation=e.msg)

glance/tests/functional/v2/test_discovery.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
# under the License.
1515

1616
import fixtures
17+
import http.client as http
1718

1819
from oslo_utils import units
1920

@@ -96,3 +97,68 @@ def test_quota_with_usage(self):
9697
expected['image_count_total']['usage'] = 1
9798
expected['image_size_total']['usage'] = 1
9899
self._assert_usage(expected)
100+
101+
def test_stores(self):
102+
# NOTE(mrjoshi): As this is a functional test, we are
103+
# testing the functionality with file stores.
104+
105+
self.start_server()
106+
107+
# If user is admin or non-admin the store list will be
108+
# displayed.
109+
stores = self.api_get('/v2/info/stores').json['stores']
110+
expected = {
111+
"stores": [
112+
{
113+
"id": "store1",
114+
"default": "true"
115+
},
116+
{
117+
"id": "store2"
118+
},
119+
{
120+
"id": "store3"
121+
}]}
122+
123+
self.assertEqual(expected['stores'], stores)
124+
125+
# If user is admin the store list will be displayed
126+
# along with store properties.
127+
stores = self.api_get('/v2/info/stores/detail').json['stores']
128+
expected = {
129+
"stores": [
130+
{
131+
"id": "store1",
132+
"default": "true",
133+
"type": "file",
134+
"properties": {
135+
"data_dir": self._store_dir('store1'),
136+
"chunk_size": 65536,
137+
"thin_provisioning": False
138+
}
139+
},
140+
{
141+
"id": "store2",
142+
"type": "file",
143+
"properties": {
144+
"data_dir": self._store_dir('store2'),
145+
"chunk_size": 65536,
146+
"thin_provisioning": False
147+
}
148+
},
149+
{
150+
"id": "store3",
151+
"type": "file",
152+
"properties": {
153+
"data_dir": self._store_dir('store3'),
154+
"chunk_size": 65536,
155+
"thin_provisioning": False
156+
}
157+
}]}
158+
159+
self.assertEqual(expected['stores'], stores)
160+
161+
# If user is non-admin 403 Error response will be returned.
162+
response = self.api_get('/v2/info/stores/detail',
163+
headers={'X-Roles': 'member'})
164+
self.assertEqual(http.FORBIDDEN, response.status_code)

glance/tests/unit/base.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
from unittest import mock
1818

1919
import glance_store as store
20+
from glance_store._drivers import cinder
2021
from glance_store._drivers import rbd as rbd_store
22+
from glance_store._drivers import swift
2123
from glance_store import location
2224
from oslo_concurrency import lockutils
2325
from oslo_config import cfg
@@ -74,21 +76,36 @@ def _create_multi_stores(self, passing_config=True):
7476
rbd_store.rados = mock.MagicMock()
7577
rbd_store.rbd = mock.MagicMock()
7678
rbd_store.Store._set_url_prefix = mock.MagicMock()
79+
cinder.cinderclient = mock.MagicMock()
80+
cinder.Store.get_cinderclient = mock.MagicMock()
81+
swift.swiftclient = mock.MagicMock()
82+
swift.BaseStore.get_store_connection = mock.MagicMock()
7783
self.config(enabled_backends={'fast': 'file', 'cheap': 'file',
7884
'readonly_store': 'http',
7985
'fast-cinder': 'cinder',
80-
'fast-rbd': 'rbd'})
86+
'fast-rbd': 'rbd', 'reliable': 'swift'})
8187
store.register_store_opts(CONF)
8288

8389
self.config(default_backend='fast',
8490
group='glance_store')
8591

8692
self.config(filesystem_store_datadir=self.test_dir,
93+
filesystem_thin_provisioning=False,
94+
filesystem_store_chunk_size=65536,
8795
group='fast')
8896
self.config(filesystem_store_datadir=self.test_dir2,
97+
filesystem_thin_provisioning=False,
98+
filesystem_store_chunk_size=65536,
8999
group='cheap')
90100
self.config(rbd_store_chunk_size=8688388, rbd_store_pool='images',
91101
rbd_thin_provisioning=False, group='fast-rbd')
102+
self.config(cinder_volume_type='lvmdriver-1',
103+
cinder_use_multipath=False, group='fast-cinder')
104+
self.config(swift_store_container='glance',
105+
swift_store_large_object_size=524288000,
106+
swift_store_large_object_chunk_size=204800000,
107+
group='reliable')
108+
92109
store.create_multi_stores(CONF)
93110

94111

glance/tests/unit/v2/test_discovery_stores.py

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def test_get_stores_with_enabled_backends_empty(self):
4040

4141
def test_get_stores(self):
4242
available_stores = ['cheap', 'fast', 'readonly_store', 'fast-cinder',
43-
'fast-rbd']
43+
'fast-rbd', 'reliable']
4444
req = unit_test_utils.get_fake_request()
4545
output = self.controller.get_stores(req)
4646
self.assertIn('stores', output)
@@ -50,7 +50,7 @@ def test_get_stores(self):
5050

5151
def test_get_stores_read_only_store(self):
5252
available_stores = ['cheap', 'fast', 'readonly_store', 'fast-cinder',
53-
'fast-rbd']
53+
'fast-rbd', 'reliable']
5454
req = unit_test_utils.get_fake_request()
5555
output = self.controller.get_stores(req)
5656
self.assertIn('stores', output)
@@ -77,22 +77,36 @@ def test_get_stores_reserved_stores_excluded(self):
7777

7878
def test_get_stores_detail(self):
7979
available_stores = ['cheap', 'fast', 'readonly_store', 'fast-cinder',
80-
'fast-rbd']
81-
available_store_type = ['file', 'file', 'http', 'cinder', 'rbd']
80+
'fast-rbd', 'reliable']
81+
available_store_type = ['file', 'file', 'http', 'cinder', 'rbd',
82+
'swift']
8283
req = unit_test_utils.get_fake_request(roles=['admin'])
8384
output = self.controller.get_stores_detail(req)
85+
self.assertEqual(len(CONF.enabled_backends), len(output['stores']))
8486
self.assertIn('stores', output)
8587
for stores in output['stores']:
8688
self.assertIn('id', stores)
8789
self.assertIn(stores['id'], available_stores)
8890
self.assertIn(stores['type'], available_store_type)
8991
self.assertIsNotNone(stores['properties'])
90-
if stores['id'] == 'fast-rbd':
91-
self.assertIn('chunk_size', stores['properties'])
92-
self.assertIn('pool', stores['properties'])
93-
self.assertIn('thin_provisioning', stores['properties'])
94-
else:
95-
self.assertEqual({}, stores['properties'])
92+
93+
def test_get_stores_detail_properties(self):
94+
store_attributes = {'rbd': ['chunk_size', 'pool', 'thin_provisioning'],
95+
'file': ['data_dir', 'chunk_size',
96+
'thin_provisioning'],
97+
'cinder': ['volume_type', 'use_multipath'],
98+
'swift': ['container',
99+
'large_object_size',
100+
'large_object_chunk_size'],
101+
'http': []}
102+
req = unit_test_utils.get_fake_request(roles=['admin'])
103+
output = self.controller.get_stores_detail(req)
104+
self.assertEqual(len(CONF.enabled_backends), len(output['stores']))
105+
self.assertIn('stores', output)
106+
for store in output['stores']:
107+
actual_attribute = list(store['properties'].keys())
108+
expected_attribute = store_attributes[store['type']]
109+
self.assertEqual(actual_attribute, expected_attribute)
96110

97111
def test_get_stores_detail_non_admin(self):
98112
req = unit_test_utils.get_fake_request()
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
features:
3+
- |
4+
This release brings expansion in the functionality of
5+
stores-detail API. The stores detail API will list the
6+
way each store is configured, whereas previously this
7+
worked only for rbd store. The API remains admin-only
8+
by default as it exposes backend information.

0 commit comments

Comments
 (0)