perf(api): auto discovery has been upgraded (#559)

This commit is contained in:
pycook 2024-06-20 20:30:04 +08:00 committed by GitHub
parent c5761fc805
commit 5e0e64861f
18 changed files with 734 additions and 164 deletions

View File

@ -190,6 +190,7 @@ def cmdb_counter():
login_user(UserCache.get('worker')) login_user(UserCache.get('worker'))
i = 0 i = 0
today = datetime.date.today()
while True: while True:
try: try:
db.session.remove() db.session.remove()
@ -200,6 +201,10 @@ def cmdb_counter():
CMDBCounterCache.flush_adc_counter() CMDBCounterCache.flush_adc_counter()
i = 0 i = 0
if datetime.date.today() != today:
CMDBCounterCache.clear_ad_exec_history()
today = datetime.date.today()
CMDBCounterCache.flush_sub_counter() CMDBCounterCache.flush_sub_counter()
i += 1 i += 1
@ -493,3 +498,48 @@ def cmdb_agent_init():
click.echo("Key : {}".format(click.style(user.key, bg='red'))) click.echo("Key : {}".format(click.style(user.key, bg='red')))
click.echo("Secret: {}".format(click.style(user.secret, bg='red'))) click.echo("Secret: {}".format(click.style(user.secret, bg='red')))
@click.command()
@click.option(
'-v',
'--version',
help='input cmdb version, e.g. 2.4.6',
required=True,
)
@with_appcontext
def cmdb_patch(version):
"""
CMDB upgrade patch
"""
version = version[1:] if version.lower().startswith("v") else version
if version >= '2.4.6':
from api.models.cmdb import CITypeRelation
for cr in CITypeRelation.get_by(to_dict=False):
if hasattr(cr, 'parent_attr_id') and cr.parent_attr_id and not cr.parent_attr_ids:
parent_attr_ids, child_attr_ids = [cr.parent_attr_id], [cr.child_attr_id]
cr.update(parent_attr_ids=parent_attr_ids, child_attr_ids=child_attr_ids, commit=False)
db.session.commit()
from api.models.cmdb import AutoDiscoveryCIType, AutoDiscoveryCITypeRelation
from api.lib.cmdb.cache import CITypeCache, AttributeCache
for adt in AutoDiscoveryCIType.get_by(to_dict=False):
if adt.relation:
if not AutoDiscoveryCITypeRelation.get_by(ad_type_id=adt.type_id):
peer_type = CITypeCache.get(list(adt.relation.values())['type_name'])
peer_type_id = peer_type and peer_type.id
peer_attr = AttributeCache.get(list(adt.relation.values())['attr_name'])
peer_attr_id = peer_attr and peer_attr.id
if peer_type_id and peer_attr_id:
AutoDiscoveryCITypeRelation.create(ad_type_id=adt.type_id,
ad_key=list(adt.relation.keys())[0],
peer_type_id=peer_type_id,
peer_attr_id=peer_attr_id,
commit=False)
if hasattr(adt, 'interval') and adt.interval and not adt.cron:
adt.cron = "*/{} * * * *".format(adt.interval // 60)
db.session.commit()

View File

@ -1,34 +1,47 @@
# -*- coding:utf-8 -*- # -*- coding:utf-8 -*-
import copy
import datetime import datetime
import json import json
import os import os
from flask import abort
from flask import current_app
from flask_login import current_user
from sqlalchemy import func
from api.extensions import db from api.extensions import db
from api.lib.cmdb.auto_discovery.const import ClOUD_MAP from api.lib.cmdb.auto_discovery.const import ClOUD_MAP
from api.lib.cmdb.auto_discovery.const import DEFAULT_HTTP
from api.lib.cmdb.cache import AttributeCache
from api.lib.cmdb.cache import CITypeAttributeCache from api.lib.cmdb.cache import CITypeAttributeCache
from api.lib.cmdb.cache import CITypeCache from api.lib.cmdb.cache import CITypeCache
from api.lib.cmdb.ci import CIManager from api.lib.cmdb.ci import CIManager
from api.lib.cmdb.ci import CIRelationManager from api.lib.cmdb.ci import CIRelationManager
from api.lib.cmdb.ci_type import CITypeGroupManager from api.lib.cmdb.ci_type import CITypeGroupManager
from api.lib.cmdb.const import AutoDiscoveryType from api.lib.cmdb.const import AutoDiscoveryType
from api.lib.cmdb.const import CMDB_QUEUE
from api.lib.cmdb.const import PermEnum from api.lib.cmdb.const import PermEnum
from api.lib.cmdb.const import ResourceTypeEnum from api.lib.cmdb.const import ResourceTypeEnum
from api.lib.cmdb.resp_format import ErrFormat from api.lib.cmdb.resp_format import ErrFormat
from api.lib.cmdb.search import SearchError from api.lib.cmdb.search import SearchError
from api.lib.cmdb.search.ci import search from api.lib.cmdb.search.ci import search as ci_search
from api.lib.common_setting.role_perm_base import CMDBApp
from api.lib.mixin import DBMixin from api.lib.mixin import DBMixin
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 is_app_admin
from api.lib.perm.acl.acl import validate_permission from api.lib.perm.acl.acl import validate_permission
from api.lib.utils import AESCrypto from api.lib.utils import AESCrypto
from api.models.cmdb import AutoDiscoveryCI from api.models.cmdb import AutoDiscoveryCI
from api.models.cmdb import AutoDiscoveryCIType from api.models.cmdb import AutoDiscoveryCIType
from api.models.cmdb import AutoDiscoveryCITypeRelation
from api.models.cmdb import AutoDiscoveryCounter
from api.models.cmdb import AutoDiscoveryExecHistory
from api.models.cmdb import AutoDiscoveryRule from api.models.cmdb import AutoDiscoveryRule
from flask import abort from api.models.cmdb import AutoDiscoveryRuleSyncHistory
from flask import current_app from api.tasks.cmdb import write_ad_rule_sync_history
from flask_login import current_user
from sqlalchemy import func
PWD = os.path.abspath(os.path.dirname(__file__)) PWD = os.path.abspath(os.path.dirname(__file__))
app_cli = CMDBApp()
def parse_plugin_script(script): def parse_plugin_script(script):
@ -100,6 +113,14 @@ class AutoDiscoveryRuleCRUD(DBMixin):
self.cls.get_by(name=kwargs['name']) and abort(400, ErrFormat.adr_duplicate.format(kwargs['name'])) self.cls.get_by(name=kwargs['name']) and abort(400, ErrFormat.adr_duplicate.format(kwargs['name']))
if kwargs.get('is_plugin') and kwargs.get('plugin_script'): if kwargs.get('is_plugin') and kwargs.get('plugin_script'):
kwargs = check_plugin_script(**kwargs) kwargs = check_plugin_script(**kwargs)
acl = ACLManager(app_cli.app_name)
if not acl.has_permission(app_cli.op.Auto_Discovery,
app_cli.resource_type_name,
app_cli.op.create_plugin) and not is_app_admin(app_cli.app_name):
return abort(403, ErrFormat.no_permission.format(
app_cli.op.Auto_Discovery, app_cli.op.create_plugin))
kwargs['owner'] = current_user.uid
return kwargs return kwargs
@ -115,6 +136,14 @@ class AutoDiscoveryRuleCRUD(DBMixin):
if other and other.id != existed.id: if other and other.id != existed.id:
return abort(400, ErrFormat.adr_duplicate.format(kwargs['name'])) return abort(400, ErrFormat.adr_duplicate.format(kwargs['name']))
if existed.is_plugin:
acl = ACLManager(app_cli.app_name)
if not acl.has_permission(app_cli.op.Auto_Discovery,
app_cli.resource_type_name,
app_cli.op.update_plugin) and not is_app_admin(app_cli.app_name):
return abort(403, ErrFormat.no_permission.format(
app_cli.op.Auto_Discovery, app_cli.op.update_plugin))
return existed return existed
def update(self, _id, **kwargs): def update(self, _id, **kwargs):
@ -122,13 +151,27 @@ class AutoDiscoveryRuleCRUD(DBMixin):
if kwargs.get('is_plugin') and kwargs.get('plugin_script'): if kwargs.get('is_plugin') and kwargs.get('plugin_script'):
kwargs = check_plugin_script(**kwargs) kwargs = check_plugin_script(**kwargs)
for item in AutoDiscoveryCIType.get_by(adr_id=_id, to_dict=False):
item.update(updated_at=datetime.datetime.now())
return super(AutoDiscoveryRuleCRUD, self).update(_id, filter_none=False, **kwargs) return super(AutoDiscoveryRuleCRUD, self).update(_id, filter_none=False, **kwargs)
def _can_delete(self, **kwargs): def _can_delete(self, **kwargs):
if AutoDiscoveryCIType.get_by(adr_id=kwargs['_id'], first=True): if AutoDiscoveryCIType.get_by(adr_id=kwargs['_id'], first=True):
return abort(400, ErrFormat.adr_referenced) return abort(400, ErrFormat.adr_referenced)
return self._can_update(**kwargs) existed = self.cls.get_by_id(kwargs['_id']) or abort(
404, ErrFormat.adr_not_found.format("id={}".format(kwargs['_id'])))
if existed.is_plugin:
acl = ACLManager(app_cli.app_name)
if not acl.has_permission(app_cli.op.Auto_Discovery,
app_cli.resource_type_name,
app_cli.op.delete_plugin) and not is_app_admin(app_cli.app_name):
return abort(403, ErrFormat.no_permission.format(
app_cli.op.Auto_Discovery, app_cli.op.delete_plugin))
return existed
class AutoDiscoveryCITypeCRUD(DBMixin): class AutoDiscoveryCITypeCRUD(DBMixin):
@ -147,14 +190,34 @@ class AutoDiscoveryCITypeCRUD(DBMixin):
return cls.cls.get_by(type_id=type_id, to_dict=False) return cls.cls.get_by(type_id=type_id, to_dict=False)
@classmethod @classmethod
def get(cls, ci_id, oneagent_id, last_update_at=None): def get_ad_attributes(cls, type_id):
result = []
adts = cls.get_by_type_id(type_id)
for adt in adts:
adr = AutoDiscoveryRuleCRUD.get_by_id(adt.adr_id)
if not adr:
continue
if adr.type == "http":
for i in DEFAULT_HTTP:
if adr.name == i['name']:
attrs = AutoDiscoveryHTTPManager.get_attributes(
i['en'], (adt.extra_option or {}).get('category')) or []
result.extend([i.get('name') for i in attrs])
break
elif adr.type == "snmp":
attributes = AutoDiscoverySNMPManager.get_attributes()
result.extend([i.get('name') for i in (attributes or [])])
else:
result.extend([i.get('name') for i in (adr.attributes or [])])
return sorted(list(set(result)))
@classmethod
def get(cls, ci_id, oneagent_id, oneagent_name, last_update_at=None):
result = [] result = []
rules = cls.cls.get_by(to_dict=True) rules = cls.cls.get_by(to_dict=True)
for rule in rules: for rule in rules:
if rule.get('relation'):
continue
if isinstance(rule.get("extra_option"), dict) and rule['extra_option'].get('secret'): if isinstance(rule.get("extra_option"), dict) and rule['extra_option'].get('secret'):
if not (current_user.username == "cmdb_agent" or current_user.uid == rule['uid']): if not (current_user.username == "cmdb_agent" or current_user.uid == rule['uid']):
rule['extra_option'].pop('secret', None) rule['extra_option'].pop('secret', None)
@ -165,7 +228,7 @@ class AutoDiscoveryCITypeCRUD(DBMixin):
result.append(rule) result.append(rule)
elif rule['query_expr']: elif rule['query_expr']:
query = rule['query_expr'].lstrip('q').lstrip('=') query = rule['query_expr'].lstrip('q').lstrip('=')
s = search(query, fl=['_id'], count=1000000) s = ci_search(query, fl=['_id'], count=1000000)
try: try:
response, _, _, _, _, _ = s.search() response, _, _, _, _, _ = s.search()
except SearchError as e: except SearchError as e:
@ -182,9 +245,6 @@ class AutoDiscoveryCITypeCRUD(DBMixin):
if adr.type in (AutoDiscoveryType.SNMP, AutoDiscoveryType.HTTP): if adr.type in (AutoDiscoveryType.SNMP, AutoDiscoveryType.HTTP):
continue continue
if not rule['updated_at']:
continue
result.append(rule) result.append(rule)
new_last_update_at = "" new_last_update_at = ""
@ -195,6 +255,9 @@ class AutoDiscoveryCITypeCRUD(DBMixin):
if new_last_update_at < __last_update_at: if new_last_update_at < __last_update_at:
new_last_update_at = __last_update_at new_last_update_at = __last_update_at
write_ad_rule_sync_history.apply_async(args=(result, oneagent_id, oneagent_name, datetime.datetime.now()),
queue=CMDB_QUEUE)
if not last_update_at or new_last_update_at > last_update_at: if not last_update_at or new_last_update_at > last_update_at:
return result, new_last_update_at return result, new_last_update_at
else: else:
@ -213,7 +276,7 @@ class AutoDiscoveryCITypeCRUD(DBMixin):
agent_id = agent_id.strip() agent_id = agent_id.strip()
q = "op_duty:{0},-rd_duty:{0},oneagent_id:{1}" q = "op_duty:{0},-rd_duty:{0},oneagent_id:{1}"
s = search(q.format(current_user.username, agent_id.strip())) s = ci_search(q.format(current_user.username, agent_id.strip()))
try: try:
response, _, _, _, _, _ = s.search() response, _, _, _, _, _ = s.search()
if response: if response:
@ -222,7 +285,7 @@ class AutoDiscoveryCITypeCRUD(DBMixin):
current_app.logger.warning(e) current_app.logger.warning(e)
return abort(400, str(e)) return abort(400, str(e))
s = search(q.format(current_user.nickname, agent_id.strip())) s = ci_search(q.format(current_user.nickname, agent_id.strip()))
try: try:
response, _, _, _, _, _ = s.search() response, _, _, _, _, _ = s.search()
if response: if response:
@ -236,7 +299,7 @@ class AutoDiscoveryCITypeCRUD(DBMixin):
if query_expr.startswith('q='): if query_expr.startswith('q='):
query_expr = query_expr[2:] query_expr = query_expr[2:]
s = search(query_expr, count=1000000) s = ci_search(query_expr, count=1000000)
try: try:
response, _, _, _, _, _ = s.search() response, _, _, _, _, _ = s.search()
for i in response: for i in response:
@ -254,13 +317,21 @@ class AutoDiscoveryCITypeCRUD(DBMixin):
def _can_add(**kwargs): def _can_add(**kwargs):
if kwargs.get('adr_id'): if kwargs.get('adr_id'):
AutoDiscoveryRule.get_by_id(kwargs['adr_id']) or abort( adr = AutoDiscoveryRule.get_by_id(kwargs['adr_id']) or abort(
404, ErrFormat.adr_not_found.format("id={}".format(kwargs['adr_id']))) 404, ErrFormat.adr_not_found.format("id={}".format(kwargs['adr_id'])))
# if not adr.is_plugin: if adr.type == "http":
# other = self.cls.get_by(adr_id=adr.id, first=True, to_dict=False) kwargs.setdefault('extra_option', dict)
# if other: en_name = None
# ci_type = CITypeCache.get(other.type_id) for i in DEFAULT_HTTP:
# return abort(400, ErrFormat.adr_default_ref_once.format(ci_type.alias)) if i['name'] == adr.name:
en_name = i['en']
break
if en_name and kwargs['extra_option'].get('category'):
for item in ClOUD_MAP[en_name]:
if item["collect_key_map"].get(kwargs['extra_option']['category']):
kwargs["extra_option"]["collect_key"] = item["collect_key_map"][
kwargs['extra_option']['category']]
break
if kwargs.get('is_plugin') and kwargs.get('plugin_script'): if kwargs.get('is_plugin') and kwargs.get('plugin_script'):
kwargs = check_plugin_script(**kwargs) kwargs = check_plugin_script(**kwargs)
@ -268,6 +339,11 @@ class AutoDiscoveryCITypeCRUD(DBMixin):
if isinstance(kwargs.get('extra_option'), dict) and kwargs['extra_option'].get('secret'): if isinstance(kwargs.get('extra_option'), dict) and kwargs['extra_option'].get('secret'):
kwargs['extra_option']['secret'] = AESCrypto.encrypt(kwargs['extra_option']['secret']) kwargs['extra_option']['secret'] = AESCrypto.encrypt(kwargs['extra_option']['secret'])
ci_type = CITypeCache.get(kwargs['type_id'])
unique = AttributeCache.get(ci_type.unique_id)
if unique and unique.name not in (kwargs.get('attributes') or {}).values():
return abort(400, ErrFormat.ad_not_unique_key.format(unique.name))
kwargs['uid'] = current_user.uid kwargs['uid'] = current_user.uid
return kwargs return kwargs
@ -276,7 +352,29 @@ class AutoDiscoveryCITypeCRUD(DBMixin):
existed = self.cls.get_by_id(kwargs['_id']) or abort( existed = self.cls.get_by_id(kwargs['_id']) or abort(
404, ErrFormat.ad_not_found.format("id={}".format(kwargs['_id']))) 404, ErrFormat.ad_not_found.format("id={}".format(kwargs['_id'])))
self.__valid_exec_target(kwargs.get('agent_id'), kwargs.get('query_expr')) adr = AutoDiscoveryRule.get_by_id(existed.adr_id) or abort(
404, ErrFormat.adr_not_found.format("id={}".format(existed.adr_id)))
if adr.type == "http":
kwargs.setdefault('extra_option', dict)
en_name = None
for i in DEFAULT_HTTP:
if i['name'] == adr.name:
en_name = i['en']
break
if en_name and kwargs['extra_option'].get('category'):
for item in ClOUD_MAP[en_name]:
if item["collect_key_map"].get(kwargs['extra_option']['category']):
kwargs["extra_option"]["collect_key"] = item["collect_key_map"][
kwargs['extra_option']['category']]
break
if 'attributes' in kwargs:
self.__valid_exec_target(kwargs.get('agent_id'), kwargs.get('query_expr'))
ci_type = CITypeCache.get(existed.type_id)
unique = AttributeCache.get(ci_type.unique_id)
if unique and unique.name not in (kwargs.get('attributes') or {}).values():
return abort(400, ErrFormat.ad_not_unique_key.format(unique.name))
if isinstance(kwargs.get('extra_option'), dict) and kwargs['extra_option'].get('secret'): if isinstance(kwargs.get('extra_option'), dict) and kwargs['extra_option'].get('secret'):
if current_user.uid != existed.uid: if current_user.uid != existed.uid:
@ -292,7 +390,15 @@ class AutoDiscoveryCITypeCRUD(DBMixin):
if isinstance(kwargs.get('extra_option'), dict) and kwargs['extra_option'].get('secret'): if isinstance(kwargs.get('extra_option'), dict) and kwargs['extra_option'].get('secret'):
kwargs['extra_option']['secret'] = AESCrypto.encrypt(kwargs['extra_option']['secret']) kwargs['extra_option']['secret'] = AESCrypto.encrypt(kwargs['extra_option']['secret'])
return super(AutoDiscoveryCITypeCRUD, self).update(_id, filter_none=False, **kwargs) inst = self._can_update(_id=_id, **kwargs)
if inst.agent_id != kwargs.get('agent_id') or inst.query_expr != kwargs.get('query_expr'):
for item in AutoDiscoveryRuleSyncHistory.get_by(adt_id=inst.id, to_dict=False):
item.delete(commit=False)
db.session.commit()
obj = inst.update(_id=_id, filter_none=False, **kwargs)
return obj
def _can_delete(self, **kwargs): def _can_delete(self, **kwargs):
if AutoDiscoveryCICRUD.get_by_adt_id(kwargs['_id']): if AutoDiscoveryCICRUD.get_by_adt_id(kwargs['_id']):
@ -303,6 +409,56 @@ class AutoDiscoveryCITypeCRUD(DBMixin):
return existed return existed
def delete(self, _id):
inst = self._can_delete(_id=_id)
inst.soft_delete()
for item in AutoDiscoveryRuleSyncHistory.get_by(adt_id=inst.id, to_dict=False):
item.delete(commit=False)
db.session.commit()
attributes = self.get_ad_attributes(inst.type_id)
for item in AutoDiscoveryCITypeRelationCRUD.get_by_type_id(inst.type_id):
if item.ad_key not in attributes:
item.soft_delete()
return inst
class AutoDiscoveryCITypeRelationCRUD(DBMixin):
cls = AutoDiscoveryCITypeRelation
@classmethod
def get_by_type_id(cls, type_id, to_dict=False):
return cls.cls.get_by(ad_type_id=type_id, to_dict=to_dict)
def upsert(self, ad_type_id, relations):
existed = self.cls.get_by(ad_type_id=ad_type_id, to_dict=False)
existed = {(i.ad_key, i.peer_type_id, i.peer_attr_id): i for i in existed}
new = []
for r in relations:
k = (r.get('ad_key'), r.get('peer_type_id'), r.get('peer_attr_id'))
if len(list(filter(lambda x: x, k))) == 3 and k not in existed:
self.cls.create(ad_type_id=ad_type_id, **r)
new.append(k)
for deleted in set(existed.keys()) - set(new):
existed[deleted].soft_delete()
return self.get_by_type_id(ad_type_id, to_dict=True)
def _can_add(self, **kwargs):
pass
def _can_update(self, **kwargs):
pass
def _can_delete(self, **kwargs):
pass
class AutoDiscoveryCICRUD(DBMixin): class AutoDiscoveryCICRUD(DBMixin):
cls = AutoDiscoveryCI cls = AutoDiscoveryCI
@ -391,16 +547,24 @@ class AutoDiscoveryCICRUD(DBMixin):
changed = False changed = False
if existed is not None: if existed is not None:
if existed.instance != kwargs['instance']: if existed.instance != kwargs['instance']:
instance = copy.deepcopy(existed.instance) or {}
instance.update(kwargs['instance'])
kwargs['instance'] = instance
existed.update(filter_none=False, **kwargs) existed.update(filter_none=False, **kwargs)
AutoDiscoveryExecHistoryCRUD().add(type_id=adt.type_id,
stdout="update resource: {}".format(kwargs.get('unique_value')))
changed = True changed = True
else: else:
existed = self.cls.create(**kwargs) existed = self.cls.create(**kwargs)
AutoDiscoveryExecHistoryCRUD().add(type_id=adt.type_id,
stdout="add resource: {}".format(kwargs.get('unique_value')))
changed = True changed = True
if adt.auto_accept and changed: if adt.auto_accept and changed:
try: try:
self.accept(existed) self.accept(existed)
except Exception as e: except Exception as e:
current_app.logger.error(e)
return abort(400, str(e)) return abort(400, str(e))
elif changed: elif changed:
existed.update(is_accept=False, accept_time=None, accept_by=None, filter_none=False) existed.update(is_accept=False, accept_time=None, accept_by=None, filter_none=False)
@ -420,6 +584,13 @@ class AutoDiscoveryCICRUD(DBMixin):
inst.delete() inst.delete()
adt = AutoDiscoveryCIType.get_by_id(inst.adt_id)
if adt:
adt.update(updated_at=datetime.datetime.now())
AutoDiscoveryExecHistoryCRUD().add(type_id=inst.type_id,
stdout="delete resource: {}".format(inst.unique_value))
self._after_delete(inst) self._after_delete(inst)
return inst return inst
@ -435,6 +606,13 @@ class AutoDiscoveryCICRUD(DBMixin):
not is_app_admin("cmdb") and validate_permission(ci_type.name, ResourceTypeEnum.CI, PermEnum.DELETE, "cmdb") not is_app_admin("cmdb") and validate_permission(ci_type.name, ResourceTypeEnum.CI, PermEnum.DELETE, "cmdb")
existed.delete() existed.delete()
adt = AutoDiscoveryCIType.get_by_id(existed.adt_id)
if adt:
adt.update(updated_at=datetime.datetime.now())
AutoDiscoveryExecHistoryCRUD().add(type_id=type_id,
stdout="delete resource: {}".format(unique_value))
# TODO: delete ci # TODO: delete ci
@classmethod @classmethod
@ -447,32 +625,34 @@ class AutoDiscoveryCICRUD(DBMixin):
ci_id = None ci_id = None
if adt.attributes: if adt.attributes:
ci_dict = {adt.attributes[k]: v for k, v in adc.instance.items() if k in adt.attributes} ci_dict = {adt.attributes[k]: v for k, v in adc.instance.items() if k in adt.attributes}
ci_id = CIManager.add(adc.type_id, is_auto_discovery=True, **ci_dict) ci_id = CIManager.add(adc.type_id, is_auto_discovery=True, _is_admin=True, **ci_dict)
AutoDiscoveryExecHistoryCRUD().add(type_id=adt.type_id,
stdout="accept resource: {}".format(adc.unique_value))
relation_adts = AutoDiscoveryCIType.get_by(type_id=adt.type_id, adr_id=None, to_dict=False) relation_ads = AutoDiscoveryCITypeRelation.get_by(ad_type_id=adt.type_id, to_dict=False)
for r_adt in relation_adts: for r_adt in relation_ads:
if not r_adt.relation or ci_id is None: ad_key = r_adt.ad_key
if not adc.instance.get(ad_key):
continue continue
for ad_key in r_adt.relation:
if not adc.instance.get(ad_key): ad_key_values = [adc.instance.get(ad_key)] if not isinstance(
continue adc.instance.get(ad_key), list) else adc.instance.get(ad_key)
cmdb_key = r_adt.relation[ad_key] for ad_key_value in ad_key_values:
query = "_type:{},{}:{}".format(cmdb_key.get('type_name'), cmdb_key.get('attr_name'), query = "_type:{},{}:{}".format(r_adt.peer_type_id, r_adt.peer_attr_id, ad_key_value)
adc.instance.get(ad_key)) s = ci_search(query, use_ci_filter=False, count=1000000)
s = search(query)
try: try:
response, _, _, _, _, _ = s.search() response, _, _, _, _, _ = s.search()
except SearchError as e: except SearchError as e:
current_app.logger.warning(e) current_app.logger.warning(e)
return abort(400, str(e)) return abort(400, str(e))
relation_ci_id = response and response[0]['_id'] for relation_ci in response:
if relation_ci_id: relation_ci_id = relation_ci['_id']
try: try:
CIRelationManager.add(ci_id, relation_ci_id) CIRelationManager.add(ci_id, relation_ci_id, valid=False)
except: except:
try: try:
CIRelationManager.add(relation_ci_id, ci_id) CIRelationManager.add(relation_ci_id, ci_id, valid=False)
except: except:
pass pass
@ -485,14 +665,35 @@ class AutoDiscoveryCICRUD(DBMixin):
class AutoDiscoveryHTTPManager(object): class AutoDiscoveryHTTPManager(object):
@staticmethod @staticmethod
def get_categories(name): def get_categories(name):
return (ClOUD_MAP.get(name) or {}).get('categories') or [] categories = (ClOUD_MAP.get(name) or {}) or []
for item in copy.deepcopy(categories):
item.pop('map', None)
return categories
def get_resources(self, name):
en_name = None
for i in DEFAULT_HTTP:
if i['name'] == name:
en_name = i['en']
break
if en_name:
categories = self.get_categories(en_name)
return [j for i in categories for j in i['items']]
return []
@staticmethod @staticmethod
def get_attributes(name, category): def get_attributes(provider, resource):
tpt = ((ClOUD_MAP.get(name) or {}).get('map') or {}).get(category) for item in (ClOUD_MAP.get(provider) or {}):
if tpt and os.path.exists(os.path.join(PWD, tpt)): for _resource in (item.get('map') or {}):
with open(os.path.join(PWD, tpt)) as f: if _resource == resource:
return json.loads(f.read()) tpt = item['map'][_resource]
if tpt and os.path.exists(os.path.join(PWD, tpt)):
with open(os.path.join(PWD, tpt)) as f:
return json.loads(f.read())
return [] return []
@ -506,3 +707,62 @@ class AutoDiscoverySNMPManager(object):
return json.loads(f.read()) return json.loads(f.read())
return [] return []
class AutoDiscoveryRuleSyncHistoryCRUD(DBMixin):
cls = AutoDiscoveryRuleSyncHistory
def _can_add(self, **kwargs):
pass
def _can_update(self, **kwargs):
pass
def _can_delete(self, **kwargs):
pass
def upsert(self, **kwargs):
existed = self.cls.get_by(adt_id=kwargs.get('adt_id'),
oneagent_id=kwargs.get('oneagent_id'),
oneagent_name=kwargs.get('oneagent_name'),
first=True,
to_dict=False)
if existed is not None:
existed.update(**kwargs)
else:
self.cls.create(**kwargs)
class AutoDiscoveryExecHistoryCRUD(DBMixin):
cls = AutoDiscoveryExecHistory
def _can_add(self, **kwargs):
pass
def _can_update(self, **kwargs):
pass
def _can_delete(self, **kwargs):
pass
class AutoDiscoveryCounterCRUD(DBMixin):
cls = AutoDiscoveryCounter
def get(self, type_id):
res = self.cls.get_by(type_id=type_id, first=True, to_dict=True)
if res is None:
return dict(rule_count=0, exec_target_count=0, instance_count=0, accept_count=0,
this_month_count=0, this_week_count=0, last_month_count=0, last_week_count=0)
return res
def _can_add(self, **kwargs):
pass
def _can_update(self, **kwargs):
pass
def _can_delete(self, **kwargs):
pass

View File

@ -3,13 +3,13 @@
from api.lib.cmdb.const import AutoDiscoveryType from api.lib.cmdb.const import AutoDiscoveryType
DEFAULT_HTTP = [ DEFAULT_HTTP = [
dict(name="阿里云", type=AutoDiscoveryType.HTTP, is_inner=True, is_plugin=False, dict(name="阿里云", en="aliyun", type=AutoDiscoveryType.HTTP, is_inner=True, is_plugin=False,
option={'icon': {'name': 'caise-aliyun'}}), option={'icon': {'name': 'caise-aliyun'}}),
dict(name="腾讯云", type=AutoDiscoveryType.HTTP, is_inner=True, is_plugin=False, dict(name="腾讯云", en="tencentcloud", type=AutoDiscoveryType.HTTP, is_inner=True, is_plugin=False,
option={'icon': {'name': 'caise-tengxunyun'}}), option={'icon': {'name': 'caise-tengxunyun'}}),
dict(name="华为云", type=AutoDiscoveryType.HTTP, is_inner=True, is_plugin=False, dict(name="华为云", en="huaweicloud", type=AutoDiscoveryType.HTTP, is_inner=True, is_plugin=False,
option={'icon': {'name': 'caise-huaweiyun'}}), option={'icon': {'name': 'caise-huaweiyun'}}),
dict(name="AWS", type=AutoDiscoveryType.HTTP, is_inner=True, is_plugin=False, dict(name="AWS", en="aws", type=AutoDiscoveryType.HTTP, is_inner=True, is_plugin=False,
option={'icon': {'name': 'caise-aws'}}), option={'icon': {'name': 'caise-aws'}}),
dict(name="交换机", type=AutoDiscoveryType.SNMP, is_inner=True, is_plugin=False, dict(name="交换机", type=AutoDiscoveryType.SNMP, is_inner=True, is_plugin=False,
@ -23,31 +23,47 @@ DEFAULT_HTTP = [
] ]
ClOUD_MAP = { ClOUD_MAP = {
"aliyun": { "aliyun": [{
"categories": ["云服务器 ECS"], "category": "计算",
"items": ["云服务器 ECS"],
"map": { "map": {
"云服务器 ECS": "templates/aliyun_ecs.json", "云服务器 ECS": "templates/aliyun_ecs.json",
},
"collect_key_map": {
"云服务器 ECS": "ali.ecs",
} }
}, }],
"tencentcloud": { "tencentcloud": [{
"categories": ["云服务器 CVM"], "category": "计算",
"items": ["云服务器 CVM"],
"map": { "map": {
"云服务器 CVM": "templates/tencent_cvm.json", "云服务器 CVM": "templates/tencent_cvm.json",
},
"collect_key_map": {
"云服务器 CVM": "tencent.cvm",
} }
}, }],
"huaweicloud": { "huaweicloud": [{
"categories": ["云服务器 ECS"], "category": "计算",
"items": ["云服务器 ECS"],
"map": { "map": {
"云服务器 ECS": "templates/huaweicloud_ecs.json", "云服务器 ECS": "templates/huaweicloud_ecs.json",
},
"collect_key_map": {
"云服务器 ECS": "huawei.ecs",
} }
}, }],
"aws": { "aws": [{
"categories": ["云服务器 EC2"], "category": "计算",
"items": ["云服务器 EC2"],
"map": { "map": {
"云服务器 EC2": "templates/aws_ec2.json", "云服务器 EC2": "templates/aws_ec2.json",
},
"collect_key_map": {
"云服务器 EC2": "aws.ec2",
} }
}, }],
} }

View File

@ -2,12 +2,19 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import datetime
from flask import current_app from flask import current_app
from api.extensions import cache from api.extensions import cache
from api.extensions import db from api.extensions import db
from api.lib.cmdb.custom_dashboard import CustomDashboardManager from api.lib.cmdb.custom_dashboard import CustomDashboardManager
from api.models.cmdb import Attribute from api.models.cmdb import Attribute, AutoDiscoveryExecHistory
from api.models.cmdb import AutoDiscoveryCI
from api.models.cmdb import AutoDiscoveryCIType
from api.models.cmdb import AutoDiscoveryCITypeRelation
from api.models.cmdb import AutoDiscoveryCounter
from api.models.cmdb import AutoDiscoveryRuleSyncHistory
from api.models.cmdb import CI from api.models.cmdb import CI
from api.models.cmdb import CIType from api.models.cmdb import CIType
from api.models.cmdb import CITypeAttribute from api.models.cmdb import CITypeAttribute
@ -448,7 +455,67 @@ class CMDBCounterCache(object):
cache.set(cls.KEY2, result, timeout=0) cache.set(cls.KEY2, result, timeout=0)
return result res = db.session.query(AutoDiscoveryCI.created_at,
AutoDiscoveryCI.updated_at,
AutoDiscoveryCI.adt_id,
AutoDiscoveryCI.type_id,
AutoDiscoveryCI.is_accept).filter(AutoDiscoveryCI.deleted.is_(False))
today = datetime.datetime.today()
this_month = datetime.datetime(today.year, today.month, 1)
last_month = this_month - datetime.timedelta(days=1)
last_month = datetime.datetime(last_month.year, last_month.month, 1)
this_week = today - datetime.timedelta(days=datetime.date.weekday(today))
this_week = datetime.datetime(this_week.year, this_week.month, this_week.day)
last_week = this_week - datetime.timedelta(days=7)
last_week = datetime.datetime(last_week.year, last_week.month, last_week.day)
result = dict()
for i in res:
if i.type_id not in result:
result[i.type_id] = dict(instance_count=0, accept_count=0,
this_month_count=0, this_week_count=0, last_month_count=0, last_week_count=0)
adts = AutoDiscoveryCIType.get_by(type_id=i.type_id, to_dict=False)
result[i.type_id]['rule_count'] = len(adts) + AutoDiscoveryCITypeRelation.get_by(
ad_type_id=i.type_id, only_query=True).count()
result[i.type_id]['exec_target_count'] = len(
set([i.oneagent_id for adt in adts for i in db.session.query(
AutoDiscoveryRuleSyncHistory.oneagent_id).filter(
AutoDiscoveryRuleSyncHistory.adt_id == adt.id)]))
result[i.type_id]['instance_count'] += 1
if i.is_accept:
result[i.type_id]['accept_count'] += 1
if last_month <= i.created_at < this_month:
result[i.type_id]['last_month_count'] += 1
elif i.created_at >= this_month:
result[i.type_id]['this_month_count'] += 1
if last_week <= i.created_at < this_week:
result[i.type_id]['last_week_count'] += 1
elif i.created_at >= this_week:
result[i.type_id]['this_week_count'] += 1
for type_id in result:
existed = AutoDiscoveryCounter.get_by(type_id=type_id, first=True, to_dict=False)
if existed is None:
AutoDiscoveryCounter.create(type_id=type_id, **result[type_id])
else:
existed.update(**result[type_id])
for i in AutoDiscoveryCounter.get_by(to_dict=False):
if i.type_id not in result:
i.delete()
@classmethod
def clear_ad_exec_history(cls):
ci_types = CIType.get_by(to_dict=False)
for ci_type in ci_types:
for i in AutoDiscoveryExecHistory.get_by(type_id=ci_type.id, only_query=True).order_by(
AutoDiscoveryExecHistory.id.desc()).offset(50000):
i.delete(commit=False)
db.session.commit()
@classmethod @classmethod
def get_adc_counter(cls): def get_adc_counter(cls):

View File

@ -223,7 +223,7 @@ class CIManager(object):
def ci_is_exist(unique_key, unique_value, type_id): def ci_is_exist(unique_key, unique_value, type_id):
""" """
:param unique_key: is a attribute :param unique_key: is an attribute
:param unique_value: :param unique_value:
:param type_id: :param type_id:
:return: :return:
@ -432,7 +432,7 @@ class CIManager(object):
for attr_id in password_dict: for attr_id in password_dict:
record_id = cls.save_password(ci.id, attr_id, password_dict[attr_id], record_id, ci_type.id) record_id = cls.save_password(ci.id, attr_id, password_dict[attr_id], record_id, ci_type.id)
if record_id or has_dynamic: # has change if record_id or has_dynamic: # has changed
ci_cache.apply_async(args=(ci.id, operate_type, record_id), queue=CMDB_QUEUE) ci_cache.apply_async(args=(ci.id, operate_type, record_id), queue=CMDB_QUEUE)
if ref_ci_dict: # add relations if ref_ci_dict: # add relations
@ -504,7 +504,7 @@ class CIManager(object):
for attr_id in password_dict: for attr_id in password_dict:
record_id = self.save_password(ci.id, attr_id, password_dict[attr_id], record_id, ci.type_id) record_id = self.save_password(ci.id, attr_id, password_dict[attr_id], record_id, ci.type_id)
if record_id or has_dynamic: # has change if record_id or has_dynamic: # has changed
if not __sync: if not __sync:
ci_cache.apply_async(args=(ci_id, OperateType.UPDATE, record_id), queue=CMDB_QUEUE) ci_cache.apply_async(args=(ci_id, OperateType.UPDATE, record_id), queue=CMDB_QUEUE)
else: else:
@ -737,7 +737,7 @@ class CIManager(object):
fields=None, value_tables=None, unique_required=False, excludes=None): fields=None, value_tables=None, unique_required=False, excludes=None):
""" """
:param ci_ids: list of CI instance ID, eg. ['1', '2'] :param ci_ids: list of CI instance ID, e.g. ['1', '2']
:param ret_key: name, id or alias :param ret_key: name, id or alias
:param fields: :param fields:
:param value_tables: :param value_tables:
@ -1296,7 +1296,7 @@ class CIRelationManager(object):
relations = _relations relations = _relations
else: else:
relations &= _relations relations &= _relations
for parent_ci_id, child_ci_id in relations: for parent_ci_id, child_ci_id in (relations or []):
CIRelationManager.add(parent_ci_id, child_ci_id, valid=False) CIRelationManager.add(parent_ci_id, child_ci_id, valid=False)
parent_items = CITypeRelation.get_by(child_id=type_id, only_query=True).filter( parent_items = CITypeRelation.get_by(child_id=type_id, only_query=True).filter(
@ -1316,7 +1316,7 @@ class CIRelationManager(object):
relations = _relations relations = _relations
else: else:
relations &= _relations relations &= _relations
for parent_ci_id, child_ci_id in relations: for parent_ci_id, child_ci_id in (relations or []):
CIRelationManager.add(parent_ci_id, child_ci_id, valid=False) CIRelationManager.add(parent_ci_id, child_ci_id, valid=False)
@classmethod @classmethod

View File

@ -229,6 +229,9 @@ class CITypeManager(object):
if CI.get_by(type_id=type_id, first=True, to_dict=False) is not None: if CI.get_by(type_id=type_id, first=True, to_dict=False) is not None:
return abort(400, ErrFormat.ci_exists_and_cannot_delete_type) return abort(400, ErrFormat.ci_exists_and_cannot_delete_type)
if CITypeInheritance.get_by(parent_id=type_id, first=True):
return abort(400, ErrFormat.ci_type_inheritance_cannot_delete)
relation_views = PreferenceRelationView.get_by(to_dict=False) relation_views = PreferenceRelationView.get_by(to_dict=False)
for rv in relation_views: for rv in relation_views:
for item in (rv.cr_ids or []): for item in (rv.cr_ids or []):
@ -253,21 +256,21 @@ class CITypeManager(object):
item.delete(commit=False) item.delete(commit=False)
for item in AutoDiscoveryCITypeRelation.get_by(ad_type_id=type_id, to_dict=False): for item in AutoDiscoveryCITypeRelation.get_by(ad_type_id=type_id, to_dict=False):
item.delete(commit=False) item.soft_delete(commit=False)
for item in AutoDiscoveryCITypeRelation.get_by(peer_type_id=type_id, to_dict=False): for item in AutoDiscoveryCITypeRelation.get_by(peer_type_id=type_id, to_dict=False):
item.delete(commit=False) item.soft_delete(commit=False)
for item in CITypeInheritance.get_by(parent_id=type_id, to_dict=False): for item in CITypeInheritance.get_by(parent_id=type_id, to_dict=False):
item.delete(commit=False) item.soft_delete(commit=False)
for item in CITypeInheritance.get_by(child_id=type_id, to_dict=False): for item in CITypeInheritance.get_by(child_id=type_id, to_dict=False):
item.delete(commit=False) item.soft_delete(commit=False)
try: try:
from api.models.cmdb import CITypeReconciliation from api.models.cmdb import CITypeReconciliation
for item in CITypeReconciliation.get_by(type_id=type_id, to_dict=False): for item in CITypeReconciliation.get_by(type_id=type_id, to_dict=False):
item.delete(commit=False) item.soft_delete(commit=False)
except Exception: except Exception:
pass pass

View File

@ -55,9 +55,9 @@ class CITypeOperateType(BaseEnum):
DELETE_UNIQUE_CONSTRAINT = "11" # 删除联合唯一 DELETE_UNIQUE_CONSTRAINT = "11" # 删除联合唯一
ADD_RELATION = "12" # 新增关系 ADD_RELATION = "12" # 新增关系
DELETE_RELATION = "13" # 删除关系 DELETE_RELATION = "13" # 删除关系
ADD_RECONCILIATION = "14" # 删除关系 ADD_RECONCILIATION = "14" # 新增数据合规
UPDATE_RECONCILIATION = "15" # 删除关系 UPDATE_RECONCILIATION = "15" # 修改数据合规
DELETE_RECONCILIATION = "16" # 删除关系 DELETE_RECONCILIATION = "16" # 删除数据合规
class RetKey(BaseEnum): class RetKey(BaseEnum):

View File

@ -13,6 +13,7 @@ from api.lib.cmdb.const import OperateType
from api.lib.cmdb.perms import CIFilterPermsCRUD from api.lib.cmdb.perms import CIFilterPermsCRUD
from api.lib.cmdb.resp_format import ErrFormat from api.lib.cmdb.resp_format import ErrFormat
from api.lib.perm.acl.cache import UserCache from api.lib.perm.acl.cache import UserCache
from api.models.cmdb import CI
from api.models.cmdb import Attribute from api.models.cmdb import Attribute
from api.models.cmdb import AttributeHistory from api.models.cmdb import AttributeHistory
from api.models.cmdb import CIRelationHistory from api.models.cmdb import CIRelationHistory
@ -306,7 +307,7 @@ class CITriggerHistoryManager(object):
def get(page, page_size, type_id=None, trigger_id=None, operate_type=None): def get(page, page_size, type_id=None, trigger_id=None, operate_type=None):
query = CITriggerHistory.get_by(only_query=True) query = CITriggerHistory.get_by(only_query=True)
if type_id: if type_id:
query = query.filter(CITriggerHistory.type_id == type_id) query = query.join(CI, CI.id == CITriggerHistory.ci_id).filter(CI.type_id == type_id)
if trigger_id: if trigger_id:
query = query.filter(CITriggerHistory.trigger_id == trigger_id) query = query.filter(CITriggerHistory.trigger_id == trigger_id)

View File

@ -62,6 +62,7 @@ class ErrFormat(CommonErrFormat):
"The model cannot be deleted because the CI already exists") # 因为CI已经存在不能删除模型 "The model cannot be deleted because the CI already exists") # 因为CI已经存在不能删除模型
ci_exists_and_cannot_delete_inheritance = _l( ci_exists_and_cannot_delete_inheritance = _l(
"The inheritance cannot be deleted because the CI already exists") # 因为CI已经存在不能删除继承关系 "The inheritance cannot be deleted because the CI already exists") # 因为CI已经存在不能删除继承关系
ci_type_inheritance_cannot_delete = _l("The model is inherited and cannot be deleted") # 该模型被继承, 不能删除
# 因为关系视图 {} 引用了该模型,不能删除模型 # 因为关系视图 {} 引用了该模型,不能删除模型
ci_relation_view_exists_and_cannot_delete_type = _l( ci_relation_view_exists_and_cannot_delete_type = _l(

View File

@ -16,7 +16,7 @@ from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION
from api.lib.cmdb.const import ResourceTypeEnum from api.lib.cmdb.const import ResourceTypeEnum
from api.lib.cmdb.resp_format import ErrFormat from api.lib.cmdb.resp_format import ErrFormat
from api.lib.cmdb.search import SearchError from api.lib.cmdb.search import SearchError
from api.lib.cmdb.search.ci import search from api.lib.cmdb.search.ci import search as ci_search
from api.lib.perm.acl.acl import ACLManager 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 is_app_admin
from api.models.cmdb import TopologyView from api.models.cmdb import TopologyView
@ -178,7 +178,7 @@ class TopologyViewManager(object):
q = (central_node_instances[2:] if central_node_instances.startswith('q=') else q = (central_node_instances[2:] if central_node_instances.startswith('q=') else
central_node_instances) central_node_instances)
s = search(q, fl=['_id', show_key.name], use_id_filter=False, use_ci_filter=False, count=1000000) s = ci_search(q, fl=['_id', show_key.name], use_id_filter=False, use_ci_filter=False, count=1000000)
try: try:
response, _, _, _, _, _ = s.search() response, _, _, _, _, _ = s.search()
except SearchError as e: except SearchError as e:
@ -238,7 +238,7 @@ class TopologyViewManager(object):
type2show[type_id] = attr.name type2show[type_id] = attr.name
if id2node: if id2node:
s = search("_id:({})".format(';'.join(id2node.keys())), fl=list(fl), s = ci_search("_id:({})".format(';'.join(id2node.keys())), fl=list(fl),
use_id_filter=False, use_ci_filter=False, count=1000000) use_id_filter=False, use_ci_filter=False, count=1000000)
try: try:
response, _, _, _, _, _ = s.search() response, _, _, _, _, _ = s.search()

View File

@ -8,6 +8,8 @@ from api.extensions import db
from api.lib.utils import get_page from api.lib.utils import get_page
from api.lib.utils import get_page_size from api.lib.utils import get_page_size
__author__ = 'pycook'
class DBMixin(object): class DBMixin(object):
cls = None cls = None
@ -17,13 +19,18 @@ class DBMixin(object):
page = get_page(page) page = get_page(page)
page_size = get_page_size(page_size) page_size = get_page_size(page_size)
if fl is None: if fl is None:
query = db.session.query(cls.cls).filter(cls.cls.deleted.is_(False)) query = db.session.query(cls.cls)
else: else:
query = db.session.query(*[getattr(cls.cls, i) for i in fl]).filter(cls.cls.deleted.is_(False)) query = db.session.query(*[getattr(cls.cls, i) for i in fl])
_query = None _query = None
if count_query: if count_query:
_query = db.session.query(func.count(cls.cls.id)).filter(cls.cls.deleted.is_(False)) _query = db.session.query(func.count(cls.cls.id))
if hasattr(cls.cls, 'deleted'):
query = query.filter(cls.cls.deleted.is_(False))
if _query:
_query = _query.filter(cls.cls.deleted.is_(False))
for k in kwargs: for k in kwargs:
if hasattr(cls.cls, k): if hasattr(cls.cls, k):

View File

@ -253,9 +253,6 @@ def is_app_admin(app=None):
if app is None: if app is None:
return False return False
if hasattr(current_user, 'username') and current_user.username == 'worker':
return True
app_id = app.id app_id = app.id
if 'acl_admin' in session.get("acl", {}).get("parentRoles", []): if 'acl_admin' in session.get("acl", {}).get("parentRoles", []):
return True return True

View File

@ -29,6 +29,6 @@ class CommonErrFormat(object):
role_required = _l("Role {} can only operate!") # 角色 {} 才能操作! role_required = _l("Role {} can only operate!") # 角色 {} 才能操作!
user_not_found = _l("User {} does not exist") # 用户 {} 不存在 user_not_found = _l("User {} does not exist") # 用户 {} 不存在
no_permission = _l("You do not have {} permission for resource: {}!") # 您没有资源: {} 的{}权限! no_permission = _l("For resource: {}, you do not have {} permission!") # 您没有资源: {} 的{}权限!
no_permission2 = _l("You do not have permission to operate!") # 您没有操作权限! no_permission2 = _l("You do not have permission to operate!") # 您没有操作权限!
no_permission_only_owner = _l("Only the creator or administrator has permission!") # 只有创建人或者管理员才有权限! no_permission_only_owner = _l("Only the creator or administrator has permission!") # 只有创建人或者管理员才有权限!

View File

@ -566,14 +566,14 @@ class AutoDiscoveryCIType(Model):
attributes = db.Column(db.JSON) # {ad_key: cmdb_key} attributes = db.Column(db.JSON) # {ad_key: cmdb_key}
relation = db.Column(db.JSON) # [{ad_key: {type_id: x, attr_id: x}}] relation = db.Column(db.JSON) # [{ad_key: {type_id: x, attr_id: x}}], CMDB > 2.4.5: deprecated
auto_accept = db.Column(db.Boolean, default=False) auto_accept = db.Column(db.Boolean, default=False)
agent_id = db.Column(db.String(8), index=True) agent_id = db.Column(db.String(8), index=True)
query_expr = db.Column(db.Text) query_expr = db.Column(db.Text)
interval = db.Column(db.Integer) # seconds interval = db.Column(db.Integer) # seconds, > 2.4.5: deprecated
cron = db.Column(db.String(128)) cron = db.Column(db.String(128))
extra_option = db.Column(db.JSON) extra_option = db.Column(db.JSON)
@ -604,6 +604,36 @@ class AutoDiscoveryCI(Model):
accept_time = db.Column(db.DateTime) accept_time = db.Column(db.DateTime)
class AutoDiscoveryRuleSyncHistory(Model2):
__tablename__ = "c_ad_rule_sync_histories"
adt_id = db.Column(db.Integer, db.ForeignKey('c_ad_ci_types.id'))
oneagent_id = db.Column(db.String(8))
oneagent_name = db.Column(db.String(64))
sync_at = db.Column(db.DateTime, default=datetime.datetime.now())
class AutoDiscoveryExecHistory(Model2):
__tablename__ = "c_ad_exec_histories"
type_id = db.Column(db.Integer, index=True)
stdout = db.Column(db.Text)
class AutoDiscoveryCounter(Model2):
__tablename__ = "c_ad_counter"
type_id = db.Column(db.Integer, index=True)
rule_count = db.Column(db.Integer, default=0)
exec_target_count = db.Column(db.Integer, default=0)
instance_count = db.Column(db.Integer, default=0)
accept_count = db.Column(db.Integer, default=0)
this_month_count = db.Column(db.Integer, default=0)
this_week_count = db.Column(db.Integer, default=0)
last_month_count = db.Column(db.Integer, default=0)
last_week_count = db.Column(db.Integer, default=0)
class CIFilterPerms(Model): class CIFilterPerms(Model):
__tablename__ = "c_ci_filter_perms" __tablename__ = "c_ci_filter_perms"

View File

@ -2,6 +2,7 @@
import json import json
import datetime
import redis_lock import redis_lock
from flask import current_app from flask import current_app
@ -25,6 +26,8 @@ from api.lib.utils import handle_arg_list
from api.models.cmdb import CI from api.models.cmdb import CI
from api.models.cmdb import CIRelation from api.models.cmdb import CIRelation
from api.models.cmdb import CITypeAttribute from api.models.cmdb import CITypeAttribute
from api.models.cmdb import AutoDiscoveryCI
from api.models.cmdb import AutoDiscoveryCIType
@celery.task(name="cmdb.ci_cache", queue=CMDB_QUEUE) @celery.task(name="cmdb.ci_cache", queue=CMDB_QUEUE)
@ -87,6 +90,13 @@ def ci_delete(ci_id):
else: else:
rd.delete(ci_id, REDIS_PREFIX_CI) rd.delete(ci_id, REDIS_PREFIX_CI)
instance = AutoDiscoveryCI.get_by(ci_id=ci_id, to_dict=False, first=True)
if instance is not None:
adt = AutoDiscoveryCIType.get_by_id(instance.adt_id)
if adt:
adt.update(updated_at=datetime.datetime.now())
instance.delete()
current_app.logger.info("{0} delete..........".format(ci_id)) current_app.logger.info("{0} delete..........".format(ci_id))
@ -249,3 +259,21 @@ def calc_computed_attribute(attr_id, uid):
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:
cim.update(ci.id, {}) cim.update(ci.id, {})
@celery.task(name="cmdb.write_ad_rule_sync_history", queue=CMDB_QUEUE)
@reconnect_db
def write_ad_rule_sync_history(rules, oneagent_id, oneagent_name, sync_at):
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryRuleSyncHistoryCRUD
for rule in rules:
AutoDiscoveryRuleSyncHistoryCRUD().upsert(adt_id=rule['id'],
oneagent_id=oneagent_id,
oneagent_name=oneagent_name,
sync_at=sync_at,
commit=False)
try:
db.session.commit()
except Exception as e:
current_app.logger.error("write auto discovery rule sync history failed: {}".format(e))
db.session.rollback()

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-05-28 18:05+0800\n" "POT-Creation-Date: 2024-06-20 19:12+0800\n"
"PO-Revision-Date: 2023-12-25 20:21+0800\n" "PO-Revision-Date: 2023-12-25 20:21+0800\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: zh\n" "Language: zh\n"
@ -81,7 +81,7 @@ msgid "User {} does not exist"
msgstr "用户 {} 不存在" msgstr "用户 {} 不存在"
#: api/lib/resp_format.py:32 #: api/lib/resp_format.py:32
msgid "You do not have {} permission for resource: {}!" msgid "For resource: {}, you do not have {} permission!"
msgstr "您没有资源: {} 的{}权限!" msgstr "您没有资源: {} 的{}权限!"
#: api/lib/resp_format.py:33 #: api/lib/resp_format.py:33
@ -238,241 +238,245 @@ msgstr "因为CI已经存在不能删除模型"
msgid "The inheritance cannot be deleted because the CI already exists" msgid "The inheritance cannot be deleted because the CI already exists"
msgstr "因为CI已经存在不能删除继承关系" msgstr "因为CI已经存在不能删除继承关系"
#: api/lib/cmdb/resp_format.py:67 #: api/lib/cmdb/resp_format.py:65
msgid "The model is inherited and cannot be deleted"
msgstr "该模型被继承, 不能删除"
#: api/lib/cmdb/resp_format.py:68
msgid "" msgid ""
"The model cannot be deleted because the model is referenced by the " "The model cannot be deleted because the model is referenced by the "
"relational view {}" "relational view {}"
msgstr "因为关系视图 {} 引用了该模型,不能删除模型" msgstr "因为关系视图 {} 引用了该模型,不能删除模型"
#: api/lib/cmdb/resp_format.py:69 #: api/lib/cmdb/resp_format.py:70
msgid "Model group {} does not exist" msgid "Model group {} does not exist"
msgstr "模型分组 {} 不存在" msgstr "模型分组 {} 不存在"
#: api/lib/cmdb/resp_format.py:70 #: api/lib/cmdb/resp_format.py:71
msgid "Model group {} already exists" msgid "Model group {} already exists"
msgstr "模型分组 {} 已经存在" msgstr "模型分组 {} 已经存在"
#: api/lib/cmdb/resp_format.py:71 #: api/lib/cmdb/resp_format.py:72
msgid "Model relationship {} does not exist" msgid "Model relationship {} does not exist"
msgstr "模型关系 {} 不存在" msgstr "模型关系 {} 不存在"
#: api/lib/cmdb/resp_format.py:72 #: api/lib/cmdb/resp_format.py:73
msgid "Attribute group {} already exists" msgid "Attribute group {} already exists"
msgstr "属性分组 {} 已存在" msgstr "属性分组 {} 已存在"
#: api/lib/cmdb/resp_format.py:73 #: api/lib/cmdb/resp_format.py:74
msgid "Attribute group {} does not exist" msgid "Attribute group {} does not exist"
msgstr "属性分组 {} 不存在" msgstr "属性分组 {} 不存在"
#: api/lib/cmdb/resp_format.py:75 #: api/lib/cmdb/resp_format.py:76
msgid "Attribute group <{0}> - attribute <{1}> does not exist" msgid "Attribute group <{0}> - attribute <{1}> does not exist"
msgstr "属性组<{0}> - 属性<{1}> 不存在" msgstr "属性组<{0}> - 属性<{1}> 不存在"
#: api/lib/cmdb/resp_format.py:76 #: api/lib/cmdb/resp_format.py:77
msgid "The unique constraint already exists!" msgid "The unique constraint already exists!"
msgstr "唯一约束已经存在!" msgstr "唯一约束已经存在!"
#: api/lib/cmdb/resp_format.py:78 #: api/lib/cmdb/resp_format.py:79
msgid "Uniquely constrained attributes cannot be JSON and multi-valued" msgid "Uniquely constrained attributes cannot be JSON and multi-valued"
msgstr "唯一约束的属性不能是 JSON 和 多值" msgstr "唯一约束的属性不能是 JSON 和 多值"
#: api/lib/cmdb/resp_format.py:79 #: api/lib/cmdb/resp_format.py:80
msgid "Duplicated trigger" msgid "Duplicated trigger"
msgstr "重复的触发器" msgstr "重复的触发器"
#: api/lib/cmdb/resp_format.py:80 #: api/lib/cmdb/resp_format.py:81
msgid "Trigger {} does not exist" msgid "Trigger {} does not exist"
msgstr "触发器 {} 不存在" msgstr "触发器 {} 不存在"
#: api/lib/cmdb/resp_format.py:81 #: api/lib/cmdb/resp_format.py:82
msgid "Duplicated reconciliation rule" msgid "Duplicated reconciliation rule"
msgstr "" msgstr ""
#: api/lib/cmdb/resp_format.py:82 #: api/lib/cmdb/resp_format.py:83
msgid "Reconciliation rule {} does not exist" msgid "Reconciliation rule {} does not exist"
msgstr "关系类型 {} 不存在" msgstr "关系类型 {} 不存在"
#: api/lib/cmdb/resp_format.py:84 #: api/lib/cmdb/resp_format.py:85
msgid "Operation record {} does not exist" msgid "Operation record {} does not exist"
msgstr "操作记录 {} 不存在" msgstr "操作记录 {} 不存在"
#: api/lib/cmdb/resp_format.py:85 #: api/lib/cmdb/resp_format.py:86
msgid "Unique identifier cannot be deleted" msgid "Unique identifier cannot be deleted"
msgstr "不能删除唯一标识" msgstr "不能删除唯一标识"
#: api/lib/cmdb/resp_format.py:86 #: api/lib/cmdb/resp_format.py:87
msgid "Cannot delete default sorted attributes" msgid "Cannot delete default sorted attributes"
msgstr "不能删除默认排序的属性" msgstr "不能删除默认排序的属性"
#: api/lib/cmdb/resp_format.py:88 #: api/lib/cmdb/resp_format.py:89
msgid "No node selected" msgid "No node selected"
msgstr "没有选择节点" msgstr "没有选择节点"
#: api/lib/cmdb/resp_format.py:89 #: api/lib/cmdb/resp_format.py:90
msgid "This search option does not exist!" msgid "This search option does not exist!"
msgstr "该搜索选项不存在!" msgstr "该搜索选项不存在!"
#: api/lib/cmdb/resp_format.py:90 #: api/lib/cmdb/resp_format.py:91
msgid "This search option has a duplicate name!" msgid "This search option has a duplicate name!"
msgstr "该搜索选项命名重复!" msgstr "该搜索选项命名重复!"
#: api/lib/cmdb/resp_format.py:92 #: api/lib/cmdb/resp_format.py:93
msgid "Relationship type {} already exists" msgid "Relationship type {} already exists"
msgstr "关系类型 {} 已经存在" msgstr "关系类型 {} 已经存在"
#: api/lib/cmdb/resp_format.py:93 #: api/lib/cmdb/resp_format.py:94
msgid "Relationship type {} does not exist" msgid "Relationship type {} does not exist"
msgstr "关系类型 {} 不存在" msgstr "关系类型 {} 不存在"
#: api/lib/cmdb/resp_format.py:95 #: api/lib/cmdb/resp_format.py:96
msgid "Invalid attribute value: {}" msgid "Invalid attribute value: {}"
msgstr "无效的属性值: {}" msgstr "无效的属性值: {}"
#: api/lib/cmdb/resp_format.py:96 #: api/lib/cmdb/resp_format.py:97
msgid "{} Invalid value: {}" msgid "{} Invalid value: {}"
msgstr "{} 无效的值: {}" msgstr "{} 无效的值: {}"
#: api/lib/cmdb/resp_format.py:97 #: api/lib/cmdb/resp_format.py:98
msgid "{} is not in the predefined values" msgid "{} is not in the predefined values"
msgstr "{} 不在预定义值里" msgstr "{} 不在预定义值里"
#: api/lib/cmdb/resp_format.py:99 #: api/lib/cmdb/resp_format.py:100
msgid "The value of attribute {} must be unique, {} already exists" msgid "The value of attribute {} must be unique, {} already exists"
msgstr "属性 {} 的值必须是唯一的, 当前值 {} 已存在" msgstr "属性 {} 的值必须是唯一的, 当前值 {} 已存在"
#: api/lib/cmdb/resp_format.py:100 #: api/lib/cmdb/resp_format.py:101
msgid "Attribute {} value must exist" msgid "Attribute {} value must exist"
msgstr "属性 {} 值必须存在" msgstr "属性 {} 值必须存在"
#: api/lib/cmdb/resp_format.py:101 #: api/lib/cmdb/resp_format.py:102
msgid "Out of range value, the maximum value is 2147483647" msgid "Out of range value, the maximum value is 2147483647"
msgstr "超过最大值限制, 最大值是2147483647" msgstr "超过最大值限制, 最大值是2147483647"
#: api/lib/cmdb/resp_format.py:103 #: api/lib/cmdb/resp_format.py:104
msgid "Unknown error when adding or modifying attribute value: {}" msgid "Unknown error when adding or modifying attribute value: {}"
msgstr "新增或者修改属性值未知错误: {}" msgstr "新增或者修改属性值未知错误: {}"
#: api/lib/cmdb/resp_format.py:105 #: api/lib/cmdb/resp_format.py:106
msgid "Duplicate custom name" msgid "Duplicate custom name"
msgstr "订制名重复" msgstr "订制名重复"
#: api/lib/cmdb/resp_format.py:107 #: api/lib/cmdb/resp_format.py:108
msgid "Number of models exceeds limit: {}" msgid "Number of models exceeds limit: {}"
msgstr "模型数超过限制: {}" msgstr "模型数超过限制: {}"
#: api/lib/cmdb/resp_format.py:108 #: api/lib/cmdb/resp_format.py:109
msgid "The number of CIs exceeds the limit: {}" msgid "The number of CIs exceeds the limit: {}"
msgstr "CI数超过限制: {}" msgstr "CI数超过限制: {}"
#: api/lib/cmdb/resp_format.py:110 #: api/lib/cmdb/resp_format.py:111
msgid "Auto-discovery rule: {} already exists!" msgid "Auto-discovery rule: {} already exists!"
msgstr "自动发现规则: {} 已经存在!" msgstr "自动发现规则: {} 已经存在!"
#: api/lib/cmdb/resp_format.py:111 #: api/lib/cmdb/resp_format.py:112
msgid "Auto-discovery rule: {} does not exist!" msgid "Auto-discovery rule: {} does not exist!"
msgstr "自动发现规则: {} 不存在!" msgstr "自动发现规则: {} 不存在!"
#: api/lib/cmdb/resp_format.py:113 #: api/lib/cmdb/resp_format.py:114
msgid "This auto-discovery rule is referenced by the model and cannot be deleted!" msgid "This auto-discovery rule is referenced by the model and cannot be deleted!"
msgstr "该自动发现规则被模型引用, 不能删除!" msgstr "该自动发现规则被模型引用, 不能删除!"
#: api/lib/cmdb/resp_format.py:115 #: api/lib/cmdb/resp_format.py:116
msgid "The application of auto-discovery rules cannot be defined repeatedly!" msgid "The application of auto-discovery rules cannot be defined repeatedly!"
msgstr "自动发现规则的应用不能重复定义!" msgstr "自动发现规则的应用不能重复定义!"
#: api/lib/cmdb/resp_format.py:116 #: api/lib/cmdb/resp_format.py:117
msgid "The auto-discovery you want to modify: {} does not exist!" msgid "The auto-discovery you want to modify: {} does not exist!"
msgstr "您要修改的自动发现: {} 不存在!" msgstr "您要修改的自动发现: {} 不存在!"
#: api/lib/cmdb/resp_format.py:117 #: api/lib/cmdb/resp_format.py:118
msgid "Attribute does not include unique identifier: {}" msgid "Attribute does not include unique identifier: {}"
msgstr "属性字段没有包括唯一标识: {}" msgstr "属性字段没有包括唯一标识: {}"
#: api/lib/cmdb/resp_format.py:118 #: api/lib/cmdb/resp_format.py:119
msgid "The auto-discovery instance does not exist!" msgid "The auto-discovery instance does not exist!"
msgstr "自动发现的实例不存在!" msgstr "自动发现的实例不存在!"
#: api/lib/cmdb/resp_format.py:119 #: api/lib/cmdb/resp_format.py:120
msgid "The model is not associated with this auto-discovery!" msgid "The model is not associated with this auto-discovery!"
msgstr "模型并未关联该自动发现!" msgstr "模型并未关联该自动发现!"
#: api/lib/cmdb/resp_format.py:120 #: api/lib/cmdb/resp_format.py:121
msgid "Only the creator can modify the Secret!" msgid "Only the creator can modify the Secret!"
msgstr "只有创建人才能修改Secret!" msgstr "只有创建人才能修改Secret!"
#: api/lib/cmdb/resp_format.py:122 #: api/lib/cmdb/resp_format.py:123
msgid "This rule already has auto-discovery instances and cannot be deleted!" msgid "This rule already has auto-discovery instances and cannot be deleted!"
msgstr "该规则已经有自动发现的实例, 不能被删除!" msgstr "该规则已经有自动发现的实例, 不能被删除!"
#: api/lib/cmdb/resp_format.py:124 #: api/lib/cmdb/resp_format.py:125
msgid "The default auto-discovery rule is already referenced by model {}!" msgid "The default auto-discovery rule is already referenced by model {}!"
msgstr "该默认的自动发现规则 已经被模型 {} 引用!" msgstr "该默认的自动发现规则 已经被模型 {} 引用!"
#: api/lib/cmdb/resp_format.py:126 #: api/lib/cmdb/resp_format.py:127
msgid "The unique_key method must return a non-empty string!" msgid "The unique_key method must return a non-empty string!"
msgstr "unique_key方法必须返回非空字符串!" msgstr "unique_key方法必须返回非空字符串!"
#: api/lib/cmdb/resp_format.py:127 #: api/lib/cmdb/resp_format.py:128
msgid "The attributes method must return a list" msgid "The attributes method must return a list"
msgstr "attributes方法必须返回的是list" msgstr "attributes方法必须返回的是list"
#: api/lib/cmdb/resp_format.py:129 #: api/lib/cmdb/resp_format.py:130
msgid "The list returned by the attributes method cannot be empty!" msgid "The list returned by the attributes method cannot be empty!"
msgstr "attributes方法返回的list不能为空!" msgstr "attributes方法返回的list不能为空!"
#: api/lib/cmdb/resp_format.py:131 #: api/lib/cmdb/resp_format.py:132
msgid "Only administrators can define execution targets as: all nodes!" msgid "Only administrators can define execution targets as: all nodes!"
msgstr "只有管理员才可以定义执行机器为: 所有节点!" msgstr "只有管理员才可以定义执行机器为: 所有节点!"
#: api/lib/cmdb/resp_format.py:132 #: api/lib/cmdb/resp_format.py:133
msgid "Execute targets permission check failed: {}" msgid "Execute targets permission check failed: {}"
msgstr "执行机器权限检查不通过: {}" msgstr "执行机器权限检查不通过: {}"
#: api/lib/cmdb/resp_format.py:134 #: api/lib/cmdb/resp_format.py:135
msgid "CI filter authorization must be named!" msgid "CI filter authorization must be named!"
msgstr "CI过滤授权 必须命名!" msgstr "CI过滤授权 必须命名!"
#: api/lib/cmdb/resp_format.py:135 #: api/lib/cmdb/resp_format.py:136
msgid "CI filter authorization is currently not supported or query" msgid "CI filter authorization is currently not supported or query"
msgstr "CI过滤授权 暂时不支持 或 查询" msgstr "CI过滤授权 暂时不支持 或 查询"
#: api/lib/cmdb/resp_format.py:138 #: api/lib/cmdb/resp_format.py:139
msgid "You do not have permission to operate attribute {}!" msgid "You do not have permission to operate attribute {}!"
msgstr "您没有属性 {} 的操作权限!" msgstr "您没有属性 {} 的操作权限!"
#: api/lib/cmdb/resp_format.py:139 #: api/lib/cmdb/resp_format.py:140
msgid "You do not have permission to operate this CI!" msgid "You do not have permission to operate this CI!"
msgstr "您没有该CI的操作权限!" msgstr "您没有该CI的操作权限!"
#: api/lib/cmdb/resp_format.py:141 #: api/lib/cmdb/resp_format.py:142
msgid "Failed to save password: {}" msgid "Failed to save password: {}"
msgstr "保存密码失败: {}" msgstr "保存密码失败: {}"
#: api/lib/cmdb/resp_format.py:142 #: api/lib/cmdb/resp_format.py:143
msgid "Failed to get password: {}" msgid "Failed to get password: {}"
msgstr "获取密码失败: {}" msgstr "获取密码失败: {}"
#: api/lib/cmdb/resp_format.py:144 #: api/lib/cmdb/resp_format.py:145
msgid "Scheduling time format error" msgid "Scheduling time format error"
msgstr "{}格式错误,应该为:%Y-%m-%d %H:%M:%S" msgstr "{}格式错误,应该为:%Y-%m-%d %H:%M:%S"
#: api/lib/cmdb/resp_format.py:145 #: api/lib/cmdb/resp_format.py:146
msgid "CMDB data reconciliation results" msgid "CMDB data reconciliation results"
msgstr "" msgstr ""
#: api/lib/cmdb/resp_format.py:146 #: api/lib/cmdb/resp_format.py:147
msgid "Number of {} illegal: {}" msgid "Number of {} illegal: {}"
msgstr "" msgstr ""
#: api/lib/cmdb/resp_format.py:148 #: api/lib/cmdb/resp_format.py:149
msgid "Topology view {} already exists" msgid "Topology view {} already exists"
msgstr "拓扑视图 {} 已经存在" msgstr "拓扑视图 {} 已经存在"
#: api/lib/cmdb/resp_format.py:149 #: api/lib/cmdb/resp_format.py:150
msgid "Topology group {} already exists" msgid "Topology group {} already exists"
msgstr "拓扑视图分组 {} 已经存在" msgstr "拓扑视图分组 {} 已经存在"
#: api/lib/cmdb/resp_format.py:151 #: api/lib/cmdb/resp_format.py:152
msgid "The group cannot be deleted because the topology view already exists" msgid "The group cannot be deleted because the topology view already exists"
msgstr "因为该分组下定义了拓扑视图,不能删除" msgstr "因为该分组下定义了拓扑视图,不能删除"

View File

@ -1,6 +1,7 @@
# -*- coding:utf-8 -*- # -*- coding:utf-8 -*-
import copy
import json import json
import uuid
from io import BytesIO from io import BytesIO
from flask import abort from flask import abort
@ -10,15 +11,19 @@ from flask_login import current_user
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryCICRUD from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryCICRUD
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryCITypeCRUD from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryCITypeCRUD
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryCITypeRelationCRUD
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryCounterCRUD
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryExecHistoryCRUD
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryHTTPManager from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryHTTPManager
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryRuleCRUD from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryRuleCRUD
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryRuleSyncHistoryCRUD
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoverySNMPManager from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoverySNMPManager
from api.lib.cmdb.auto_discovery.const import DEFAULT_HTTP from api.lib.cmdb.auto_discovery.const import DEFAULT_HTTP
from api.lib.cmdb.const import PermEnum from api.lib.cmdb.const import PermEnum
from api.lib.cmdb.const import ResourceTypeEnum from api.lib.cmdb.const import ResourceTypeEnum
from api.lib.cmdb.resp_format import ErrFormat from api.lib.cmdb.resp_format import ErrFormat
from api.lib.cmdb.search import SearchError from api.lib.cmdb.search import SearchError
from api.lib.cmdb.search.ci import search from api.lib.cmdb.search.ci import search as ci_search
from api.lib.decorator import args_required from api.lib.decorator import args_required
from api.lib.decorator import args_validate from api.lib.decorator import args_validate
from api.lib.perm.acl.acl import has_perm_from_args from api.lib.perm.acl.acl import has_perm_from_args
@ -37,14 +42,19 @@ class AutoDiscoveryRuleView(APIView):
rebuild = False rebuild = False
exists = {i['name'] for i in res} exists = {i['name'] for i in res}
for i in DEFAULT_HTTP: for i in copy.deepcopy(DEFAULT_HTTP):
if i['name'] not in exists: if i['name'] not in exists:
i.pop('en', None)
AutoDiscoveryRuleCRUD().add(**i) AutoDiscoveryRuleCRUD().add(**i)
rebuild = True rebuild = True
if rebuild: if rebuild:
_, res = AutoDiscoveryRuleCRUD.search(page=1, page_size=100000, **request.values) _, res = AutoDiscoveryRuleCRUD.search(page=1, page_size=100000, **request.values)
for i in res:
if i['type'] == 'http':
i['resources'] = AutoDiscoveryHTTPManager().get_resources(i['name'])
return self.jsonify(res) return self.jsonify(res)
@args_required("name", value_required=True) @args_required("name", value_required=True)
@ -98,7 +108,8 @@ class AutoDiscoveryRuleTemplateFileView(APIView):
class AutoDiscoveryRuleHTTPView(APIView): class AutoDiscoveryRuleHTTPView(APIView):
url_prefix = ("/adr/http/<string:name>/categories", "/adr/http/<string:name>/attributes", url_prefix = ("/adr/http/<string:name>/categories",
"/adr/http/<string:name>/attributes",
"/adr/snmp/<string:name>/attributes") "/adr/snmp/<string:name>/attributes")
def get(self, name): def get(self, name):
@ -106,16 +117,21 @@ class AutoDiscoveryRuleHTTPView(APIView):
return self.jsonify(AutoDiscoverySNMPManager.get_attributes()) return self.jsonify(AutoDiscoverySNMPManager.get_attributes())
if "attributes" in request.url: if "attributes" in request.url:
category = request.values.get('category') resource = request.values.get('resource')
return self.jsonify(AutoDiscoveryHTTPManager.get_attributes(name, category)) return self.jsonify(AutoDiscoveryHTTPManager.get_attributes(name, resource))
return self.jsonify(AutoDiscoveryHTTPManager.get_categories(name)) return self.jsonify(AutoDiscoveryHTTPManager.get_categories(name))
class AutoDiscoveryCITypeView(APIView): class AutoDiscoveryCITypeView(APIView):
url_prefix = ("/adt/ci_types/<int:type_id>", "/adt/<int:adt_id>") url_prefix = ("/adt/ci_types/<int:type_id>",
"/adt/ci_types/<int:type_id>/attributes",
"/adt/<int:adt_id>")
def get(self, type_id): def get(self, type_id):
if "attributes" in request.url:
return self.jsonify(AutoDiscoveryCITypeCRUD.get_ad_attributes(type_id))
_, res = AutoDiscoveryCITypeCRUD.search(page=1, page_size=100000, type_id=type_id, **request.values) _, res = AutoDiscoveryCITypeCRUD.search(page=1, page_size=100000, type_id=type_id, **request.values)
for i in res: for i in res:
if isinstance(i.get("extra_option"), dict) and i['extra_option'].get('secret'): if isinstance(i.get("extra_option"), dict) and i['extra_option'].get('secret'):
@ -146,6 +162,27 @@ class AutoDiscoveryCITypeView(APIView):
return self.jsonify(adt_id=adt_id) return self.jsonify(adt_id=adt_id)
class AutoDiscoveryCITypeRelationView(APIView):
url_prefix = ("/adt/ci_types/<int:type_id>/relations", "/adt/relations/<int:_id>")
def get(self, type_id):
_, res = AutoDiscoveryCITypeRelationCRUD.search(page=1, page_size=100000, ad_type_id=type_id, **request.values)
return self.jsonify(res)
@args_required("relations")
def post(self, type_id):
return self.jsonify(AutoDiscoveryCITypeRelationCRUD().upsert(type_id, request.values['relations']))
def put(self):
return self.post()
def delete(self, _id):
AutoDiscoveryCITypeRelationCRUD().delete(_id)
return self.jsonify(id=_id)
class AutoDiscoveryCIView(APIView): class AutoDiscoveryCIView(APIView):
url_prefix = ("/adc", "/adc/<int:adc_id>", "/adc/ci_types/<int:type_id>/attributes", "/adc/ci_types") url_prefix = ("/adc", "/adc/<int:adc_id>", "/adc/ci_types/<int:type_id>/attributes", "/adc/ci_types")
@ -220,9 +257,8 @@ class AutoDiscoveryRuleSyncView(APIView):
oneagent_id = request.values.get('oneagent_id') oneagent_id = request.values.get('oneagent_id')
last_update_at = request.values.get('last_update_at') last_update_at = request.values.get('last_update_at')
query = "{},oneagent_id:{}".format(oneagent_name, oneagent_id) query = "oneagent_id:{}".format(oneagent_id)
current_app.logger.info(query) s = ci_search(query)
s = search(query)
try: try:
response, _, _, _, _, _ = s.search() response, _, _, _, _, _ = s.search()
except SearchError as e: except SearchError as e:
@ -230,7 +266,77 @@ class AutoDiscoveryRuleSyncView(APIView):
current_app.logger.error(traceback.format_exc()) current_app.logger.error(traceback.format_exc())
return abort(400, str(e)) return abort(400, str(e))
ci_id = response and response[0]["_id"] for res in response:
rules, last_update_at = AutoDiscoveryCITypeCRUD.get(ci_id, oneagent_id, last_update_at) if res.get('{}_name'.format(res['ci_type'])) == oneagent_name or oneagent_name == res.get('oneagent_name'):
ci_id = res["_id"]
rules, last_update_at = AutoDiscoveryCITypeCRUD.get(ci_id, oneagent_id, oneagent_name, last_update_at)
return self.jsonify(rules=rules, last_update_at=last_update_at)
rules, last_update_at = AutoDiscoveryCITypeCRUD.get(None, oneagent_id, oneagent_name, last_update_at)
return self.jsonify(rules=rules, last_update_at=last_update_at) return self.jsonify(rules=rules, last_update_at=last_update_at)
class AutoDiscoveryRuleSyncHistoryView(APIView):
url_prefix = ("/adt/<int:adt_id>/sync/histories",)
def get(self, adt_id):
page = get_page(request.values.pop('page', 1))
page_size = get_page_size(request.values.pop('page_size', None))
numfound, res = AutoDiscoveryRuleSyncHistoryCRUD.search(page=page,
page_size=page_size,
adt_id=adt_id,
**request.values)
return self.jsonify(page=page,
page_size=page_size,
numfound=numfound,
total=len(res),
result=res)
class AutoDiscoveryTestView(APIView):
url_prefix = ("/adt/<int:adt_id>/test", "/adt/test/<string:exec_id>/result")
def get(self, exec_id):
return self.jsonify(stdout="1\n2\n3", exec_id=exec_id)
def post(self, adt_id):
return self.jsonify(exec_id=uuid.uuid4().hex)
class AutoDiscoveryExecHistoryView(APIView):
url_prefix = ("/adc/exec/histories",)
@args_required('type_id')
def get(self):
page = get_page(request.values.pop('page', 1))
page_size = get_page_size(request.values.pop('page_size', None))
numfound, res = AutoDiscoveryExecHistoryCRUD.search(page=page,
page_size=page_size,
**request.values)
return self.jsonify(page=page,
page_size=page_size,
numfound=numfound,
total=len(res),
result=res)
@args_required('type_id')
@args_required('stdout')
def post(self):
AutoDiscoveryExecHistoryCRUD().add(type_id=request.values.get('type_id'),
stdout=request.values.get('stdout'))
return self.jsonify(code=200)
class AutoDiscoveryCounterView(APIView):
url_prefix = ("/adc/counter",)
@args_required('type_id')
def get(self):
type_id = request.values.get('type_id')
return self.jsonify(AutoDiscoveryCounterCRUD().get(type_id))