refactor: CI triggers

This commit is contained in:
pycook 2023-09-22 17:39:54 +08:00
parent cd451060e3
commit deabebb922
12 changed files with 431 additions and 128 deletions

View File

@ -48,6 +48,7 @@ six = "==1.12.0"
bs4 = ">=0.0.1"
toposort = ">=1.5"
requests = ">=2.22.0"
requests_oauthlib = "==1.3.1"
PyJWT = "==2.4.0"
elasticsearch = "==7.17.9"
future = "==0.18.3"

View File

@ -4,6 +4,7 @@
import copy
import datetime
import json
import threading
from flask import abort
from flask import current_app
@ -24,27 +25,33 @@ from api.lib.cmdb.const import CMDB_QUEUE
from api.lib.cmdb.const import ConstraintEnum
from api.lib.cmdb.const import ExistPolicy
from api.lib.cmdb.const import OperateType
from api.lib.cmdb.const import PermEnum, ResourceTypeEnum
from api.lib.cmdb.const import PermEnum
from api.lib.cmdb.const import REDIS_PREFIX_CI
from api.lib.cmdb.const import ResourceTypeEnum
from api.lib.cmdb.const import RetKey
from api.lib.cmdb.history import AttributeHistoryManger
from api.lib.cmdb.history import CIRelationHistoryManager
from api.lib.cmdb.history import CITriggerHistoryManager
from api.lib.cmdb.perms import CIFilterPermsCRUD
from api.lib.cmdb.resp_format import ErrFormat
from api.lib.cmdb.utils import TableMap
from api.lib.cmdb.utils import ValueTypeMap
from api.lib.cmdb.value import AttributeValueManager
from api.lib.decorator import kwargs_required
from api.lib.notify import notify_send
from api.lib.perm.acl.acl import ACLManager
from api.lib.perm.acl.acl import is_app_admin
from api.lib.perm.acl.acl import validate_permission
from api.lib.utils import Lock
from api.lib.utils import handle_arg_list
from api.lib.webhook import webhook_request
from api.models.cmdb import AttributeHistory
from api.models.cmdb import AutoDiscoveryCI
from api.models.cmdb import CI
from api.models.cmdb import CIRelation
from api.models.cmdb import CITypeAttribute
from api.models.cmdb import CITypeRelation
from api.models.cmdb import CITypeTrigger
from api.tasks.cmdb import ci_cache
from api.tasks.cmdb import ci_delete
from api.tasks.cmdb import ci_relation_add
@ -378,16 +385,17 @@ class CIManager(object):
key2attr = value_manager.valid_attr_value(ci_dict, ci_type.id, ci and ci.id,
ci_type_attrs_name, ci_type_attrs_alias, ci_attr2type_attr)
operate_type = OperateType.UPDATE if ci is not None else OperateType.ADD
try:
ci = ci or CI.create(type_id=ci_type.id, is_auto_discovery=is_auto_discovery)
record_id = value_manager.create_or_update_attr_value2(ci, ci_dict, key2attr)
record_id = value_manager.create_or_update_attr_value(ci, ci_dict, key2attr)
except BadRequest as e:
if existed is None:
cls.delete(ci.id)
raise e
if record_id: # has change
ci_cache.apply_async([ci.id], queue=CMDB_QUEUE)
ci_cache.apply_async(args=(ci.id, operate_type, record_id), queue=CMDB_QUEUE)
if ref_ci_dict: # add relations
ci_relation_add.apply_async(args=(ref_ci_dict, ci.id, current_user.uid), queue=CMDB_QUEUE)
@ -427,12 +435,12 @@ class CIManager(object):
return abort(403, ErrFormat.ci_filter_perm_attr_no_permission.format(k))
try:
record_id = value_manager.create_or_update_attr_value2(ci, ci_dict, key2attr)
record_id = value_manager.create_or_update_attr_value(ci, ci_dict, key2attr)
except BadRequest as e:
raise e
if record_id: # has change
ci_cache.apply_async([ci_id], queue=CMDB_QUEUE)
ci_cache.apply_async(args=(ci_id, OperateType.UPDATE, record_id), queue=CMDB_QUEUE)
ref_ci_dict = {k: v for k, v in ci_dict.items() if k.startswith("$") and "." in k}
if ref_ci_dict:
@ -442,9 +450,10 @@ class CIManager(object):
def update_unique_value(ci_id, unique_name, unique_value):
ci = CI.get_by_id(ci_id) or abort(404, ErrFormat.ci_not_found.format("id={}".format(ci_id)))
AttributeValueManager().create_or_update_attr_value(unique_name, unique_value, ci)
key2attr = {unique_name: AttributeCache.get(unique_name)}
record_id = AttributeValueManager().create_or_update_attr_value(ci, {unique_name: unique_value}, key2attr)
ci_cache.apply_async([ci_id], queue=CMDB_QUEUE)
ci_cache.apply_async(args=(ci_id, OperateType.UPDATE, record_id), queue=CMDB_QUEUE)
@classmethod
def delete(cls, ci_id):
@ -477,9 +486,9 @@ class CIManager(object):
db.session.commit()
AttributeHistoryManger.add(None, ci_id, [(None, OperateType.DELETE, ci_dict, None)], ci.type_id)
record_id = AttributeHistoryManger.add(None, ci_id, [(None, OperateType.DELETE, ci_dict, None)], ci.type_id)
ci_delete.apply_async([ci.id], queue=CMDB_QUEUE)
ci_delete.apply_async(args=(ci_dict, OperateType.DELETE, record_id), queue=CMDB_QUEUE)
return ci_id
@ -896,3 +905,128 @@ class CIRelationManager(object):
for parent_id in parents:
for ci_id in ci_ids:
cls.delete_2(parent_id, ci_id)
class CITriggerManager(object):
@staticmethod
def get(type_id):
return CITypeTrigger.get_by(type_id=type_id, to_dict=False)
@staticmethod
def _exec_webhook(operate_type, webhook, ci_dict, trigger_id, record_id):
try:
response = webhook_request(webhook, ci_dict).text
is_ok = True
except Exception as e:
current_app.logger.warning("exec webhook failed: {}".format(e))
response = e
is_ok = False
CITriggerHistoryManager.add(operate_type,
record_id,
ci_dict.get('_id'),
trigger_id,
is_ok=is_ok,
webhook=response)
return is_ok
@staticmethod
def _exec_notify(operate_type, notify, ci_dict, trigger_id, record_id, ci_id=None):
if ci_id is not None:
ci_dict = CIManager().get_ci_by_id_from_db(ci_id, need_children=False, use_master=False)
try:
response = notify_send(notify.get('subject'), notify.get('body'), notify.get('tos'), ci_dict)
is_ok = True
except Exception as e:
current_app.logger.warning("send notify failed: {}".format(e))
response = e
is_ok = False
CITriggerHistoryManager.add(operate_type,
record_id,
ci_dict.get('_id'),
trigger_id,
is_ok=is_ok,
notify=response)
return is_ok
@staticmethod
def ci_filter(ci_id, other_filter):
from api.lib.cmdb.search import SearchError
from api.lib.cmdb.search.ci import search
query = "_id:{},{}".format(ci_id, other_filter)
try:
_, _, _, _, numfound, _ = search(query).search()
return numfound
except SearchError as e:
current_app.logger.warning("ci search failed: {}".format(e))
@classmethod
def fire(cls, operate_type, ci_dict, record_id):
type_id = ci_dict.get('_type')
triggers = cls.get(type_id) or []
for trigger in triggers:
if not trigger.option.get('enable'):
continue
if trigger.option.get('filter') and not cls.ci_filter(ci_dict.get('_id'), trigger.option['filter']):
continue
if trigger.option.get('attr_ids') and isinstance(trigger.option['attr_ids'], list):
if not (set(trigger.option['attr_ids']) &
set([i.attr_id for i in AttributeHistory.get_by(record_id=record_id, to_dict=False)])):
continue
if trigger.option.get('action') == operate_type:
if trigger.option.get('webhooks'):
cls._exec_webhook(operate_type, trigger.option['webhooks'], ci_dict, trigger.id, record_id)
elif trigger.option.get('notifies'):
cls._exec_notify(operate_type, trigger.option['notifies'], ci_dict, trigger.id, record_id)
@classmethod
def waiting_cis(cls, trigger):
now = datetime.datetime.today()
delta_time = datetime.timedelta(days=(trigger.option.get('before_days', 0) or 0))
attr = AttributeCache.get(trigger.attr_id)
value_table = TableMap(attr=attr).table
values = value_table.get_by(attr_id=attr.id, to_dict=False)
result = []
for v in values:
if (isinstance(v.value, (datetime.date, datetime.datetime)) and
(v.value - delta_time).strftime('%Y%m%d') == now.strftime("%Y%m%d")):
if trigger.option.get('filter') and not cls.ci_filter(v.ci_id, trigger.option['filter']):
continue
result.append(v)
return result
@classmethod
def trigger_notify(cls, trigger, ci):
"""
only for date attribute
:param trigger:
:param ci:
:return:
"""
if (trigger.notify.get('notify_at') == datetime.datetime.now().strftime("%H:%M") or
not trigger.option.get('notify_at')):
threading.Thread(target=cls._exec_notify, args=(
None, trigger.option['notifies'], None, trigger.id, None, ci.id)).start()
return True
return False

