diff --git a/cloudkitty/api/v1/controllers/rating.py b/cloudkitty/api/v1/controllers/rating.py index b46458ef..773b5cf6 100644 --- a/cloudkitty/api/v1/controllers/rating.py +++ b/cloudkitty/api/v1/controllers/rating.py @@ -53,8 +53,7 @@ class ModulesController(rest.RestController): modules_list.append(rating_models.CloudkittyModule(**infos)) return rating_models.CloudkittyModuleCollection( - modules=modules_list - ) + modules=modules_list) @wsme_pecan.wsexpose(rating_models.CloudkittyModule, wtypes.text) def get_one(self, module_id): @@ -65,7 +64,7 @@ class ModulesController(rest.RestController): try: module = self.extensions[module_id] except KeyError: - pecan.abort(404) + pecan.abort(404, 'Module not found.') infos = module.obj.module_info.copy() infos['module_id'] = infos.pop('name') return rating_models.CloudkittyModule(**infos) @@ -75,16 +74,19 @@ class ModulesController(rest.RestController): body=rating_models.CloudkittyModule, status_code=302) def put(self, module_id, module): - """Change the state of a module (enabled/disabled) + """Change the state and priority of a module. :param module_id: name of the module to modify :param module: CloudKittyModule object describing the new desired state - ## :return: CloudKittyModule object describing the desired state """ try: - self.extensions[module_id].obj.set_state(module.enabled) + ext = self.extensions[module_id].obj except KeyError: - pecan.abort(404) + pecan.abort(404, 'Module not found.') + if ext.enabled != module.enabled: + ext.set_state(module.enabled) + if ext.priority != module.priority: + ext.set_priority(module.priority) pecan.response.location = pecan.request.path diff --git a/cloudkitty/api/v1/datamodels/rating.py b/cloudkitty/api/v1/datamodels/rating.py index 0e5d6866..f7517002 100644 --- a/cloudkitty/api/v1/datamodels/rating.py +++ b/cloudkitty/api/v1/datamodels/rating.py @@ -85,12 +85,16 @@ class CloudkittyModule(wtypes.Base): hot_config = wtypes.wsattr(bool, default=False, name='hot-config') """On-the-fly configuration support.""" + priority = wtypes.wsattr(int, default=1) + """Priority of the extension.""" + @classmethod def sample(cls): sample = cls(name='example', description='Sample extension.', enabled=True, - hot_config=False) + hot_config=False, + priority=2) return sample diff --git a/cloudkitty/db/api.py b/cloudkitty/db/api.py index 4519e6c8..c419078d 100644 --- a/cloudkitty/db/api.py +++ b/cloudkitty/db/api.py @@ -84,13 +84,34 @@ class ModuleEnableState(object): @abc.abstractmethod def set_state(self, name, state): - """Retrieve the module state. + """Set the module state. :param name: Name of the module :param value: State of the module """ +@six.add_metaclass(abc.ABCMeta) +class ModuleInfo(ModuleEnableState): + """Base class for module info management.""" + + @abc.abstractmethod + def get_priority(self, name): + """Retrieve the module priority. + + :param name: Name of the module + :return int: Priority of the module + """ + + @abc.abstractmethod + def set_priority(self, name, priority): + """Set the module state. + + :param name: Name of the module + :param priority: New priority of the module + """ + + class NoSuchMapping(Exception): """Raised when the mapping doesn't exist.""" diff --git a/cloudkitty/db/sqlalchemy/alembic/versions/385e33fef139_added_priority_to_modules_state.py b/cloudkitty/db/sqlalchemy/alembic/versions/385e33fef139_added_priority_to_modules_state.py new file mode 100644 index 00000000..a0dba3f3 --- /dev/null +++ b/cloudkitty/db/sqlalchemy/alembic/versions/385e33fef139_added_priority_to_modules_state.py @@ -0,0 +1,22 @@ +"""Added priority to modules_state. + +Revision ID: 385e33fef139 +Revises: 2ac2217dcbd9 +Create Date: 2015-03-17 17:50:15.229896 + +""" + +# revision identifiers, used by Alembic. +revision = '385e33fef139' +down_revision = '2ac2217dcbd9' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + op.add_column('modules_state', sa.Column('priority', sa.Integer(), nullable=True)) + + +def downgrade(): + op.drop_column('modules_state', 'priority') diff --git a/cloudkitty/db/sqlalchemy/api.py b/cloudkitty/db/sqlalchemy/api.py index 0b9d6a7b..27f3138e 100644 --- a/cloudkitty/db/sqlalchemy/api.py +++ b/cloudkitty/db/sqlalchemy/api.py @@ -39,15 +39,11 @@ class State(api.State): def get_state(self, name): session = db.get_session() - try: - return utils.model_query( - models.StateInfo, - session - ).filter_by( - name=name, - ).value('state') - except sqlalchemy.orm.exc.NoResultFound: - return None + q = utils.model_query( + models.StateInfo, + session) + q = q.filter(models.StateInfo.name == name) + return q.value(models.StateInfo.state) def set_state(self, name, state): session = db.get_session() @@ -55,54 +51,55 @@ class State(api.State): try: q = utils.model_query( models.StateInfo, - session - ).filter_by( - name=name, - ).with_lockmode('update') + session) + q = q.filter(models.StateInfo.name == name) + q = q.with_lockmode('update') db_state = q.one() db_state.state = state except sqlalchemy.orm.exc.NoResultFound: - db_state = models.StateInfo(name=name, state=state) + db_state = models.StateInfo(name=name, + state=state) session.add(db_state) return db_state.state def get_metadata(self, name): session = db.get_session() - return utils.model_query( + q = utils.model_query( models.StateInfo, - session - ).filter_by( - name=name, - ).value('s_metadata') + session) + q.filter(models.StateInfo.name == name) + return q.value(models.StateInfo.s_metadata) def set_metadata(self, name, metadata): session = db.get_session() - try: - db_state = utils.model_query( - models.StateInfo, - session - ).filter_by( - name=name, - ).with_lockmode('update').one() - db_state.s_metadata = metadata - except sqlalchemy.orm.exc.NoResultFound: - db_state = models.StateInfo(name=name, s_metadata=metadata) - session.add(db_state) - finally: - session.flush() + with session.begin(): + try: + q = utils.model_query( + models.StateInfo, + session) + q = q.filter(models.StateInfo.name == name) + q = q.with_lockmode('update') + db_state = q.one() + db_state.s_metadata = metadata + except sqlalchemy.orm.exc.NoResultFound: + db_state = models.StateInfo(name=name, + s_metadata=metadata) + session.add(db_state) class ModuleEnableState(api.ModuleEnableState): + """Deprecated, use ModuleInfo instead. + """ def get_state(self, name): session = db.get_session() try: - return bool(utils.model_query( + q = utils.model_query( models.ModuleStateInfo, - session - ).filter_by( - name=name, - ).value('state')) + session) + q = q.filter(models.ModuleStateInfo.name == name) + res = q.value(models.ModuleStateInfo.state) + return bool(res) except sqlalchemy.orm.exc.NoResultFound: return None @@ -112,10 +109,9 @@ class ModuleEnableState(api.ModuleEnableState): try: q = utils.model_query( models.ModuleStateInfo, - session - ).filter_by( - name=name, - ).with_lockmode('update') + session) + q = q.filter(models.ModuleStateInfo.name == name) + q = q.with_lockmode('update') db_state = q.one() db_state.state = state except sqlalchemy.orm.exc.NoResultFound: @@ -124,19 +120,52 @@ class ModuleEnableState(api.ModuleEnableState): return bool(db_state.state) +class ModuleInfo(ModuleEnableState): + """Base class for module info management.""" + + def get_priority(self, name): + session = db.get_session() + q = utils.model_query( + models.ModuleStateInfo, + session) + q = q.filter(models.ModuleStateInfo.name == name) + res = q.value(models.ModuleStateInfo.priority) + if res: + return int(res) + else: + return 1 + + def set_priority(self, name, priority): + session = db.get_session() + with session.begin(): + try: + q = utils.model_query( + models.ModuleStateInfo, + session) + q = q.filter( + models.ModuleStateInfo.name == name) + q = q.with_lockmode('update') + db_state = q.one() + db_state.priority = priority + except sqlalchemy.orm.exc.NoResultFound: + db_state = models.ModuleStateInfo(name=name, + priority=priority) + session.add(db_state) + return int(db_state.priority) + + class ServiceToCollectorMapping(object): """Base class for service to collector mapping.""" def get_mapping(self, service): session = db.get_session() try: - res = utils.model_query( + q = utils.model_query( models.ServiceToCollectorMapping, - session - ).filter_by( - service=service, - ).one() - return res + session) + q.filter( + models.ServiceToCollectorMapping.service == service) + return q.one() except sqlalchemy.orm.exc.NoResultFound: raise api.NoSuchMapping(service) @@ -146,15 +175,16 @@ class ServiceToCollectorMapping(object): try: q = utils.model_query( models.ServiceToCollectorMapping, - session - ).filter_by( - service=service, - ).with_lockmode('update') + session) + q = q.filter_by( + service=service) + q = q.with_lockmode('update') db_mapping = q.one() db_mapping.collector = collector except sqlalchemy.orm.exc.NoResultFound: model = models.ServiceToCollectorMapping - db_mapping = model(service=service, collector=collector) + db_mapping = model(service=service, + collector=collector) session.add(db_mapping) return db_mapping @@ -162,21 +192,18 @@ class ServiceToCollectorMapping(object): session = db.get_session() q = utils.model_query( models.ServiceToCollectorMapping, - session - ) + session) res = q.distinct().values( - models.ServiceToCollectorMapping.service - ) + models.ServiceToCollectorMapping.service) return res def delete_mapping(self, service): session = db.get_session() - r = utils.model_query( + q = utils.model_query( models.ServiceToCollectorMapping, - session - ).filter_by( - service=service, - ).delete() + session) + q = q.filter(models.ServiceToCollectorMapping.service == service) + r = q.delete() if not r: raise api.NoSuchMapping(service) @@ -191,6 +218,10 @@ class DBAPIManager(object): def get_module_enable_state(): return ModuleEnableState() + @staticmethod + def get_module_info(): + return ModuleInfo() + @staticmethod def get_service_to_collector_mapping(): return ServiceToCollectorMapping() diff --git a/cloudkitty/db/sqlalchemy/models.py b/cloudkitty/db/sqlalchemy/models.py index 6d249891..3d99ea67 100644 --- a/cloudkitty/db/sqlalchemy/models.py +++ b/cloudkitty/db/sqlalchemy/models.py @@ -60,6 +60,9 @@ class ModuleStateInfo(Base, models.ModelBase): sqlalchemy.Boolean(), nullable=False, default=False) + priority = sqlalchemy.Column( + sqlalchemy.Integer(), + default=1) def __repr__(self): return ('