# Copyright 2016 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import datetime import sqlalchemy from sqlalchemy import and_ from sqlalchemy import create_engine, pool from sqlalchemy import inspect from oslo_utils import timeutils from tacker._i18n import _ from tacker.common import exceptions GRANULARITY = {'days': 86400, 'hours': 3600, 'minutes': 60, 'seconds': 1} def _generate_associated_tables_map(inspector): assoc_map = {} table_names = inspector.get_table_names() for t in table_names: fk_list = inspector.get_foreign_keys(t) for fk in fk_list: k = str(fk['referred_table']) v = str(fk['constrained_columns'][0]) if k not in assoc_map: assoc_map[k] = {str(t): v} else: assoc_map[k][str(t)] = v assoc_keys = assoc_map.keys() for k, v in assoc_map.items(): for k1 in v: if k1 in assoc_keys: del assoc_map[k][k1] return assoc_map def _purge_resource_tables(t, meta, engine, time_line, assoc_map): table_load = sqlalchemy.Table(t, meta, autoload=True) table_del_query = table_load.delete().where( table_load.c.deleted_at <= time_line) if t in assoc_map: select_id_query = sqlalchemy.select([table_load.c.id]).where( table_load.c.deleted_at <= time_line) resource_ids = [i[0] for i in list(engine.execute(select_id_query))] if resource_ids: for key, val in assoc_map[t].items(): assoc_table_load = sqlalchemy.Table(key, meta, autoload=True) assoc_table_del_query = assoc_table_load.delete().where( assoc_table_load.c[val].in_(resource_ids)) engine.execute(assoc_table_del_query) engine.execute(table_del_query) def _purge_events_table(meta, engine, time_line): tname = "events" event_table_load = sqlalchemy.Table(tname, meta, autoload=True) event_select_query = sqlalchemy.select( [event_table_load.c.resource_id] ).where( and_(event_table_load.c.event_type == 'DELETE', event_table_load.c.timestamp <= time_line ) ) resource_ids = [i[0] for i in list(engine.execute(event_select_query))] if resource_ids: event_delete_query = event_table_load.delete().where( event_table_load.c.resource_id.in_(resource_ids) ) engine.execute(event_delete_query) def purge_deleted(tacker_config, table_name, age, granularity='days'): try: age = int(age) except ValueError: msg = _("'%s' - age should be an integer") % age raise exceptions.InvalidInput(error_message=msg) if age < 0: msg = _("'%s' - age should be a positive integer") % age raise exceptions.InvalidInput(error_message=msg) if granularity not in GRANULARITY: msg = _("'%s' granularity should be days, hours, minutes, " "or seconds") % granularity raise exceptions.InvalidInput(error_message=msg) age *= GRANULARITY[granularity] time_line = timeutils.utcnow() - datetime.timedelta(seconds=age) engine = get_engine(tacker_config) meta = sqlalchemy.MetaData() meta.bind = engine inspector = inspect(engine) assoc_map = _generate_associated_tables_map(inspector) if table_name == 'events': _purge_events_table(meta, engine, time_line) elif table_name == 'all': _purge_events_table(meta, engine, time_line) for t in ['vnf', 'vnfd', 'vims']: _purge_resource_tables(t, meta, engine, time_line, assoc_map) else: _purge_resource_tables(table_name, meta, engine, time_line, assoc_map) def get_engine(tacker_config): return create_engine(tacker_config.database.connection, poolclass=pool.NullPool)