View File

@ -1166,16 +1166,18 @@ class CITypeUniqueConstraintManager(object):
class CITypeTriggerManager(object):
@staticmethod
def get(type_id):
return CITypeTrigger.get_by(type_id=type_id, to_dict=True)
def get(type_id, to_dict=True):
return CITypeTrigger.get_by(type_id=type_id, to_dict=to_dict)
@staticmethod
def add(type_id, attr_id, notify):
CITypeTrigger.get_by(type_id=type_id, attr_id=attr_id) and abort(400, ErrFormat.ci_type_trigger_duplicate)
def add(type_id, attr_id, option):
for i in CITypeTrigger.get_by(type_id=type_id, attr_id=attr_id, to_dict=False):
if i.option == option:
return abort(400, ErrFormat.ci_type_trigger_duplicate)
not isinstance(notify, dict) and abort(400, ErrFormat.argument_invalid.format("notify"))
not isinstance(option, dict) and abort(400, ErrFormat.argument_invalid.format("option"))
trigger = CITypeTrigger.create(type_id=type_id, attr_id=attr_id, notify=notify)
trigger = CITypeTrigger.create(type_id=type_id, attr_id=attr_id, option=option)
CITypeHistoryManager.add(CITypeOperateType.ADD_TRIGGER,
type_id,
@ -1185,12 +1187,12 @@ class CITypeTriggerManager(object):
return trigger.to_dict()
@staticmethod
def update(_id, notify):
def update(_id, option):
existed = (CITypeTrigger.get_by_id(_id) or
abort(404, ErrFormat.ci_type_trigger_not_found.format("id={}".format(_id))))
existed2 = existed.to_dict()
new = existed.update(notify=notify)
new = existed.update(option=option)
CITypeHistoryManager.add(CITypeOperateType.UPDATE_TRIGGER,
existed.type_id,
@ -1210,35 +1212,3 @@ class CITypeTriggerManager(object):
existed.type_id,
trigger_id=_id,
change=existed.to_dict())
@staticmethod
def waiting_cis(trigger):
now = datetime.datetime.today()
delta_time = datetime.timedelta(days=(trigger.notify.get('before_days', 0) or 0))
attr = AttributeCache.get(trigger.attr_id)
value_table = TableMap(attr=attr).table
values = value_table.get_by(attr_id=attr.id, to_dict=False)
result = []
for v in values:
if (isinstance(v.value, (datetime.date, datetime.datetime)) and
(v.value - delta_time).strftime('%Y%m%d') == now.strftime("%Y%m%d")):
result.append(v)
return result
@staticmethod
def trigger_notify(trigger, ci):
if (trigger.notify.get('notify_at') == datetime.datetime.now().strftime("%H:%M") or
not trigger.notify.get('notify_at')):
from api.tasks.cmdb import trigger_notify
trigger_notify.apply_async(args=(trigger.notify, ci.ci_id), queue=CMDB_QUEUE)
return True
return False

View File

@ -16,6 +16,7 @@ from api.lib.perm.acl.cache import UserCache
from api.models.cmdb import Attribute
from api.models.cmdb import AttributeHistory
from api.models.cmdb import CIRelationHistory
from api.models.cmdb import CITriggerHistory
from api.models.cmdb import CITypeHistory
from api.models.cmdb import CITypeTrigger
from api.models.cmdb import CITypeUniqueConstraint
@ -286,3 +287,67 @@ class CITypeHistoryManager(object):
change=change)
CITypeHistory.create(**payload)
class CITriggerHistoryManager(object):
@staticmethod
def get(page, page_size, type_id=None, trigger_id=None, operate_type=None):
query = CITriggerHistory.get_by(only_query=True)
if type_id is not None:
query = query.filter(CITriggerHistory.type_id == type_id)
if trigger_id:
query = query.filter(CITriggerHistory.trigger_id == trigger_id)
if operate_type is not None:
query = query.filter(CITriggerHistory.operate_type == operate_type)
numfound = query.count()
query = query.order_by(CITriggerHistory.id.desc())
result = query.offset((page - 1) * page_size).limit(page_size)
result = [i.to_dict() for i in result]
for res in result:
if res.get('trigger_id'):
trigger = CITypeTrigger.get_by_id(res['trigger_id'])
res['trigger'] = trigger and trigger.to_dict()
return numfound, result
@staticmethod
def get_by_ci_id(ci_id):
res = db.session.query(CITriggerHistory, CITypeTrigger, OperationRecord).join(
CITypeTrigger, CITypeTrigger.id == CITriggerHistory.trigger_id).join(
OperationRecord, OperationRecord.id == CITriggerHistory.record_id).filter(
CITriggerHistory.ci_id == ci_id).order_by(CITriggerHistory.id.desc())
result = []
id2trigger = dict()
for i in res:
hist = i.CITriggerHistory
record = i.OperationRecord
item = dict(is_ok=hist.is_ok,
operate_type=hist.operate_type,
notify=hist.notify,
webhook=hist.webhook,
created_at=record.created_at.strftime('%Y-%m-%d %H:%M:%S'),
record_id=record.id,
hid=hist.id
)
if i.CITypeTrigger.id not in id2trigger:
id2trigger[i.CITypeTrigger.id] = i.CITypeTrigger.to_dict()
result.append(item)
return dict(items=result, id2trigger=id2trigger)
@staticmethod
def add(operate_type, record_id, ci_id, trigger_id, is_ok=False, notify=None, webhook=None):
CITriggerHistory.create(operate_type=operate_type,
record_id=record_id,
ci_id=ci_id,
trigger_id=trigger_id,
is_ok=is_ok,
notify=notify,
webhook=webhook)

