From 355eedc628d5c7d647219912c86fefb0499ae74c Mon Sep 17 00:00:00 2001 From: pycook Date: Tue, 26 Nov 2024 18:56:22 +0800 Subject: [PATCH] feat(api): dcim dev --- cmdb-api/api/commands/click_cmdb.py | 5 +- cmdb-api/api/lib/cmdb/ci.py | 8 +- cmdb-api/api/lib/cmdb/const.py | 5 + cmdb-api/api/lib/cmdb/dcim/__init__.py | 1 + cmdb-api/api/lib/cmdb/dcim/base.py | 33 ++++ cmdb-api/api/lib/cmdb/dcim/const.py | 17 ++ cmdb-api/api/lib/cmdb/dcim/history.py | 40 ++++ cmdb-api/api/lib/cmdb/dcim/idc.py | 19 ++ cmdb-api/api/lib/cmdb/dcim/rack.py | 179 ++++++++++++++++++ cmdb-api/api/lib/cmdb/dcim/region.py | 29 +++ cmdb-api/api/lib/cmdb/dcim/server_room.py | 56 ++++++ cmdb-api/api/lib/cmdb/dcim/tree_view.py | 85 +++++++++ cmdb-api/api/lib/cmdb/resp_format.py | 5 + .../api/lib/common_setting/role_perm_base.py | 1 + cmdb-api/api/models/cmdb.py | 11 ++ .../translations/zh/LC_MESSAGES/messages.mo | Bin 18755 -> 19006 bytes .../translations/zh/LC_MESSAGES/messages.po | 18 +- cmdb-api/api/views/cmdb/dcim/__init__.py | 1 + cmdb-api/api/views/cmdb/dcim/dcim_history.py | 30 +++ cmdb-api/api/views/cmdb/dcim/idc.py | 35 ++++ cmdb-api/api/views/cmdb/dcim/rack.py | 89 +++++++++ cmdb-api/api/views/cmdb/dcim/region.py | 33 ++++ cmdb-api/api/views/cmdb/dcim/server_room.py | 43 +++++ cmdb-api/api/views/cmdb/dcim/tree_view.py | 19 ++ 24 files changed, 754 insertions(+), 8 deletions(-) create mode 100644 cmdb-api/api/lib/cmdb/dcim/__init__.py create mode 100644 cmdb-api/api/lib/cmdb/dcim/base.py create mode 100644 cmdb-api/api/lib/cmdb/dcim/const.py create mode 100644 cmdb-api/api/lib/cmdb/dcim/history.py create mode 100644 cmdb-api/api/lib/cmdb/dcim/idc.py create mode 100644 cmdb-api/api/lib/cmdb/dcim/rack.py create mode 100644 cmdb-api/api/lib/cmdb/dcim/region.py create mode 100644 cmdb-api/api/lib/cmdb/dcim/server_room.py create mode 100644 cmdb-api/api/lib/cmdb/dcim/tree_view.py create mode 100644 cmdb-api/api/views/cmdb/dcim/__init__.py create mode 100644 cmdb-api/api/views/cmdb/dcim/dcim_history.py create mode 100644 cmdb-api/api/views/cmdb/dcim/idc.py create mode 100644 cmdb-api/api/views/cmdb/dcim/rack.py create mode 100644 cmdb-api/api/views/cmdb/dcim/region.py create mode 100644 cmdb-api/api/views/cmdb/dcim/server_room.py create mode 100644 cmdb-api/api/views/cmdb/dcim/tree_view.py diff --git a/cmdb-api/api/commands/click_cmdb.py b/cmdb-api/api/commands/click_cmdb.py index 6d82a8d0..0ea9ba14 100644 --- a/cmdb-api/api/commands/click_cmdb.py +++ b/cmdb-api/api/commands/click_cmdb.py @@ -23,6 +23,7 @@ from api.lib.cmdb.const import ResourceTypeEnum from api.lib.cmdb.const import RoleEnum from api.lib.cmdb.const import ValueTypeEnum +from api.lib.cmdb.dcim.rack import RackManager from api.lib.exception import AbortException from api.lib.perm.acl.acl import ACLManager from api.lib.perm.acl.acl import UserCache @@ -195,7 +196,7 @@ def cmdb_counter(): today = datetime.date.today() while True: try: - db.session.remove() + db.session.commit() CMDBCounterCache.reset() @@ -209,6 +210,8 @@ def cmdb_counter(): CMDBCounterCache.flush_sub_counter() + RackManager().check_u_slot() + i += 1 except: import traceback diff --git a/cmdb-api/api/lib/cmdb/ci.py b/cmdb-api/api/lib/cmdb/ci.py index b048ed3d..fd3d57fa 100644 --- a/cmdb-api/api/lib/cmdb/ci.py +++ b/cmdb-api/api/lib/cmdb/ci.py @@ -1289,10 +1289,10 @@ def add(cls, first_ci_id, second_ci_id, return existed.id @staticmethod - def delete(cr_id, apply_async=True): + def delete(cr_id, apply_async=True, valid=True): cr = CIRelation.get_by_id(cr_id) or abort(404, ErrFormat.relation_not_found.format("id={}".format(cr_id))) - if current_app.config.get('USE_ACL') and current_user.username != 'worker': + if current_app.config.get('USE_ACL') and current_user.username != 'worker' and valid: resource_name = CITypeRelationManager.acl_resource_name(cr.first_ci.ci_type.name, cr.second_ci.ci_type.name) if not ACLManager().has_permission( resource_name, @@ -1331,7 +1331,7 @@ def delete_2(cls, first_ci_id, second_ci_id, ancestor_ids=None): return cr @classmethod - def delete_3(cls, first_ci_id, second_ci_id, apply_async=True): + def delete_3(cls, first_ci_id, second_ci_id, apply_async=True, valid=True): cr = CIRelation.get_by(first_ci_id=first_ci_id, second_ci_id=second_ci_id, to_dict=False, @@ -1341,7 +1341,7 @@ def delete_3(cls, first_ci_id, second_ci_id, apply_async=True): # ci_relation_delete.apply_async(args=(first_ci_id, second_ci_id, cr.ancestor_ids), queue=CMDB_QUEUE) # delete_id_filter.apply_async(args=(second_ci_id,), queue=CMDB_QUEUE) - cls.delete(cr.id, apply_async=apply_async) + cls.delete(cr.id, apply_async=apply_async, valid=valid) return cr diff --git a/cmdb-api/api/lib/cmdb/const.py b/cmdb-api/api/lib/cmdb/const.py index 25fdaa86..6f51cac2 100644 --- a/cmdb-api/api/lib/cmdb/const.py +++ b/cmdb-api/api/lib/cmdb/const.py @@ -123,6 +123,11 @@ class BuiltinModelEnum(BaseEnum): IPAM_ADDRESS = "ipam_address" IPAM_SCOPE = "ipam_scope" + DCIM_REGION = "dcim_region" + DCIM_IDC = "dcim_idc" + DCIM_SERVER_ROOM = "dcim_server_room" + DCIM_RACK = "dcim_rack" + BUILTIN_ATTRIBUTES = { "_updated_at": _l("Update Time"), diff --git a/cmdb-api/api/lib/cmdb/dcim/__init__.py b/cmdb-api/api/lib/cmdb/dcim/__init__.py new file mode 100644 index 00000000..380474e0 --- /dev/null +++ b/cmdb-api/api/lib/cmdb/dcim/__init__.py @@ -0,0 +1 @@ +# -*- coding:utf-8 -*- diff --git a/cmdb-api/api/lib/cmdb/dcim/base.py b/cmdb-api/api/lib/cmdb/dcim/base.py new file mode 100644 index 00000000..b9fbe0fd --- /dev/null +++ b/cmdb-api/api/lib/cmdb/dcim/base.py @@ -0,0 +1,33 @@ +# -*- coding:utf-8 -*- + +from api.lib.cmdb.ci import CIManager +from api.lib.cmdb.ci import CIRelationManager +from api.lib.cmdb.const import ExistPolicy + + +class DCIMBase(object): + def __init__(self): + self.type_id = None + + @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) + + def add(self, parent_id, **kwargs): + ci_id = CIManager().add(self.type_id, exist_policy=ExistPolicy.REJECT, **kwargs) + + if parent_id: + self.add_relation(parent_id, ci_id) + + return ci_id + + @classmethod + def update(cls, _id, **kwargs): + CIManager().update(_id, **kwargs) + + @classmethod + def delete(cls, _id): + CIManager().delete(_id) diff --git a/cmdb-api/api/lib/cmdb/dcim/const.py b/cmdb-api/api/lib/cmdb/dcim/const.py new file mode 100644 index 00000000..b6e2f21c --- /dev/null +++ b/cmdb-api/api/lib/cmdb/dcim/const.py @@ -0,0 +1,17 @@ +# -*- coding:utf-8 -*- + + +from api.lib.utils import BaseEnum + + +class RackBuiltinAttributes(BaseEnum): + U_COUNT = 'u_count' + U_START = 'u_start' + FREE_U_COUNT = 'free_u_count' + U_SLOT_ABNORMAL = 'u_slot_abnormal' + + +class OperateTypeEnum(BaseEnum): + ADD_DEVICE = "0" + REMOVE_DEVICE = "1" + MOVE_DEVICE = "2" diff --git a/cmdb-api/api/lib/cmdb/dcim/history.py b/cmdb-api/api/lib/cmdb/dcim/history.py new file mode 100644 index 00000000..0b276965 --- /dev/null +++ b/cmdb-api/api/lib/cmdb/dcim/history.py @@ -0,0 +1,40 @@ +from flask_login import current_user + +from api.lib.cmdb.cache import AttributeCache +from api.lib.cmdb.cache import CITypeCache +from api.lib.cmdb.ci import CIManager +from api.lib.mixin import DBMixin +from api.models.cmdb import DCIMOperationHistory + + +class OperateHistoryManager(DBMixin): + cls = DCIMOperationHistory + + @classmethod + def search(cls, page, page_size, fl=None, only_query=False, reverse=False, count_query=False, + last_size=None, **kwargs): + numfound, result = super(OperateHistoryManager, cls).search(page, page_size, fl, only_query, reverse, + count_query, last_size, **kwargs) + + ci_ids = [i['ci_id'] for i in result] + id2ci = {i['_id']: i for i in (CIManager.get_cis_by_ids(ci_ids) or []) if i} + type2show_key = dict() + for i in id2ci.values(): + if i.get('_type') not in type2show_key: + ci_type = CITypeCache.get(i.get('_type')) + if ci_type: + show_key = AttributeCache.get(ci_type.show_id or ci_type.unique_id) + type2show_key[i['_type']] = show_key and show_key.name + + return numfound, result, id2ci, type2show_key + + def _can_add(self, **kwargs): + kwargs['uid'] = current_user.uid + + return kwargs + + def _can_update(self, **kwargs): + pass + + def _can_delete(self, **kwargs): + pass diff --git a/cmdb-api/api/lib/cmdb/dcim/idc.py b/cmdb-api/api/lib/cmdb/dcim/idc.py new file mode 100644 index 00000000..e6bfc591 --- /dev/null +++ b/cmdb-api/api/lib/cmdb/dcim/idc.py @@ -0,0 +1,19 @@ +# -*- coding:utf-8 -*- + + +from flask import abort + +from api.lib.cmdb.cache import CITypeCache +from api.lib.cmdb.const import BuiltinModelEnum +from api.lib.cmdb.dcim.base import DCIMBase +from api.lib.cmdb.resp_format import ErrFormat + + +class IDCManager(DCIMBase): + def __init__(self): + super(IDCManager, self).__init__() + + self.ci_type = CITypeCache.get(BuiltinModelEnum.DCIM_IDC) or abort( + 404, ErrFormat.dcim_builtin_model_not_found.format(BuiltinModelEnum.DCIM_IDC)) + + self.type_id = self.ci_type.id diff --git a/cmdb-api/api/lib/cmdb/dcim/rack.py b/cmdb-api/api/lib/cmdb/dcim/rack.py new file mode 100644 index 00000000..93066004 --- /dev/null +++ b/cmdb-api/api/lib/cmdb/dcim/rack.py @@ -0,0 +1,179 @@ +# -*- coding:utf-8 -*- + +import itertools +import redis_lock +from flask import abort + +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.dcim.base import DCIMBase +from api.lib.cmdb.dcim.const import OperateTypeEnum +from api.lib.cmdb.dcim.const import RackBuiltinAttributes +from api.lib.cmdb.dcim.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 RackManager(DCIMBase): + def __init__(self): + super(RackManager, self).__init__() + + self.ci_type = CITypeCache.get(BuiltinModelEnum.DCIM_RACK) or abort( + 404, ErrFormat.dcim_builtin_model_not_found.format(BuiltinModelEnum.DCIM_RACK)) + + self.type_id = self.ci_type.id + + @classmethod + def update(cls, _id, **kwargs): + if RackBuiltinAttributes.U_COUNT in kwargs: + devices, _, _, _, _, _ = RelationSearch( + [_id], + level=[1], + fl=[RackBuiltinAttributes.U_COUNT, RackBuiltinAttributes.U_START], + count=1000000).search() + for device in devices: + u_start = device.get(RackBuiltinAttributes.U_START) + u_count = device.get(RackBuiltinAttributes.U_COUNT) or 2 + if u_start and u_start + u_count - 1 > kwargs[RackBuiltinAttributes.U_COUNT]: + return abort(400, ErrFormat.dcim_rack_u_count_invalid) + + CIManager().update(_id, _sync=True, **kwargs) + + if RackBuiltinAttributes.U_COUNT in kwargs: + payload = {RackBuiltinAttributes.FREE_U_COUNT: cls._calc_u_free_count(_id)} + + CIManager().update(_id, _sync=True, **payload) + + def delete(self, _id): + super(RackManager, self).delete(_id) + + payload = {RackBuiltinAttributes.U_START: None} + _, _, second_cis = CIRelationManager.get_second_cis(_id, per_page='all') + for ci in second_cis: + CIManager().update(ci['_id'], **payload) + + @staticmethod + def _calc_u_free_count(rack_id, device_id=None, u_start=None, u_count=None): + rack = CIManager.get_ci_by_id(rack_id, need_children=False) + if not rack.get(RackBuiltinAttributes.U_COUNT): + return 0 + + if device_id is not None and u_count is None: + ci = CIManager().get_ci_by_id(device_id, need_children=False) + u_count = ci.get(RackBuiltinAttributes.U_COUNT) or 2 + + if u_start and u_start + u_count - 1 > rack.get(RackBuiltinAttributes.U_COUNT): + return abort(400, ErrFormat.dcim_rack_u_slot_invalid) + + devices, _, _, _, _, _ = RelationSearch( + [rack_id], + level=[1], + fl=[RackBuiltinAttributes.U_COUNT, RackBuiltinAttributes.U_START], + count=1000000).search() + + u_count_sum = 0 + for device in devices: + u_count_sum += (device.get(RackBuiltinAttributes.U_COUNT) or 2) + if device_id is not None: + _u_start = device.get(RackBuiltinAttributes.U_START) + _u_count = device.get(RackBuiltinAttributes.U_COUNT) or 2 + if not _u_start: + continue + + if device.get('_id') != device_id and set(range(u_start, u_start + u_count)) & set( + range(_u_start, _u_start + _u_count)): + return abort(400, ErrFormat.dcim_rack_u_slot_invalid) + + return rack[RackBuiltinAttributes.U_COUNT] - u_count_sum + + def check_u_slot(self): + racks, _, _, _, _, _ = SearchFromDB( + "_type:{}".format(self.type_id), + count=10000000, + fl=[RackBuiltinAttributes.U_START, RackBuiltinAttributes.U_COUNT, RackBuiltinAttributes.U_SLOT_ABNORMAL], + parent_node_perm_passed=True).search() + + for rack in racks: + devices, _, _, _, _, _ = RelationSearch( + [rack['_id']], + level=[1], + fl=[RackBuiltinAttributes.U_COUNT, RackBuiltinAttributes.U_START], + count=1000000).search() + + u_slot_sets = [] + for device in devices: + u_start = device.get(RackBuiltinAttributes.U_START) + u_count = device.get(RackBuiltinAttributes.U_COUNT) or 2 + if u_start is not None and str(u_start).isdigit(): + u_slot_sets.append(set(range(u_start, u_start + u_count))) + + if len(u_slot_sets) > 1: + for a, b in itertools.combinations(u_slot_sets, 2): + u_slot_abnormal = bool(a.intersection(b)) + if u_slot_abnormal != rack.get(RackBuiltinAttributes.U_SLOT_ABNORMAL): + payload = {RackBuiltinAttributes.U_SLOT_ABNORMAL: u_slot_abnormal} + CIManager().update(rack['_id'], **payload) + + def add_device(self, rack_id, device_id, u_start, u_count=None): + with (redis_lock.Lock(rd.r, "DCIM_RACK_OPERATE_{}".format(rack_id))): + self._calc_u_free_count(rack_id, device_id, u_start, u_count) + + self.add_relation(rack_id, device_id) + + payload = {RackBuiltinAttributes.U_START: u_start} + if u_count: + payload[RackBuiltinAttributes.U_COUNT] = u_count + CIManager().update(device_id, _sync=True, **payload) + + payload = { + RackBuiltinAttributes.FREE_U_COUNT: self._calc_u_free_count(rack_id, device_id, u_start, u_count)} + CIManager().update(rack_id, _sync=True, **payload) + + OperateHistoryManager().add(operate_type=OperateTypeEnum.ADD_DEVICE, rack_id=rack_id, ci_id=device_id) + + def remove_device(self, rack_id, device_id): + with (redis_lock.Lock(rd.r, "DCIM_RACK_OPERATE_{}".format(rack_id))): + CIRelationManager.delete_3(rack_id, device_id, apply_async=False, valid=False) + + payload = {RackBuiltinAttributes.FREE_U_COUNT: self._calc_u_free_count(rack_id)} + CIManager().update(rack_id, _sync=True, **payload) + + payload = {RackBuiltinAttributes.U_START: None} + CIManager().update(device_id, _sync=True, **payload) + + OperateHistoryManager().add(operate_type=OperateTypeEnum.REMOVE_DEVICE, rack_id=rack_id, ci_id=device_id) + + def move_device(self, rack_id, device_id, to_u_start): + with (redis_lock.Lock(rd.r, "DCIM_RACK_OPERATE_{}".format(rack_id))): + payload = {RackBuiltinAttributes.FREE_U_COUNT: self._calc_u_free_count(rack_id, device_id, to_u_start)} + CIManager().update(rack_id, _sync=True, **payload) + + CIManager().update(device_id, _sync=True, **{RackBuiltinAttributes.U_START: to_u_start}) + + OperateHistoryManager().add(operate_type=OperateTypeEnum.MOVE_DEVICE, rack_id=rack_id, ci_id=device_id) + + def migrate_device(self, rack_id, device_id, to_rack_id, to_u_start): + with (redis_lock.Lock(rd.r, "DCIM_RACK_OPERATE_{}".format(rack_id))): + self._calc_u_free_count(to_rack_id, device_id, to_u_start) + + if rack_id != to_rack_id: + CIRelationManager.delete_3(rack_id, device_id, apply_async=False, valid=False) + + self.add_relation(to_rack_id, device_id) + + payload = { + RackBuiltinAttributes.FREE_U_COUNT: self._calc_u_free_count(to_rack_id, device_id, to_u_start)} + CIManager().update(to_rack_id, _sync=True, **payload) + + CIManager().update(device_id, _sync=True, **{RackBuiltinAttributes.U_START: to_u_start}) + + if rack_id != to_rack_id: + payload = {RackBuiltinAttributes.FREE_U_COUNT: self._calc_u_free_count(rack_id)} + CIManager().update(rack_id, _sync=True, **payload) + + OperateHistoryManager().add(operate_type=OperateTypeEnum.REMOVE_DEVICE, rack_id=rack_id, ci_id=device_id) + OperateHistoryManager().add(operate_type=OperateTypeEnum.ADD_DEVICE, rack_id=to_rack_id, ci_id=device_id) diff --git a/cmdb-api/api/lib/cmdb/dcim/region.py b/cmdb-api/api/lib/cmdb/dcim/region.py new file mode 100644 index 00000000..3e28a73e --- /dev/null +++ b/cmdb-api/api/lib/cmdb/dcim/region.py @@ -0,0 +1,29 @@ +# -*- coding:utf-8 -*- + + +from flask import abort + +from api.lib.cmdb.cache import CITypeCache +from api.lib.cmdb.ci import CIManager +from api.lib.cmdb.const import BuiltinModelEnum +from api.lib.cmdb.const import ExistPolicy +from api.lib.cmdb.resp_format import ErrFormat + + +class RegionManager(object): + def __init__(self): + self.ci_type = CITypeCache.get(BuiltinModelEnum.DCIM_REGION) or abort( + 404, ErrFormat.dcim_builtin_model_not_found.format(BuiltinModelEnum.DCIM_REGION)) + + self.type_id = self.ci_type.id + + def add(self, **kwargs): + return CIManager().add(self.type_id, exist_policy=ExistPolicy.REJECT, **kwargs) + + @classmethod + def update(cls, _id, **kwargs): + CIManager().update(_id, **kwargs) + + @classmethod + def delete(cls, _id): + CIManager().delete(_id) diff --git a/cmdb-api/api/lib/cmdb/dcim/server_room.py b/cmdb-api/api/lib/cmdb/dcim/server_room.py new file mode 100644 index 00000000..828d3933 --- /dev/null +++ b/cmdb-api/api/lib/cmdb/dcim/server_room.py @@ -0,0 +1,56 @@ +# -*- coding:utf-8 -*- + + +from flask import abort + +from api.lib.cmdb.cache import CITypeCache +from api.lib.cmdb.const import BuiltinModelEnum +from api.lib.cmdb.dcim.base import DCIMBase +from api.lib.cmdb.dcim.const import RackBuiltinAttributes +from api.lib.cmdb.resp_format import ErrFormat +from api.lib.cmdb.search.ci.db.search import Search as SearchFromDB +from api.models.cmdb import CI +from api.models.cmdb import CIRelation + + +class ServerRoomManager(DCIMBase): + def __init__(self): + super(ServerRoomManager, self).__init__() + + self.ci_type = CITypeCache.get(BuiltinModelEnum.DCIM_SERVER_ROOM) or abort( + 404, ErrFormat.dcim_builtin_model_not_found.format(BuiltinModelEnum.DCIM_SERVER_ROOM)) + self.type_id = self.ci_type.id + + @staticmethod + def get_racks(_id, q=None): + rack_type = CITypeCache.get(BuiltinModelEnum.DCIM_RACK) or abort( + 404, ErrFormat.dcim_builtin_model_not_found.format(BuiltinModelEnum.DCIM_RACK)) + + relations = CIRelation.get_by(first_ci_id=_id, only_query=True).join( + CI, CI.id == CIRelation.second_ci_id).filter(CI.type_id == rack_type.id) + rack_ids = [i.second_ci_id for i in relations] + + q = "_type:{}".format(rack_type.id) if not q else "_type:{},{}".format(rack_type.id, q) + if rack_ids: + response, _, _, _, numfound, _ = SearchFromDB( + q, + ci_ids=list(rack_ids), + count=1000000, + parent_node_perm_passed=True).search() + else: + response, numfound = [], 0 + + counter = dict(rack_count=numfound) + u_count = 0 + free_u_count = 0 + for i in response: + _u_count = i.get(RackBuiltinAttributes.U_COUNT) or 0 + u_count += _u_count + free_u_count += (_u_count if i.get(RackBuiltinAttributes.FREE_U_COUNT) is None else + i.get(RackBuiltinAttributes.FREE_U_COUNT)) + counter["u_count"] = u_count + counter["u_used_count"] = u_count - free_u_count + counter["device_count"] = CIRelation.get_by(only_query=True).filter( + CIRelation.first_ci_id.in_(rack_ids)).count() + + return counter, response diff --git a/cmdb-api/api/lib/cmdb/dcim/tree_view.py b/cmdb-api/api/lib/cmdb/dcim/tree_view.py new file mode 100644 index 00000000..c571b88b --- /dev/null +++ b/cmdb-api/api/lib/cmdb/dcim/tree_view.py @@ -0,0 +1,85 @@ +# -*- coding:utf-8 -*- + +from collections import defaultdict + +from flask import abort + +from api.lib.cmdb.cache import AttributeCache +from api.lib.cmdb.cache import CITypeCache +from api.lib.cmdb.const import BuiltinModelEnum +from api.lib.cmdb.resp_format import ErrFormat +from api.lib.cmdb.search.ci.db.search import Search as SearchFromDB +from api.models.cmdb import CI +from api.models.cmdb import CIRelation + + +class TreeViewManager(object): + @classmethod + def get(cls): + region_type = CITypeCache.get(BuiltinModelEnum.DCIM_REGION) or abort( + 404, ErrFormat.dcim_builtin_model_not_found.format(BuiltinModelEnum.DCIM_REGION)) + + idc_type = CITypeCache.get(BuiltinModelEnum.DCIM_IDC) or abort( + 404, ErrFormat.dcim_builtin_model_not_found.format(BuiltinModelEnum.DCIM_IDC)) + + server_room_type = CITypeCache.get(BuiltinModelEnum.DCIM_SERVER_ROOM) or abort( + 404, ErrFormat.dcim_builtin_model_not_found.format(BuiltinModelEnum.DCIM_SERVER_ROOM)) + + rack_type = CITypeCache.get(BuiltinModelEnum.DCIM_RACK) or abort( + 404, ErrFormat.dcim_builtin_model_not_found.format(BuiltinModelEnum.DCIM_RACK)) + + relations = defaultdict(set) + ids = set() + has_parent_ids = set() + + for i in CIRelation.get_by(only_query=True).join(CI, CI.id == CIRelation.first_ci_id).filter( + CI.type_id.in_([region_type.id, idc_type.id])): + relations[i.first_ci_id].add(i.second_ci_id) + ids.add(i.first_ci_id) + ids.add(i.second_ci_id) + has_parent_ids.add(i.second_ci_id) + + for i in CIRelation.get_by(only_query=True).join( + CI, CI.id == CIRelation.second_ci_id).filter(CI.type_id.in_([idc_type.id, server_room_type.id])): + relations[i.first_ci_id].add(i.second_ci_id) + ids.add(i.first_ci_id) + ids.add(i.second_ci_id) + has_parent_ids.add(i.second_ci_id) + + for i in CI.get_by(only_query=True).filter(CI.type_id.in_([region_type.id, idc_type.id])): + ids.add(i.id) + + for _id in ids: + if _id not in has_parent_ids: + relations[None].add(_id) + + type2name = dict() + type2name[region_type.id] = AttributeCache.get(region_type.show_id or region_type.unique_id).name + type2name[idc_type.id] = AttributeCache.get(idc_type.show_id or idc_type.unique_id).name + type2name[server_room_type.id] = AttributeCache.get(server_room_type.show_id or server_room_type.unique_id).name + + response, _, _, _, _, _ = SearchFromDB( + "_type:({})".format(";".join(map(str, [region_type.id, idc_type.id, server_room_type.id]))), + ci_ids=list(ids), + count=1000000, + fl=list(type2name.values()), + parent_node_perm_passed=True).search() + id2ci = {i['_id']: i for i in response} + + def _build_tree(_tree, parent_id=None): + tree = [] + for child_id in _tree.get(parent_id, []): + children = sorted(_build_tree(_tree, child_id), key=lambda x: x['_id']) + if not id2ci.get(child_id): + continue + ci = id2ci[child_id] + if ci['ci_type'] == BuiltinModelEnum.DCIM_SERVER_ROOM: + ci['rack_count'] = CIRelation.get_by(first_ci_id=child_id, only_query=True).join( + CI, CI.id == CIRelation.second_ci_id).filter(CI.type_id == rack_type.id).count() + + tree.append({'children': children, **ci}) + return tree + + result = sorted(_build_tree(relations), key=lambda x: x['_id']) + + return result, type2name diff --git a/cmdb-api/api/lib/cmdb/resp_format.py b/cmdb-api/api/lib/cmdb/resp_format.py index 18649aac..71f12362 100644 --- a/cmdb-api/api/lib/cmdb/resp_format.py +++ b/cmdb-api/api/lib/cmdb/resp_format.py @@ -169,3 +169,8 @@ class ErrFormat(CommonErrFormat): ipam_subnet_cannot_delete = _l("Cannot delete because child nodes exist") ipam_subnet_not_found = _l("Subnet is not found") ipam_scope_cannot_delete = _l("Cannot delete because child nodes exist") + + # # DCIM + dcim_builtin_model_not_found = _l("The dcim model {} does not exist") + dcim_rack_u_slot_invalid = _l("Irregularities in Rack Units") + dcim_rack_u_count_invalid = _l("The device's position is greater than the rack unit height") diff --git a/cmdb-api/api/lib/common_setting/role_perm_base.py b/cmdb-api/api/lib/common_setting/role_perm_base.py index 4db9ad41..7a210a5b 100644 --- a/cmdb-api/api/lib/common_setting/role_perm_base.py +++ b/cmdb-api/api/lib/common_setting/role_perm_base.py @@ -54,6 +54,7 @@ class CMDBApp(BaseApp): "create_topology_view"], }, {"page": "IPAM", "page_cn": "IPAM", "perms": ["read"]}, + {"page": "DCIM", "page_cn": "数据中心", "perms": ["read"]}, ] def __init__(self): diff --git a/cmdb-api/api/models/cmdb.py b/cmdb-api/api/models/cmdb.py index 3936f757..351af9b8 100644 --- a/cmdb-api/api/models/cmdb.py +++ b/cmdb-api/api/models/cmdb.py @@ -707,3 +707,14 @@ class IPAMOperationHistory(Model2): cidr = db.Column(db.String(18), index=True) operate_type = db.Column(db.Enum(*OperateTypeEnum.all())) description = db.Column(db.Text) + + +class DCIMOperationHistory(Model2): + __tablename__ = "c_dcim_operation_histories" + + from api.lib.cmdb.dcim.const import OperateTypeEnum + + uid = db.Column(db.Integer, index=True) + rack_id = db.Column(db.Integer, index=True) + ci_id = db.Column(db.Integer, index=True) + operate_type = db.Column(db.Enum(*OperateTypeEnum.all())) diff --git a/cmdb-api/api/translations/zh/LC_MESSAGES/messages.mo b/cmdb-api/api/translations/zh/LC_MESSAGES/messages.mo index 66ad6acc276d86dbdae42e3c9b48405a122e06c8..c7c7667953b6f29f5066d96d9eeb19a8fc503ba8 100644 GIT binary patch delta 3902 zcmYM#4|L8|9LMo%aSSVCd+d+lXA{|&zZ=_RGcz_;DwgEm+Meby+jutr5)VS^Xv$KO z=#&l#r^B2Qk|K{rb|1;_iVM)=^J4T}&Uc>|prlZpLN39?qb=@q~gwG;XH`|bHHg(9Y z<|wLCKcN;7Mp<-!PwOaTcTEXq;QR>cPuuL{BNppXCH)&UQ76(==4q$_^U%R!)W9oH z72AwT_z>#8Mr#l^soxPNVJfO}FIji1{yME+6Hp@mSlhB(-58Iln2iH)G4{e*WRd0^ zs?@qzd+R|}CKh{QKV(Q#faGN6Tc5Y_?ExCf{4m}c5IHBemc`g6oaHIOUa0;URH@5R z6o?)82~k^l8nxg*T?5ABxO+OlIu0piND8RjIi+469HJtw$a58V zvPNS!%)+^tj&+!UH&6o)=HfIg#uR)V$KrR`1N%{~B%Fe(=nELn{HC5pBHl#pRU+xf zV?L_1i;*$RyGTyvIO-7ogWO``xk&?$MkTNub^iezjK3q*Ha*BviI2jOxDo@oG)~YM zhH>5934BP%ti()w4;{RM8Yqgg<686VQ)K{mu8db@|NO8<%)MMCz551^H zVknMBw%?TW47ktjGX!oYu+Dz51vTMks0n{SmHr}XW#P=CN({smO^g|bs>o^fMP5f; z7s39ipN0f~x~Gbb4#;7spV~^F>qzTJ~`h>5STvWSoSPQ3LEpZ9#+eCMxl$ zzHU4bb#_LeR$Pjz)GE~32^^#mLgO5U;$>u;O>?eNVtr7F+=H#ri%M`7>iR0wbsJIl z?L!^LMr7Mf4Ev*rhhtmxp$8vFDikoQX=vbGZoqtuE$IJ>RMGr}RM&K2RK0i}K%Egk zhU3$y_r*q3V(;62z4Zcmh=(M*^TeVO>5HLy{)gE>zSU=a95wJN)WC1q_#ss3j$=E# zfl4TRfZOk39g3Q0JgQQqHog!Y`fJd`{AL#oJvK*Chviq)jm-vzKnccWJDm9-Ij)4z&JJf8P1Z(TD4HI5IpfG02pU$gNK0=DrDD&s#T&}Hrnt1MLk7f8E(ZgQHccZr=f|hDyHxHSspo;W~jj<-eich*wZ&BWQ#>P#7xV1k@QwM^#`9>ZzE9dH5Kr z^q*iH{(`DpNDfs}W(hP@+Dz0$MW~6(Py^1l@#k#(Wz+y$PzfBio>R%I`_+ZO6jqCR|ZrC;UOx-gL`<689+0(gZ#=vXMn`RBo$*Z0@ y)KguOk=AAXQ@`xk->~V)v+I^}-PyVw4VyNf*;}PsH?Ai*wsFndHLqv98vGxkr`k*a delta 3707 zcmYk-c~DkW7{~EL!-#BxEV2mt0%i(`AgiGAf+!$i8?KePq?6)0(pa>-m3wA7qLqdF zIwmFHQjp4i)aURHzp4&uq&>`5Zq|{ z_hVn$zhXS`O%Qj(=oo}#WQs8qXP^tKF%>_+OgxJU`~VNhL{x7R^ zpTPiZM^)}NMiJix5M3qhi+XS*YM^Ph{}t>>dlM$$r>F|F*#5s!rH7AM1ID5Yvrz+&M-TiapFAau#Znu2B7nVe06Rr%(g6U@mrGHfAvWfjAqPjHyMHxDmCc&8Ui; z#{t-mylTQ2jMyf{T8!$Sj7q%P!$s$S$bK-5$h+n=D#M>`+lP&z633$|m4h1iNzBJ8 zEX0GTz4Rxlk`YWd4f9d&Ex>eKkNwco%mshVEq*kkAnMAyrVkFq0#wEx=mFcMG$6;qc)Do^o&3L!98C^R6*PILEPm9T#WNQhk0?(qBW+iIiZK(EV zs6@}9Dt8lguDyw(N+sb4EI`e4Icjqsz`=MC3w8c`kfs7nz(?^_EW+{-d%QT2>Qc#=j zG31tc9X0SCR01uifuk607#1PLG>cJ*@4zx_!%;Yp>_=cVYW!p9(Tv*oDa3&O&hdH# zHPC$2W?YLZ{fD-F3bkiik;$9;sDSZglZ#VPdubE;V*~nO6KXGKuQ|G&u| zCtw_JD_|C?G$p8+RiY}f23L3)^D(L-IjoC(0`=Tn+g^t*+I6=5Eh@pQsLdM8I_tdw zsN-AW;X)Odf|~gYs3lpAlkhNVfbcYD35Hn5qMn~`+e=YHK~4qS z7)-kySvBLC#Dy|jfl6c-2H{auhG$VP{DOM!4(i2FM%QK>imaYlhzh(JL+}`e<2h7? zuA;^bWF6IxMPHr&0xqbIDMPL4OUO6H>_csmGZ=F>QUF{@mF)H9RRG=48 ziL69Tpw_nQtj9WK{m*fsf!a|6Kal0T*b`N{6jWs@PzhDq_G0V%s6h3oKqqbgd34eK z4Z|@o+c`A}s69}CA;dRNaY0qhJPgI{sDuuq*8T_VfgPxs1?M<>!G+qKxv2XSQA@PI z-hUevXs>ObK%M^<)N=`Zy*xVCZZ70R)Ie3JL{?!0Zb2n-1hqLYVHEo0Ix~;6W>|}@ z6H$9&4l3Xp)O-6;alXuD{dJ>-4z2lJR0*Tc$w#>0h5T#d1~&fYj+`@cr*{x;kHm+cSDciu}v z-5-uRJ>yXoTaHTPT~y$M_WoC>rR>1o=yAE7^Xo>HtP&$}7xIj0!YsUx12MC}X-`KD zP>VYMM^WE}E6C(c_d@%B3C7c&k9w{KHPI%F)A_&1g)+R4y)bOJvuV;%{bNw2s>IHc zV`s^c&$wwuwvG7%HE?Q?6L2(YPb@&K`BLQ5Vd^m*+i@`QO*Cgh16H6)HWxE-6~^On z+kY9Ah}TGG55%CBE*%xP3{|t5m{mWq_t6r%#oL#oK8;-x4*7RY gOLL`V=Vqifo=6$z*O)x`u1`&VL0;pwf=ylj1K4bho&W#< diff --git a/cmdb-api/api/translations/zh/LC_MESSAGES/messages.po b/cmdb-api/api/translations/zh/LC_MESSAGES/messages.po index 353c726a..356e6216 100644 --- a/cmdb-api/api/translations/zh/LC_MESSAGES/messages.po +++ b/cmdb-api/api/translations/zh/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-11-11 17:40+0800\n" +"POT-Creation-Date: 2024-11-26 18:54+0800\n" "PO-Revision-Date: 2023-12-25 20:21+0800\n" "Last-Translator: FULL NAME \n" "Language: zh\n" @@ -92,11 +92,11 @@ msgstr "您没有操作权限!" msgid "Only the creator or administrator has permission!" msgstr "只有创建人或者管理员才有权限!" -#: api/lib/cmdb/const.py:128 +#: api/lib/cmdb/const.py:133 msgid "Update Time" msgstr "更新时间" -#: api/lib/cmdb/const.py:129 +#: api/lib/cmdb/const.py:134 msgid "Updated By" msgstr "更新人" @@ -544,6 +544,18 @@ msgstr "因为子节点已经存在,不能删除" msgid "Subnet is not found" msgstr "子网不存在" +#: api/lib/cmdb/resp_format.py:174 +msgid "The dcim model {} does not exist" +msgstr "DCIM模型 {} 不存在!" + +#: api/lib/cmdb/resp_format.py:175 +msgid "Irregularities in Rack Units" +msgstr "机架U位异常!" + +#: api/lib/cmdb/resp_format.py:176 +msgid "The device's position is greater than the rack unit height" +msgstr "有设备的位置大于机柜的U数!" + #: api/lib/common_setting/resp_format.py:8 msgid "Company info already existed" msgstr "公司信息已存在,无法创建!" diff --git a/cmdb-api/api/views/cmdb/dcim/__init__.py b/cmdb-api/api/views/cmdb/dcim/__init__.py new file mode 100644 index 00000000..380474e0 --- /dev/null +++ b/cmdb-api/api/views/cmdb/dcim/__init__.py @@ -0,0 +1 @@ +# -*- coding:utf-8 -*- diff --git a/cmdb-api/api/views/cmdb/dcim/dcim_history.py b/cmdb-api/api/views/cmdb/dcim/dcim_history.py new file mode 100644 index 00000000..ab267065 --- /dev/null +++ b/cmdb-api/api/views/cmdb/dcim/dcim_history.py @@ -0,0 +1,30 @@ +# -*- coding:utf-8 -*- + +from flask import request + +from api.lib.cmdb.dcim.history import OperateHistoryManager +from api.lib.common_setting.decorator import perms_role_required +from api.lib.common_setting.role_perm_base import CMDBApp +from api.lib.utils import get_page +from api.lib.utils import get_page_size +from api.lib.utils import handle_arg_list +from api.resource import APIView + +app_cli = CMDBApp() + + +class DCIMOperateHistoryView(APIView): + url_prefix = ("/dcim/history/operate",) + + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.DCIM, + app_cli.op.read, app_cli.admin_name) + def get(self): + page = get_page(request.values.pop("page", 1)) + page_size = get_page_size(request.values.pop("page_size", None)) + operate_type = handle_arg_list(request.values.pop('operate_type', [])) + if operate_type: + request.values["operate_type"] = operate_type + + numfound, result, id2ci, type2show_key = OperateHistoryManager.search(page, page_size, **request.values) + + return self.jsonify(numfound=numfound, result=result, id2ci=id2ci, type2show_key=type2show_key) diff --git a/cmdb-api/api/views/cmdb/dcim/idc.py b/cmdb-api/api/views/cmdb/dcim/idc.py new file mode 100644 index 00000000..44d3aee5 --- /dev/null +++ b/cmdb-api/api/views/cmdb/dcim/idc.py @@ -0,0 +1,35 @@ +# -*- coding:utf-8 -*- + +from flask import request + +from api.lib.cmdb.dcim.idc import IDCManager +from api.lib.common_setting.decorator import perms_role_required +from api.lib.common_setting.role_perm_base import CMDBApp +from api.resource import APIView + +app_cli = CMDBApp() + + +class IDCView(APIView): + url_prefix = ("/dcim/idc", "/dcim/idc/") + + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.DCIM, + app_cli.op.read, app_cli.admin_name) + def post(self): + parent_id = request.values.pop("parent_id") + + return self.jsonify(ci_id=IDCManager().add(parent_id, **request.values)) + + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.DCIM, + app_cli.op.read, app_cli.admin_name) + def put(self, _id): + IDCManager().update(_id, **request.values) + + return self.jsonify(ci_id=_id) + + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.DCIM, + app_cli.op.read, app_cli.admin_name) + def delete(self, _id): + IDCManager().delete(_id) + + return self.jsonify(ci_id=_id) diff --git a/cmdb-api/api/views/cmdb/dcim/rack.py b/cmdb-api/api/views/cmdb/dcim/rack.py new file mode 100644 index 00000000..8441869d --- /dev/null +++ b/cmdb-api/api/views/cmdb/dcim/rack.py @@ -0,0 +1,89 @@ +# -*- coding:utf-8 -*- + +from flask import request + +from api.lib.cmdb.dcim.const import RackBuiltinAttributes +from api.lib.cmdb.dcim.rack import RackManager +from api.lib.common_setting.decorator import perms_role_required +from api.lib.common_setting.role_perm_base import CMDBApp +from api.lib.decorator import args_required +from api.resource import APIView + +app_cli = CMDBApp() + + +class RackView(APIView): + url_prefix = ("/dcim/rack", "/dcim/rack/") + + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.DCIM, + app_cli.op.read, app_cli.admin_name) + @args_required("parent_id") + def post(self): + parent_id = request.values.pop("parent_id") + + return self.jsonify(ci_id=RackManager().add(parent_id, **request.values)) + + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.DCIM, + app_cli.op.read, app_cli.admin_name) + def put(self, _id): + RackManager().update(_id, **request.values) + + return self.jsonify(ci_id=_id) + + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.DCIM, + app_cli.op.read, app_cli.admin_name) + def delete(self, _id): + RackManager().delete(_id) + + return self.jsonify(ci_id=_id) + + +class RackDetailView(APIView): + url_prefix = ("/dcim/rack//device/",) + + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.DCIM, + app_cli.op.read, app_cli.admin_name) + @args_required(RackBuiltinAttributes.U_START) + def post(self, rack_id, device_id): + u_start = request.values.pop(RackBuiltinAttributes.U_START) + u_count = request.values.get(RackBuiltinAttributes.U_COUNT) + + RackManager().add_device(rack_id, device_id, u_start, u_count) + + return self.jsonify(rack_id=rack_id, device_id=device_id) + + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.DCIM, + app_cli.op.read, app_cli.admin_name) + @args_required("to_u_start") + def put(self, rack_id, device_id): + to_u_start = request.values.pop("to_u_start") + + RackManager().move_device(rack_id, device_id, to_u_start) + + return self.jsonify(rack_id=rack_id, device_id=device_id, to_u_start=to_u_start) + + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.DCIM, + app_cli.op.read, app_cli.admin_name) + def delete(self, rack_id, device_id): + RackManager().remove_device(rack_id, device_id) + + return self.jsonify(code=200) + + +class RackDeviceMigrateView(APIView): + url_prefix = ("/dcim/rack//device//migrate",) + + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.DCIM, + app_cli.op.read, app_cli.admin_name) + @args_required("to_rack_id") + @args_required("to_u_start") + def put(self, rack_id, device_id): + to_rack_id = request.values.pop("to_rack_id") + to_u_start = request.values.pop("to_u_start") + + RackManager().migrate_device(rack_id, device_id, to_rack_id, to_u_start) + + return self.jsonify(rack_id=rack_id, + device_id=device_id, + to_u_start=to_u_start, + to_rack_id=to_rack_id) diff --git a/cmdb-api/api/views/cmdb/dcim/region.py b/cmdb-api/api/views/cmdb/dcim/region.py new file mode 100644 index 00000000..65c8cb4a --- /dev/null +++ b/cmdb-api/api/views/cmdb/dcim/region.py @@ -0,0 +1,33 @@ +# -*- coding:utf-8 -*- + +from flask import request + +from api.lib.cmdb.dcim.region import RegionManager +from api.lib.common_setting.decorator import perms_role_required +from api.lib.common_setting.role_perm_base import CMDBApp +from api.resource import APIView + +app_cli = CMDBApp() + + +class RegionView(APIView): + url_prefix = ("/dcim/region", "/dcim/region/") + + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.DCIM, + app_cli.op.read, app_cli.admin_name) + def post(self): + return self.jsonify(ci_id=RegionManager().add(**request.values)) + + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.DCIM, + app_cli.op.read, app_cli.admin_name) + def put(self, _id): + RegionManager().update(_id, **request.values) + + return self.jsonify(ci_id=_id) + + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.DCIM, + app_cli.op.read, app_cli.admin_name) + def delete(self, _id): + RegionManager().delete(_id) + + return self.jsonify(ci_id=_id) diff --git a/cmdb-api/api/views/cmdb/dcim/server_room.py b/cmdb-api/api/views/cmdb/dcim/server_room.py new file mode 100644 index 00000000..6c7f2215 --- /dev/null +++ b/cmdb-api/api/views/cmdb/dcim/server_room.py @@ -0,0 +1,43 @@ +# -*- coding:utf-8 -*- + +from flask import request + +from api.lib.cmdb.dcim.server_room import ServerRoomManager +from api.lib.common_setting.decorator import perms_role_required +from api.lib.common_setting.role_perm_base import CMDBApp +from api.lib.decorator import args_required +from api.resource import APIView + +app_cli = CMDBApp() + + +class ServerRoomView(APIView): + url_prefix = ("/dcim/server_room", "/dcim/server_room/", "/dcim/server_room//racks") + + def get(self, _id): + q = request.values.get('q') + counter, result = ServerRoomManager.get_racks(_id, q) + + return self.jsonify(counter=counter, result=result) + + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.DCIM, + app_cli.op.read, app_cli.admin_name) + @args_required("parent_id") + def post(self): + parent_id = request.values.pop("parent_id") + + return self.jsonify(ci_id=ServerRoomManager().add(parent_id, **request.values)) + + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.DCIM, + app_cli.op.read, app_cli.admin_name) + def put(self, _id): + ServerRoomManager().update(_id, **request.values) + + return self.jsonify(ci_id=_id) + + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.DCIM, + app_cli.op.read, app_cli.admin_name) + def delete(self, _id): + ServerRoomManager().delete(_id) + + return self.jsonify(ci_id=_id) diff --git a/cmdb-api/api/views/cmdb/dcim/tree_view.py b/cmdb-api/api/views/cmdb/dcim/tree_view.py new file mode 100644 index 00000000..9ac44b1e --- /dev/null +++ b/cmdb-api/api/views/cmdb/dcim/tree_view.py @@ -0,0 +1,19 @@ +# -*- coding:utf-8 -*- + +from api.lib.cmdb.dcim.tree_view import TreeViewManager +from api.lib.common_setting.decorator import perms_role_required +from api.lib.common_setting.role_perm_base import CMDBApp +from api.resource import APIView + +app_cli = CMDBApp() + + +class DCIMTreeView(APIView): + url_prefix = "/dcim/tree_view" + + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.DCIM, + app_cli.op.read, app_cli.admin_name) + def get(self): + result, type2name = TreeViewManager.get() + + return self.jsonify(result=result, type2name=type2name)