from vms import connect_to_vcenter, get_vm_info_and_save_to_db, update_vm_power_status from flask import Flask, render_template, flash, send_from_directory, redirect, url_for from db_manager import db, User, VirtualMachine, Stables from flask_login import LoginManager, login_required, current_user from routers.user_routers import user_blueprint from routers.vm_routers import vm_blueprint from flask_principal import Principal from flask_socketio import SocketIO from flask_migrate import Migrate from flask_session import Session from flask.cli import AppGroup from dotenv import load_dotenv from docx import Document from io import BytesIO import threading import schedule import requests import logging import redis import time import os import re # Создаем директорию для логов, если ее нет log_dir = 'logs' if not os.path.exists(log_dir): os.makedirs(log_dir) # Настройки логирования log_format = '%(name)s - %(levelname)s - %(message)s' log_path = os.path.join(log_dir, 'app.log') logging.basicConfig(filename=log_path, level=logging.INFO, format=log_format, filemode='w') dotenv_path = os.path.join(os.path.dirname(__file__), '.env') if os.path.exists(dotenv_path): load_dotenv(dotenv_path) else: exit('[STOP SYSTEM STARTUP] >> Не обнаружен файл переменных окружения ".env". \n' 'Файл должен располагаться на одном уровне с "app.py".') app = Flask(__name__) socketio = SocketIO(app) login_manager = LoginManager() app.register_blueprint(user_blueprint) app.register_blueprint(vm_blueprint) login_manager.init_app(app) principal = Principal(app) migrate = Migrate(app, db) app.secret_key = os.environ.get('SECRET') # Блок создания кастомных комманд group_command_one = AppGroup('backtask') @group_command_one.command('full') def command(): logging.info('Запустить в ручном режиме выполнение выполнения - full_upd') full_upd() @group_command_one.command('power') def command(): logging.info('Запустить в ручном режиме выполнение выполнения - power_status_upd') power_status_upd() @group_command_one.command('version') def command(): logging.info('Запустить в ручном режиме выполнение запуска - стабильный_upd') stables_upd() app.cli.add_command(group_command_one) # End block if (os.environ.get('DB_TYPE')).lower() == "sqlite": app.config['SQLALCHEMY_DATABASE_URI'] = f"sqlite:///{os.environ.get('DB_NAME')}.db" elif (os.environ.get('DB_TYPE')).lower() == "postgresql": app.config['SQLALCHEMY_DATABASE_URI'] = (f"postgresql://{os.environ.get('DB_USER')}:{os.environ.get('DB_PASS')}@" f"{os.environ.get('DB_HOST')}:{os.environ.get('DB_PORT')}/" f"{os.environ.get('DB_NAME')}") elif (os.environ.get('DB_TYPE')).lower() == "mysql": app.config['SQLALCHEMY_DATABASE_URI'] = (f"mysql+mysqlconnector://{os.environ.get('DB_USER')}:" f"{os.environ.get('DB_PASS')}@{os.environ.get('DB_HOST')}:" f"{os.environ.get('DB_PORT')}/{os.environ.get('DB_NAME')}") else: exit('[DB ERROR] >> Неверно указаны настройки базы данных (параметр - DB_TYPE)!') db.init_app(app) if (os.environ.get('SESSION_TYPE')).lower() == 'redis': app.config['SESSION_TYPE'] = 'redis' app.config['SESSION_PERMANENT'] = True app.config['SESSION_USE_SIGNER'] = True app.config['SESSION_KEY_PREFIX'] = os.environ.get('SESSION_KEY_PREFIX') app.config['SESSION_REDIS'] = redis.StrictRedis( host=os.environ.get('REDIS_HOST'), port=os.environ.get('REDIS_PORT'), db=os.environ.get('REDIS_DB'), password=os.environ.get('REDIS_PASS') ) app.config['PERMANENT_SESSION_LIFETIME'] = int(os.environ.get('SESSION_LIFETIME')) elif (os.environ.get('SESSION_TYPE')).lower() == 'file': app.config['SESSION_TYPE'] = 'filesystem' else: exit('[SESSION ERROR] >> Неверно указаны настройки сессии(параметр - SESSION_TYPE)!') Session(app) @app.route('/') def index(): total_vm = VirtualMachine.query.count() number_of_employees = VirtualMachine.query.filter_by(status="Занято").count() number_of_technical = VirtualMachine.query.filter_by(technical=True).count() quantity_for_tests = int(total_vm) - (int(number_of_technical) + int(number_of_employees)) stables_version = Stables.query.all() return render_template('home.html', total_vm=total_vm, number_of_employees=number_of_employees, number_of_technical=number_of_technical, quantity_for_tests=quantity_for_tests, stables_version=stables_version) @app.route('/favicon.ico') def fav(): return send_from_directory(os.path.join(app.root_path, 'static'), 'image/fav.ico') @app.route('/about') def about(): return render_template('about.html') @app.route('/admin') @login_required def admin(): if current_user.is_admin: all_user = User.query.all() return render_template('admin.html', all_user=all_user) else: flash(f'Пользователь {current_user} не администратор!', 'danger') return redirect(url_for('index')) @login_manager.user_loader def load_user(user_id): return db.session.get(User, user_id) @login_manager.unauthorized_handler def unauthorized(): flash('Доступ разрешен только авторизованным!', 'danger') return render_template('login.html') vcenter_connections = [ {"host": f"{os.environ.get('HYPER1_HOST')}", "user": f"{os.environ.get('HYPER1_USER')}", "password": f"{os.environ.get('HYPER1_PASS')}"}, {"host": f"{os.environ.get('HYPER2_HOST')}", "user": f"{os.environ.get('HYPER2_USER')}", "password": f"{os.environ.get('HYPER2_PASS')}"}, {"host": f"{os.environ.get('HYPER3_HOST')}", "user": f"{os.environ.get('HYPER3_USER')}", "password": f"{os.environ.get('HYPER3_PASS')}"}, {"host": f"{os.environ.get('HYPER4_HOST')}", "user": f"{os.environ.get('HYPER4_USER')}", "password": f"{os.environ.get('HYPER4_PASS')}"} ] def stables_upd(): with app.app_context(): url = "https://s3.printum.io/stablerefs/Printum%20software.docx" response = requests.get(url) docx_file = BytesIO(response.content) doc = Document(docx_file) pattern = re.compile(r'(\d+\.\d+\.\d+)') target_values = [] for paragraph in doc.paragraphs: matches = pattern.findall(paragraph.text) if matches: target_values.extend(matches) monitoring_value = target_values[0] printmanager_value = target_values[2] existing_stables = Stables.query.first() if existing_stables: existing_stables.monitoring = monitoring_value existing_stables.printmanager = printmanager_value db.session.commit() else: new_version = Stables(monitoring=monitoring_value, printmanager=printmanager_value) db.session.add(new_version) db.session.commit() logging.info('Фоновая задача «stables_upd» выполнена успешно.') def power_status_upd(): with app.app_context(): for connection in vcenter_connections: content = connect_to_vcenter(connection['host'], connection['user'], connection['password']) if content: update_vm_power_status(content, connection['host']) logging.info('Фоновая задача «power_status_upd» выполнена успешно.') def full_upd(): with app.app_context(): for connection in vcenter_connections: content = connect_to_vcenter(connection['host'], connection['user'], connection['password']) if content: get_vm_info_and_save_to_db(content, connection['host']) logging.info('Фоновая задача «full_upd» выполнена успешно.') # Настройки обработки фоновых задач if (os.environ.get('DISABLING_TASK')).lower() == 'false': lock = threading.Lock() def run_tasks(): while True: lock.acquire() schedule.run_pending() lock.release() time.sleep(1) schedule.every(int(os.environ.get('STABLES_UPDATE'))).minutes.do(stables_upd) schedule.every(int(os.environ.get('POWER_STATUS_UPDATE'))).minutes.do(power_status_upd) if (os.environ.get('PERFORMANCE')).lower() == 'period': schedule.every(int(os.environ.get('FULL_UPDATE'))).minutes.do(full_upd) elif (os.environ.get('PERFORMANCE')).lower() == 'time': schedule.every().day.at(f"{os.environ.get('HOUR_FULL_UPDATE')}").do(full_upd) else: logging.error("Неверно указано время или период выполнения полного обновления!") exit(1) thread = threading.Thread(target=run_tasks) thread.start() else: logging.error("Выполнение фоновых задач отключено!!") # Первичная инициализация БД при первом запуске if not os.path.exists(".init") and os.path.exists(".create"): print("Первоначальная инициализация и загрузка данных в базу данных осуществляется...") try: full_upd() stables_upd() with open(".init", "w") as f: f.write("Первая инициализация БД прошла успешно") except Exception as e: logging.error(f"ОШИБКА при первой инициализации: {e}") if __name__ == '__main__': socketio.run(app, debug=True, allow_unsafe_werkzeug=True)