View File

@ -18,7 +18,6 @@ from api.extensions import db
from api.lib.cmdb.attribute import AttributeManager
from api.lib.cmdb.cache import AttributeCache
from api.lib.cmdb.cache import CITypeAttributeCache
from api.lib.cmdb.const import ExistPolicy
from api.lib.cmdb.const import OperateType
from api.lib.cmdb.const import ValueTypeEnum
from api.lib.cmdb.history import AttributeHistoryManger
@ -140,6 +139,7 @@ class AttributeValueManager(object):
try:
db.session.commit()
except Exception as e:
db.session.rollback()
current_app.logger.error("write change failed: {}".format(str(e)))
return record_id
@ -235,7 +235,7 @@ class AttributeValueManager(object):
return key2attr
def create_or_update_attr_value2(self, ci, ci_dict, key2attr):
def create_or_update_attr_value(self, ci, ci_dict, key2attr):
"""
add or update attribute value, then write history
:param ci: instance object
@ -288,66 +288,6 @@ class AttributeValueManager(object):
return self._write_change2(changed)
def create_or_update_attr_value(self, key, value, ci, _no_attribute_policy=ExistPolicy.IGNORE, record_id=None):
"""
add or update attribute value, then write history
:param key: id, name or alias
:param value:
:param ci: instance object
:param _no_attribute_policy: ignore or reject
:param record_id: op record
:return:
"""
attr = self._get_attr(key)
if attr is None:
if _no_attribute_policy == ExistPolicy.IGNORE:
return
if _no_attribute_policy == ExistPolicy.REJECT:
return abort(400, ErrFormat.attribute_not_found.format(key))
value_table = TableMap(attr=attr).table
try:
if attr.is_list:
value_list = [self._validate(attr, i, value_table, ci) for i in handle_arg_list(value)]
if not value_list:
self._check_is_required(ci.type_id, attr, '')
existed_attrs = value_table.get_by(attr_id=attr.id, ci_id=ci.id, to_dict=False)
existed_values = [i.value for i in existed_attrs]
added = set(value_list) - set(existed_values)
deleted = set(existed_values) - set(value_list)
for v in added:
value_table.create(ci_id=ci.id, attr_id=attr.id, value=v)
record_id = self._write_change(ci.id, attr.id, OperateType.ADD, None, v, record_id, ci.type_id)
for v in deleted:
existed_attr = existed_attrs[existed_values.index(v)]
existed_attr.delete()
record_id = self._write_change(ci.id, attr.id, OperateType.DELETE, v, None, record_id, ci.type_id)
else:
value = self._validate(attr, value, value_table, ci)
existed_attr = value_table.get_by(attr_id=attr.id, ci_id=ci.id, first=True, to_dict=False)
existed_value = existed_attr and existed_attr.value
if existed_value is None and value is not None:
value_table.create(ci_id=ci.id, attr_id=attr.id, value=value)
record_id = self._write_change(ci.id, attr.id, OperateType.ADD, None, value, record_id, ci.type_id)
else:
if existed_value != value:
if value is None:
existed_attr.delete()
else:
existed_attr.update(value=value)
record_id = self._write_change(ci.id, attr.id, OperateType.UPDATE,
existed_value, value, record_id, ci.type_id)
return record_id
except Exception as e:
current_app.logger.warning(str(e))
return abort(400, ErrFormat.attribute_value_invalid2.format("{}({})".format(attr.alias, attr.name), value))
@staticmethod
def delete_attr_value(attr_id, ci_id):
attr = AttributeCache.get(attr_id)

View File

@ -0,0 +1,45 @@
# -*- coding:utf-8 -*-
import json
import requests
from flask import current_app
from jinja2 import Template
from api.lib.mail import send_mail
def _request_messenger(subject, body, tos, sender):
params = dict(sender=sender, title=subject,
tos=[to[sender] for to in tos if to.get(sender)])
if not params['tos']:
raise Exception("no receivers")
if sender == "email":
params['msgtype'] = 'text/html'
params['content'] = body
else:
params['msgtype'] = 'text'
params['content'] = json.dumps(dict(content=subject or body))
resp = requests.post(current_app.config.get('MESSENGER_URL'), json=params)
if resp.status_code != 200:
raise Exception(resp.text)
return resp.text
def notify_send(subject, body, methods, tos, payload=None):
payload = payload or {}
subject = Template(subject).render(payload)
body = Template(body).render(payload)
res = ''
for method in methods:
if method == "email" and not current_app.config.get('USE_MESSENGER', True):
send_mail(None, [to.get('email') for to in tos], subject, body)
res += _request_messenger(subject, body, tos, method) + "\n"
return res

105
cmdb-api/api/lib/webhook.py Normal file
View File

@ -0,0 +1,105 @@
# -*- coding:utf-8 -*-
import json
from functools import partial
import requests
from jinja2 import Template
from requests.auth import HTTPBasicAuth
from requests_oauthlib import OAuth2Session
class BearerAuth(requests.auth.AuthBase):
def __init__(self, token):
self.token = token
def __call__(self, r):
r.headers["authorization"] = "Bearer {}".format(self.token)
return r
def _wrap_auth(**kwargs):
auth_type = (kwargs.get('type') or "").lower()
if auth_type == "basicauth":
return HTTPBasicAuth(kwargs.get('username'), kwargs.get('password'))
elif auth_type == "bearer":
return BearerAuth(kwargs.get('token'))
elif auth_type == 'oauth2.0':
client_id = kwargs.get('client_id')
client_secret = kwargs.get('client_secret')
authorization_base_url = kwargs.get('authorization_base_url')
token_url = kwargs.get('token_url')
redirect_url = kwargs.get('redirect_url')
scope = kwargs.get('scope')
oauth2_session = OAuth2Session(client_id, scope=scope or None)
oauth2_session.authorization_url(authorization_base_url)
oauth2_session.fetch_token(token_url, client_secret=client_secret, authorization_response=redirect_url)
return oauth2_session
elif auth_type == "apikey":
return HTTPBasicAuth(kwargs.get('key'), kwargs.get('value'))
def webhook_request(webhook, payload):
"""
:param webhook:
{
"url": "https://veops.cn"
"method": "GET|POST|PUT|DELETE"
"body": {},
"headers": {
"Content-Type": "Application/json"
},
"parameters": {
"key": "value"
},
"authorization": {
"type": "BasicAuth|Bearer|OAuth2.0|APIKey",
"password": "mmmm", # BasicAuth
"username": "bbb", # BasicAuth
"token": "xxx", # Bearer
"key": "xxx", # APIKey
"value": "xxx", # APIKey
"client_id": "xxx", # OAuth2.0
"client_secret": "xxx", # OAuth2.0
"authorization_base_url": "xxx", # OAuth2.0
"token_url": "xxx", # OAuth2.0
"redirect_url": "xxx", # OAuth2.0
"scope": "xxx" # OAuth2.0
}
}
:param payload:
:return:
"""
assert webhook.get('url') is not None
url = Template(webhook['url']).render(payload)
params = webhook.get('parameters') or None
if isinstance(params, dict):
params = json.loads(Template(json.dumps(params)).render(payload))
data = Template(json.dumps(webhook.get('body', ''))).render(payload)
auth = _wrap_auth(**webhook.get('authorization', {}))
if (webhook.get('authorization', {}).get("type") or '').lower() == 'oauth2.0':
request = getattr(auth, webhook.get('method', 'GET').lower())
else:
request = partial(requests.request, webhook.get('method', 'GET'))
return request(
url,
params=params,
headers=webhook.get('headers') or None,
data=data,
auth=auth
)

View File

@ -125,16 +125,26 @@ class CITypeAttributeGroupItem(Model):
class CITypeTrigger(Model):
# __tablename__ = "c_ci_type_triggers"
__tablename__ = "c_c_t_t"
type_id = db.Column(db.Integer, db.ForeignKey('c_ci_types.id'), nullable=False)
attr_id = db.Column(db.Integer, db.ForeignKey("c_attributes.id"), nullable=False)
notify = db.Column(db.JSON) # {subject: x, body: x, wx_to: [], mail_to: [], before_days: 0, notify_at: 08:00}
attr_id = db.Column(db.Integer, db.ForeignKey("c_attributes.id"))
option = db.Column('notify', db.JSON)
class CITriggerHistory(Model):
__tablename__ = "c_ci_trigger_histories"
operate_type = db.Column(db.Enum(*OperateType.all(), name="operate_type"))
record_id = db.Column(db.Integer, db.ForeignKey("c_records.id"))
ci_id = db.Column(db.Integer, index=True, nullable=False)
trigger_id = db.Column(db.Integer, db.ForeignKey("c_c_t_t.id"))
is_ok = db.Column(db.Boolean, default=False)
notify = db.Column(db.Text)
webhook = db.Column(db.Text)
class CITypeUniqueConstraint(Model):
# __tablename__ = "c_ci_type_unique_constraints"
__tablename__ = "c_c_t_u_c"
type_id = db.Column(db.Integer, db.ForeignKey('c_ci_types.id'), nullable=False)
@ -363,7 +373,6 @@ class CITypeHistory(Model):
# preference
class PreferenceShowAttributes(Model):
# __tablename__ = "c_preference_show_attributes"
__tablename__ = "c_psa"
uid = db.Column(db.Integer, index=True, nullable=False)
@ -377,7 +386,6 @@ class PreferenceShowAttributes(Model):
class PreferenceTreeView(Model):
# __tablename__ = "c_preference_tree_views"
__tablename__ = "c_ptv"
uid = db.Column(db.Integer, index=True, nullable=False)
@ -386,7 +394,6 @@ class PreferenceTreeView(Model):
class PreferenceRelationView(Model):
# __tablename__ = "c_preference_relation_views"
__tablename__ = "c_prv"
uid = db.Column(db.Integer, index=True, nullable=False)

View File

@ -185,8 +185,8 @@ class CIUnique(APIView):
@has_perm_from_args("ci_id", ResourceTypeEnum.CI, PermEnum.UPDATE, CIManager.get_type_name)
def put(self, ci_id):
params = request.values
unique_name = params.keys()[0]
unique_value = params.values()[0]
unique_name = list(params.keys())[0]
unique_value = list(params.values())[0]
CIManager.update_unique_value(ci_id, unique_name, unique_value)

View File

@ -419,22 +419,21 @@ class CITypeTriggerView(APIView):
return self.jsonify(CITypeTriggerManager.get(type_id))
@has_perm_from_args("type_id", ResourceTypeEnum.CI, PermEnum.CONFIG, CITypeManager.get_name_by_id)
@args_required("attr_id")
@args_required("notify")
@args_required("option")
def post(self, type_id):
attr_id = request.values.get('attr_id')
notify = request.values.get('notify')
attr_id = request.values.get('attr_id') or None
option = request.values.get('option')
return self.jsonify(CITypeTriggerManager().add(type_id, attr_id, notify))
return self.jsonify(CITypeTriggerManager().add(type_id, attr_id, option))
@has_perm_from_args("type_id", ResourceTypeEnum.CI, PermEnum.CONFIG, CITypeManager.get_name_by_id)
@args_required("notify")
@args_required("option")
def put(self, type_id, _id):
assert type_id is not None
notify = request.values.get('notify')
option = request.values.get('option')
return self.jsonify(CITypeTriggerManager().update(_id, notify))
return self.jsonify(CITypeTriggerManager().update(_id, option))
@has_perm_from_args("type_id", ResourceTypeEnum.CI, PermEnum.CONFIG, CITypeManager.get_name_by_id)
def delete(self, type_id, _id):

View File

@ -5,15 +5,18 @@ import datetime
from flask import abort
from flask import request
from flask import session
from api.lib.cmdb.ci import CIManager
from api.lib.cmdb.const import PermEnum
from api.lib.cmdb.const import ResourceTypeEnum
from api.lib.cmdb.const import RoleEnum
from api.lib.cmdb.history import AttributeHistoryManger
from api.lib.cmdb.history import CITriggerHistoryManager
from api.lib.cmdb.history import CITypeHistoryManager
from api.lib.cmdb.resp_format import ErrFormat
from api.lib.perm.acl.acl import has_perm_from_args
from api.lib.perm.acl.acl import is_app_admin
from api.lib.perm.acl.acl import role_required
from api.lib.utils import get_page
from api.lib.utils import get_page_size
@ -76,6 +79,39 @@ class CIHistoryView(APIView):
return self.jsonify(result)
class CITriggerHistoryView(APIView):
url_prefix = ("/history/ci_triggers/<int:ci_id>", "/history/ci_triggers")
@has_perm_from_args("ci_id", ResourceTypeEnum.CI, PermEnum.READ, CIManager.get_type_name)
def get(self, ci_id=None):
if ci_id is not None:
result = CITriggerHistoryManager.get_by_ci_id(ci_id)
return self.jsonify(result)
if RoleEnum.CONFIG not in session.get("acl", {}).get("parentRoles", []) and not is_app_admin("cmdb"):
return abort(403, ErrFormat.role_required.format(RoleEnum.CONFIG))
type_id = request.values.get("type_id")
trigger_id = request.values.get("trigger_id")
operate_type = request.values.get("operate_type")
page = get_page(request.values.get('page', 1))
page_size = get_page_size(request.values.get('page_size', 1))
numfound, result = CITriggerHistoryManager.get(page,
page_size,
type_id=type_id,
trigger_id=trigger_id,
operate_type=operate_type)
return self.jsonify(page=page,
page_size=page_size,
numfound=numfound,
total=len(result),
result=result)
class CITypeHistoryView(APIView):
url_prefix = "/history/ci_types"

View File

@ -36,6 +36,7 @@ python-ldap==3.4.0
PyYAML==6.0
redis==4.6.0
requests==2.31.0
requests_oauthlib==1.3.1
six==1.12.0
SQLAlchemy==1.4.49
supervisor==4.0.3