mirror of https://github.com/veops/cmdb.git
Merge branch 'master' into dev_ui
This commit is contained in:
commit
2b1b0333ff
|
@ -230,3 +230,59 @@ def init_department():
|
||||||
cli.init_wide_company()
|
cli.init_wide_company()
|
||||||
cli.create_acl_role_with_department()
|
cli.create_acl_role_with_department()
|
||||||
cli.init_backend_resource()
|
cli.init_backend_resource()
|
||||||
|
|
||||||
|
|
||||||
|
@click.command()
|
||||||
|
@with_appcontext
|
||||||
|
def common_check_new_columns():
|
||||||
|
"""
|
||||||
|
add new columns to tables
|
||||||
|
"""
|
||||||
|
from api.extensions import db
|
||||||
|
from sqlalchemy import inspect, text
|
||||||
|
|
||||||
|
def get_model_by_table_name(table_name):
|
||||||
|
for model in db.Model.registry._class_registry.values():
|
||||||
|
if hasattr(model, '__tablename__') and model.__tablename__ == table_name:
|
||||||
|
return model
|
||||||
|
return None
|
||||||
|
|
||||||
|
def add_new_column(table_name, new_column):
|
||||||
|
column_type = new_column.type.compile(engine.dialect)
|
||||||
|
default_value = new_column.default.arg if new_column.default else None
|
||||||
|
|
||||||
|
sql = f"ALTER TABLE {table_name} ADD COLUMN {new_column.name} {column_type} "
|
||||||
|
if new_column.comment:
|
||||||
|
sql += f" comment '{new_column.comment}'"
|
||||||
|
|
||||||
|
if column_type == 'JSON':
|
||||||
|
pass
|
||||||
|
elif default_value:
|
||||||
|
if column_type.startswith('VAR') or column_type.startswith('Text'):
|
||||||
|
if default_value is None or len(default_value) == 0:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
sql += f" DEFAULT {default_value}"
|
||||||
|
|
||||||
|
sql = text(sql)
|
||||||
|
db.session.execute(sql)
|
||||||
|
|
||||||
|
engine = db.get_engine()
|
||||||
|
inspector = inspect(engine)
|
||||||
|
table_names = inspector.get_table_names()
|
||||||
|
for table_name in table_names:
|
||||||
|
existed_columns = inspector.get_columns(table_name)
|
||||||
|
existed_column_name_list = [c['name'] for c in existed_columns]
|
||||||
|
|
||||||
|
model = get_model_by_table_name(table_name)
|
||||||
|
if model is None:
|
||||||
|
continue
|
||||||
|
model_columns = model.__table__.columns._all_columns
|
||||||
|
for column in model_columns:
|
||||||
|
if column.name not in existed_column_name_list:
|
||||||
|
try:
|
||||||
|
add_new_column(table_name, column)
|
||||||
|
current_app.logger.info(f"add new column [{column.name}] in table [{table_name}] success.")
|
||||||
|
except Exception as e:
|
||||||
|
current_app.logger.error(f"add new column [{column.name}] in table [{table_name}] err:")
|
||||||
|
current_app.logger.error(e)
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
from api.models.common_setting import NoticeConfig
|
||||||
|
from wtforms import Form
|
||||||
|
from wtforms import StringField
|
||||||
|
from wtforms import validators
|
||||||
|
from flask import abort
|
||||||
|
import smtplib
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
from email.utils import formataddr
|
||||||
|
|
||||||
|
|
||||||
|
class NoticeConfigCRUD(object):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def add_notice_config(**kwargs):
|
||||||
|
NoticeConfigCRUD.check_platform(kwargs.get('platform'))
|
||||||
|
try:
|
||||||
|
return NoticeConfig.create(
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return abort(400, str(e))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def check_platform(platform):
|
||||||
|
NoticeConfig.get_by(first=True, to_dict=False, platform=platform) and abort(400, f"{platform} 已存在!")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def edit_notice_config(_id, **kwargs):
|
||||||
|
existed = NoticeConfigCRUD.get_notice_config_by_id(_id)
|
||||||
|
try:
|
||||||
|
return existed.update(**kwargs)
|
||||||
|
except Exception as e:
|
||||||
|
return abort(400, str(e))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_notice_config_by_id(_id):
|
||||||
|
return NoticeConfig.get_by(first=True, to_dict=False, id=_id) or abort(400, f"{_id} 配置项不存在!")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_all():
|
||||||
|
return NoticeConfig.get_by(to_dict=True)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def test_send_email(receive_address, **kwargs):
|
||||||
|
# 设置发送方和接收方的电子邮件地址
|
||||||
|
sender_email = 'test@test.com'
|
||||||
|
sender_name = 'Test Sender'
|
||||||
|
recipient_email = receive_address
|
||||||
|
recipient_name = receive_address
|
||||||
|
|
||||||
|
subject = 'Test Email'
|
||||||
|
body = 'This is a test email'
|
||||||
|
|
||||||
|
message = MIMEText(body, 'plain', 'utf-8')
|
||||||
|
message['From'] = formataddr((sender_name, sender_email))
|
||||||
|
message['To'] = formataddr((recipient_name, recipient_email))
|
||||||
|
message['Subject'] = subject
|
||||||
|
|
||||||
|
smtp_server = kwargs.get('server')
|
||||||
|
smtp_port = kwargs.get('port')
|
||||||
|
smtp_username = kwargs.get('username')
|
||||||
|
smtp_password = kwargs.get('password')
|
||||||
|
|
||||||
|
if kwargs.get('mail_type') == 'SMTP':
|
||||||
|
smtp_connection = smtplib.SMTP(smtp_server, smtp_port)
|
||||||
|
else:
|
||||||
|
smtp_connection = smtplib.SMTP_SSL(smtp_server, smtp_port)
|
||||||
|
|
||||||
|
if kwargs.get('is_login'):
|
||||||
|
smtp_connection.login(smtp_username, smtp_password)
|
||||||
|
|
||||||
|
smtp_connection.sendmail(sender_email, recipient_email, message.as_string())
|
||||||
|
smtp_connection.quit()
|
||||||
|
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
class NoticeConfigForm(Form):
|
||||||
|
platform = StringField(validators=[
|
||||||
|
validators.DataRequired(message="平台 不能为空"),
|
||||||
|
validators.Length(max=255),
|
||||||
|
])
|
||||||
|
info = StringField(validators=[
|
||||||
|
validators.DataRequired(message="信息 不能为空"),
|
||||||
|
validators.Length(max=255),
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
class NoticeConfigUpdateForm(Form):
|
||||||
|
info = StringField(validators=[
|
||||||
|
validators.DataRequired(message="信息 不能为空"),
|
||||||
|
validators.Length(max=255),
|
||||||
|
])
|
|
@ -53,5 +53,6 @@ class ErrFormat(CommonErrFormat):
|
||||||
username_is_required = "username不能为空"
|
username_is_required = "username不能为空"
|
||||||
email_is_required = "邮箱不能为空"
|
email_is_required = "邮箱不能为空"
|
||||||
email_format_error = "邮箱格式错误"
|
email_format_error = "邮箱格式错误"
|
||||||
|
email_send_timeout = "邮件发送超时"
|
||||||
|
|
||||||
common_data_not_found = "ID {} 找不到记录"
|
common_data_not_found = "ID {} 找不到记录"
|
||||||
|
|
|
@ -47,6 +47,8 @@ class Employee(ModelWithoutPK):
|
||||||
last_login = db.Column(db.TIMESTAMP, nullable=True)
|
last_login = db.Column(db.TIMESTAMP, nullable=True)
|
||||||
block = db.Column(db.Integer, default=0)
|
block = db.Column(db.Integer, default=0)
|
||||||
|
|
||||||
|
notice_info = db.Column(db.JSON, default={})
|
||||||
|
|
||||||
_department = db.relationship(
|
_department = db.relationship(
|
||||||
'Department', backref='common_employee.department_id',
|
'Department', backref='common_employee.department_id',
|
||||||
lazy='joined'
|
lazy='joined'
|
||||||
|
@ -87,3 +89,10 @@ class CommonData(Model):
|
||||||
|
|
||||||
data_type = db.Column(db.VARCHAR(255), default='')
|
data_type = db.Column(db.VARCHAR(255), default='')
|
||||||
data = db.Column(db.JSON)
|
data = db.Column(db.JSON)
|
||||||
|
|
||||||
|
|
||||||
|
class NoticeConfig(Model):
|
||||||
|
__tablename__ = "common_notice_config"
|
||||||
|
|
||||||
|
platform = db.Column(db.VARCHAR(255), nullable=False)
|
||||||
|
info = db.Column(db.JSON)
|
||||||
|
|
|
@ -4,8 +4,6 @@
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import jinja2
|
|
||||||
import requests
|
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from flask_login import login_user
|
from flask_login import login_user
|
||||||
|
|
||||||
|
@ -18,7 +16,6 @@ from api.lib.cmdb.cache import CITypeAttributesCache
|
||||||
from api.lib.cmdb.const import CMDB_QUEUE
|
from api.lib.cmdb.const import CMDB_QUEUE
|
||||||
from api.lib.cmdb.const import REDIS_PREFIX_CI
|
from api.lib.cmdb.const import REDIS_PREFIX_CI
|
||||||
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION
|
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION
|
||||||
from api.lib.mail import send_mail
|
|
||||||
from api.lib.perm.acl.cache import UserCache
|
from api.lib.perm.acl.cache import UserCache
|
||||||
from api.lib.utils import Lock
|
from api.lib.utils import Lock
|
||||||
from api.lib.utils import handle_arg_list
|
from api.lib.utils import handle_arg_list
|
||||||
|
@ -28,7 +25,9 @@ from api.models.cmdb import CITypeAttribute
|
||||||
|
|
||||||
|
|
||||||
@celery.task(name="cmdb.ci_cache", queue=CMDB_QUEUE)
|
@celery.task(name="cmdb.ci_cache", queue=CMDB_QUEUE)
|
||||||
def ci_cache(ci_id):
|
def ci_cache(ci_id, operate_type, record_id):
|
||||||
|
from api.lib.cmdb.ci import CITriggerManager
|
||||||
|
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
db.session.remove()
|
db.session.remove()
|
||||||
|
|
||||||
|
@ -42,9 +41,14 @@ def ci_cache(ci_id):
|
||||||
|
|
||||||
current_app.logger.info("{0} flush..........".format(ci_id))
|
current_app.logger.info("{0} flush..........".format(ci_id))
|
||||||
|
|
||||||
|
current_app.test_request_context().push()
|
||||||
|
login_user(UserCache.get('worker'))
|
||||||
|
|
||||||
|
CITriggerManager.fire(operate_type, ci_dict, record_id)
|
||||||
|
|
||||||
|
|
||||||
@celery.task(name="cmdb.batch_ci_cache", queue=CMDB_QUEUE)
|
@celery.task(name="cmdb.batch_ci_cache", queue=CMDB_QUEUE)
|
||||||
def batch_ci_cache(ci_ids):
|
def batch_ci_cache(ci_ids, ): # only for attribute change index
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
db.session.remove()
|
db.session.remove()
|
||||||
|
|
||||||
|
@ -61,7 +65,10 @@ def batch_ci_cache(ci_ids):
|
||||||
|
|
||||||
|
|
||||||
@celery.task(name="cmdb.ci_delete", queue=CMDB_QUEUE)
|
@celery.task(name="cmdb.ci_delete", queue=CMDB_QUEUE)
|
||||||
def ci_delete(ci_id):
|
def ci_delete(ci_dict, operate_type, record_id):
|
||||||
|
from api.lib.cmdb.ci import CITriggerManager
|
||||||
|
|
||||||
|
ci_id = ci_dict.get('_id')
|
||||||
current_app.logger.info(ci_id)
|
current_app.logger.info(ci_id)
|
||||||
|
|
||||||
if current_app.config.get("USE_ES"):
|
if current_app.config.get("USE_ES"):
|
||||||
|
@ -71,6 +78,11 @@ def ci_delete(ci_id):
|
||||||
|
|
||||||
current_app.logger.info("{0} delete..........".format(ci_id))
|
current_app.logger.info("{0} delete..........".format(ci_id))
|
||||||
|
|
||||||
|
current_app.test_request_context().push()
|
||||||
|
login_user(UserCache.get('worker'))
|
||||||
|
|
||||||
|
CITriggerManager.fire(operate_type, ci_dict, record_id)
|
||||||
|
|
||||||
|
|
||||||
@celery.task(name="cmdb.ci_relation_cache", queue=CMDB_QUEUE)
|
@celery.task(name="cmdb.ci_relation_cache", queue=CMDB_QUEUE)
|
||||||
def ci_relation_cache(parent_id, child_id):
|
def ci_relation_cache(parent_id, child_id):
|
||||||
|
@ -168,46 +180,6 @@ def ci_type_attribute_order_rebuild(type_id):
|
||||||
order += 1
|
order += 1
|
||||||
|
|
||||||
|
|
||||||
@celery.task(name='cmdb.trigger_notify', queue=CMDB_QUEUE)
|
|
||||||
def trigger_notify(notify, ci_id):
|
|
||||||
from api.lib.perm.acl.cache import UserCache
|
|
||||||
|
|
||||||
def _wrap_mail(mail_to):
|
|
||||||
if "@" not in mail_to:
|
|
||||||
user = UserCache.get(mail_to)
|
|
||||||
if user:
|
|
||||||
return user.email
|
|
||||||
|
|
||||||
return mail_to
|
|
||||||
|
|
||||||
db.session.remove()
|
|
||||||
|
|
||||||
m = api.lib.cmdb.ci.CIManager()
|
|
||||||
ci_dict = m.get_ci_by_id_from_db(ci_id, need_children=False, use_master=False)
|
|
||||||
|
|
||||||
subject = jinja2.Template(notify.get('subject') or "").render(ci_dict)
|
|
||||||
body = jinja2.Template(notify.get('body') or "").render(ci_dict)
|
|
||||||
|
|
||||||
if notify.get('wx_to'):
|
|
||||||
to_user = jinja2.Template('|'.join(notify['wx_to'])).render(ci_dict)
|
|
||||||
url = current_app.config.get("WX_URI")
|
|
||||||
data = {"to_user": to_user, "content": subject}
|
|
||||||
try:
|
|
||||||
requests.post(url, data=data)
|
|
||||||
except Exception as e:
|
|
||||||
current_app.logger.error(str(e))
|
|
||||||
|
|
||||||
if notify.get('mail_to'):
|
|
||||||
try:
|
|
||||||
if len(subject) > 700:
|
|
||||||
subject = subject[:600] + "..." + subject[-100:]
|
|
||||||
|
|
||||||
send_mail("", [_wrap_mail(jinja2.Template(i).render(ci_dict))
|
|
||||||
for i in notify['mail_to'] if i], subject, body)
|
|
||||||
except Exception as e:
|
|
||||||
current_app.logger.error("Send mail failed: {0}".format(str(e)))
|
|
||||||
|
|
||||||
|
|
||||||
@celery.task(name="cmdb.calc_computed_attribute", queue=CMDB_QUEUE)
|
@celery.task(name="cmdb.calc_computed_attribute", queue=CMDB_QUEUE)
|
||||||
def calc_computed_attribute(attr_id, uid):
|
def calc_computed_attribute(attr_id, uid):
|
||||||
from api.lib.cmdb.ci import CIManager
|
from api.lib.cmdb.ci import CIManager
|
||||||
|
@ -217,7 +189,8 @@ def calc_computed_attribute(attr_id, uid):
|
||||||
current_app.test_request_context().push()
|
current_app.test_request_context().push()
|
||||||
login_user(UserCache.get(uid))
|
login_user(UserCache.get(uid))
|
||||||
|
|
||||||
|
cim = CIManager()
|
||||||
for i in CITypeAttribute.get_by(attr_id=attr_id, to_dict=False):
|
for i in CITypeAttribute.get_by(attr_id=attr_id, to_dict=False):
|
||||||
cis = CI.get_by(type_id=i.type_id, to_dict=False)
|
cis = CI.get_by(type_id=i.type_id, to_dict=False)
|
||||||
for ci in cis:
|
for ci in cis:
|
||||||
CIManager.update(ci.id, {})
|
cim.update(ci.id, {})
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
from flask import request, abort, current_app
|
||||||
|
from werkzeug.datastructures import MultiDict
|
||||||
|
|
||||||
|
from api.lib.perm.auth import auth_with_app_token
|
||||||
|
from api.models.common_setting import NoticeConfig
|
||||||
|
from api.resource import APIView
|
||||||
|
from api.lib.common_setting.notice_config import NoticeConfigForm, NoticeConfigUpdateForm, NoticeConfigCRUD
|
||||||
|
from api.lib.decorator import args_required
|
||||||
|
from api.lib.common_setting.resp_format import ErrFormat
|
||||||
|
|
||||||
|
prefix = '/notice_config'
|
||||||
|
|
||||||
|
|
||||||
|
class NoticeConfigView(APIView):
|
||||||
|
url_prefix = (f'{prefix}',)
|
||||||
|
|
||||||
|
@args_required('platform')
|
||||||
|
@auth_with_app_token
|
||||||
|
def get(self):
|
||||||
|
platform = request.args.get('platform')
|
||||||
|
res = NoticeConfig.get_by(first=True, to_dict=True, platform=platform) or {}
|
||||||
|
return self.jsonify(res)
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
form = NoticeConfigForm(MultiDict(request.json))
|
||||||
|
if not form.validate():
|
||||||
|
abort(400, ','.join(['{}: {}'.format(filed, ','.join(msg)) for filed, msg in form.errors.items()]))
|
||||||
|
|
||||||
|
data = NoticeConfigCRUD.add_notice_config(**form.data)
|
||||||
|
return self.jsonify(data.to_dict())
|
||||||
|
|
||||||
|
|
||||||
|
class NoticeConfigUpdateView(APIView):
|
||||||
|
url_prefix = (f'{prefix}/<int:_id>',)
|
||||||
|
|
||||||
|
def put(self, _id):
|
||||||
|
form = NoticeConfigUpdateForm(MultiDict(request.json))
|
||||||
|
if not form.validate():
|
||||||
|
abort(400, ','.join(['{}: {}'.format(filed, ','.join(msg)) for filed, msg in form.errors.items()]))
|
||||||
|
|
||||||
|
data = NoticeConfigCRUD.edit_notice_config(_id, **form.data)
|
||||||
|
return self.jsonify(data.to_dict())
|
||||||
|
|
||||||
|
|
||||||
|
class CheckEmailServer(APIView):
|
||||||
|
url_prefix = (f'{prefix}/send_test_email',)
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
receive_address = request.args.get('receive_address')
|
||||||
|
info = request.values.get('info')
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
result = NoticeConfigCRUD.test_send_email(receive_address, **info)
|
||||||
|
return self.jsonify(result=result)
|
||||||
|
except Exception as e:
|
||||||
|
current_app.logger.error('test_send_email err:')
|
||||||
|
current_app.logger.error(e)
|
||||||
|
if 'Timed Out' in str(e):
|
||||||
|
abort(400, ErrFormat.email_send_timeout)
|
||||||
|
abort(400, f"{str(e)}")
|
||||||
|
|
||||||
|
|
||||||
|
class NoticeConfigGetView(APIView):
|
||||||
|
method_decorators = []
|
||||||
|
url_prefix = (f'{prefix}/all',)
|
||||||
|
|
||||||
|
@auth_with_app_token
|
||||||
|
def get(self):
|
||||||
|
res = NoticeConfigCRUD.get_all()
|
||||||
|
return self.jsonify(res)
|
Loading…
Reference in New Issue