feat(api): update ipam

This commit is contained in:
pycook 2024-11-25 20:19:01 +08:00
parent f28ad4d041
commit 900cf1f617
9 changed files with 65 additions and 42 deletions

View File

@ -512,7 +512,7 @@ class CMDBCounterCache(object):
result[i.type_id]['rule_count'] = len(adts) + AutoDiscoveryCITypeRelation.get_by( result[i.type_id]['rule_count'] = len(adts) + AutoDiscoveryCITypeRelation.get_by(
ad_type_id=i.type_id, only_query=True).count() ad_type_id=i.type_id, only_query=True).count()
result[i.type_id]['exec_target_count'] = len( result[i.type_id]['exec_target_count'] = len(
set([i.oneagent_id for adt in adts for i in db.session.query( set([j.oneagent_id for adt in adts for j in db.session.query(
AutoDiscoveryRuleSyncHistory.oneagent_id).filter( AutoDiscoveryRuleSyncHistory.oneagent_id).filter(
AutoDiscoveryRuleSyncHistory.adt_id == adt.id)])) AutoDiscoveryRuleSyncHistory.adt_id == adt.id)]))

View File

@ -357,6 +357,7 @@ class CIManager(object):
is_auto_discovery=False, is_auto_discovery=False,
_is_admin=False, _is_admin=False,
ticket_id=None, ticket_id=None,
_sync=False,
**ci_dict): **ci_dict):
""" """
add ci add ci
@ -366,6 +367,7 @@ class CIManager(object):
:param is_auto_discovery: default is False :param is_auto_discovery: default is False
:param _is_admin: default is False :param _is_admin: default is False
:param ticket_id: :param ticket_id:
:param _sync:
:param ci_dict: :param ci_dict:
:return: :return:
""" """
@ -496,10 +498,16 @@ class CIManager(object):
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 changed if record_id or has_dynamic: # has changed
ci_cache.apply_async(args=(ci.id, operate_type, record_id), queue=CMDB_QUEUE) if not _sync:
ci_cache.apply_async(args=(ci.id, operate_type, record_id), queue=CMDB_QUEUE)
else:
ci_cache(ci.id, operate_type, record_id)
if ref_ci_dict: # add relations if ref_ci_dict: # add relations
ci_relation_add.apply_async(args=(ref_ci_dict, ci.id, current_user.uid), queue=CMDB_QUEUE) if not _sync:
ci_relation_add.apply_async(args=(ref_ci_dict, ci.id, current_user.uid), queue=CMDB_QUEUE)
else:
ci_relation_add(ref_ci_dict, ci.id, current_user.uid)
return ci.id return ci.id

View File

