From f204280187faa4b2378ad2031d7b999da696e57e Mon Sep 17 00:00:00 2001 From: wanghao Date: Fri, 29 Jan 2016 15:38:28 +0800 Subject: [PATCH] "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 --- zaqar/common/api/schemas/flavors.py | 14 +++- zaqar/storage/errors.py | 8 +++ zaqar/storage/mongodb/flavors.py | 28 ++++---- zaqar/storage/mongodb/pools.py | 2 +- zaqar/storage/pooling.py | 2 +- zaqar/storage/sqlalchemy/flavors.py | 21 +++--- zaqar/storage/sqlalchemy/tables.py | 4 +- zaqar/tests/unit/storage/base.py | 18 ++--- .../unit/transport/wsgi/v1_1/test_flavors.py | 68 +++++++++---------- .../unit/transport/wsgi/v2_0/test_flavors.py | 63 ++++++++--------- zaqar/transport/wsgi/v1_1/flavors.py | 31 ++++++--- zaqar/transport/wsgi/v2_0/flavors.py | 43 ++++++++---- 12 files changed, 175 insertions(+), 127 deletions(-) diff --git a/zaqar/common/api/schemas/flavors.py b/zaqar/common/api/schemas/flavors.py index ea254def..254d936a 100644 --- a/zaqar/common/api/schemas/flavors.py +++ b/zaqar/common/api/schemas/flavors.py @@ -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 } diff --git a/zaqar/storage/errors.py b/zaqar/storage/errors.py index 4e056033..cb24a898 100644 --- a/zaqar/storage/errors.py +++ b/zaqar/storage/errors.py @@ -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' diff --git a/zaqar/storage/mongodb/flavors.py b/zaqar/storage/mongodb/flavors.py index 0da9497c..4ace5da8 100644 --- a/zaqar/storage/mongodb/flavors.py +++ b/zaqar/storage/mongodb/flavors.py @@ -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: diff --git a/zaqar/storage/mongodb/pools.py b/zaqar/storage/mongodb/pools.py index 31aa687a..28d76d0b 100644 --- a/zaqar/storage/mongodb/pools.py +++ b/zaqar/storage/mongodb/pools.py @@ -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 diff --git a/zaqar/storage/pooling.py b/zaqar/storage/pooling.py index 4ad7bd48..a48f7283 100644 --- a/zaqar/storage/pooling.py +++ b/zaqar/storage/pooling.py @@ -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 diff --git a/zaqar/storage/sqlalchemy/flavors.py b/zaqar/storage/sqlalchemy/flavors.py index 68def242..865fc2d7 100644 --- a/zaqar/storage/sqlalchemy/flavors.py +++ b/zaqar/storage/sqlalchemy/flavors.py @@ -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: diff --git a/zaqar/storage/sqlalchemy/tables.py b/zaqar/storage/sqlalchemy/tables.py index 06810fdf..60d76b9a 100644 --- a/zaqar/storage/sqlalchemy/tables.py +++ b/zaqar/storage/sqlalchemy/tables.py @@ -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())) diff --git a/zaqar/tests/unit/storage/base.py b/zaqar/tests/unit/storage/base.py index 77306ef8..a1822f20 100644 --- a/zaqar/tests/unit/storage/base.py +++ b/zaqar/tests/unit/storage/base.py @@ -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, diff --git a/zaqar/tests/unit/transport/wsgi/v1_1/test_flavors.py b/zaqar/tests/unit/transport/wsgi/v1_1/test_flavors.py index 955648ec..4aa8ed68 100644 --- a/zaqar/tests/unit/transport/wsgi/v1_1/test_flavors.py +++ b/zaqar/tests/unit/transport/wsgi/v1_1/test_flavors.py @@ -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} diff --git a/zaqar/tests/unit/transport/wsgi/v2_0/test_flavors.py b/zaqar/tests/unit/transport/wsgi/v2_0/test_flavors.py index ca670185..94f1f7da 100644 --- a/zaqar/tests/unit/transport/wsgi/v2_0/test_flavors.py +++ b/zaqar/tests/unit/transport/wsgi/v2_0/test_flavors.py @@ -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} diff --git a/zaqar/transport/wsgi/v1_1/flavors.py b/zaqar/transport/wsgi/v1_1/flavors.py index 7d541e40..ab2a003c 100644 --- a/zaqar/transport/wsgi/v1_1/flavors.py +++ b/zaqar/transport/wsgi/v1_1/flavors.py @@ -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) diff --git a/zaqar/transport/wsgi/v2_0/flavors.py b/zaqar/transport/wsgi/v2_0/flavors.py index 25370b2c..e65a6ec4 100644 --- a/zaqar/transport/wsgi/v2_0/flavors.py +++ b/zaqar/transport/wsgi/v2_0/flavors.py @@ -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: