"group" word should be used instead "pool"
Currently, when creating a new flavor, here it need to set pool group name for the first argument "pool". For making clear between a pool and pool group, So we should replace "pool" word with "pool_group" at all the places. According the discussion in IRC, for giving users time to mirgate, we will support 'pool' and 'pool_group' both in M, and will remove 'pool' in N. APIImpact Change-Id: Id4c976a1fc9cc7fb9b72482b4e3a7f7f80f94492 Closes-Bug: #1504959
This commit is contained in:
parent
a425e1104d
commit
f204280187
@ -38,14 +38,26 @@ patch_pool = {
|
||||
}
|
||||
}
|
||||
|
||||
patch_pool_group = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'pool_group': {
|
||||
'type': 'string'
|
||||
},
|
||||
'additionalProperties': False
|
||||
}
|
||||
}
|
||||
|
||||
create = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'pool_group': patch_pool_group['properties']['pool_group'],
|
||||
'pool': patch_pool['properties']['pool'],
|
||||
'capabilities': patch_capabilities['properties']['capabilities']
|
||||
},
|
||||
# NOTE(flaper87): capabilities need not be present. Storage drivers
|
||||
# must provide reasonable defaults.
|
||||
'required': ['pool'],
|
||||
# NOTE(wanghao): remove pool in Newton release.
|
||||
'oneOf': [{'required': ['pool_group']}, {'required': ['pool']}],
|
||||
'additionalProperties': False
|
||||
}
|
||||
|
@ -149,6 +149,14 @@ class PoolDoesNotExist(DoesNotExist):
|
||||
super(PoolDoesNotExist, self).__init__(pool=pool)
|
||||
|
||||
|
||||
class PoolGroupDoesNotExist(DoesNotExist):
|
||||
|
||||
msg_format = u'Pool group {pool_group} does not exist'
|
||||
|
||||
def __init__(self, pool_group):
|
||||
super(PoolGroupDoesNotExist, self).__init__(pool_group=pool_group)
|
||||
|
||||
|
||||
class FlavorDoesNotExist(DoesNotExist):
|
||||
|
||||
msg_format = u'Flavor {flavor} does not exist'
|
||||
|
@ -16,7 +16,7 @@
|
||||
Schema:
|
||||
'n': name :: six.text_type
|
||||
'p': project :: six.text_type
|
||||
's': storage pool :: six.text_type
|
||||
's': storage pool_group :: six.text_type
|
||||
'c': capabilities :: dict
|
||||
"""
|
||||
|
||||
@ -56,13 +56,13 @@ class FlavorsController(base.FlavorsBase):
|
||||
unique=True)
|
||||
self._col.ensure_index(FLAVORS_STORAGE_POOL_INDEX,
|
||||
background=True,
|
||||
name='flavors_storage_pool_name')
|
||||
name='flavors_storage_pool_group_name')
|
||||
|
||||
self._pools_ctrl = self.driver.pools_controller
|
||||
|
||||
@utils.raises_conn_error
|
||||
def _list_by_pool(self, pool, limit=10, detailed=False):
|
||||
query = {'s': pool}
|
||||
def _list_by_pool_group(self, pool_group, limit=10, detailed=False):
|
||||
query = {'s': pool_group}
|
||||
cursor = self._col.find(query, projection=_field_spec(detailed),
|
||||
limit=limit).sort('n', 1)
|
||||
|
||||
@ -97,16 +97,18 @@ class FlavorsController(base.FlavorsBase):
|
||||
return _normalize(res, detailed)
|
||||
|
||||
@utils.raises_conn_error
|
||||
def create(self, name, pool, project=None, capabilities=None):
|
||||
def create(self, name, pool_group, project=None, capabilities=None):
|
||||
|
||||
# NOTE(flaper87): Check if there are pools in this group.
|
||||
# Should there be a `group_exists` method?
|
||||
if not list(self._pools_ctrl.get_pools_by_group(pool)):
|
||||
raise errors.PoolDoesNotExist(pool)
|
||||
# NOTE(wanghao): Since we didn't pass the group name just pool name,
|
||||
# so we don't need to get the pool by group.
|
||||
if not list(self._pools_ctrl.get_pools_by_group(pool_group)):
|
||||
raise errors.PoolGroupDoesNotExist(pool_group)
|
||||
|
||||
capabilities = {} if capabilities is None else capabilities
|
||||
self._col.update({'n': name, 'p': project},
|
||||
{'$set': {'s': pool, 'c': capabilities}},
|
||||
{'$set': {'s': pool_group, 'c': capabilities}},
|
||||
upsert=True)
|
||||
|
||||
@utils.raises_conn_error
|
||||
@ -114,16 +116,16 @@ class FlavorsController(base.FlavorsBase):
|
||||
return self._col.find_one({'n': name, 'p': project}) is not None
|
||||
|
||||
@utils.raises_conn_error
|
||||
def update(self, name, project=None, pool=None, capabilities=None):
|
||||
def update(self, name, project=None, pool_group=None, capabilities=None):
|
||||
fields = {}
|
||||
|
||||
if capabilities is not None:
|
||||
fields['c'] = capabilities
|
||||
|
||||
if pool is not None:
|
||||
fields['s'] = pool
|
||||
if pool_group is not None:
|
||||
fields['s'] = pool_group
|
||||
|
||||
assert fields, '`pool` or `capabilities` not found in kwargs'
|
||||
assert fields, '`pool_group` or `capabilities` not found in kwargs'
|
||||
res = self._col.update({'n': name, 'p': project},
|
||||
{'$set': fields},
|
||||
upsert=False)
|
||||
@ -144,7 +146,7 @@ class FlavorsController(base.FlavorsBase):
|
||||
def _normalize(flavor, detailed=False):
|
||||
ret = {
|
||||
'name': flavor['n'],
|
||||
'pool': flavor['s'],
|
||||
'pool_group': flavor['s'],
|
||||
}
|
||||
|
||||
if detailed:
|
||||
|
@ -137,7 +137,7 @@ class PoolsController(base.PoolsBase):
|
||||
pool = self.get(name)
|
||||
pools_group = self.get_pools_by_group(pool['group'])
|
||||
flavor_ctl = self.driver.flavors_controller
|
||||
res = list(flavor_ctl._list_by_pool(pool['group']))
|
||||
res = list(flavor_ctl._list_by_pool_group(pool['group']))
|
||||
|
||||
# NOTE(flaper87): If this is the only pool in the
|
||||
# group and it's being used by a flavor, don't allow
|
||||
|
@ -490,7 +490,7 @@ class Catalog(object):
|
||||
if flavor is not None:
|
||||
flavor = self._flavor_ctrl.get(flavor, project=project)
|
||||
pools = self._pools_ctrl.get_pools_by_group(
|
||||
group=flavor['pool'],
|
||||
group=flavor['pool_group'],
|
||||
detailed=True)
|
||||
pool = select.weighted(pools)
|
||||
pool = pool and pool['name'] or None
|
||||
|
@ -71,21 +71,22 @@ class FlavorsController(base.FlavorsBase):
|
||||
return _normalize(flavor, detailed)
|
||||
|
||||
@utils.raises_conn_error
|
||||
def create(self, name, pool, project=None, capabilities=None):
|
||||
def create(self, name, pool_group, project=None, capabilities=None):
|
||||
cap = None if capabilities is None else utils.json_encode(capabilities)
|
||||
|
||||
try:
|
||||
stmt = sa.sql.expression.insert(tables.Flavors).values(
|
||||
name=name, pool=pool, project=project, capabilities=cap
|
||||
name=name, pool_group=pool_group, project=project,
|
||||
capabilities=cap
|
||||
)
|
||||
self.driver.connection.execute(stmt)
|
||||
except sa.exc.IntegrityError:
|
||||
if not self._pools_ctrl.get_pools_by_group(pool):
|
||||
raise errors.PoolDoesNotExist(pool)
|
||||
if not self._pools_ctrl.get_pools_by_group(pool_group):
|
||||
raise errors.PoolGroupDoesNotExist(pool_group)
|
||||
|
||||
# TODO(flaper87): merge update/create into a single
|
||||
# method with introduction of upsert
|
||||
self.update(name, pool=pool,
|
||||
self.update(name, pool_group=pool_group,
|
||||
project=project,
|
||||
capabilities=cap)
|
||||
|
||||
@ -98,16 +99,16 @@ class FlavorsController(base.FlavorsBase):
|
||||
return self.driver.connection.execute(stmt).fetchone() is not None
|
||||
|
||||
@utils.raises_conn_error
|
||||
def update(self, name, project=None, pool=None, capabilities=None):
|
||||
def update(self, name, project=None, pool_group=None, capabilities=None):
|
||||
fields = {}
|
||||
|
||||
if capabilities is not None:
|
||||
fields['capabilities'] = capabilities
|
||||
|
||||
if pool is not None:
|
||||
fields['pool'] = pool
|
||||
if pool_group is not None:
|
||||
fields['pool_group'] = pool_group
|
||||
|
||||
assert fields, '`pool` or `capabilities` not found in kwargs'
|
||||
assert fields, '`pool_group` or `capabilities` not found in kwargs'
|
||||
if 'capabilities' in fields:
|
||||
fields['capabilities'] = utils.json_encode(fields['capabilities'])
|
||||
|
||||
@ -136,7 +137,7 @@ class FlavorsController(base.FlavorsBase):
|
||||
def _normalize(flavor, detailed=False):
|
||||
ret = {
|
||||
'name': flavor[0],
|
||||
'pool': flavor[2],
|
||||
'pool_group': flavor[2],
|
||||
}
|
||||
|
||||
if detailed:
|
||||
|
@ -46,8 +46,8 @@ Pools = sa.Table('Pools', metadata,
|
||||
Flavors = sa.Table('Flavors', metadata,
|
||||
sa.Column('name', sa.String(64), primary_key=True),
|
||||
sa.Column('project', sa.String(64)),
|
||||
sa.Column('pool', sa.ForeignKey('PoolGroup.name',
|
||||
ondelete='CASCADE'),
|
||||
sa.Column('pool_group', sa.ForeignKey('PoolGroup.name',
|
||||
ondelete='CASCADE'),
|
||||
nullable=False),
|
||||
sa.Column('capabilities', sa.Text()))
|
||||
|
||||
|
@ -1424,8 +1424,8 @@ class FlavorsControllerTest(ControllerBaseTest):
|
||||
self.assertIn('name', flavor)
|
||||
self.assertEqual(xname, flavor['name'])
|
||||
self.assertNotIn('project', flavor)
|
||||
self.assertIn('pool', flavor)
|
||||
self.assertEqual(xpool, flavor['pool'])
|
||||
self.assertIn('pool_group', flavor)
|
||||
self.assertEqual(xpool, flavor['pool_group'])
|
||||
|
||||
def test_create_replaces_on_duplicate_insert(self):
|
||||
name = str(uuid.uuid1())
|
||||
@ -1493,18 +1493,18 @@ class FlavorsControllerTest(ControllerBaseTest):
|
||||
detailed=True)
|
||||
|
||||
p = 'olympic'
|
||||
group = 'sports'
|
||||
pool_group = 'sports'
|
||||
self.pools_controller.create(p, 100, 'localhost2',
|
||||
group=group, options={})
|
||||
group=pool_group, options={})
|
||||
self.addCleanup(self.pools_controller.delete, p)
|
||||
|
||||
new_capabilities = {'fifo': False}
|
||||
self.flavors_controller.update(name, project=self.project,
|
||||
pool=group,
|
||||
pool_group=pool_group,
|
||||
capabilities={'fifo': False})
|
||||
res = self.flavors_controller.get(name, project=self.project,
|
||||
detailed=True)
|
||||
self._flavors_expects(res, name, self.project, group)
|
||||
self._flavors_expects(res, name, self.project, pool_group)
|
||||
self.assertEqual(new_capabilities, res['capabilities'])
|
||||
|
||||
def test_delete_works(self):
|
||||
@ -1529,13 +1529,15 @@ class FlavorsControllerTest(ControllerBaseTest):
|
||||
name_gen = lambda i: chr(ord('A') + i)
|
||||
for i in range(15):
|
||||
pool = str(i)
|
||||
pool_group = pool
|
||||
uri = 'localhost:2701' + pool
|
||||
self.pools_controller.create(pool, 100, uri,
|
||||
group=pool, options={})
|
||||
group=pool_group, options={})
|
||||
self.addCleanup(self.pools_controller.delete, pool)
|
||||
|
||||
self.flavors_controller.create(name_gen(i), project=self.project,
|
||||
pool=pool, capabilities={})
|
||||
pool_group=pool_group,
|
||||
capabilities={})
|
||||
|
||||
def get_res(**kwargs):
|
||||
cursor = self.flavors_controller.list(project=self.project,
|
||||
|
@ -24,7 +24,7 @@ from zaqar.tests.unit.transport.wsgi import base
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def flavor(test, name, pool, capabilities={}):
|
||||
def flavor(test, name, pool_group, capabilities={}):
|
||||
"""A context manager for constructing a flavor for use in testing.
|
||||
|
||||
Deletes the flavor after exiting the context.
|
||||
@ -32,27 +32,27 @@ def flavor(test, name, pool, capabilities={}):
|
||||
:param test: Must expose simulate_* methods
|
||||
:param name: Name for this flavor
|
||||
:type name: six.text_type
|
||||
:type pool: six.text_type
|
||||
:type pool_group: six.text_type
|
||||
:type capabilities: dict
|
||||
:returns: (name, uri, capabilities)
|
||||
:rtype: see above
|
||||
|
||||
"""
|
||||
|
||||
doc = {'pool': pool, 'capabilities': capabilities}
|
||||
doc = {'pool_group': pool_group, 'capabilities': capabilities}
|
||||
path = test.url_prefix + '/flavors/' + name
|
||||
|
||||
test.simulate_put(path, body=jsonutils.dumps(doc))
|
||||
|
||||
try:
|
||||
yield name, pool, capabilities
|
||||
yield name, pool_group, capabilities
|
||||
|
||||
finally:
|
||||
test.simulate_delete(path)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def flavors(test, count, pool):
|
||||
def flavors(test, count, pool_group):
|
||||
"""A context manager for constructing flavors for use in testing.
|
||||
|
||||
Deletes the flavors after exiting the context.
|
||||
@ -60,7 +60,7 @@ def flavors(test, count, pool):
|
||||
:param test: Must expose simulate_* methods
|
||||
:param count: Number of pools to create
|
||||
:type count: int
|
||||
:returns: (paths, pool, capabilities)
|
||||
:returns: (paths, pool_group, capabilities)
|
||||
:rtype: ([six.text_type], [six.text_type], [dict])
|
||||
|
||||
"""
|
||||
@ -69,7 +69,7 @@ def flavors(test, count, pool):
|
||||
args = sorted([(base + str(i), {str(i): i}, str(i)) for i in range(count)],
|
||||
key=lambda tup: tup[2])
|
||||
for path, capabilities, _ in args:
|
||||
doc = {'pool': pool, 'capabilities': capabilities}
|
||||
doc = {'pool_group': pool_group, 'capabilities': capabilities}
|
||||
test.simulate_put(path, body=jsonutils.dumps(doc))
|
||||
|
||||
try:
|
||||
@ -99,7 +99,7 @@ class TestFlavorsMongoDB(base.V1_1Base):
|
||||
self.simulate_put(self.pool_path, body=jsonutils.dumps(self.pool_doc))
|
||||
|
||||
self.flavor = 'test-flavor'
|
||||
self.doc = {'capabilities': {}, 'pool': self.pool_group}
|
||||
self.doc = {'capabilities': {}, 'pool_group': self.pool_group}
|
||||
self.flavor_path = self.url_prefix + '/flavors/' + self.flavor
|
||||
self.simulate_put(self.flavor_path, body=jsonutils.dumps(self.doc))
|
||||
self.assertEqual(falcon.HTTP_201, self.srmock.status)
|
||||
@ -114,7 +114,7 @@ class TestFlavorsMongoDB(base.V1_1Base):
|
||||
|
||||
def test_put_flavor_works(self):
|
||||
name = str(uuid.uuid1())
|
||||
with flavor(self, name, self.doc['pool']):
|
||||
with flavor(self, name, self.doc['pool_group']):
|
||||
self.assertEqual(falcon.HTTP_201, self.srmock.status)
|
||||
|
||||
def test_put_raises_if_missing_fields(self):
|
||||
@ -130,13 +130,13 @@ class TestFlavorsMongoDB(base.V1_1Base):
|
||||
def test_put_raises_if_invalid_pool(self, pool):
|
||||
path = self.url_prefix + '/flavors/' + str(uuid.uuid1())
|
||||
self.simulate_put(path,
|
||||
body=jsonutils.dumps({'pool': pool}))
|
||||
body=jsonutils.dumps({'pool_group': pool}))
|
||||
self.assertEqual(falcon.HTTP_400, self.srmock.status)
|
||||
|
||||
@ddt.data(-1, 'wee', [])
|
||||
def test_put_raises_if_invalid_capabilities(self, capabilities):
|
||||
path = self.url_prefix + '/flavors/' + str(uuid.uuid1())
|
||||
doc = {'pool': 'a', 'capabilities': capabilities}
|
||||
doc = {'pool_group': 'a', 'capabilities': capabilities}
|
||||
self.simulate_put(path, body=jsonutils.dumps(doc))
|
||||
self.assertEqual(falcon.HTTP_400, self.srmock.status)
|
||||
|
||||
@ -150,9 +150,9 @@ class TestFlavorsMongoDB(base.V1_1Base):
|
||||
result = self.simulate_get(self.flavor_path)
|
||||
self.assertEqual(falcon.HTTP_200, self.srmock.status)
|
||||
doc = jsonutils.loads(result[0])
|
||||
self.assertEqual(expect['pool'], doc['pool'])
|
||||
self.assertEqual(expect['pool_group'], doc['pool_group'])
|
||||
|
||||
def test_create_flavor_no_pool(self):
|
||||
def test_create_flavor_no_pool_group(self):
|
||||
self.simulate_delete(self.flavor_path)
|
||||
self.assertEqual(falcon.HTTP_204, self.srmock.status)
|
||||
|
||||
@ -164,7 +164,7 @@ class TestFlavorsMongoDB(base.V1_1Base):
|
||||
self.assertEqual(falcon.HTTP_400, self.srmock.status)
|
||||
self.assertEqual(
|
||||
{'description': 'Flavor test-flavor could not be created. '
|
||||
'Pool mypool-group does not exist',
|
||||
'Pool group mypool-group does not exist',
|
||||
'title': 'Unable to create'},
|
||||
jsonutils.loads(resp[0]))
|
||||
|
||||
@ -183,23 +183,23 @@ class TestFlavorsMongoDB(base.V1_1Base):
|
||||
self.assertIn('href', flavor)
|
||||
self.assertIn('name', flavor)
|
||||
self.assertEqual(xhref, flavor['href'])
|
||||
self.assertIn('pool', flavor)
|
||||
self.assertEqual(xpool, flavor['pool'])
|
||||
self.assertIn('pool_group', flavor)
|
||||
self.assertEqual(xpool, flavor['pool_group'])
|
||||
|
||||
def test_get_works(self):
|
||||
result = self.simulate_get(self.flavor_path)
|
||||
self.assertEqual(falcon.HTTP_200, self.srmock.status)
|
||||
pool = jsonutils.loads(result[0])
|
||||
self._flavor_expect(pool, self.flavor_path, self.doc['pool'])
|
||||
flavor = jsonutils.loads(result[0])
|
||||
self._flavor_expect(flavor, self.flavor_path, self.doc['pool_group'])
|
||||
|
||||
def test_detailed_get_works(self):
|
||||
result = self.simulate_get(self.flavor_path,
|
||||
query_string='detailed=True')
|
||||
self.assertEqual(falcon.HTTP_200, self.srmock.status)
|
||||
pool = jsonutils.loads(result[0])
|
||||
self._flavor_expect(pool, self.flavor_path, self.doc['pool'])
|
||||
self.assertIn('capabilities', pool)
|
||||
self.assertEqual({}, pool['capabilities'])
|
||||
flavor = jsonutils.loads(result[0])
|
||||
self._flavor_expect(flavor, self.flavor_path, self.doc['pool_group'])
|
||||
self.assertIn('capabilities', flavor)
|
||||
self.assertEqual({}, flavor['capabilities'])
|
||||
|
||||
def test_patch_raises_if_missing_fields(self):
|
||||
self.simulate_patch(self.flavor_path,
|
||||
@ -214,23 +214,23 @@ class TestFlavorsMongoDB(base.V1_1Base):
|
||||
result = self.simulate_get(self.flavor_path,
|
||||
query_string='detailed=True')
|
||||
self.assertEqual(falcon.HTTP_200, self.srmock.status)
|
||||
pool = jsonutils.loads(result[0])
|
||||
self._flavor_expect(pool, self.flavor_path, doc['pool'])
|
||||
self.assertEqual(doc['capabilities'], pool['capabilities'])
|
||||
flavor = jsonutils.loads(result[0])
|
||||
self._flavor_expect(flavor, self.flavor_path, doc['pool_group'])
|
||||
self.assertEqual(doc['capabilities'], flavor['capabilities'])
|
||||
|
||||
def test_patch_works(self):
|
||||
doc = {'pool': 'my-pool', 'capabilities': {'a': 1}}
|
||||
doc = {'pool_group': 'my-pool-group', 'capabilities': {'a': 1}}
|
||||
self._patch_test(doc)
|
||||
|
||||
def test_patch_works_with_extra_fields(self):
|
||||
doc = {'pool': 'my-pool', 'capabilities': {'a': 1},
|
||||
doc = {'pool_group': 'my-pool-group', 'capabilities': {'a': 1},
|
||||
'location': 100, 'partition': 'taco'}
|
||||
self._patch_test(doc)
|
||||
|
||||
@ddt.data(-1, 2**32+1, [])
|
||||
def test_patch_raises_400_on_invalid_pool(self, pool):
|
||||
def test_patch_raises_400_on_invalid_pool_group(self, pool_group):
|
||||
self.simulate_patch(self.flavor_path,
|
||||
body=jsonutils.dumps({'pool': pool}))
|
||||
body=jsonutils.dumps({'pool_group': pool_group}))
|
||||
self.assertEqual(falcon.HTTP_400, self.srmock.status)
|
||||
|
||||
@ddt.data(-1, 'wee', [])
|
||||
@ -241,7 +241,7 @@ class TestFlavorsMongoDB(base.V1_1Base):
|
||||
|
||||
def test_patch_raises_404_if_flavor_not_found(self):
|
||||
self.simulate_patch(self.url_prefix + '/flavors/notexists',
|
||||
body=jsonutils.dumps({'pool': 'test'}))
|
||||
body=jsonutils.dumps({'pool_group': 'test'}))
|
||||
self.assertEqual(self.srmock.status, falcon.HTTP_404)
|
||||
|
||||
def test_empty_listing(self):
|
||||
@ -261,7 +261,7 @@ class TestFlavorsMongoDB(base.V1_1Base):
|
||||
if marker:
|
||||
query += '&marker={2}'.format(marker)
|
||||
|
||||
with flavors(self, count, self.doc['pool']) as expected:
|
||||
with flavors(self, count, self.doc['pool_group']) as expected:
|
||||
result = self.simulate_get(self.url_prefix + '/flavors',
|
||||
query_string=query)
|
||||
self.assertEqual(falcon.HTTP_200, self.srmock.status)
|
||||
@ -297,7 +297,7 @@ class TestFlavorsMongoDB(base.V1_1Base):
|
||||
for i, s in enumerate(flavors_list + next_flavors_list):
|
||||
expect = expected[i]
|
||||
path, capabilities = expect[:2]
|
||||
self._flavor_expect(s, path, self.doc['pool'])
|
||||
self._flavor_expect(s, path, self.doc['pool_group'])
|
||||
if detailed:
|
||||
self.assertIn('capabilities', s)
|
||||
self.assertEqual(s['capabilities'], capabilities)
|
||||
@ -317,14 +317,14 @@ class TestFlavorsMongoDB(base.V1_1Base):
|
||||
def test_listing_marker_is_respected(self):
|
||||
self.simulate_delete(self.flavor_path)
|
||||
|
||||
with flavors(self, 10, self.doc['pool']) as expected:
|
||||
with flavors(self, 10, self.doc['pool_group']) as expected:
|
||||
result = self.simulate_get(self.url_prefix + '/flavors',
|
||||
query_string='marker=3')
|
||||
self.assertEqual(falcon.HTTP_200, self.srmock.status)
|
||||
flavor_list = jsonutils.loads(result[0])['flavors']
|
||||
self.assertEqual(6, len(flavor_list))
|
||||
path, capabilities = expected[4][:2]
|
||||
self._flavor_expect(flavor_list[0], path, self.doc['pool'])
|
||||
self._flavor_expect(flavor_list[0], path, self.doc['pool_group'])
|
||||
|
||||
def test_queue_create_works(self):
|
||||
metadata = {'_flavor': self.flavor}
|
||||
|
@ -24,7 +24,7 @@ from zaqar.tests.unit.transport.wsgi import base
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def flavor(test, name, pool):
|
||||
def flavor(test, name, pool_group):
|
||||
"""A context manager for constructing a flavor for use in testing.
|
||||
|
||||
Deletes the flavor after exiting the context.
|
||||
@ -38,20 +38,20 @@ def flavor(test, name, pool):
|
||||
|
||||
"""
|
||||
|
||||
doc = {'pool': pool}
|
||||
doc = {'pool_group': pool_group}
|
||||
path = test.url_prefix + '/flavors/' + name
|
||||
|
||||
test.simulate_put(path, body=jsonutils.dumps(doc))
|
||||
|
||||
try:
|
||||
yield name, pool
|
||||
yield name, pool_group
|
||||
|
||||
finally:
|
||||
test.simulate_delete(path)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def flavors(test, count, pool):
|
||||
def flavors(test, count, pool_group):
|
||||
"""A context manager for constructing flavors for use in testing.
|
||||
|
||||
Deletes the flavors after exiting the context.
|
||||
@ -59,7 +59,7 @@ def flavors(test, count, pool):
|
||||
:param test: Must expose simulate_* methods
|
||||
:param count: Number of pools to create
|
||||
:type count: int
|
||||
:returns: (paths, pool, capabilities)
|
||||
:returns: (paths, pool_group, capabilities)
|
||||
:rtype: ([six.text_type], [six.text_type], [dict])
|
||||
|
||||
"""
|
||||
@ -68,7 +68,7 @@ def flavors(test, count, pool):
|
||||
args = sorted([(base + str(i), str(i)) for i in range(count)],
|
||||
key=lambda tup: tup[1])
|
||||
for path, _ in args:
|
||||
doc = {'pool': pool}
|
||||
doc = {'pool_group': pool_group}
|
||||
test.simulate_put(path, body=jsonutils.dumps(doc))
|
||||
|
||||
try:
|
||||
@ -98,7 +98,7 @@ class TestFlavorsMongoDB(base.V2Base):
|
||||
self.simulate_put(self.pool_path, body=jsonutils.dumps(self.pool_doc))
|
||||
|
||||
self.flavor = 'test-flavor'
|
||||
self.doc = {'capabilities': {}, 'pool': self.pool_group}
|
||||
self.doc = {'capabilities': {}, 'pool_group': self.pool_group}
|
||||
self.flavor_path = self.url_prefix + '/flavors/' + self.flavor
|
||||
self.simulate_put(self.flavor_path, body=jsonutils.dumps(self.doc))
|
||||
self.assertEqual(falcon.HTTP_201, self.srmock.status)
|
||||
@ -113,7 +113,7 @@ class TestFlavorsMongoDB(base.V2Base):
|
||||
|
||||
def test_put_flavor_works(self):
|
||||
name = str(uuid.uuid1())
|
||||
with flavor(self, name, self.doc['pool']):
|
||||
with flavor(self, name, self.doc['pool_group']):
|
||||
self.assertEqual(falcon.HTTP_201, self.srmock.status)
|
||||
|
||||
def test_put_raises_if_missing_fields(self):
|
||||
@ -126,16 +126,16 @@ class TestFlavorsMongoDB(base.V2Base):
|
||||
self.assertEqual(falcon.HTTP_400, self.srmock.status)
|
||||
|
||||
@ddt.data(1, 2**32+1, [])
|
||||
def test_put_raises_if_invalid_pool(self, pool):
|
||||
def test_put_raises_if_invalid_pool(self, pool_group):
|
||||
path = self.url_prefix + '/flavors/' + str(uuid.uuid1())
|
||||
self.simulate_put(path,
|
||||
body=jsonutils.dumps({'pool': pool}))
|
||||
body=jsonutils.dumps({'pool_group': pool_group}))
|
||||
self.assertEqual(falcon.HTTP_400, self.srmock.status)
|
||||
|
||||
@ddt.data(-1, 'wee', [])
|
||||
def test_put_raises_if_invalid_capabilities(self, capabilities):
|
||||
path = self.url_prefix + '/flavors/' + str(uuid.uuid1())
|
||||
doc = {'pool': 'a', 'capabilities': capabilities}
|
||||
doc = {'pool_group': 'a', 'capabilities': capabilities}
|
||||
self.simulate_put(path, body=jsonutils.dumps(doc))
|
||||
self.assertEqual(falcon.HTTP_400, self.srmock.status)
|
||||
|
||||
@ -149,9 +149,9 @@ class TestFlavorsMongoDB(base.V2Base):
|
||||
result = self.simulate_get(self.flavor_path)
|
||||
self.assertEqual(falcon.HTTP_200, self.srmock.status)
|
||||
doc = jsonutils.loads(result[0])
|
||||
self.assertEqual(expect['pool'], doc['pool'])
|
||||
self.assertEqual(expect['pool_group'], doc['pool_group'])
|
||||
|
||||
def test_create_flavor_no_pool(self):
|
||||
def test_create_flavor_no_pool_group(self):
|
||||
self.simulate_delete(self.flavor_path)
|
||||
self.assertEqual(falcon.HTTP_204, self.srmock.status)
|
||||
|
||||
@ -163,7 +163,7 @@ class TestFlavorsMongoDB(base.V2Base):
|
||||
self.assertEqual(falcon.HTTP_400, self.srmock.status)
|
||||
self.assertEqual(
|
||||
{'description': 'Flavor test-flavor could not be created. '
|
||||
'Pool mypool-group does not exist',
|
||||
'Pool group mypool-group does not exist',
|
||||
'title': 'Unable to create'},
|
||||
jsonutils.loads(resp[0]))
|
||||
|
||||
@ -178,22 +178,22 @@ class TestFlavorsMongoDB(base.V2Base):
|
||||
self.simulate_get(self.url_prefix + '/flavors/nonexisting')
|
||||
self.assertEqual(falcon.HTTP_404, self.srmock.status)
|
||||
|
||||
def _flavor_expect(self, flavor, xhref, xpool):
|
||||
def _flavor_expect(self, flavor, xhref, xpool_group):
|
||||
self.assertIn('href', flavor)
|
||||
self.assertIn('name', flavor)
|
||||
self.assertEqual(xhref, flavor['href'])
|
||||
self.assertIn('pool', flavor)
|
||||
self.assertEqual(xpool, flavor['pool'])
|
||||
self.assertIn('pool_group', flavor)
|
||||
self.assertEqual(xpool_group, flavor['pool_group'])
|
||||
|
||||
def test_get_works(self):
|
||||
result = self.simulate_get(self.flavor_path)
|
||||
self.assertEqual(falcon.HTTP_200, self.srmock.status)
|
||||
pool = jsonutils.loads(result[0])
|
||||
self._flavor_expect(pool, self.flavor_path, self.doc['pool'])
|
||||
flavor = jsonutils.loads(result[0])
|
||||
self._flavor_expect(flavor, self.flavor_path, self.doc['pool_group'])
|
||||
|
||||
store_caps = ['FIFO', 'CLAIMS', 'DURABILITY',
|
||||
'AOD', 'HIGH_THROUGHPUT']
|
||||
self.assertEqual(store_caps, pool['capabilities'])
|
||||
self.assertEqual(store_caps, flavor['capabilities'])
|
||||
|
||||
def test_patch_raises_if_missing_fields(self):
|
||||
self.simulate_patch(self.flavor_path,
|
||||
@ -205,28 +205,29 @@ class TestFlavorsMongoDB(base.V2Base):
|
||||
body=jsonutils.dumps(doc))
|
||||
self.assertEqual(falcon.HTTP_200, self.srmock.status)
|
||||
updated_flavor = jsonutils.loads(result[0])
|
||||
self._flavor_expect(updated_flavor, self.flavor_path, doc['pool'])
|
||||
self._flavor_expect(updated_flavor, self.flavor_path,
|
||||
doc['pool_group'])
|
||||
self.assertEqual(doc['capabilities'], updated_flavor['capabilities'])
|
||||
|
||||
result = self.simulate_get(self.flavor_path)
|
||||
self.assertEqual(falcon.HTTP_200, self.srmock.status)
|
||||
flavor = jsonutils.loads(result[0])
|
||||
self._flavor_expect(flavor, self.flavor_path, doc['pool'])
|
||||
self._flavor_expect(flavor, self.flavor_path, doc['pool_group'])
|
||||
self.assertEqual(doc['capabilities'], flavor['capabilities'])
|
||||
|
||||
def test_patch_works(self):
|
||||
doc = {'pool': 'mypool', 'capabilities': []}
|
||||
doc = {'pool_group': 'mypoolgroup', 'capabilities': []}
|
||||
self._patch_test(doc)
|
||||
|
||||
def test_patch_works_with_extra_fields(self):
|
||||
doc = {'pool': 'mypool', 'capabilities': [],
|
||||
doc = {'pool_group': 'mypoolgroup', 'capabilities': [],
|
||||
'location': 100, 'partition': 'taco'}
|
||||
self._patch_test(doc)
|
||||
|
||||
@ddt.data(-1, 2**32+1, [])
|
||||
def test_patch_raises_400_on_invalid_pool(self, pool):
|
||||
def test_patch_raises_400_on_invalid_pool_group(self, pool_group):
|
||||
self.simulate_patch(self.flavor_path,
|
||||
body=jsonutils.dumps({'pool': pool}))
|
||||
body=jsonutils.dumps({'pool_group': pool_group}))
|
||||
self.assertEqual(falcon.HTTP_400, self.srmock.status)
|
||||
|
||||
@ddt.data(-1, 'wee', [])
|
||||
@ -237,7 +238,7 @@ class TestFlavorsMongoDB(base.V2Base):
|
||||
|
||||
def test_patch_raises_404_if_flavor_not_found(self):
|
||||
self.simulate_patch(self.url_prefix + '/flavors/notexists',
|
||||
body=jsonutils.dumps({'pool': 'test'}))
|
||||
body=jsonutils.dumps({'pool_group': 'test'}))
|
||||
self.assertEqual(falcon.HTTP_404, self.srmock.status)
|
||||
|
||||
def test_empty_listing(self):
|
||||
@ -257,7 +258,7 @@ class TestFlavorsMongoDB(base.V2Base):
|
||||
if marker:
|
||||
query += '&marker={2}'.format(marker)
|
||||
|
||||
with flavors(self, count, self.doc['pool']) as expected:
|
||||
with flavors(self, count, self.doc['pool_group']) as expected:
|
||||
result = self.simulate_get(self.url_prefix + '/flavors',
|
||||
query_string=query)
|
||||
self.assertEqual(falcon.HTTP_200, self.srmock.status)
|
||||
@ -295,7 +296,7 @@ class TestFlavorsMongoDB(base.V2Base):
|
||||
path = expect[0]
|
||||
capabilities = ['FIFO', 'CLAIMS', 'DURABILITY',
|
||||
'AOD', 'HIGH_THROUGHPUT']
|
||||
self._flavor_expect(s, path, self.doc['pool'])
|
||||
self._flavor_expect(s, path, self.doc['pool_group'])
|
||||
if detailed:
|
||||
self.assertIn('capabilities', s)
|
||||
self.assertEqual(s['capabilities'], capabilities)
|
||||
@ -315,14 +316,14 @@ class TestFlavorsMongoDB(base.V2Base):
|
||||
def test_listing_marker_is_respected(self):
|
||||
self.simulate_delete(self.flavor_path)
|
||||
|
||||
with flavors(self, 10, self.doc['pool']) as expected:
|
||||
with flavors(self, 10, self.doc['pool_group']) as expected:
|
||||
result = self.simulate_get(self.url_prefix + '/flavors',
|
||||
query_string='marker=3')
|
||||
self.assertEqual(falcon.HTTP_200, self.srmock.status)
|
||||
flavor_list = jsonutils.loads(result[0])['flavors']
|
||||
self.assertEqual(6, len(flavor_list))
|
||||
path, capabilities = expected[4][:2]
|
||||
self._flavor_expect(flavor_list[0], path, self.doc['pool'])
|
||||
self._flavor_expect(flavor_list[0], path, self.doc['pool_group'])
|
||||
|
||||
def test_queue_create_works(self):
|
||||
metadata = {'_flavor': self.flavor}
|
||||
|
@ -73,6 +73,8 @@ class Listing(object):
|
||||
|
||||
for entry in flavors:
|
||||
entry['href'] = request.path + '/' + entry['name']
|
||||
# NOTE(wanghao): remove this in Newton.
|
||||
entry['pool'] = entry['pool_group']
|
||||
|
||||
results['links'] = [
|
||||
{
|
||||
@ -98,6 +100,8 @@ class Resource(object):
|
||||
validator_type = jsonschema.Draft4Validator
|
||||
self._validators = {
|
||||
'create': validator_type(schema.create),
|
||||
'pool_group': validator_type(schema.patch_pool_group),
|
||||
# NOTE(wanghao): Remove this in Newton.
|
||||
'pool': validator_type(schema.patch_pool),
|
||||
'capabilities': validator_type(schema.patch_capabilities),
|
||||
}
|
||||
@ -107,7 +111,7 @@ class Resource(object):
|
||||
|
||||
::
|
||||
|
||||
{"pool": "", capabilities: {...}}
|
||||
{"pool_group": "", capabilities: {...}}
|
||||
|
||||
:returns: HTTP | [200, 404]
|
||||
"""
|
||||
@ -120,7 +124,8 @@ class Resource(object):
|
||||
data = self._ctrl.get(flavor,
|
||||
project=project_id,
|
||||
detailed=detailed)
|
||||
|
||||
# NOTE(wanghao): remove this in Newton.
|
||||
data['pool'] = data['pool_group']
|
||||
except errors.FlavorDoesNotExist as ex:
|
||||
LOG.debug(ex)
|
||||
raise falcon.HTTPNotFound()
|
||||
@ -134,7 +139,7 @@ class Resource(object):
|
||||
|
||||
::
|
||||
|
||||
{"pool": "my-pool", "capabilities": {}}
|
||||
{"pool_group": "my-pool-group", "capabilities": {}}
|
||||
|
||||
A capabilities object may also be provided.
|
||||
|
||||
@ -145,19 +150,19 @@ class Resource(object):
|
||||
|
||||
data = wsgi_utils.load(request)
|
||||
wsgi_utils.validate(self._validators['create'], data)
|
||||
|
||||
pool_group = data.get('pool_group') or data.get('pool')
|
||||
try:
|
||||
self._ctrl.create(flavor,
|
||||
pool=data['pool'],
|
||||
pool_group=pool_group,
|
||||
project=project_id,
|
||||
capabilities=data['capabilities'])
|
||||
response.status = falcon.HTTP_201
|
||||
response.location = request.path
|
||||
except errors.PoolDoesNotExist as ex:
|
||||
except errors.PoolGroupDoesNotExist as ex:
|
||||
LOG.exception(ex)
|
||||
description = (_(u'Flavor %(flavor)s could not be created. '
|
||||
u'Pool %(pool)s does not exist') %
|
||||
dict(flavor=flavor, pool=data['pool']))
|
||||
u'Pool group %(pool_group)s does not exist') %
|
||||
dict(flavor=flavor, pool_group=pool_group))
|
||||
raise falcon.HTTPBadRequest(_('Unable to create'), description)
|
||||
|
||||
def on_delete(self, request, response, project_id, flavor):
|
||||
@ -174,7 +179,7 @@ class Resource(object):
|
||||
"""Allows one to update a flavors's pool and/or capabilities.
|
||||
|
||||
This method expects the user to submit a JSON object
|
||||
containing at least one of: 'pool', 'capabilities'. If
|
||||
containing at least one of: 'pool_group', 'capabilities'. If
|
||||
none are found, the request is flagged as bad. There is also
|
||||
strict format checking through the use of
|
||||
jsonschema. Appropriate errors are returned in each case for
|
||||
@ -186,11 +191,11 @@ class Resource(object):
|
||||
LOG.debug(u'PATCH flavor - name: %s', flavor)
|
||||
data = wsgi_utils.load(request)
|
||||
|
||||
EXPECT = ('pool', 'capabilities')
|
||||
EXPECT = ('pool_group', 'capabilities', 'pool')
|
||||
if not any([(field in data) for field in EXPECT]):
|
||||
LOG.debug(u'PATCH flavor, bad params')
|
||||
raise wsgi_errors.HTTPBadRequestBody(
|
||||
'One of `pool` or `capabilities` needs '
|
||||
'One of `pool_group` or `capabilities` or `pool` needs '
|
||||
'to be specified'
|
||||
)
|
||||
|
||||
@ -199,6 +204,10 @@ class Resource(object):
|
||||
|
||||
fields = common_utils.fields(data, EXPECT,
|
||||
pred=lambda v: v is not None)
|
||||
# NOTE(wanghao): remove this in Newton.
|
||||
if fields.get('pool') and fields.get('pool_group') is None:
|
||||
fields['pool_group'] = fields.get('pool')
|
||||
fields.pop('pool')
|
||||
|
||||
try:
|
||||
self._ctrl.update(flavor, project=project_id, **fields)
|
||||
|
@ -47,7 +47,7 @@ class Listing(object):
|
||||
|
||||
{
|
||||
"flavors": [
|
||||
{"href": "", "capabilities": {}, "pool": ""},
|
||||
{"href": "", "capabilities": {}, "pool_group": ""},
|
||||
...
|
||||
],
|
||||
"links": [
|
||||
@ -76,8 +76,11 @@ class Listing(object):
|
||||
|
||||
for entry in flavors:
|
||||
entry['href'] = request.path + '/' + entry['name']
|
||||
pool_group = entry['pool_group']
|
||||
# NOTE(wanghao): remove this in Newton.
|
||||
entry['pool'] = entry['pool_group']
|
||||
if detailed:
|
||||
caps = self._pools_ctrl.capabilities(group=entry['pool'])
|
||||
caps = self._pools_ctrl.capabilities(group=pool_group)
|
||||
entry['capabilities'] = [str(cap).split('.')[-1]
|
||||
for cap in caps]
|
||||
|
||||
@ -111,6 +114,8 @@ class Resource(object):
|
||||
validator_type = jsonschema.Draft4Validator
|
||||
self._validators = {
|
||||
'create': validator_type(schema.create),
|
||||
'pool_group': validator_type(schema.patch_pool_group),
|
||||
# NOTE(wanghao): Remove this in Newton.
|
||||
'pool': validator_type(schema.patch_pool),
|
||||
'capabilities': validator_type(schema.patch_capabilities),
|
||||
}
|
||||
@ -131,7 +136,10 @@ class Resource(object):
|
||||
|
||||
try:
|
||||
data = self._ctrl.get(flavor, project=project_id)
|
||||
capabilities = self._pools_ctrl.capabilities(group=data['pool'])
|
||||
pool_group = data['pool_group']
|
||||
# NOTE(wanghao): remove this in Newton.
|
||||
data['pool'] = data['pool_group']
|
||||
capabilities = self._pools_ctrl.capabilities(group=pool_group)
|
||||
data['capabilities'] = [str(cap).split('.')[-1]
|
||||
for cap in capabilities]
|
||||
|
||||
@ -149,7 +157,7 @@ class Resource(object):
|
||||
|
||||
::
|
||||
|
||||
{"pool": "my-pool", "capabilities": {}}
|
||||
{"pool_group": "my-pool-group", "capabilities": {}}
|
||||
|
||||
A capabilities object may also be provided.
|
||||
|
||||
@ -160,18 +168,18 @@ class Resource(object):
|
||||
|
||||
data = wsgi_utils.load(request)
|
||||
wsgi_utils.validate(self._validators['create'], data)
|
||||
|
||||
pool_group = data.get('pool_group') or data.get('pool')
|
||||
try:
|
||||
self._ctrl.create(flavor,
|
||||
pool=data['pool'],
|
||||
pool_group=pool_group,
|
||||
project=project_id)
|
||||
response.status = falcon.HTTP_201
|
||||
response.location = request.path
|
||||
except errors.PoolDoesNotExist as ex:
|
||||
except errors.PoolGroupDoesNotExist as ex:
|
||||
LOG.exception(ex)
|
||||
description = (_(u'Flavor %(flavor)s could not be created. '
|
||||
u'Pool %(pool)s does not exist') %
|
||||
dict(flavor=flavor, pool=data['pool']))
|
||||
u'Pool group %(pool_group)s does not exist') %
|
||||
dict(flavor=flavor, pool_group=pool_group))
|
||||
raise falcon.HTTPBadRequest(_('Unable to create'), description)
|
||||
|
||||
@acl.enforce("flavors:delete")
|
||||
@ -187,11 +195,11 @@ class Resource(object):
|
||||
|
||||
@acl.enforce("flavors:update")
|
||||
def on_patch(self, request, response, project_id, flavor):
|
||||
"""Allows one to update a flavors's pool.
|
||||
"""Allows one to update a flavors's pool_group.
|
||||
|
||||
This method expects the user to submit a JSON object
|
||||
containing 'pool'. If none is found, the request is flagged as bad.
|
||||
There is also strict format checking through the use of
|
||||
containing 'pool_group'. If none is found, the request is flagged
|
||||
as bad. There is also strict format checking through the use of
|
||||
jsonschema. Appropriate errors are returned in each case for
|
||||
badly formatted input.
|
||||
|
||||
@ -201,11 +209,11 @@ class Resource(object):
|
||||
LOG.debug(u'PATCH flavor - name: %s', flavor)
|
||||
data = wsgi_utils.load(request)
|
||||
|
||||
EXPECT = ('pool', )
|
||||
EXPECT = ('pool_group', 'pool')
|
||||
if not any([(field in data) for field in EXPECT]):
|
||||
LOG.debug(u'PATCH flavor, bad params')
|
||||
raise wsgi_errors.HTTPBadRequestBody(
|
||||
'`pool` needs to be specified'
|
||||
'`pool_group` or `pool` needs to be specified'
|
||||
)
|
||||
|
||||
for field in EXPECT:
|
||||
@ -213,12 +221,17 @@ class Resource(object):
|
||||
|
||||
fields = common_utils.fields(data, EXPECT,
|
||||
pred=lambda v: v is not None)
|
||||
# NOTE(wanghao): remove this in Newton.
|
||||
if fields.get('pool') and fields.get('pool_group') is None:
|
||||
fields['pool_group'] = fields.get('pool')
|
||||
fields.pop('pool')
|
||||
|
||||
resp_data = None
|
||||
try:
|
||||
self._ctrl.update(flavor, project=project_id, **fields)
|
||||
resp_data = self._ctrl.get(flavor, project=project_id)
|
||||
capabilities = self._pools_ctrl.capabilities(
|
||||
group=resp_data['pool'])
|
||||
group=resp_data['pool_group'])
|
||||
resp_data['capabilities'] = [str(cap).split('.')[-1]
|
||||
for cap in capabilities]
|
||||
except errors.FlavorDoesNotExist as ex:
|
||||
|
Loading…
Reference in New Issue
Block a user