@ -879,6 +879,8 @@ class CITypeRelationManager(object):
def _wrap_relation_type_dict(type_id, relation_inst): def _wrap_relation_type_dict(type_id, relation_inst):
ci_type_dict = CITypeCache.get(type_id).to_dict() ci_type_dict = CITypeCache.get(type_id).to_dict()
ci_type_dict["ctr_id"] = relation_inst.id ci_type_dict["ctr_id"] = relation_inst.id
show_key = AttributeCache.get(ci_type_dict.get('show_id') or ci_type_dict['unique_id'])
ci_type_dict["show_key"] = show_key and show_key.name
ci_type_dict["attributes"] = CITypeAttributeManager.get_attributes_by_type_id(ci_type_dict["id"]) ci_type_dict["attributes"] = CITypeAttributeManager.get_attributes_by_type_id(ci_type_dict["id"])
attr_filter = CIFilterPermsCRUD.get_attr_filter(type_id) attr_filter = CIFilterPermsCRUD.get_attr_filter(type_id)
if attr_filter: if attr_filter:
@ -1551,7 +1553,10 @@ class CITypeTemplateManager(object):
if existed is None: if existed is None:
_group['type_id'] = type_id_map.get(_group['type_id'], _group['type_id']) _group['type_id'] = type_id_map.get(_group['type_id'], _group['type_id'])
existed = CITypeAttributeGroup.create(flush=True, **_group) try:
existed = CITypeAttributeGroup.create(flush=True, **_group)
except:
continue
for order, attr in enumerate(group['attributes'] or []): for order, attr in enumerate(group['attributes'] or []):
item_existed = CITypeAttributeGroupItem.get_by(group_id=existed.id, item_existed = CITypeAttributeGroupItem.get_by(group_id=existed.id,

View File

@ -3,7 +3,6 @@
import redis_lock import redis_lock
from flask import abort from flask import abort
from api.extensions import db
from api.extensions import rd from api.extensions import rd
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
@ -21,9 +20,8 @@ from api.lib.cmdb.search.ci_relation.search import Search as RelationSearch
class IpAddressManager(object): class IpAddressManager(object):
def __init__(self): def __init__(self):
self.ci_type = CITypeCache.get(BuiltinModelEnum.IPAM_ADDRESS) self.ci_type = CITypeCache.get(BuiltinModelEnum.IPAM_ADDRESS) or abort(
not self.ci_type and abort(400, ErrFormat.ipam_address_model_not_found.format( 404, ErrFormat.ipam_address_model_not_found.format(BuiltinModelEnum.IPAM_ADDRESS))
BuiltinModelEnum.IPAM_ADDRESS))
self.type_id = self.ci_type.id self.type_id = self.ci_type.id
@ -48,25 +46,28 @@ class IpAddressManager(object):
CIRelationManager().add(parent_id, child_id, valid=False, apply_async=False) CIRelationManager().add(parent_id, child_id, valid=False, apply_async=False)
@staticmethod @staticmethod
def calc_free_count(subnet_id): def calc_used_count(subnet_id):
db.session.commit()
q = "{}:(0;2),-{}:true".format(IPAddressBuiltinAttributes.ASSIGN_STATUS, IPAddressBuiltinAttributes.IS_USED) q = "{}:(0;2),-{}:true".format(IPAddressBuiltinAttributes.ASSIGN_STATUS, IPAddressBuiltinAttributes.IS_USED)
return len(set(RelationSearch([subnet_id], level=[1], query=q).search(only_ids=True) or [])) return len(set(RelationSearch([subnet_id], level=[1], query=q, count=1000000).search(only_ids=True) or []))
def _update_subnet_count(self, subnet_id, assign_count, used_count=None): @staticmethod
def _calc_assign_count(subnet_id):
q = "{}:(0;2)".format(IPAddressBuiltinAttributes.ASSIGN_STATUS)
return len(set(RelationSearch([subnet_id], level=[1], query=q, count=1000000).search(only_ids=True) or []))
def _update_subnet_count(self, subnet_id, assign_count_computed, used_count=None):
payload = {} payload = {}
cur = CIManager.get_ci_by_id(subnet_id, need_children=False) cur = CIManager.get_ci_by_id(subnet_id, need_children=False)
if assign_count is not None: if assign_count_computed:
payload[SubnetBuiltinAttributes.ASSIGN_COUNT] = (cur.get( payload[SubnetBuiltinAttributes.ASSIGN_COUNT] = self._calc_assign_count(subnet_id)
SubnetBuiltinAttributes.ASSIGN_COUNT) or 0) + assign_count
if used_count is not None: if used_count is not None:
payload[SubnetBuiltinAttributes.USED_COUNT] = used_count payload[SubnetBuiltinAttributes.USED_COUNT] = used_count
payload[SubnetBuiltinAttributes.FREE_COUNT] = (cur[SubnetBuiltinAttributes.HOSTS_COUNT] - payload[SubnetBuiltinAttributes.FREE_COUNT] = (cur[SubnetBuiltinAttributes.HOSTS_COUNT] -
self.calc_free_count(subnet_id)) self.calc_used_count(subnet_id))
CIManager().update(subnet_id, **payload) CIManager().update(subnet_id, **payload)
def assign_ips(self, ips, subnet_id, cidr, **kwargs): def assign_ips(self, ips, subnet_id, cidr, **kwargs):
@ -95,35 +96,28 @@ class IpAddressManager(object):
ip2ci = {ci[IPAddressBuiltinAttributes.IP]: ci for ci in cis} ip2ci = {ci[IPAddressBuiltinAttributes.IP]: ci for ci in cis}
ci_ids = [] ci_ids = []
status_change_num = 0
for ip in ips: for ip in ips:
kwargs['name'] = ip kwargs['name'] = ip
kwargs[IPAddressBuiltinAttributes.IP] = ip kwargs[IPAddressBuiltinAttributes.IP] = ip
if ip not in ip2ci: if ip not in ip2ci:
ci_id = CIManager.add(self.type_id, _sync=True, **kwargs) ci_id = CIManager.add(self.type_id, _sync=True, **kwargs)
status_change_num += 1
else: else:
ci_id = ip2ci[ip]['_id'] ci_id = ip2ci[ip]['_id']
CIManager().update(ci_id, _sync=True, **kwargs) CIManager().update(ci_id, _sync=True, **kwargs)
if IPAddressBuiltinAttributes.ASSIGN_STATUS in kwargs and (
(kwargs[IPAddressBuiltinAttributes.ASSIGN_STATUS] or 2) !=
(ip2ci[ip].get(IPAddressBuiltinAttributes.ASSIGN_STATUS) or 2)):
status_change_num += 1
ci_ids.append(ci_id) ci_ids.append(ci_id)
self._add_relation(subnet_id, ci_id) self._add_relation(subnet_id, ci_id)
if ips and IPAddressBuiltinAttributes.ASSIGN_STATUS in kwargs: if ips and IPAddressBuiltinAttributes.ASSIGN_STATUS in kwargs:
self._update_subnet_count(subnet_id, -status_change_num if kwargs.get( self._update_subnet_count(subnet_id, True)
IPAddressBuiltinAttributes.ASSIGN_STATUS) == IPAddressAssignStatus.UNASSIGNED else status_change_num)
if ips and IPAddressBuiltinAttributes.IS_USED in kwargs: if ips and IPAddressBuiltinAttributes.IS_USED in kwargs:
q = "{}:true".format(IPAddressBuiltinAttributes.IS_USED) q = "{}:true".format(IPAddressBuiltinAttributes.IS_USED)
cur_used_ids = RelationSearch([subnet_id], level=[1], query=q).search(only_ids=True) cur_used_ids = RelationSearch([subnet_id], level=[1], query=q).search(only_ids=True)
for _id in set(cur_used_ids) - set(ci_ids): for _id in set(cur_used_ids) - set(ci_ids):
CIManager().update(_id, _sync=True, **{IPAddressBuiltinAttributes.IS_USED: False}) CIManager().update(_id, **{IPAddressBuiltinAttributes.IS_USED: False})
self._update_subnet_count(subnet_id, None, used_count=len(ips)) self._update_subnet_count(subnet_id, False, used_count=len(ips))
if kwargs.get(IPAddressBuiltinAttributes.ASSIGN_STATUS) in ( if kwargs.get(IPAddressBuiltinAttributes.ASSIGN_STATUS) in (
IPAddressAssignStatus.ASSIGNED, IPAddressAssignStatus.RESERVED): IPAddressAssignStatus.ASSIGNED, IPAddressAssignStatus.RESERVED):

View File

@ -50,6 +50,10 @@ class ScanHistoryManager(DBMixin):
if scan_rule is not None: if scan_rule is not None:
scan_rule.update(last_scan_time=kwargs.get('start_at')) scan_rule.update(last_scan_time=kwargs.get('start_at'))
for i in self.cls.get_by(subnet_scan_id=kwargs.get('subnet_scan_id'), only_query=True).order_by(
self.cls.id.desc()).offset(100):
i.delete()
def _can_update(self, **kwargs): def _can_update(self, **kwargs):
pass pass

View File

@ -18,15 +18,13 @@ from api.models.cmdb import IPAMSubnetScan
class Stats(object): class Stats(object):
def __init__(self): def __init__(self):
self.address_type = CITypeCache.get(BuiltinModelEnum.IPAM_ADDRESS) self.address_type = CITypeCache.get(BuiltinModelEnum.IPAM_ADDRESS) or abort(
not self.address_type and abort(400, ErrFormat.ipam_address_model_not_found.format( 404, ErrFormat.ipam_address_model_not_found.format(BuiltinModelEnum.IPAM_ADDRESS))
BuiltinModelEnum.IPAM_ADDRESS))
self.address_type_id = self.address_type.id self.address_type_id = self.address_type.id
self.subnet_type = CITypeCache.get(BuiltinModelEnum.IPAM_SUBNET) self.subnet_type = CITypeCache.get(BuiltinModelEnum.IPAM_SUBNET) or abort(
not self.subnet_type and abort(400, ErrFormat.ipam_address_model_not_found.format( 404, ErrFormat.ipam_address_model_not_found.format(BuiltinModelEnum.IPAM_ADDRESS))
BuiltinModelEnum.IPAM_ADDRESS))
self.subnet_type_id = self.subnet_type.id self.subnet_type_id = self.subnet_type.id
@ -40,8 +38,10 @@ class Stats(object):
return list(set(ci_ids) - set(has_children_ci_ids)) return list(set(ci_ids) - set(has_children_ci_ids))
else: else:
type_id = CIManager().get_by_id(parent_id).type_id _type = CIManager().get_by_id(parent_id)
key = [(str(parent_id), type_id)] if not _type:
return abort(404, ErrFormat.ipam_subnet_not_found)
key = [(str(parent_id), _type.type_id)]
result = [] result = []
while True: while True:
res = [json.loads(x).items() for x in [i or '{}' for i in rd.get( res = [json.loads(x).items() for x in [i or '{}' for i in rd.get(

View File

@ -2,6 +2,7 @@
from collections import defaultdict from collections import defaultdict
import datetime
import ipaddress import ipaddress
from flask import abort from flask import abort
@ -9,7 +10,7 @@ from api.lib.cmdb.cache import AttributeCache
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.const import BuiltinModelEnum, BUILTIN_ATTRIBUTES from api.lib.cmdb.const import BuiltinModelEnum
from api.lib.cmdb.ipam.const import OperateTypeEnum from api.lib.cmdb.ipam.const import OperateTypeEnum
from api.lib.cmdb.ipam.const import SubnetBuiltinAttributes from api.lib.cmdb.ipam.const import SubnetBuiltinAttributes
from api.lib.cmdb.ipam.history import OperateHistoryManager from api.lib.cmdb.ipam.history import OperateHistoryManager
@ -22,9 +23,8 @@ from api.models.cmdb import IPAMSubnetScan
class SubnetManager(object): class SubnetManager(object):
def __init__(self): def __init__(self):
self.ci_type = CITypeCache.get(BuiltinModelEnum.IPAM_SUBNET) self.ci_type = CITypeCache.get(BuiltinModelEnum.IPAM_SUBNET) or abort(
not self.ci_type and abort(400, ErrFormat.ipam_subnet_model_not_found.format( 404, ErrFormat.ipam_subnet_model_not_found.format(BuiltinModelEnum.IPAM_SUBNET))
BuiltinModelEnum.IPAM_SUBNET))
self.type_id = self.ci_type.id self.type_id = self.ci_type.id
@ -47,7 +47,7 @@ class SubnetManager(object):
new_last_update_at = "" new_last_update_at = ""
for i in result: for i in result:
__last_update_at = max([i['updated_at'] or "", i['created_at'] or ""]) __last_update_at = max([i['rule_updated_at'] or "", i['created_at'] or ""])
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
@ -131,7 +131,11 @@ class SubnetManager(object):
@staticmethod @staticmethod
def _is_valid_cidr(cidr): def _is_valid_cidr(cidr):
try: try:
return str(ipaddress.ip_network(cidr)) cidr = ipaddress.ip_network(cidr)
if not (8 <= cidr.prefixlen <= 31):
raise ValueError
return str(cidr)
except ValueError: except ValueError:
return abort(400, ErrFormat.ipam_cidr_invalid_notation.format(cidr)) return abort(400, ErrFormat.ipam_cidr_invalid_notation.format(cidr))
@ -143,6 +147,7 @@ class SubnetManager(object):
root_nodes = set(all_nodes) - set(none_root_nodes) - set(_id and [_id] or []) root_nodes = set(all_nodes) - set(none_root_nodes) - set(_id and [_id] or [])
response, _, _, _, _, _ = SearchFromDB("_type:{}".format(self.type_id), response, _, _, _, _, _ = SearchFromDB("_type:{}".format(self.type_id),
ci_ids=list(root_nodes), ci_ids=list(root_nodes),
count=1000000,
parent_node_perm_passed=True).search() parent_node_perm_passed=True).search()
cur_subnet = ipaddress.ip_network(cidr) cur_subnet = ipaddress.ip_network(cidr)
@ -163,6 +168,7 @@ class SubnetManager(object):
response, _, _, _, _, _ = SearchFromDB("_type:{}".format(self.type_id), response, _, _, _, _, _ = SearchFromDB("_type:{}".format(self.type_id),
ci_ids=list(child_nodes), ci_ids=list(child_nodes),
count=1000000,
parent_node_perm_passed=True).search() parent_node_perm_passed=True).search()
cur_subnet = ipaddress.ip_network(cidr) cur_subnet = ipaddress.ip_network(cidr)
@ -240,7 +246,8 @@ class SubnetManager(object):
def _update_scan_rule(ci_id, agent_id, cron, scan_enabled=True): def _update_scan_rule(ci_id, agent_id, cron, scan_enabled=True):
existed = IPAMSubnetScan.get_by(ci_id=ci_id, first=True, to_dict=False) existed = IPAMSubnetScan.get_by(ci_id=ci_id, first=True, to_dict=False)
if existed is not None: if existed is not None:
existed.update(ci_id=ci_id, agent_id=agent_id, cron=cron, scan_enabled=scan_enabled) existed.update(ci_id=ci_id, agent_id=agent_id, cron=cron, scan_enabled=scan_enabled,
rule_updated_at=datetime.datetime.now())
else: else:
IPAMSubnetScan.create(ci_id=ci_id, agent_id=agent_id, cron=cron, scan_enabled=scan_enabled) IPAMSubnetScan.create(ci_id=ci_id, agent_id=agent_id, cron=cron, scan_enabled=scan_enabled)
@ -273,7 +280,9 @@ class SubnetManager(object):
existed = IPAMSubnetScan.get_by(ci_id=_id, first=True, to_dict=False) existed = IPAMSubnetScan.get_by(ci_id=_id, first=True, to_dict=False)
existed and existed.delete() existed and existed.delete()
delete_ci_ids = []
for i in CIRelation.get_by(first_ci_id=_id, to_dict=False): for i in CIRelation.get_by(first_ci_id=_id, to_dict=False):
delete_ci_ids.append(i.second_ci_id)
i.delete() i.delete()
cur = CIManager.get_ci_by_id(_id, need_children=False) cur = CIManager.get_ci_by_id(_id, need_children=False)
@ -284,6 +293,8 @@ class SubnetManager(object):
cidr=cur.get(SubnetBuiltinAttributes.CIDR), cidr=cur.get(SubnetBuiltinAttributes.CIDR),
description=cur.get(SubnetBuiltinAttributes.CIDR)) description=cur.get(SubnetBuiltinAttributes.CIDR))
# batch_delete_ci.apply_async(args=(delete_ci_ids,))
return _id return _id

View File

@ -676,6 +676,7 @@ class IPAMSubnetScan(Model):
ci_id = db.Column(db.Integer, index=True, nullable=False) ci_id = db.Column(db.Integer, index=True, nullable=False)
scan_enabled = db.Column(db.Boolean, default=True) scan_enabled = db.Column(db.Boolean, default=True)
rule_updated_at = db.Column(db.DateTime)
last_scan_time = db.Column(db.DateTime) last_scan_time = db.Column(db.DateTime)
# scan rules # scan rules