backend/bms_app/operation/views.py (84 lines of code) (raw):

# Copyright 2022 Google LLC # # 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. from flask import jsonify, request from sqlalchemy import func from sqlalchemy.orm import joinedload from bms_app.models import Operation, OperationDetails, OperationType, Wave, db from bms_app.operation import bp from bms_app.services.operations.restore import ( FailOverOperation, PreRestoreOperation, RestoreOperation, RollbackRestoreOperation ) from bms_app.services.rman import RmanLogFileParser from bms_app.services.scheduled_tasks import ( delete_planned_task, get_planned_task ) from .schema import SourceDBIdSchema @bp.route('', methods=['GET']) def get_deployment_operations(): """Return only deployed operations.""" total_mappings = db.session.query(Operation.id, func.count()) \ .join(OperationDetails, Operation.id == OperationDetails.operation_id) \ .filter(Operation.operation_type == OperationType.DEPLOYMENT) \ .group_by(Operation.id) \ .all() total_mappings = dict(total_mappings) query = db.session.query(Wave, Operation) \ .join(Operation, Wave.id == Operation.wave_id) \ .filter(Operation.operation_type == OperationType.DEPLOYMENT) # Return only deployed operations for particular project project_id = request.args.get('project_id', type=int) if project_id: query = query.filter(Wave.project_id == project_id) deployments = query.all() data = [] for row in deployments: data.append({ 'wave_id': row.Wave.id, 'wave_name': row.Wave.name, 'id': row.Operation.id, 'started_at': row.Operation.started_at, 'completed_at': row.Operation.completed_at, 'status': row.Operation.status.value, 'mappings_count': total_mappings.get(row.Operation.id, 0), }) return { 'data': data } @bp.route('/pre-restores', methods=['POST']) def pre_restore(): validated_data = SourceDBIdSchema().load(request.json) PreRestoreOperation().run(db_id=validated_data['db_id']) return {}, 201 @bp.route('/restores', methods=['POST']) def restore(): """Start RESTORE operation.""" validated_data = SourceDBIdSchema().load(request.json) # delete scheduled task if exists task = get_planned_task(validated_data['db_id']) if task: delete_planned_task(task) RestoreOperation().run(db_id=validated_data['db_id']) return {}, 201 @bp.route('/rollback-restores', methods=['POST']) def rollback_restore(): validated_data = SourceDBIdSchema().load(request.json) RollbackRestoreOperation().run(db_id=validated_data['db_id']) return {}, 201 @bp.route('/dt-failover', methods=['POST']) def fail_over_restore(): """Start FailOver operation.""" validated_data = SourceDBIdSchema().load(request.json) FailOverOperation().run(db_id=validated_data['db_id']) return {}, 201 @bp.route('/<int:operation_id>/errors', methods=['GET']) def get_operation_errors(operation_id): """Return Pre-restore operation errors.""" # Pre-restore operaiton is run only for one db # there is only one OperationDetails operaion_details = db.session.query(OperationDetails) \ .options(joinedload(OperationDetails.errors)) \ .filter(OperationDetails.operation_id == operation_id) \ .first() data = [] rman_parser = RmanLogFileParser(operaion_details.operation_id) for err in operaion_details.errors: if err.message.startswith('rman '): details = rman_parser.get_cmd_output(err.message) else: details = '' data.append({ 'name': err.message, 'details': details }) return jsonify(data)