VM-Dashboard-Manager/app.py

266 lines
10 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)