From b4ae79c2bedca5b1413ed1367f39ace2a97bb972 Mon Sep 17 00:00:00 2001 From: Zhenguo Niu Date: Thu, 15 Jun 2017 11:48:24 +0800 Subject: [PATCH] Adds `disabled` field for flavors. The `disabled` field is intended to be used when phasing out flavors. In this case, a delete wouldn't work because the flavor needs to still be available for live servers using that flavor, but we don't want to allow *new* servers created from that flavor. Partially Implements: bp new-flavor Change-Id: I12713bfe13f506a0d9bb79f733b00bd637ef227b --- mogan/api/controllers/v1/flavors.py | 3 +++ mogan/api/controllers/v1/schemas/flavor.py | 1 + mogan/common/exception.py | 2 +- .../91941bf1ebc9_initial_migration.py | 1 + mogan/db/sqlalchemy/api.py | 8 ++++---- mogan/db/sqlalchemy/models.py | 1 + mogan/engine/api.py | 2 ++ mogan/objects/flavor.py | 1 + mogan/tests/tempest/api/test_flavors.py | 2 ++ mogan/tests/unit/db/utils.py | 1 + mogan/tests/unit/engine/test_engine_api.py | 20 +++++++++++++++++++ mogan/tests/unit/objects/test_objects.py | 2 +- 12 files changed, 38 insertions(+), 6 deletions(-) diff --git a/mogan/api/controllers/v1/flavors.py b/mogan/api/controllers/v1/flavors.py index 783b610e..9ced72f5 100644 --- a/mogan/api/controllers/v1/flavors.py +++ b/mogan/api/controllers/v1/flavors.py @@ -60,6 +60,9 @@ class Flavor(base.APIBase): is_public = types.boolean """Indicates whether the flavor is public.""" + disabled = types.boolean + """Indicates whether the flavor is disabled.""" + extra_specs = {wtypes.text: types.jsontype} """The extra specs of the flavor""" diff --git a/mogan/api/controllers/v1/schemas/flavor.py b/mogan/api/controllers/v1/schemas/flavor.py index 66d8defe..6f0a3fd6 100644 --- a/mogan/api/controllers/v1/schemas/flavor.py +++ b/mogan/api/controllers/v1/schemas/flavor.py @@ -23,6 +23,7 @@ create_flavor = { 'name': parameter_types.name, 'description': parameter_types.description, 'is_public': parameter_types.boolean, + 'disabled': parameter_types.boolean, }, 'required': ['name', 'description'], 'additionalProperties': False, diff --git a/mogan/common/exception.py b/mogan/common/exception.py index 70f970cf..fd55c5fa 100644 --- a/mogan/common/exception.py +++ b/mogan/common/exception.py @@ -153,7 +153,7 @@ class FlavorAlreadyExists(MoganException): class FlavorNotFound(NotFound): - _msg_fmt = _("Flavor %(type_id)s could not be found.") + _msg_fmt = _("Flavor %(flavor_id)s could not be found.") class ServerAlreadyExists(MoganException): diff --git a/mogan/db/sqlalchemy/alembic/versions/91941bf1ebc9_initial_migration.py b/mogan/db/sqlalchemy/alembic/versions/91941bf1ebc9_initial_migration.py index e27304c0..0d44591d 100644 --- a/mogan/db/sqlalchemy/alembic/versions/91941bf1ebc9_initial_migration.py +++ b/mogan/db/sqlalchemy/alembic/versions/91941bf1ebc9_initial_migration.py @@ -37,6 +37,7 @@ def upgrade(): sa.Column('description', sa.String(length=255), nullable=True), sa.Column('extra_specs', sa.Text(), nullable=True), sa.Column('is_public', sa.Boolean(), nullable=False), + sa.Column('disabled', sa.Boolean(), nullable=False), sa.PrimaryKeyConstraint('uuid'), mysql_ENGINE='InnoDB', mysql_DEFAULT_CHARSET='UTF8' diff --git a/mogan/db/sqlalchemy/api.py b/mogan/db/sqlalchemy/api.py index 9c7b586e..b3d5b076 100644 --- a/mogan/db/sqlalchemy/api.py +++ b/mogan/db/sqlalchemy/api.py @@ -136,7 +136,7 @@ class Connection(api.Connection): return query.one() except NoResultFound: raise exception.FlavorNotFound( - type_id=flavor_uuid) + flavor_id=flavor_uuid) def flavor_update(self, context, flavor_id, values): with _session_for_write(): @@ -146,7 +146,7 @@ class Connection(api.Connection): ref = query.with_lockmode('update').one() except NoResultFound: raise exception.FlavorNotFound( - type_id=flavor_id) + flavor_id=flavor_id) ref.update(values) return ref @@ -180,7 +180,7 @@ class Connection(api.Connection): count = query.delete() if count != 1: raise exception.FlavorNotFound( - type_id=flavor_uuid) + flavor_id=flavor_uuid) def server_create(self, context, values): if not values.get('uuid'): @@ -863,7 +863,7 @@ def _type_get_id_from_type_query(context, type_id): def _type_get_id_from_type(context, type_id): result = _type_get_id_from_type_query(context, type_id).first() if not result: - raise exception.FlavorNotFound(type_id=type_id) + raise exception.FlavorNotFound(flavor_id=type_id) return result.uuid diff --git a/mogan/db/sqlalchemy/models.py b/mogan/db/sqlalchemy/models.py index 34bd347a..8f79ad0f 100644 --- a/mogan/db/sqlalchemy/models.py +++ b/mogan/db/sqlalchemy/models.py @@ -180,6 +180,7 @@ class Flavors(Base): description = Column(String(255), nullable=True) extra_specs = Column(db_types.JsonEncodedDict) is_public = Column(Boolean, default=True) + disabled = Column(Boolean, default=False) servers = orm.relationship( Server, backref=orm.backref('flavor', uselist=False), diff --git a/mogan/engine/api.py b/mogan/engine/api.py index f8030c98..e4c16217 100644 --- a/mogan/engine/api.py +++ b/mogan/engine/api.py @@ -81,6 +81,8 @@ class API(object): requested_networks, user_data, key_name, max_count): """Verify all the input parameters""" + if flavor['disabled']: + raise exception.FlavorNotFound(flavor_id=flavor['uuid']) if user_data: l = len(user_data) diff --git a/mogan/objects/flavor.py b/mogan/objects/flavor.py index 75b5f89e..011d4022 100644 --- a/mogan/objects/flavor.py +++ b/mogan/objects/flavor.py @@ -36,6 +36,7 @@ class Flavor(base.MoganObject, object_base.VersionedObjectDictCompat): 'name': object_fields.StringField(nullable=True), 'description': object_fields.StringField(nullable=True), 'is_public': object_fields.BooleanField(), + 'disabled': object_fields.BooleanField(), 'extra_specs': object_fields.FlexibleDictField(nullable=True), 'projects': object_fields.ListOfStringsField(), } diff --git a/mogan/tests/tempest/api/test_flavors.py b/mogan/tests/tempest/api/test_flavors.py index 090d13c2..b23b5020 100644 --- a/mogan/tests/tempest/api/test_flavors.py +++ b/mogan/tests/tempest/api/test_flavors.py @@ -56,6 +56,7 @@ class BaremetalComputeAPITest(base.BaseBaremetalComputeTest): self.assertEqual('mogan flavor description', resp['description']) self.assertEqual(True, resp['is_public']) + self.assertEqual(False, resp['disabled']) self.assertIn('uuid', resp) self.assertIn('extra_specs', resp) self.assertIn('links', resp) @@ -68,6 +69,7 @@ class BaremetalComputeAPITest(base.BaseBaremetalComputeTest): self.assertEqual('mogan flavor description', resp['description']) self.assertEqual(True, resp['is_public']) + self.assertEqual(False, resp['disabled']) self.assertIn('uuid', resp) self.assertIn('extra_specs', resp) self.assertIn('links', resp) diff --git a/mogan/tests/unit/db/utils.py b/mogan/tests/unit/db/utils.py index 6bd62be4..0db02970 100644 --- a/mogan/tests/unit/db/utils.py +++ b/mogan/tests/unit/db/utils.py @@ -200,6 +200,7 @@ def get_test_flavor(**kw): 'name': kw.get('name', 'test'), 'description': kw.get('description', 'test'), 'is_public': kw.get('is_public', 1), + 'disabled': kw.get('disabled', 0), 'updated_at': kw.get('updated_at'), 'created_at': kw.get('created_at'), } diff --git a/mogan/tests/unit/engine/test_engine_api.py b/mogan/tests/unit/engine/test_engine_api.py index eb099e6a..20f1499c 100644 --- a/mogan/tests/unit/engine/test_engine_api.py +++ b/mogan/tests/unit/engine/test_engine_api.py @@ -72,6 +72,26 @@ class ComputeAPIUnitTest(base.DbTestCase): self.assertEqual('test_az', base_opts['availability_zone']) self.assertIsNone(key_pair) + def test__validate_and_build_base_options_flavor_disabled(self): + flavor = self._create_flavor() + flavor.disabled = True + flavor.save() + + self.assertRaises( + exception.FlavorNotFound, + self.engine_api._validate_and_build_base_options, + self.context, + flavor, + 'fake-uuid', + 'fake-name', + 'fake-descritpion', + 'test_az', + {'k1', 'v1'}, + [{'uuid': 'fake'}], + None, + None, + 1) + @mock.patch.object(objects.Server, 'create') def test__provision_servers(self, mock_server_create): mock_server_create.return_value = mock.MagicMock() diff --git a/mogan/tests/unit/objects/test_objects.py b/mogan/tests/unit/objects/test_objects.py index aa9fd5f0..498fbbb5 100644 --- a/mogan/tests/unit/objects/test_objects.py +++ b/mogan/tests/unit/objects/test_objects.py @@ -391,7 +391,7 @@ expected_object_fingerprints = { 'ComputeDiskList': '1.0-33a2e1bb91ad4082f9f63429b77c1244', 'ServerFault': '1.0-74349ff701259e4834b4e9dc2dac1b12', 'ServerFaultList': '1.0-43e8aad0258652921f929934e9e048fd', - 'Flavor': '1.0-2ec0238ca39a3f55201c1eea4e84f280', + 'Flavor': '1.0-1ffc02ecf4565f4b5e88b8369cb70553', 'MyObj': '1.1-aad62eedc5a5cc8bcaf2982c285e753f', 'ServerNic': '1.0-ebbd767c2f6a7f14bd524c6067f2b382', 'ServerNics': '1.0-33a2e1bb91ad4082f9f63429b77c1244',