-
Notifications
You must be signed in to change notification settings - Fork 423
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: ipam api * fix: ipam
- Loading branch information
Showing
25 changed files
with
1,046 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# -*- coding:utf-8 -*- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
# -*- coding:utf-8 -*- | ||
|
||
import redis_lock | ||
from flask import abort | ||
|
||
from api.extensions import db | ||
from api.extensions import rd | ||
from api.lib.cmdb.cache import CITypeCache | ||
from api.lib.cmdb.ci import CIManager | ||
from api.lib.cmdb.ci import CIRelationManager | ||
from api.lib.cmdb.const import BuiltinModelEnum | ||
from api.lib.cmdb.ipam.const import IPAddressAssignStatus | ||
from api.lib.cmdb.ipam.const import IPAddressBuiltinAttributes | ||
from api.lib.cmdb.ipam.const import OperateTypeEnum | ||
from api.lib.cmdb.ipam.const import SubnetBuiltinAttributes | ||
from api.lib.cmdb.ipam.history import OperateHistoryManager | ||
from api.lib.cmdb.resp_format import ErrFormat | ||
from api.lib.cmdb.search.ci.db.search import Search as SearchFromDB | ||
from api.lib.cmdb.search.ci_relation.search import Search as RelationSearch | ||
|
||
|
||
class IpAddressManager(object): | ||
def __init__(self): | ||
self.ci_type = CITypeCache.get(BuiltinModelEnum.IPAM_ADDRESS) | ||
not self.ci_type and abort(400, ErrFormat.ipam_address_model_not_found.format( | ||
BuiltinModelEnum.IPAM_ADDRESS)) | ||
|
||
self.type_id = self.ci_type.id | ||
|
||
@staticmethod | ||
def list_ip_address(parent_id): | ||
numfound, _, result = CIRelationManager.get_second_cis(parent_id, per_page="all") | ||
|
||
return numfound, result | ||
|
||
def _get_cis(self, ips): | ||
response, _, _, _, _, _ = SearchFromDB( | ||
"_type:{},{}:({})".format(self.type_id, IPAddressBuiltinAttributes.IP, ";".join(ips or [])), | ||
count=10000000, parent_node_perm_passed=True).search() | ||
|
||
return response | ||
|
||
@staticmethod | ||
def _add_relation(parent_id, child_id): | ||
if not parent_id or not child_id: | ||
return | ||
|
||
CIRelationManager().add(parent_id, child_id, valid=False, apply_async=False) | ||
|
||
@staticmethod | ||
def calc_free_count(subnet_id): | ||
db.session.commit() | ||
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 [])) | ||
|
||
def _update_subnet_count(self, subnet_id, assign_count, used_count=None): | ||
payload = {} | ||
|
||
cur = CIManager.get_ci_by_id(subnet_id, need_children=False) | ||
if assign_count is not None: | ||
payload[SubnetBuiltinAttributes.ASSIGN_COUNT] = (cur.get( | ||
SubnetBuiltinAttributes.ASSIGN_COUNT) or 0) + assign_count | ||
|
||
if used_count is not None: | ||
payload[SubnetBuiltinAttributes.USED_COUNT] = used_count | ||
|
||
payload[SubnetBuiltinAttributes.FREE_COUNT] = (cur[SubnetBuiltinAttributes.HOSTS_COUNT] - | ||
self.calc_free_count(subnet_id)) | ||
CIManager().update(subnet_id, **payload) | ||
|
||
def assign_ips(self, ips, subnet_id, cidr, **kwargs): | ||
""" | ||
:param ips: ip list | ||
:param subnet_id: subnet id | ||
:param cidr: subnet cidr | ||
:param kwargs: other attributes for ip address | ||
:return: | ||
""" | ||
if subnet_id is not None: | ||
subnet = CIManager.get_ci_by_id(subnet_id) | ||
else: | ||
cis, _, _, _, _, _ = SearchFromDB("_type:{},{}:{}".format( | ||
BuiltinModelEnum.IPAM_SUBNET, SubnetBuiltinAttributes.CIDR, cidr), | ||
parent_node_perm_passed=True).search() | ||
if cis: | ||
subnet = cis[0] | ||
subnet_id = subnet['_id'] | ||
else: | ||
return abort(400, ErrFormat.ipam_address_model_not_found) | ||
|
||
with (redis_lock.Lock(rd.r, "IPAM_ASSIGN_ADDRESS_{}".format(subnet_id))): | ||
cis = self._get_cis(ips) | ||
ip2ci = {ci[IPAddressBuiltinAttributes.IP]: ci for ci in cis} | ||
|
||
ci_ids = [] | ||
status_change_num = 0 | ||
for ip in ips: | ||
kwargs['name'] = ip | ||
kwargs[IPAddressBuiltinAttributes.IP] = ip | ||
if ip not in ip2ci: | ||
ci_id = CIManager.add(self.type_id, _sync=True, **kwargs) | ||
status_change_num += 1 | ||
else: | ||
ci_id = ip2ci[ip]['_id'] | ||
CIManager().update(ci_id, _sync=True, **kwargs) | ||
if IPAddressBuiltinAttributes.ASSIGN_STATUS in kwargs and ( | ||
kwargs[IPAddressBuiltinAttributes.ASSIGN_STATUS] != | ||
ip2ci[ip].get(IPAddressBuiltinAttributes.ASSIGN_STATUS)): | ||
status_change_num += 1 | ||
ci_ids.append(ci_id) | ||
|
||
self._add_relation(subnet_id, ci_id) | ||
|
||
if ips and IPAddressBuiltinAttributes.ASSIGN_STATUS in kwargs: | ||
self._update_subnet_count(subnet_id, -status_change_num if kwargs.get( | ||
IPAddressBuiltinAttributes.ASSIGN_STATUS) == IPAddressAssignStatus.UNASSIGNED else status_change_num) | ||
|
||
if ips and IPAddressBuiltinAttributes.IS_USED in kwargs: | ||
q = "{}:true".format(IPAddressBuiltinAttributes.IS_USED) | ||
cur_used_ids = RelationSearch([subnet_id], level=[1], query=q).search(only_ids=True) | ||
for _id in set(cur_used_ids) - set(ci_ids): | ||
CIManager().update(_id, _sync=True, **{IPAddressBuiltinAttributes.IS_USED: False}) | ||
|
||
self._update_subnet_count(subnet_id, None, used_count=len(ips)) | ||
|
||
if kwargs.get(IPAddressBuiltinAttributes.ASSIGN_STATUS) in ( | ||
IPAddressAssignStatus.ASSIGNED, IPAddressAssignStatus.RESERVED): | ||
OperateHistoryManager().add(operate_type=OperateTypeEnum.ASSIGN_ADDRESS, | ||
cidr=subnet.get(SubnetBuiltinAttributes.CIDR), | ||
description=" | ".join(ips)) | ||
|
||
elif kwargs.get(IPAddressBuiltinAttributes.ASSIGN_STATUS) == IPAddressAssignStatus.UNASSIGNED: | ||
OperateHistoryManager().add(operate_type=OperateTypeEnum.REVOKE_ADDRESS, | ||
cidr=subnet.get(SubnetBuiltinAttributes.CIDR), | ||
description=" | ".join(ips)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# -*- coding:utf-8 -*- | ||
|
||
from api.lib.utils import BaseEnum | ||
|
||
|
||
class IPAddressAssignStatus(BaseEnum): | ||
ASSIGNED = 0 | ||
UNASSIGNED = 1 | ||
RESERVED = 2 | ||
|
||
|
||
class OperateTypeEnum(BaseEnum): | ||
ADD_SCOPE = "0" | ||
UPDATE_SCOPE = "1" | ||
DELETE_SCOPE = "2" | ||
ADD_SUBNET = "3" | ||
UPDATE_SUBNET = "4" | ||
DELETE_SUBNET = "5" | ||
ASSIGN_ADDRESS = "6" | ||
REVOKE_ADDRESS = "7" | ||
|
||
|
||
class SubnetBuiltinAttributes(BaseEnum): | ||
NAME = 'name' | ||
CIDR = 'cidr' | ||
HOSTS_COUNT = 'hosts_count' | ||
ASSIGN_COUNT = 'assign_count' | ||
USED_COUNT = 'used_count' | ||
FREE_COUNT = 'free_count' | ||
|
||
|
||
class IPAddressBuiltinAttributes(BaseEnum): | ||
IP = 'ip' | ||
ASSIGN_STATUS = 'assign_status' # enum: 0 - assigned 1 - unassigned 2 - reserved | ||
IS_USED = 'is_used' # bool |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
# -*- coding:utf-8 -*- | ||
|
||
from flask_login import current_user | ||
|
||
from api.lib.cmdb.ipam.const import IPAddressBuiltinAttributes | ||
from api.lib.mixin import DBMixin | ||
from api.models.cmdb import IPAMOperationHistory | ||
from api.models.cmdb import IPAMSubnetScan | ||
from api.models.cmdb import IPAMSubnetScanHistory | ||
|
||
|
||
class OperateHistoryManager(DBMixin): | ||
cls = IPAMOperationHistory | ||
|
||
def _can_add(self, **kwargs): | ||
kwargs['uid'] = current_user.uid | ||
|
||
return kwargs | ||
|
||
def _can_update(self, **kwargs): | ||
pass | ||
|
||
def _can_delete(self, **kwargs): | ||
pass | ||
|
||
|
||
class ScanHistoryManager(DBMixin): | ||
cls = IPAMSubnetScanHistory | ||
|
||
def _can_add(self, **kwargs): | ||
return kwargs | ||
|
||
def add(self, **kwargs): | ||
kwargs.pop('_key', None) | ||
kwargs.pop('_secret', None) | ||
ci_id = kwargs.pop('ci_id', None) | ||
|
||
existed = self.cls.get_by(exec_id=kwargs['exec_id'], first=True, to_dict=False) | ||
if existed is None: | ||
self.cls.create(**kwargs) | ||
else: | ||
existed.update(**kwargs) | ||
|
||
if kwargs.get('ips'): | ||
from api.lib.cmdb.ipam.address import IpAddressManager | ||
IpAddressManager().assign_ips(kwargs['ips'], None, kwargs.get('cidr'), | ||
**{IPAddressBuiltinAttributes.IS_USED: 1}) | ||
|
||
scan_rule = IPAMSubnetScan.get_by(ci_id=ci_id, first=True, to_dict=False) | ||
if scan_rule is not None: | ||
scan_rule.update(last_scan_time=kwargs.get('start_at')) | ||
|
||
def _can_update(self, **kwargs): | ||
pass | ||
|
||
def _can_delete(self, **kwargs): | ||
pass |
Oops, something went wrong.