Compare commits

..

1 Commits

Author SHA1 Message Date
dependabot[bot]
249e955f0d build(deps): bump axios from 0.18.0 to 1.6.0 in /cmdb-ui
Bumps [axios](https://github.com/axios/axios) from 0.18.0 to 1.6.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.18.0...v1.6.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-10 04:36:15 +00:00
31 changed files with 207 additions and 758 deletions

View File

@@ -73,33 +73,19 @@
## 安装 ## 安装
### Docker 一键快速构建 ### Docker 一键快速构建
> 方法一 - 进入主目录(先安装 docker 环境, 注意要clone整个项目
- 第一步: 先安装 docker 环境, 以及docker-compose
- 第二步: 拷贝项目
```shell
git clone https://github.com/veops/cmdb.git
```
- 第三步:进入主目录,执行:
``` ```
docker-compose up -d docker-compose up -d
``` ```
> 方法二, 该方法适用于linux系统
- 第一步: 先安装 docker 环境, 以及docker-compose
- 第二步: 直接使用项目根目录下的install.sh 文件进行 `安装`、`启动`、`暂停`、`查状态`、`删除`、`卸载`
```shell
curl -so install.sh https://raw.githubusercontent.com/veops/cmdb/master/install.sh
sh install.sh install
```
### [本地开发环境搭建](docs/local.md)
### [Makefile 安装](docs/makefile.md)
## 验证
- 浏览器打开: [http://127.0.0.1:8000](http://127.0.0.1:8000) - 浏览器打开: [http://127.0.0.1:8000](http://127.0.0.1:8000)
- username: demo 或者 admin - username: demo 或者 admin
- password: 123456 - password: 123456
### [本地开发环境搭建](docs/local.md)
### [Makefile 安装](docs/makefile.md)
--- ---

View File

@@ -19,7 +19,6 @@ from api.lib.cmdb.cache import AttributeCache
from api.lib.cmdb.const import PermEnum from api.lib.cmdb.const import PermEnum
from api.lib.cmdb.const import REDIS_PREFIX_CI from api.lib.cmdb.const import REDIS_PREFIX_CI
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION2
from api.lib.cmdb.const import ResourceTypeEnum from api.lib.cmdb.const import ResourceTypeEnum
from api.lib.cmdb.const import RoleEnum from api.lib.cmdb.const import RoleEnum
from api.lib.cmdb.const import ValueTypeEnum from api.lib.cmdb.const import ValueTypeEnum
@@ -50,17 +49,12 @@ def cmdb_init_cache():
ci_relations = CIRelation.get_by(to_dict=False) ci_relations = CIRelation.get_by(to_dict=False)
relations = dict() relations = dict()
relations2 = dict()
for cr in ci_relations: for cr in ci_relations:
relations.setdefault(cr.first_ci_id, {}).update({cr.second_ci_id: cr.second_ci.type_id}) relations.setdefault(cr.first_ci_id, {}).update({cr.second_ci_id: cr.second_ci.type_id})
if cr.ancestor_ids:
relations2.setdefault(cr.ancestor_ids, {}).update({cr.second_ci_id: cr.second_ci.type_id})
for i in relations: for i in relations:
relations[i] = json.dumps(relations[i]) relations[i] = json.dumps(relations[i])
if relations: if relations:
rd.create_or_update(relations, REDIS_PREFIX_CI_RELATION) rd.create_or_update(relations, REDIS_PREFIX_CI_RELATION)
if relations2:
rd.create_or_update(relations2, REDIS_PREFIX_CI_RELATION2)
es = None es = None
if current_app.config.get("USE_ES"): if current_app.config.get("USE_ES"):

View File

@@ -182,9 +182,6 @@ class CIManager(object):
need_children and res.update(CIRelationManager.get_children(ci_id, ret_key=ret_key)) # one floor need_children and res.update(CIRelationManager.get_children(ci_id, ret_key=ret_key)) # one floor
ci_type = CITypeCache.get(ci.type_id) ci_type = CITypeCache.get(ci.type_id)
if not ci_type:
return res
res["ci_type"] = ci_type.name res["ci_type"] = ci_type.name
fields = CITypeAttributeManager.get_attr_names_by_type_id(ci.type_id) if not fields else fields fields = CITypeAttributeManager.get_attr_names_by_type_id(ci.type_id) if not fields else fields
@@ -521,13 +518,11 @@ class CIManager(object):
item.delete(commit=False) item.delete(commit=False)
for item in CIRelation.get_by(first_ci_id=ci_id, to_dict=False): for item in CIRelation.get_by(first_ci_id=ci_id, to_dict=False):
ci_relation_delete.apply_async( ci_relation_delete.apply_async(args=(item.first_ci_id, item.second_ci_id), queue=CMDB_QUEUE)
args=(item.first_ci_id, item.second_ci_id, item.ancestor_ids), queue=CMDB_QUEUE)
item.delete(commit=False) item.delete(commit=False)
for item in CIRelation.get_by(second_ci_id=ci_id, to_dict=False): for item in CIRelation.get_by(second_ci_id=ci_id, to_dict=False):
ci_relation_delete.apply_async( ci_relation_delete.apply_async(args=(item.first_ci_id, item.second_ci_id), queue=CMDB_QUEUE)
args=(item.first_ci_id, item.second_ci_id, item.ancestor_ids), queue=CMDB_QUEUE)
item.delete(commit=False) item.delete(commit=False)
ad_ci = AutoDiscoveryCI.get_by(ci_id=ci_id, to_dict=False, first=True) ad_ci = AutoDiscoveryCI.get_by(ci_id=ci_id, to_dict=False, first=True)
@@ -891,14 +886,12 @@ class CIRelationManager(object):
@classmethod @classmethod
def get_ancestor_ids(cls, ci_ids, level=1): def get_ancestor_ids(cls, ci_ids, level=1):
level2ids = dict() for _ in range(level):
for _level in range(1, level + 1): cis = db.session.query(CIRelation.first_ci_id).filter(
cis = db.session.query(CIRelation.first_ci_id, CIRelation.ancestor_ids).filter(
CIRelation.second_ci_id.in_(ci_ids)).filter(CIRelation.deleted.is_(False)) CIRelation.second_ci_id.in_(ci_ids)).filter(CIRelation.deleted.is_(False))
ci_ids = [i.first_ci_id for i in cis] ci_ids = [i.first_ci_id for i in cis]
level2ids[_level + 1] = {int(i.ancestor_ids.split(',')[-1]) for i in cis if i.ancestor_ids}
return ci_ids, level2ids return ci_ids
@staticmethod @staticmethod
def _check_constraint(first_ci_id, first_type_id, second_ci_id, second_type_id, type_relation): def _check_constraint(first_ci_id, first_type_id, second_ci_id, second_type_id, type_relation):
@@ -925,14 +918,13 @@ class CIRelationManager(object):
return abort(400, ErrFormat.relation_constraint.format("1-N")) return abort(400, ErrFormat.relation_constraint.format("1-N"))
@classmethod @classmethod
def add(cls, first_ci_id, second_ci_id, more=None, relation_type_id=None, ancestor_ids=None): def add(cls, first_ci_id, second_ci_id, more=None, relation_type_id=None):
first_ci = CIManager.confirm_ci_existed(first_ci_id) first_ci = CIManager.confirm_ci_existed(first_ci_id)
second_ci = CIManager.confirm_ci_existed(second_ci_id) second_ci = CIManager.confirm_ci_existed(second_ci_id)
existed = CIRelation.get_by(first_ci_id=first_ci_id, existed = CIRelation.get_by(first_ci_id=first_ci_id,
second_ci_id=second_ci_id, second_ci_id=second_ci_id,
ancestor_ids=ancestor_ids,
to_dict=False, to_dict=False,
first=True) first=True)
if existed is not None: if existed is not None:
@@ -968,12 +960,11 @@ class CIRelationManager(object):
existed = CIRelation.create(first_ci_id=first_ci_id, existed = CIRelation.create(first_ci_id=first_ci_id,
second_ci_id=second_ci_id, second_ci_id=second_ci_id,
relation_type_id=relation_type_id, relation_type_id=relation_type_id)
ancestor_ids=ancestor_ids)
CIRelationHistoryManager().add(existed, OperateType.ADD) CIRelationHistoryManager().add(existed, OperateType.ADD)
ci_relation_cache.apply_async(args=(first_ci_id, second_ci_id, ancestor_ids), queue=CMDB_QUEUE) ci_relation_cache.apply_async(args=(first_ci_id, second_ci_id), queue=CMDB_QUEUE)
if more is not None: if more is not None:
existed.upadte(more=more) existed.upadte(more=more)
@@ -997,56 +988,53 @@ class CIRelationManager(object):
his_manager = CIRelationHistoryManager() his_manager = CIRelationHistoryManager()
his_manager.add(cr, operate_type=OperateType.DELETE) his_manager.add(cr, operate_type=OperateType.DELETE)
ci_relation_delete.apply_async(args=(cr.first_ci_id, cr.second_ci_id, cr.ancestor_ids), queue=CMDB_QUEUE) ci_relation_delete.apply_async(args=(cr.first_ci_id, cr.second_ci_id), queue=CMDB_QUEUE)
return cr_id return cr_id
@classmethod @classmethod
def delete_2(cls, first_ci_id, second_ci_id, ancestor_ids=None): def delete_2(cls, first_ci_id, second_ci_id):
cr = CIRelation.get_by(first_ci_id=first_ci_id, cr = CIRelation.get_by(first_ci_id=first_ci_id,
second_ci_id=second_ci_id, second_ci_id=second_ci_id,
ancestor_ids=ancestor_ids,
to_dict=False, to_dict=False,
first=True) first=True)
ci_relation_delete.apply_async(args=(first_ci_id, second_ci_id, ancestor_ids), queue=CMDB_QUEUE) ci_relation_delete.apply_async(args=(first_ci_id, second_ci_id), queue=CMDB_QUEUE)
return cr and cls.delete(cr.id) return cls.delete(cr.id)
@classmethod @classmethod
def batch_update(cls, ci_ids, parents, children, ancestor_ids=None): def batch_update(cls, ci_ids, parents, children):
""" """
only for many to one only for many to one
:param ci_ids: :param ci_ids:
:param parents: :param parents:
:param children: :param children:
:param ancestor_ids:
:return: :return:
""" """
if isinstance(parents, list): if isinstance(parents, list):
for parent_id in parents: for parent_id in parents:
for ci_id in ci_ids: for ci_id in ci_ids:
cls.add(parent_id, ci_id, ancestor_ids=ancestor_ids) cls.add(parent_id, ci_id)
if isinstance(children, list): if isinstance(children, list):
for child_id in children: for child_id in children:
for ci_id in ci_ids: for ci_id in ci_ids:
cls.add(ci_id, child_id, ancestor_ids=ancestor_ids) cls.add(ci_id, child_id)
@classmethod @classmethod
def batch_delete(cls, ci_ids, parents, ancestor_ids=None): def batch_delete(cls, ci_ids, parents):
""" """
only for many to one only for many to one
:param ci_ids: :param ci_ids:
:param parents: :param parents:
:param ancestor_ids:
:return: :return:
""" """
if isinstance(parents, list): if isinstance(parents, list):
for parent_id in parents: for parent_id in parents:
for ci_id in ci_ids: for ci_id in ci_ids:
cls.delete_2(parent_id, ci_id, ancestor_ids=ancestor_ids) cls.delete_2(parent_id, ci_id)
class CITriggerManager(object): class CITriggerManager(object):

View File

@@ -637,16 +637,6 @@ class CITypeRelationManager(object):
current_app.logger.warning(str(e)) current_app.logger.warning(str(e))
return abort(400, ErrFormat.circular_dependency_error) return abort(400, ErrFormat.circular_dependency_error)
if constraint == ConstraintEnum.Many2Many:
other_c = CITypeRelation.get_by(parent_id=p.id, constraint=ConstraintEnum.Many2Many,
to_dict=False, first=True)
other_p = CITypeRelation.get_by(child_id=c.id, constraint=ConstraintEnum.Many2Many,
to_dict=False, first=True)
if other_c and other_c.child_id != c.id:
return abort(400, ErrFormat.m2m_relation_constraint.format(p.name, other_c.child.name))
if other_p and other_p.parent_id != p.id:
return abort(400, ErrFormat.m2m_relation_constraint.format(other_p.parent.name, c.name))
existed = cls._get(p.id, c.id) existed = cls._get(p.id, c.id)
if existed is not None: if existed is not None:
existed.update(relation_type_id=relation_type_id, existed.update(relation_type_id=relation_type_id,
@@ -696,24 +686,6 @@ class CITypeRelationManager(object):
cls.delete(ctr.id) cls.delete(ctr.id)
@staticmethod
def get_level2constraint(root_id, level):
level = level + 1 if level == 1 else level
ci = CI.get_by_id(root_id)
if ci is None:
return dict()
root_id = ci.type_id
level2constraint = dict()
for lv in range(1, int(level) + 1):
for i in CITypeRelation.get_by(parent_id=root_id, to_dict=False):
if i.constraint == ConstraintEnum.Many2Many:
root_id = i.child_id
level2constraint[lv] = ConstraintEnum.Many2Many
break
return level2constraint
class CITypeAttributeGroupManager(object): class CITypeAttributeGroupManager(object):
cls = CITypeAttributeGroup cls = CITypeAttributeGroup

View File

@@ -100,7 +100,6 @@ class AttributeDefaultValueEnum(BaseEnum):
CMDB_QUEUE = "one_cmdb_async" CMDB_QUEUE = "one_cmdb_async"
REDIS_PREFIX_CI = "ONE_CMDB" REDIS_PREFIX_CI = "ONE_CMDB"
REDIS_PREFIX_CI_RELATION = "CMDB_CI_RELATION" REDIS_PREFIX_CI_RELATION = "CMDB_CI_RELATION"
REDIS_PREFIX_CI_RELATION2 = "CMDB_CI_RELATION2"
BUILTIN_KEYWORDS = {'id', '_id', 'ci_id', 'type', '_type', 'ci_type'} BUILTIN_KEYWORDS = {'id', '_id', 'ci_id', 'type', '_type', 'ci_type'}

View File

@@ -14,10 +14,7 @@ from api.lib.cmdb.attribute import AttributeManager
from api.lib.cmdb.cache import AttributeCache from api.lib.cmdb.cache import AttributeCache
from api.lib.cmdb.cache import CITypeAttributesCache from api.lib.cmdb.cache import CITypeAttributesCache
from api.lib.cmdb.cache import CITypeCache from api.lib.cmdb.cache import CITypeCache
from api.lib.cmdb.const import ConstraintEnum from api.lib.cmdb.const import PermEnum, ResourceTypeEnum, RoleEnum
from api.lib.cmdb.const import PermEnum
from api.lib.cmdb.const import ResourceTypeEnum
from api.lib.cmdb.const import RoleEnum
from api.lib.cmdb.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.exception import AbortException from api.lib.exception import AbortException
@@ -232,28 +229,14 @@ class PreferenceManager(object):
if not parents: if not parents:
return return
for _l in leaf: for l in leaf:
_find_parent(_l) _find_parent(l)
for node_id in node2show_types: for node_id in node2show_types:
node2show_types[node_id] = [CITypeCache.get(i).to_dict() for i in set(node2show_types[node_id])] node2show_types[node_id] = [CITypeCache.get(i).to_dict() for i in set(node2show_types[node_id])]
topo_flatten = list(toposort.toposort_flatten(topo))
level2constraint = {}
for i, _ in enumerate(topo_flatten[1:]):
ctr = CITypeRelation.get_by(
parent_id=topo_flatten[i], child_id=topo_flatten[i + 1], first=True, to_dict=False)
level2constraint[i + 1] = ctr and ctr.constraint
if leaf2show_types.get(topo_flatten[-1]):
ctr = CITypeRelation.get_by(
parent_id=topo_flatten[-1],
child_id=leaf2show_types[topo_flatten[-1]][0], first=True, to_dict=False)
level2constraint[len(topo_flatten)] = ctr and ctr.constraint
result[view_name] = dict(topo=list(map(list, toposort.toposort(topo))), result[view_name] = dict(topo=list(map(list, toposort.toposort(topo))),
topo_flatten=topo_flatten, topo_flatten=list(toposort.toposort_flatten(topo)),
level2constraint=level2constraint,
leaf=leaf, leaf=leaf,
leaf2show_types=leaf2show_types, leaf2show_types=leaf2show_types,
node2show_types=node2show_types, node2show_types=node2show_types,
@@ -355,29 +338,3 @@ class PreferenceManager(object):
for i in PreferenceTreeView.get_by(type_id=type_id, uid=uid, to_dict=False): for i in PreferenceTreeView.get_by(type_id=type_id, uid=uid, to_dict=False):
i.soft_delete() i.soft_delete()
@staticmethod
def can_edit_relation(parent_id, child_id):
views = PreferenceRelationView.get_by(to_dict=False)
for view in views:
has_m2m = False
last_node_id = None
for cr in view.cr_ids:
_rel = CITypeRelation.get_by(parent_id=cr['parent_id'], child_id=cr['child_id'],
first=True, to_dict=False)
if _rel and _rel.constraint == ConstraintEnum.Many2Many:
has_m2m = True
if parent_id == _rel.parent_id and child_id == _rel.child_id:
return False
if _rel:
last_node_id = _rel.child_id
if parent_id == last_node_id:
rels = CITypeRelation.get_by(parent_id=last_node_id, to_dict=False)
for rel in rels:
if rel.child_id == child_id and has_m2m:
return False
return True

View File

@@ -31,7 +31,6 @@ class ErrFormat(CommonErrFormat):
unique_key_required = "主键字段 {} 缺失" unique_key_required = "主键字段 {} 缺失"
ci_is_already_existed = "CI 已经存在!" ci_is_already_existed = "CI 已经存在!"
relation_constraint = "关系约束: {}, 校验失败 " relation_constraint = "关系约束: {}, 校验失败 "
m2m_relation_constraint = "多对多关系 限制: 模型 {} <-> {} 已经存在多对多关系!"
relation_not_found = "CI关系: {} 不存在" relation_not_found = "CI关系: {} 不存在"
ci_search_Parentheses_invalid = "搜索表达式里小括号前不支持: 或、非" ci_search_Parentheses_invalid = "搜索表达式里小括号前不支持: 或、非"

View File

@@ -1,4 +1,6 @@
# -*- coding:utf-8 -*- # -*- coding:utf-8 -*-
import json import json
from collections import Counter from collections import Counter
@@ -8,14 +10,11 @@ from flask import current_app
from api.extensions import rd from api.extensions import rd
from api.lib.cmdb.ci import CIRelationManager from api.lib.cmdb.ci import CIRelationManager
from api.lib.cmdb.ci_type import CITypeRelationManager from api.lib.cmdb.ci_type import CITypeRelationManager
from api.lib.cmdb.const import ConstraintEnum
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION2
from api.lib.cmdb.resp_format import ErrFormat 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.db.search import Search as SearchFromDB
from api.lib.cmdb.search.ci.es.search import Search as SearchFromES from api.lib.cmdb.search.ci.es.search import Search as SearchFromES
from api.models.cmdb import CI from api.models.cmdb import CI
from api.models.cmdb import CIRelation
class Search(object): class Search(object):
@@ -27,8 +26,7 @@ class Search(object):
page=1, page=1,
count=None, count=None,
sort=None, sort=None,
reverse=False, reverse=False):
ancestor_ids=None):
self.orig_query = query self.orig_query = query
self.fl = fl self.fl = fl
self.facet_field = facet_field self.facet_field = facet_field
@@ -40,81 +38,25 @@ class Search(object):
self.level = level or 0 self.level = level or 0
self.reverse = reverse self.reverse = reverse
self.level2constraint = CITypeRelationManager.get_level2constraint( def _get_ids(self):
root_id[0] if root_id and isinstance(root_id, list) else root_id,
level[0] if isinstance(level, list) and level else level)
self.ancestor_ids = ancestor_ids
self.has_m2m = False
if self.ancestor_ids:
self.has_m2m = True
else:
level = level[0] if isinstance(level, list) and level else level
for _l, c in self.level2constraint.items():
if _l < int(level) and c == ConstraintEnum.Many2Many:
self.has_m2m = True
def _get_ids(self, ids):
if self.level[-1] == 1 and len(ids) == 1:
if self.ancestor_ids is None:
return [i.second_ci_id for i in CIRelation.get_by(first_ci_id=ids[0], to_dict=False)]
else:
seconds = {i.second_ci_id for i in CIRelation.get_by(first_ci_id=ids[0],
ancestor_ids=self.ancestor_ids,
to_dict=False)}
return list(seconds)
merge_ids = [] merge_ids = []
key = [] ids = [self.root_id] if not isinstance(self.root_id, list) else self.root_id
_tmp = []
for level in range(1, sorted(self.level)[-1] + 1): for level in range(1, sorted(self.level)[-1] + 1):
if not self.has_m2m: _tmp = list(map(lambda x: list(json.loads(x).keys()),
_tmp = map(lambda x: json.loads(x).keys(), filter(lambda x: x is not None, rd.get(ids, REDIS_PREFIX_CI_RELATION) or [])))
filter(lambda x: x is not None, rd.get(ids, REDIS_PREFIX_CI_RELATION) or []))
ids = [j for i in _tmp for j in i]
key, prefix = ids, REDIS_PREFIX_CI_RELATION
else:
if not self.ancestor_ids:
if level == 1:
key, prefix = list(map(str, ids)), REDIS_PREFIX_CI_RELATION
else:
key = list(set(["{},{}".format(i, j) for idx, i in enumerate(key) for j in _tmp[idx]]))
prefix = REDIS_PREFIX_CI_RELATION2
else:
if level == 1:
key, prefix = ["{},{}".format(self.ancestor_ids, i) for i in ids], REDIS_PREFIX_CI_RELATION2
else:
key = list(set(["{},{}".format(i, j) for idx, i in enumerate(key) for j in _tmp[idx]]))
prefix = REDIS_PREFIX_CI_RELATION2
if not key:
return []
_tmp = list(map(lambda x: json.loads(x).keys() if x else [], rd.get(key, prefix) or []))
ids = [j for i in _tmp for j in i] ids = [j for i in _tmp for j in i]
if level in self.level: if level in self.level:
merge_ids.extend(ids) merge_ids.extend(ids)
return merge_ids return merge_ids
def _get_reverse_ids(self, ids): def _get_reverse_ids(self):
merge_ids = [] merge_ids = []
level2ids = {} ids = [self.root_id] if not isinstance(self.root_id, list) else self.root_id
for level in range(1, sorted(self.level)[-1] + 1): for level in range(1, sorted(self.level)[-1] + 1):
ids, _level2ids = CIRelationManager.get_ancestor_ids(ids, 1) ids = CIRelationManager.get_ancestor_ids(ids, 1)
if _level2ids.get(2):
level2ids[level + 1] = _level2ids[2]
if level in self.level: if level in self.level:
if level in level2ids and level2ids[level]: merge_ids.extend(ids)
merge_ids.extend(set(ids) & set(level2ids[level]))
else:
merge_ids.extend(ids)
return merge_ids return merge_ids
@@ -122,7 +64,7 @@ class Search(object):
ids = [self.root_id] if not isinstance(self.root_id, list) else self.root_id ids = [self.root_id] if not isinstance(self.root_id, list) else self.root_id
cis = [CI.get_by_id(_id) or abort(404, ErrFormat.ci_not_found.format("id={}".format(_id))) for _id in ids] cis = [CI.get_by_id(_id) or abort(404, ErrFormat.ci_not_found.format("id={}".format(_id))) for _id in ids]
merge_ids = self._get_ids(ids) if not self.reverse else self._get_reverse_ids(ids) merge_ids = self._get_ids() if not self.reverse else self._get_reverse_ids()
if not self.orig_query or ("_type:" not in self.orig_query if not self.orig_query or ("_type:" not in self.orig_query
and "type_id:" not in self.orig_query and "type_id:" not in self.orig_query
@@ -134,11 +76,11 @@ class Search(object):
type_ids.extend(CITypeRelationManager.get_child_type_ids(ci.type_id, level)) type_ids.extend(CITypeRelationManager.get_child_type_ids(ci.type_id, level))
else: else:
type_ids.extend(CITypeRelationManager.get_parent_type_ids(ci.type_id, level)) type_ids.extend(CITypeRelationManager.get_parent_type_ids(ci.type_id, level))
type_ids = set(type_ids) type_ids = list(set(type_ids))
if self.orig_query: if self.orig_query:
self.orig_query = "_type:({0}),{1}".format(";".join(map(str, type_ids)), self.orig_query) self.orig_query = "_type:({0}),{1}".format(";".join(list(map(str, type_ids))), self.orig_query)
else: else:
self.orig_query = "_type:({0})".format(";".join(map(str, type_ids))) self.orig_query = "_type:({0})".format(";".join(list(map(str, type_ids))))
if not merge_ids: if not merge_ids:
# cis, counter, total, self.page, numfound, facet_ # cis, counter, total, self.page, numfound, facet_
@@ -163,65 +105,35 @@ class Search(object):
def statistics(self, type_ids): def statistics(self, type_ids):
self.level = int(self.level) self.level = int(self.level)
ids = [self.root_id] if not isinstance(self.root_id, list) else self.root_id
_tmp = [] _tmp = []
level2ids = {} ids = [self.root_id] if not isinstance(self.root_id, list) else self.root_id
for lv in range(1, self.level + 1): for lv in range(0, self.level):
level2ids[lv] = [] if not lv:
if type_ids and lv == self.level - 1:
if lv == 1:
if not self.has_m2m:
key, prefix = ids, REDIS_PREFIX_CI_RELATION
else:
if not self.ancestor_ids:
key, prefix = ids, REDIS_PREFIX_CI_RELATION
else:
key = ["{},{}".format(self.ancestor_ids, _id) for _id in ids]
prefix = REDIS_PREFIX_CI_RELATION2
level2ids[lv] = [[i] for i in key]
if not key:
_tmp = []
continue
if type_ids and lv == self.level:
_tmp = list(map(lambda x: [i for i in x if i[1] in type_ids], _tmp = list(map(lambda x: [i for i in x if i[1] in type_ids],
(map(lambda x: list(json.loads(x).items()), (map(lambda x: list(json.loads(x).items()),
[i or '{}' for i in rd.get(key, prefix) or []])))) [i or '{}' for i in rd.get(ids, REDIS_PREFIX_CI_RELATION) or []]))))
else: else:
_tmp = list(map(lambda x: list(json.loads(x).items()), _tmp = list(map(lambda x: list(json.loads(x).items()),
[i or '{}' for i in rd.get(key, prefix) or []])) [i or '{}' for i in rd.get(ids, REDIS_PREFIX_CI_RELATION) or []]))
else: else:
for idx, item in enumerate(_tmp): for idx, item in enumerate(_tmp):
if item: if item:
if not self.has_m2m: if type_ids and lv == self.level - 1:
key, prefix = [i[0] for i in item], REDIS_PREFIX_CI_RELATION __tmp = list(
map(lambda x: [(_id, type_id) for _id, type_id in json.loads(x).items()
if type_id in type_ids],
filter(lambda x: x is not None,
rd.get([i[0] for i in item], REDIS_PREFIX_CI_RELATION) or [])))
else: else:
key = list(set(['{},{}'.format(j, i[0]) for i in item for j in level2ids[lv - 1][idx]]))
prefix = REDIS_PREFIX_CI_RELATION2
level2ids[lv].append(key) __tmp = list(map(lambda x: list(json.loads(x).items()),
filter(lambda x: x is not None,
if key: rd.get([i[0] for i in item], REDIS_PREFIX_CI_RELATION) or [])))
if type_ids and lv == self.level:
__tmp = map(lambda x: [(_id, type_id) for _id, type_id in json.loads(x).items()
if type_id in type_ids],
filter(lambda x: x is not None,
rd.get(key, prefix) or []))
else:
__tmp = map(lambda x: list(json.loads(x).items()),
filter(lambda x: x is not None,
rd.get(key, prefix) or []))
else:
__tmp = []
_tmp[idx] = [j for i in __tmp for j in i] _tmp[idx] = [j for i in __tmp for j in i]
else: else:
_tmp[idx] = [] _tmp[idx] = []
level2ids[lv].append([])
result = {str(_id): len(_tmp[idx]) for idx, _id in enumerate(ids)} result = {str(_id): len(_tmp[idx]) for idx, _id in enumerate(ids)}

View File

@@ -38,6 +38,7 @@ def string_to_bytes(value):
byte_string = value byte_string = value
else: else:
byte_string = value.encode("utf-8") byte_string = value.encode("utf-8")
return byte_string return byte_string
@@ -313,7 +314,7 @@ class KeyManage:
secrets_root_key = current_app.config.get("secrets_root_key") secrets_root_key = current_app.config.get("secrets_root_key")
msg, ok = self.is_valid_root_key(secrets_root_key) msg, ok = self.is_valid_root_key(secrets_root_key)
if not ok: if not ok:
return true return {"message": msg, "status": "failed"}
status = self.backend.get(backend_seal_key) status = self.backend.get(backend_seal_key)
return status == "block" return status == "block"

View File

@@ -218,8 +218,6 @@ class CIRelation(Model):
relation_type_id = db.Column(db.Integer, db.ForeignKey("c_relation_types.id"), nullable=False) relation_type_id = db.Column(db.Integer, db.ForeignKey("c_relation_types.id"), nullable=False)
more = db.Column(db.Integer, db.ForeignKey("c_cis.id")) more = db.Column(db.Integer, db.ForeignKey("c_cis.id"))
ancestor_ids = db.Column(db.String(128), index=True)
first_ci = db.relationship("CI", primaryjoin="CI.id==CIRelation.first_ci_id") first_ci = db.relationship("CI", primaryjoin="CI.id==CIRelation.first_ci_id")
second_ci = db.relationship("CI", primaryjoin="CI.id==CIRelation.second_ci_id") second_ci = db.relationship("CI", primaryjoin="CI.id==CIRelation.second_ci_id")
relation_type = db.relationship("RelationType", backref="c_ci_relations.relation_type_id") relation_type = db.relationship("RelationType", backref="c_ci_relations.relation_type_id")

View File

@@ -16,7 +16,6 @@ from api.lib.cmdb.cache import CITypeAttributesCache
from api.lib.cmdb.const import CMDB_QUEUE from api.lib.cmdb.const import CMDB_QUEUE
from api.lib.cmdb.const import REDIS_PREFIX_CI from api.lib.cmdb.const import REDIS_PREFIX_CI
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION2
from api.lib.decorator import flush_db from api.lib.decorator import flush_db
from api.lib.decorator import reconnect_db from api.lib.decorator import reconnect_db
from api.lib.perm.acl.cache import UserCache from api.lib.perm.acl.cache import UserCache
@@ -98,30 +97,16 @@ def ci_delete_trigger(trigger, operate_type, ci_dict):
@celery.task(name="cmdb.ci_relation_cache", queue=CMDB_QUEUE) @celery.task(name="cmdb.ci_relation_cache", queue=CMDB_QUEUE)
@flush_db @flush_db
@reconnect_db @reconnect_db
def ci_relation_cache(parent_id, child_id, ancestor_ids): def ci_relation_cache(parent_id, child_id):
with Lock("CIRelation_{}".format(parent_id)): with Lock("CIRelation_{}".format(parent_id)):
if ancestor_ids is None: children = rd.get([parent_id], REDIS_PREFIX_CI_RELATION)[0]
children = rd.get([parent_id], REDIS_PREFIX_CI_RELATION)[0] children = json.loads(children) if children is not None else {}
children = json.loads(children) if children is not None else {}
cr = CIRelation.get_by(first_ci_id=parent_id, second_ci_id=child_id, ancestor_ids=ancestor_ids, cr = CIRelation.get_by(first_ci_id=parent_id, second_ci_id=child_id, first=True, to_dict=False)
first=True, to_dict=False) if str(child_id) not in children:
if str(child_id) not in children: children[str(child_id)] = cr.second_ci.type_id
children[str(child_id)] = cr.second_ci.type_id
rd.create_or_update({parent_id: json.dumps(children)}, REDIS_PREFIX_CI_RELATION) rd.create_or_update({parent_id: json.dumps(children)}, REDIS_PREFIX_CI_RELATION)
else:
key = "{},{}".format(ancestor_ids, parent_id)
grandson = rd.get([key], REDIS_PREFIX_CI_RELATION2)[0]
grandson = json.loads(grandson) if grandson is not None else {}
cr = CIRelation.get_by(first_ci_id=parent_id, second_ci_id=child_id, ancestor_ids=ancestor_ids,
first=True, to_dict=False)
if cr and str(cr.second_ci_id) not in grandson:
grandson[str(cr.second_ci_id)] = cr.second_ci.type_id
rd.create_or_update({key: json.dumps(grandson)}, REDIS_PREFIX_CI_RELATION2)
current_app.logger.info("ADD ci relation cache: {0} -> {1}".format(parent_id, child_id)) current_app.logger.info("ADD ci relation cache: {0} -> {1}".format(parent_id, child_id))
@@ -171,31 +156,20 @@ def ci_relation_add(parent_dict, child_id, uid):
try: try:
db.session.commit() db.session.commit()
except: except:
db.session.rollback() pass
@celery.task(name="cmdb.ci_relation_delete", queue=CMDB_QUEUE) @celery.task(name="cmdb.ci_relation_delete", queue=CMDB_QUEUE)
@reconnect_db @reconnect_db
def ci_relation_delete(parent_id, child_id, ancestor_ids): def ci_relation_delete(parent_id, child_id):
with Lock("CIRelation_{}".format(parent_id)): with Lock("CIRelation_{}".format(parent_id)):
if ancestor_ids is None: children = rd.get([parent_id], REDIS_PREFIX_CI_RELATION)[0]
children = rd.get([parent_id], REDIS_PREFIX_CI_RELATION)[0] children = json.loads(children) if children is not None else {}
children = json.loads(children) if children is not None else {}
if str(child_id) in children: if str(child_id) in children:
children.pop(str(child_id)) children.pop(str(child_id))
rd.create_or_update({parent_id: json.dumps(children)}, REDIS_PREFIX_CI_RELATION) rd.create_or_update({parent_id: json.dumps(children)}, REDIS_PREFIX_CI_RELATION)
else:
key = "{},{}".format(ancestor_ids, parent_id)
grandson = rd.get([key], REDIS_PREFIX_CI_RELATION2)[0]
grandson = json.loads(grandson) if grandson is not None else {}
if str(child_id) in grandson:
grandson.pop(str(child_id))
rd.create_or_update({key: json.dumps(grandson)}, REDIS_PREFIX_CI_RELATION2)
current_app.logger.info("DELETE ci relation cache: {0} -> {1}".format(parent_id, child_id)) current_app.logger.info("DELETE ci relation cache: {0} -> {1}".format(parent_id, child_id))

View File

@@ -35,7 +35,6 @@ class CIRelationSearchView(APIView):
count = get_page_size(request.values.get("count") or request.values.get("page_size")) count = get_page_size(request.values.get("count") or request.values.get("page_size"))
root_id = request.values.get('root_id') root_id = request.values.get('root_id')
ancestor_ids = request.values.get('ancestor_ids') or None # only for many to many
level = list(map(int, handle_arg_list(request.values.get('level', '1')))) level = list(map(int, handle_arg_list(request.values.get('level', '1'))))
query = request.values.get('q', "") query = request.values.get('q', "")
@@ -45,7 +44,7 @@ class CIRelationSearchView(APIView):
reverse = request.values.get("reverse") in current_app.config.get('BOOL_TRUE') reverse = request.values.get("reverse") in current_app.config.get('BOOL_TRUE')
start = time.time() start = time.time()
s = Search(root_id, level, query, fl, facet, page, count, sort, reverse, ancestor_ids=ancestor_ids) s = Search(root_id, level, query, fl, facet, page, count, sort, reverse)
try: try:
response, counter, total, page, numfound, facet = s.search() response, counter, total, page, numfound, facet = s.search()
except SearchError as e: except SearchError as e:
@@ -68,10 +67,9 @@ class CIRelationStatisticsView(APIView):
root_ids = list(map(int, handle_arg_list(request.values.get('root_ids')))) root_ids = list(map(int, handle_arg_list(request.values.get('root_ids'))))
level = request.values.get('level', 1) level = request.values.get('level', 1)
type_ids = set(map(int, handle_arg_list(request.values.get('type_ids', [])))) type_ids = set(map(int, handle_arg_list(request.values.get('type_ids', []))))
ancestor_ids = request.values.get('ancestor_ids') or None # only for many to many
start = time.time() start = time.time()
s = Search(root_ids, level, ancestor_ids=ancestor_ids) s = Search(root_ids, level)
try: try:
result = s.statistics(type_ids) result = s.statistics(type_ids)
except SearchError as e: except SearchError as e:
@@ -123,18 +121,14 @@ class CIRelationView(APIView):
url_prefix = "/ci_relations/<int:first_ci_id>/<int:second_ci_id>" url_prefix = "/ci_relations/<int:first_ci_id>/<int:second_ci_id>"
def post(self, first_ci_id, second_ci_id): def post(self, first_ci_id, second_ci_id):
ancestor_ids = request.values.get('ancestor_ids') or None
manager = CIRelationManager() manager = CIRelationManager()
res = manager.add(first_ci_id, second_ci_id, ancestor_ids=ancestor_ids) res = manager.add(first_ci_id, second_ci_id)
return self.jsonify(cr_id=res) return self.jsonify(cr_id=res)
def delete(self, first_ci_id, second_ci_id): def delete(self, first_ci_id, second_ci_id):
ancestor_ids = request.values.get('ancestor_ids') or None
manager = CIRelationManager() manager = CIRelationManager()
manager.delete_2(first_ci_id, second_ci_id, ancestor_ids=ancestor_ids) manager.delete_2(first_ci_id, second_ci_id)
return self.jsonify(message="CIType Relation is deleted") return self.jsonify(message="CIType Relation is deleted")
@@ -157,9 +151,8 @@ class BatchCreateOrUpdateCIRelationView(APIView):
ci_ids = list(map(int, request.values.get('ci_ids'))) ci_ids = list(map(int, request.values.get('ci_ids')))
parents = list(map(int, request.values.get('parents', []))) parents = list(map(int, request.values.get('parents', [])))
children = list(map(int, request.values.get('children', []))) children = list(map(int, request.values.get('children', [])))
ancestor_ids = request.values.get('ancestor_ids') or None
CIRelationManager.batch_update(ci_ids, parents, children, ancestor_ids=ancestor_ids) CIRelationManager.batch_update(ci_ids, parents, children)
return self.jsonify(code=200) return self.jsonify(code=200)
@@ -173,8 +166,7 @@ class BatchCreateOrUpdateCIRelationView(APIView):
def delete(self): def delete(self):
ci_ids = list(map(int, request.values.get('ci_ids'))) ci_ids = list(map(int, request.values.get('ci_ids')))
parents = list(map(int, request.values.get('parents', []))) parents = list(map(int, request.values.get('parents', [])))
ancestor_ids = request.values.get('ancestor_ids') or None
CIRelationManager.batch_delete(ci_ids, parents, ancestor_ids=ancestor_ids) CIRelationManager.batch_delete(ci_ids, parents)
return self.jsonify(code=200) return self.jsonify(code=200)

View File

@@ -9,7 +9,6 @@ from api.lib.cmdb.ci_type import CITypeRelationManager
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.const import RoleEnum from api.lib.cmdb.const import RoleEnum
from api.lib.cmdb.preference import PreferenceManager
from api.lib.cmdb.resp_format import ErrFormat from api.lib.cmdb.resp_format import ErrFormat
from api.lib.decorator import args_required from api.lib.decorator import args_required
from api.lib.perm.acl.acl import ACLManager from api.lib.perm.acl.acl import ACLManager
@@ -110,10 +109,3 @@ class CITypeRelationRevokeView(APIView):
acl.revoke_resource_from_role_by_rid(resource_name, rid, ResourceTypeEnum.CI_TYPE_RELATION, perms) acl.revoke_resource_from_role_by_rid(resource_name, rid, ResourceTypeEnum.CI_TYPE_RELATION, perms)
return self.jsonify(code=200) return self.jsonify(code=200)
class CITypeRelationCanEditView(APIView):
url_prefix = "/ci_type_relations/<int:parent_id>/<int:child_id>/can_edit"
def get(self, parent_id, child_id):
return self.jsonify(result=PreferenceManager.can_edit_relation(parent_id, child_id))

View File

@@ -20,7 +20,7 @@
"@wangeditor/editor": "^5.1.23", "@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^1.0.0", "@wangeditor/editor-for-vue": "^1.0.0",
"ant-design-vue": "^1.6.5", "ant-design-vue": "^1.6.5",
"axios": "0.18.0", "axios": "1.6.0",
"babel-eslint": "^8.2.2", "babel-eslint": "^8.2.2",
"butterfly-dag": "^4.3.26", "butterfly-dag": "^4.3.26",
"codemirror": "^5.65.13", "codemirror": "^5.65.13",

View File

@@ -68,7 +68,6 @@
ref="xTable" ref="xTable"
row-id="id" row-id="id"
show-overflow show-overflow
resizable
> >
<!-- 1 --> <!-- 1 -->
<vxe-table-column type="checkbox" fixed="left" :width="45"></vxe-table-column> <vxe-table-column type="checkbox" fixed="left" :width="45"></vxe-table-column>

View File

@@ -1,13 +1,13 @@
import { axios } from '@/utils/request' import { axios } from '@/utils/request'
export function getFirstCIsByCiId(ciId) { export function getFirstCIs(ciId) {
return axios({ return axios({
url: '/v0.1/ci_relations/' + ciId + '/first_cis', url: '/v0.1/ci_relations/' + ciId + '/first_cis',
method: 'GET' method: 'GET'
}) })
} }
export function getSecondCIsByCiId(ciId) { export function getSecondCIs(ciId) {
return axios({ return axios({
url: '/v0.1/ci_relations/' + ciId + '/second_cis', url: '/v0.1/ci_relations/' + ciId + '/second_cis',
method: 'GET' method: 'GET'
@@ -30,11 +30,11 @@ export function statisticsCIRelation(params) {
} }
// 批量添加子节点 // 批量添加子节点
export function batchUpdateCIRelationChildren(ciIds, parents, ancestor_ids = undefined) { export function batchUpdateCIRelationChildren(ciIds, parents) {
return axios({ return axios({
url: '/v0.1/ci_relations/batch', url: '/v0.1/ci_relations/batch',
method: 'POST', method: 'POST',
data: { ci_ids: ciIds, parents, ancestor_ids } data: { ci_ids: ciIds, parents: parents }
}) })
} }
@@ -48,28 +48,26 @@ export function batchUpdateCIRelationParents(ciIds, children) {
} }
// 批量删除 // 批量删除
export function batchDeleteCIRelation(ciIds, parents, ancestor_ids = undefined) { export function batchDeleteCIRelation(ciIds, parents) {
return axios({ return axios({
url: '/v0.1/ci_relations/batch', url: '/v0.1/ci_relations/batch',
method: 'DELETE', method: 'DELETE',
data: { ci_ids: ciIds, parents, ancestor_ids } data: { ci_ids: ciIds, parents: parents }
}) })
} }
// 单个添加 // 单个添加
export function addCIRelationView(firstCiId, secondCiId, data) { export function addCIRelationView(firstCiId, secondCiId) {
return axios({ return axios({
url: `/v0.1/ci_relations/${firstCiId}/${secondCiId}`, url: `/v0.1/ci_relations/${firstCiId}/${secondCiId}`,
method: 'POST', method: 'POST',
data
}) })
} }
// 单个删除 // 单个删除
export function deleteCIRelationView(firstCiId, secondCiId, data) { export function deleteCIRelationView(firstCiId, secondCiId) {
return axios({ return axios({
url: `/v0.1/ci_relations/${firstCiId}/${secondCiId}`, url: `/v0.1/ci_relations/${firstCiId}/${secondCiId}`,
method: 'DELETE', method: 'DELETE',
data
}) })
} }

View File

@@ -68,10 +68,3 @@ export function getRecursive_level2children(type_id) {
method: 'GET' method: 'GET'
}) })
} }
export function getCanEditByParentIdChildId(parent_id, child_id) {
return axios({
url: `/v0.1/ci_type_relations/${parent_id}/${child_id}/can_edit`,
method: 'GET'
})
}

View File

@@ -40,7 +40,7 @@
全选 全选
</a-checkbox> </a-checkbox>
<br /> <br />
<a-checkbox-group style="width:100%" v-model="checkedAttrs"> <a-checkbox-group v-model="checkedAttrs">
<a-row> <a-row>
<a-col :span="6" v-for="item in selectCiTypeAttrList.attributes" :key="item.alias || item.name"> <a-col :span="6" v-for="item in selectCiTypeAttrList.attributes" :key="item.alias || item.name">
<a-checkbox :disabled="item.name === selectCiTypeAttrList.unique" :value="item.alias || item.name"> <a-checkbox :disabled="item.name === selectCiTypeAttrList.unique" :value="item.alias || item.name">
@@ -87,11 +87,10 @@
</template> </template>
<script> <script>
import _ from 'lodash'
import { downloadExcel } from '../../../utils/helper' import { downloadExcel } from '../../../utils/helper'
import { getCITypes } from '@/modules/cmdb/api/CIType' import { getCITypes } from '@/modules/cmdb/api/CIType'
import { getCITypeAttributesById } from '@/modules/cmdb/api/CITypeAttr' import { getCITypeAttributesById } from '@/modules/cmdb/api/CITypeAttr'
import { getCITypeParent, getCanEditByParentIdChildId } from '@/modules/cmdb/api/CITypeRelation' import { getCITypeParent } from '@/modules/cmdb/api/CITypeRelation'
export default { export default {
name: 'CiTypeChoice', name: 'CiTypeChoice',
@@ -108,7 +107,6 @@ export default {
parentsType: [], parentsType: [],
parentsForm: {}, parentsForm: {},
checkedParents: [], checkedParents: [],
canEdit: {},
} }
}, },
created: function() { created: function() {
@@ -145,17 +143,8 @@ export default {
}, },
openModal() { openModal() {
getCITypeParent(this.selectNum).then(async (res) => { getCITypeParent(this.selectNum).then((res) => {
for (let i = 0; i < res.parents.length; i++) { this.parentsType = res.parents
await getCanEditByParentIdChildId(res.parents[i].id, this.selectNum).then((p_res) => {
this.canEdit = {
..._.cloneDeep(this.canEdit),
[res.parents[i].id]: p_res.result,
}
})
}
this.parentsType = res.parents.filter((parent) => this.canEdit[parent.id])
const _parentsForm = {} const _parentsForm = {}
res.parents.forEach((item) => { res.parents.forEach((item) => {
const _find = item.attributes.find((attr) => attr.id === item.unique_id) const _find = item.attributes.find((attr) => attr.id === item.unique_id)

View File

@@ -122,12 +122,12 @@
<a-button type="primary" ghost icon="plus" @click="handleAdd">新增修改字段</a-button> <a-button type="primary" ghost icon="plus" @click="handleAdd">新增修改字段</a-button>
</a-form> </a-form>
</template> </template>
<!-- </a-form> -->
<JsonEditor ref="jsonEditor" @jsonEditorOk="jsonEditorOk" /> <JsonEditor ref="jsonEditor" @jsonEditorOk="jsonEditorOk" />
</CustomDrawer> </CustomDrawer>
</template> </template>
<script> <script>
import _ from 'lodash'
import moment from 'moment' import moment from 'moment'
import { Select, Option } from 'element-ui' import { Select, Option } from 'element-ui'
import { getCIType, getCITypeGroupById } from '@/modules/cmdb/api/CIType' import { getCIType, getCITypeGroupById } from '@/modules/cmdb/api/CIType'
@@ -135,7 +135,7 @@ import { addCI } from '@/modules/cmdb/api/ci'
import JsonEditor from '../../../components/JsonEditor/jsonEditor.vue' import JsonEditor from '../../../components/JsonEditor/jsonEditor.vue'
import { valueTypeMap } from '../../../utils/const' import { valueTypeMap } from '../../../utils/const'
import CreateInstanceFormByGroup from './createInstanceFormByGroup.vue' import CreateInstanceFormByGroup from './createInstanceFormByGroup.vue'
import { getCITypeParent, getCanEditByParentIdChildId } from '@/modules/cmdb/api/CITypeRelation' import { getCITypeParent } from '@/modules/cmdb/api/CITypeRelation'
export default { export default {
name: 'CreateInstanceForm', name: 'CreateInstanceForm',
@@ -166,7 +166,6 @@ export default {
attributesByGroup: [], attributesByGroup: [],
parentsType: [], parentsType: [],
parentsForm: {}, parentsForm: {},
canEdit: {},
} }
}, },
computed: { computed: {
@@ -301,16 +300,8 @@ export default {
this.batchUpdateLists = [{ name: this.attributeList[0].name }] this.batchUpdateLists = [{ name: this.attributeList[0].name }]
}) })
if (action === 'create') { if (action === 'create') {
getCITypeParent(this.typeId).then(async (res) => { getCITypeParent(this.typeId).then((res) => {
for (let i = 0; i < res.parents.length; i++) { this.parentsType = res.parents
await getCanEditByParentIdChildId(res.parents[i].id, this.typeId).then((p_res) => {
this.canEdit = {
..._.cloneDeep(this.canEdit),
[res.parents[i].id]: p_res.result,
}
})
}
this.parentsType = res.parents.filter((parent) => this.canEdit[parent.id])
const _parentsForm = {} const _parentsForm = {}
res.parents.forEach((item) => { res.parents.forEach((item) => {
const _find = item.attributes.find((attr) => attr.id === item.unique_id) const _find = item.attributes.find((attr) => attr.id === item.unique_id)

View File

@@ -15,7 +15,6 @@
<div class="ci-detail-relation-table-title"> <div class="ci-detail-relation-table-title">
{{ parent.alias || parent.name }} {{ parent.alias || parent.name }}
<a <a
:disabled="!canEdit[parent.id]"
@click=" @click="
() => { () => {
$refs.addTableModal.openModal({ [`${ci.unique}`]: ci[ci.unique] }, ci._id, parent.id, 'parents') $refs.addTableModal.openModal({ [`${ci.unique}`]: ci[ci.unique] }, ci._id, parent.id, 'parents')
@@ -24,7 +23,6 @@
><a-icon ><a-icon
type="plus-square" type="plus-square"
/></a> /></a>
<span v-if="!canEdit[parent.id]">当前模型关系为多对多请前往关系视图进行增删操作</span>
</div> </div>
<vxe-grid <vxe-grid
v-if="firstCIs[parent.name]" v-if="firstCIs[parent.name]"
@@ -40,14 +38,7 @@
> >
<template #operation_default="{ row }"> <template #operation_default="{ row }">
<a-popconfirm arrowPointAtCenter title="确认删除关系?" @confirm="deleteRelation(row._id, ciId)"> <a-popconfirm arrowPointAtCenter title="确认删除关系?" @confirm="deleteRelation(row._id, ciId)">
<a <a :style="{ color: 'red' }"><a-icon type="delete"/></a>
:disabled="!canEdit[parent.id]"
:style="{
color: !canEdit[parent.id] ? 'rgba(0, 0, 0, 0.25)' : 'red',
}"
><a-icon
type="delete"
/></a>
</a-popconfirm> </a-popconfirm>
</template> </template>
</vxe-grid> </vxe-grid>
@@ -59,7 +50,6 @@
<div class="ci-detail-relation-table-title"> <div class="ci-detail-relation-table-title">
{{ child.alias || child.name }} {{ child.alias || child.name }}
<a <a
:disabled="!canEdit[child.id]"
@click=" @click="
() => { () => {
$refs.addTableModal.openModal({ [`${ci.unique}`]: ci[ci.unique] }, ci._id, child.id, 'children') $refs.addTableModal.openModal({ [`${ci.unique}`]: ci[ci.unique] }, ci._id, child.id, 'children')
@@ -68,7 +58,6 @@
><a-icon ><a-icon
type="plus-square" type="plus-square"
/></a> /></a>
<span v-if="!canEdit[child.id]">当前模型关系为多对多请前往关系视图进行增删操作</span>
</div> </div>
<vxe-grid <vxe-grid
v-if="secondCIs[child.name]" v-if="secondCIs[child.name]"
@@ -83,14 +72,7 @@
> >
<template #operation_default="{ row }"> <template #operation_default="{ row }">
<a-popconfirm arrowPointAtCenter title="确认删除关系?" @confirm="deleteRelation(ciId, row._id)"> <a-popconfirm arrowPointAtCenter title="确认删除关系?" @confirm="deleteRelation(ciId, row._id)">
<a <a :style="{ color: 'red' }"><a-icon type="delete"/></a>
:disabled="!canEdit[child.id]"
:style="{
color: !canEdit[child.id] ? 'rgba(0, 0, 0, 0.25)' : 'red',
}"
><a-icon
type="delete"
/></a>
</a-popconfirm> </a-popconfirm>
</template> </template>
</vxe-grid> </vxe-grid>
@@ -103,7 +85,7 @@
<script> <script>
import _ from 'lodash' import _ from 'lodash'
import { getCITypeChildren, getCITypeParent, getCanEditByParentIdChildId } from '@/modules/cmdb/api/CITypeRelation' import { getCITypeChildren, getCITypeParent } from '@/modules/cmdb/api/CITypeRelation'
import { searchCIRelation, deleteCIRelationView } from '@/modules/cmdb/api/CIRelation' import { searchCIRelation, deleteCIRelationView } from '@/modules/cmdb/api/CIRelation'
import CiDetailRelationTopo from './ciDetailRelationTopo/index.vue' import CiDetailRelationTopo from './ciDetailRelationTopo/index.vue'
import Node from './ciDetailRelationTopo/node.js' import Node from './ciDetailRelationTopo/node.js'
@@ -136,7 +118,6 @@ export default {
secondCIColumns: {}, secondCIColumns: {},
firstCIJsonAttr: {}, firstCIJsonAttr: {},
secondCIJsonAttr: {}, secondCIJsonAttr: {},
canEdit: {},
} }
}, },
computed: { computed: {
@@ -312,85 +293,76 @@ export default {
.catch((e) => {}) .catch((e) => {})
}, },
async getParentCITypes() { async getParentCITypes() {
const res = await getCITypeParent(this.typeId) await getCITypeParent(this.typeId)
this.parentCITypes = res.parents .then((res) => {
for (let i = 0; i < res.parents.length; i++) { this.parentCITypes = res.parents
await getCanEditByParentIdChildId(res.parents[i].id, this.typeId).then((p_res) => {
this.canEdit = {
..._.cloneDeep(this.canEdit),
[res.parents[i].id]: p_res.result,
}
})
}
const firstCIColumns = {}
const firstCIJsonAttr = {}
res.parents.forEach((item) => {
const columns = []
const jsonAttr = []
item.attributes.forEach((attr) => {
columns.push({ key: 'p_' + attr.id, field: attr.name, title: attr.alias, minWidth: '100px' })
if (attr.value_type === '6') {
jsonAttr.push(attr.name)
}
})
firstCIJsonAttr[item.id] = jsonAttr
firstCIColumns[item.id] = columns
firstCIColumns[item.id].push({
key: 'p_operation',
field: 'operation',
title: '操作',
width: '60px',
fixed: 'right',
slots: {
default: 'operation_default',
},
align: 'center',
})
})
this.firstCIColumns = firstCIColumns const firstCIColumns = {}
this.firstCIJsonAttr = firstCIJsonAttr const firstCIJsonAttr = {}
res.parents.forEach((item) => {
const columns = []
const jsonAttr = []
item.attributes.forEach((attr) => {
columns.push({ key: 'p_' + attr.id, field: attr.name, title: attr.alias, minWidth: '100px' })
if (attr.value_type === '6') {
jsonAttr.push(attr.name)
}
})
firstCIJsonAttr[item.id] = jsonAttr
firstCIColumns[item.id] = columns
firstCIColumns[item.id].push({
key: 'p_operation',
field: 'operation',
title: '操作',
width: '60px',
fixed: 'right',
slots: {
default: 'operation_default',
},
align: 'center',
})
})
this.firstCIColumns = firstCIColumns
this.firstCIJsonAttr = firstCIJsonAttr
})
.catch((e) => {})
}, },
async getChildCITypes() { async getChildCITypes() {
const res = await getCITypeChildren(this.typeId) await getCITypeChildren(this.typeId)
.then((res) => {
this.childCITypes = res.children
this.childCITypes = res.children const secondCIColumns = {}
for (let i = 0; i < res.children.length; i++) { const secondCIJsonAttr = {}
await getCanEditByParentIdChildId(this.typeId, res.children[i].id).then((c_res) => { res.children.forEach((item) => {
this.canEdit = { const columns = []
..._.cloneDeep(this.canEdit), const jsonAttr = []
[res.children[i].id]: c_res.result, item.attributes.forEach((attr) => {
} columns.push({ key: 'c_' + attr.id, field: attr.name, title: attr.alias, minWidth: '100px' })
}) if (attr.value_type === '6') {
} jsonAttr.push(attr.name)
const secondCIColumns = {} }
const secondCIJsonAttr = {} })
res.children.forEach((item) => { secondCIJsonAttr[item.id] = jsonAttr
const columns = [] secondCIColumns[item.id] = columns
const jsonAttr = [] secondCIColumns[item.id].push({
item.attributes.forEach((attr) => { key: 'c_operation',
columns.push({ key: 'c_' + attr.id, field: attr.name, title: attr.alias, minWidth: '100px' }) field: 'operation',
if (attr.value_type === '6') { title: '操作',
jsonAttr.push(attr.name) width: '60px',
} fixed: 'right',
}) slots: {
secondCIJsonAttr[item.id] = jsonAttr default: 'operation_default',
secondCIColumns[item.id] = columns },
secondCIColumns[item.id].push({ align: 'center',
key: 'c_operation', })
field: 'operation', })
title: '操作',
width: '60px',
fixed: 'right',
slots: {
default: 'operation_default',
},
align: 'center',
})
})
this.secondCIColumns = secondCIColumns this.secondCIColumns = secondCIColumns
this.secondCIJsonAttr = secondCIJsonAttr this.secondCIJsonAttr = secondCIJsonAttr
})
.catch((e) => {})
}, },
reload() { reload() {
this.init() this.init()

View File

@@ -372,7 +372,7 @@ export default {
width: 3, width: 3,
fontColor: '#ffffff', fontColor: '#ffffff',
bgColor: ['#6ABFFE', '#5375EB'], bgColor: ['#6ABFFE', '#5375EB'],
chartColor: '#5DADF2,#86DFB7,#5A6F96,#7BD5FF,#FFB980,#4D58D6,#D9B6E9,#8054FF', // 图表颜色 chartColor: '#6592FD,#6EE3EB,#44C2FD,#5F59F7,#1A348F,#7D8FCF,#A6D1E5,#8E56DD', // 图表颜色
isShowPreview: false, isShowPreview: false,
filterExp: undefined, filterExp: undefined,
previewData: null, previewData: null,
@@ -410,7 +410,7 @@ export default {
this.width = width this.width = width
this.chartType = chartType this.chartType = chartType
this.filterExp = item?.options?.filter ?? '' this.filterExp = item?.options?.filter ?? ''
this.chartColor = item?.options?.chartColor ?? '#5DADF2,#86DFB7,#5A6F96,#7BD5FF,#FFB980,#4D58D6,#D9B6E9,#8054FF' this.chartColor = item?.options?.chartColor ?? '#6592FD,#6EE3EB,#44C2FD,#5F59F7,#1A348F,#7D8FCF,#A6D1E5,#8E56DD'
this.isShadow = item?.options?.isShadow ?? false this.isShadow = item?.options?.isShadow ?? false
if (chartType === 'count') { if (chartType === 'count') {

View File

@@ -24,7 +24,7 @@ export const category_1_bar_options = (data, options) => {
}) })
return { return {
color: (options?.chartColor ?? '#5DADF2,#86DFB7,#5A6F96,#7BD5FF,#FFB980,#4D58D6,#D9B6E9,#8054FF').split(','), color: (options?.chartColor ?? '#6592FD,#6EE3EB,#44C2FD,#5F59F7,#1A348F,#7D8FCF,#A6D1E5,#8E56DD').split(','),
grid: { grid: {
top: 15, top: 15,
left: 'left', left: 'left',
@@ -83,7 +83,7 @@ export const category_1_bar_options = (data, options) => {
export const category_1_line_options = (data, options) => { export const category_1_line_options = (data, options) => {
const xData = Object.keys(data) const xData = Object.keys(data)
return { return {
color: (options?.chartColor ?? '#5DADF2,#86DFB7,#5A6F96,#7BD5FF,#FFB980,#4D58D6,#D9B6E9,#8054FF').split(','), color: (options?.chartColor ?? '#6592FD,#6EE3EB,#44C2FD,#5F59F7,#1A348F,#7D8FCF,#A6D1E5,#8E56DD').split(','),
grid: { grid: {
top: 15, top: 15,
left: 'left', left: 'left',
@@ -117,7 +117,7 @@ export const category_1_line_options = (data, options) => {
x2: 0, x2: 0,
y2: 1, y2: 1,
colorStops: [{ colorStops: [{
offset: 0, color: (options?.chartColor ?? '#5DADF2,#86DFB7,#5A6F96,#7BD5FF,#FFB980,#4D58D6,#D9B6E9,#8054FF').split(',')[0] // 0% 处的颜色 offset: 0, color: (options?.chartColor ?? '#6592FD,#6EE3EB,#44C2FD,#5F59F7,#1A348F,#7D8FCF,#A6D1E5,#8E56DD').split(',')[0] // 0% 处的颜色
}, { }, {
offset: 1, color: '#ffffff' // 100% 处的颜色 offset: 1, color: '#ffffff' // 100% 处的颜色
}], }],
@@ -131,7 +131,7 @@ export const category_1_line_options = (data, options) => {
export const category_1_pie_options = (data, options) => { export const category_1_pie_options = (data, options) => {
return { return {
color: (options?.chartColor ?? '#5DADF2,#86DFB7,#5A6F96,#7BD5FF,#FFB980,#4D58D6,#D9B6E9,#8054FF').split(','), color: (options?.chartColor ?? '#6592FD,#6EE3EB,#44C2FD,#5F59F7,#1A348F,#7D8FCF,#A6D1E5,#8E56DD').split(','),
grid: { grid: {
top: 10, top: 10,
left: 'left', left: 'left',
@@ -181,7 +181,7 @@ export const category_2_bar_options = (data, options, chartType) => {
}) })
const legend = [...new Set(_legend)] const legend = [...new Set(_legend)]
return { return {
color: (options?.chartColor ?? '#5DADF2,#86DFB7,#5A6F96,#7BD5FF,#FFB980,#4D58D6,#D9B6E9,#8054FF').split(','), color: (options?.chartColor ?? '#6592FD,#6EE3EB,#44C2FD,#5F59F7,#1A348F,#7D8FCF,#A6D1E5,#8E56DD').split(','),
grid: { grid: {
top: 15, top: 15,
left: 'left', left: 'left',
@@ -249,7 +249,7 @@ export const category_2_bar_options = (data, options, chartType) => {
x2: 0, x2: 0,
y2: 1, y2: 1,
colorStops: [{ colorStops: [{
offset: 0, color: (options?.chartColor ?? '#5DADF2,#86DFB7,#5A6F96,#7BD5FF,#FFB980,#4D58D6,#D9B6E9,#8054FF').split(',')[index % 8] // 0% 处的颜色 offset: 0, color: (options?.chartColor ?? '#6592FD,#6EE3EB,#44C2FD,#5F59F7,#1A348F,#7D8FCF,#A6D1E5,#8E56DD').split(',')[index % 8] // 0% 处的颜色
}, { }, {
offset: 1, color: '#ffffff' // 100% 处的颜色 offset: 1, color: '#ffffff' // 100% 处的颜色
}], }],
@@ -269,7 +269,7 @@ export const category_2_pie_options = (data, options) => {
}) })
}) })
return { return {
color: (options?.chartColor ?? '#5DADF2,#86DFB7,#5A6F96,#7BD5FF,#FFB980,#4D58D6,#D9B6E9,#8054FF').split(','), color: (options?.chartColor ?? '#6592FD,#6EE3EB,#44C2FD,#5F59F7,#1A348F,#7D8FCF,#A6D1E5,#8E56DD').split(','),
grid: { grid: {
top: 15, top: 15,
left: 'left', left: 'left',

View File

@@ -24,8 +24,10 @@ export default {
data() { data() {
return { return {
list: [ list: [
'#5DADF2,#86DFB7,#5A6F96,#7BD5FF,#FFB980,#4D58D6,#D9B6E9,#8054FF', '#6592FD,#6EE3EB,#44C2FD,#5F59F7,#1A348F,#7D8FCF,#A6D1E5,#8E56DD',
'#9BA1F9,#0F2BA8,#A2EBFE,#4982F6,#FEB09C,#6C78E8,#FFDDAB,#4D66BD', '#C1A9DC,#E2B5CD,#EE8EBC,#8483C3,#4D66BD,#213764,#D9B6E9,#DD88EB',
'#6FC4DF,#9FE8CE,#16B4BE,#86E6FB,#1871A3,#E1BF8D,#ED8D8D,#DD88EB',
'#F8B751,#FC9054,#FFE380,#DF963F,#AB5200,#EA9387,#FFBB7C,#D27467',
], ],
} }
}, },

View File

@@ -392,7 +392,6 @@ export default {
origShowTypes: [], origShowTypes: [],
leaf2showTypes: {}, leaf2showTypes: {},
node2ShowTypes: {}, node2ShowTypes: {},
level2constraint: {},
leaf: [], leaf: [],
typeId: null, typeId: null,
viewId: null, viewId: null,
@@ -596,17 +595,6 @@ export default {
} }
} else { } else {
q += `&root_id=${this.treeKeys[this.treeKeys.length - 1].split('%')[0]}` q += `&root_id=${this.treeKeys[this.treeKeys.length - 1].split('%')[0]}`
if (
Object.keys(this.level2constraint).some(
(le) => le < Object.keys(this.level2constraint).length && this.level2constraint[le] === '2'
)
) {
q += `&ancestor_ids=${this.treeKeys
.slice(0, this.treeKeys.length - 1)
.map((item) => item.split('%')[0])
.join(',')}`
}
const typeId = parseInt(this.treeKeys[this.treeKeys.length - 1].split('%')[1]) const typeId = parseInt(this.treeKeys[this.treeKeys.length - 1].split('%')[1])
let level = [] let level = []
@@ -661,19 +649,7 @@ export default {
if (refreshType === 'refreshNumber') { if (refreshType === 'refreshNumber') {
const promises = this.treeKeys.map((key, index) => { const promises = this.treeKeys.map((key, index) => {
let ancestor_ids
if (
Object.keys(this.level2constraint).some(
(le) => le < Object.keys(this.level2constraint).length && this.level2constraint[le] === '2'
)
) {
ancestor_ids = `${this.treeKeys
.slice(0, index)
.map((item) => item.split('%')[0])
.join(',')}`
}
statisticsCIRelation({ statisticsCIRelation({
ancestor_ids,
root_ids: key.split('%')[0], root_ids: key.split('%')[0],
level: this.treeKeys.length - index, level: this.treeKeys.length - index,
type_ids: this.showTypes.map((type) => type.id).join(','), type_ids: this.showTypes.map((type) => type.id).join(','),
@@ -804,36 +780,16 @@ export default {
const index = topo_flatten.findIndex((id) => id === typeId) const index = topo_flatten.findIndex((id) => id === typeId)
const _type = topo_flatten[index + 1] const _type = topo_flatten[index + 1]
if (_type) { if (_type) {
let q = `q=_type:${_type}&root_id=${rootId}&level=1&count=10000` searchCIRelation(`q=_type:${_type}&root_id=${rootId}&level=1&count=10000`).then(async (res) => {
if (
Object.keys(this.level2constraint).some(
(le) => le < Object.keys(this.level2constraint).length && this.level2constraint[le] === '2'
)
) {
q += `&ancestor_ids=${this.treeKeys
.slice(0, this.treeKeys.length - 1)
.map((item) => item.split('%')[0])
.join(',')}`
}
searchCIRelation(q).then(async (res) => {
const facet = [] const facet = []
const ciIds = [] const ciIds = []
res.result.forEach((item) => { res.result.forEach((item) => {
facet.push([item[item.unique], 0, item._id, item._type, item.unique]) facet.push([item[item.unique], 0, item._id, item._type, item.unique])
ciIds.push(item._id) ciIds.push(item._id)
}) })
let ancestor_ids
if (
Object.keys(this.level2constraint).some(
(le) => le < Object.keys(this.level2constraint).length && this.level2constraint[le] === '2'
)
) {
ancestor_ids = `${this.treeKeys.map((item) => item.split('%')[0]).join(',')}`
}
const promises = level.map((_level) => { const promises = level.map((_level) => {
if (_level > 1) { if (_level > 1) {
return statisticsCIRelation({ return statisticsCIRelation({
ancestor_ids,
root_ids: ciIds.join(','), root_ids: ciIds.join(','),
level: _level - 1, level: _level - 1,
type_ids: this.showTypes.map((type) => type.id).join(','), type_ids: this.showTypes.map((type) => type.id).join(','),
@@ -933,7 +889,6 @@ export default {
this.origShowTypeIds = showTypeIds this.origShowTypeIds = showTypeIds
this.leaf2showTypes = this.relationViews.views[this.viewName].leaf2show_types this.leaf2showTypes = this.relationViews.views[this.viewName].leaf2show_types
this.node2ShowTypes = this.relationViews.views[this.viewName].node2show_types this.node2ShowTypes = this.relationViews.views[this.viewName].node2show_types
this.level2constraint = this.relationViews.views[this.viewName].level2constraint
this.leaf = this.relationViews.views[this.viewName].leaf this.leaf = this.relationViews.views[this.viewName].leaf
this.currentView = `${this.viewId}` this.currentView = `${this.viewId}`
this.typeId = this.levels[0][0] this.typeId = this.levels[0][0]
@@ -968,17 +923,6 @@ export default {
const _tempTree = splitTreeKey[splitTreeKey.length - 1].split('%') const _tempTree = splitTreeKey[splitTreeKey.length - 1].split('%')
const firstCIObj = JSON.parse(_tempTree[2]) const firstCIObj = JSON.parse(_tempTree[2])
const firstCIId = _tempTree[0] const firstCIId = _tempTree[0]
let ancestor_ids
if (
Object.keys(this.level2constraint).some(
(le) => le < Object.keys(this.level2constraint).length && this.level2constraint[le] === '2'
)
) {
const ancestor = treeKey
.split('@^@')
.slice(0, menuKey === 'delete' ? treeKey.split('@^@').length - 2 : treeKey.split('@^@').length - 1)
ancestor_ids = ancestor.map((item) => item.split('%')[0]).join(',')
}
if (menuKey === 'delete') { if (menuKey === 'delete') {
const _tempTreeParent = splitTreeKey[splitTreeKey.length - 2].split('%') const _tempTreeParent = splitTreeKey[splitTreeKey.length - 2].split('%')
const that = this const that = this
@@ -991,17 +935,16 @@ export default {
</div> </div>
), ),
onOk() { onOk() {
deleteCIRelationView(_tempTreeParent[0], _tempTree[0], { ancestor_ids }).then((res) => { deleteCIRelationView(_tempTreeParent[0], _tempTree[0]).then((res) => {
that.$message.success('删除成功!') that.$message.success('删除成功!')
setTimeout(() => { that.reload()
that.reload()
}, 500)
}) })
}, },
}) })
} else { } else {
const childTypeId = menuKey const childTypeId = menuKey
this.$refs.addTableModal.openModal(firstCIObj, firstCIId, childTypeId, 'children', ancestor_ids) console.log(menuKey)
this.$refs.addTableModal.openModal(firstCIObj, firstCIId, childTypeId, 'children')
} }
} }
}, },
@@ -1021,21 +964,9 @@ export default {
onOk() { onOk() {
const _tempTree = that.treeKeys[that.treeKeys.length - 1].split('%') const _tempTree = that.treeKeys[that.treeKeys.length - 1].split('%')
const first_ci_id = Number(_tempTree[0]) const first_ci_id = Number(_tempTree[0])
let ancestor_ids
if (
Object.keys(that.level2constraint).some(
(le) => le < Object.keys(that.level2constraint).length && that.level2constraint[le] === '2'
)
) {
ancestor_ids = `${that.treeKeys
.slice(0, that.treeKeys.length - 1)
.map((item) => item.split('%')[0])
.join(',')}`
}
batchDeleteCIRelation( batchDeleteCIRelation(
that.selectedRowKeys.map((item) => item._id), that.selectedRowKeys.map((item) => item._id),
[first_ci_id], [first_ci_id]
ancestor_ids
).then((res) => { ).then((res) => {
that.$refs.xTable.clearCheckboxRow() that.$refs.xTable.clearCheckboxRow()
that.$refs.xTable.clearCheckboxReserve() that.$refs.xTable.clearCheckboxReserve()
@@ -1199,10 +1130,7 @@ export default {
const $table = this.$refs['xTable'] const $table = this.$refs['xTable']
const data = {} const data = {}
this.columns.forEach((item) => { this.columns.forEach((item) => {
if ( if (!(item.field in this.initialPasswordValue) && !_.isEqual(row[item.field], this.initialInstanceList[rowIndex][item.field])) {
!(item.field in this.initialPasswordValue) &&
!_.isEqual(row[item.field], this.initialInstanceList[rowIndex][item.field])
) {
data[item.field] = row[item.field] ?? null data[item.field] = row[item.field] ?? null
} }
}) })
@@ -1252,18 +1180,7 @@ export default {
}, },
sumbitFromCreateInstance({ ci_id }) { sumbitFromCreateInstance({ ci_id }) {
const first_ci_id = this.treeKeys[this.treeKeys.length - 1].split('%')[0] const first_ci_id = this.treeKeys[this.treeKeys.length - 1].split('%')[0]
let ancestor_ids addCIRelationView(first_ci_id, ci_id).then((res) => {
if (
Object.keys(this.level2constraint).some(
(le) => le < Object.keys(this.level2constraint).length && this.level2constraint[le] === '2'
)
) {
ancestor_ids = `${this.treeKeys
.slice(0, this.treeKeys.length - 1)
.map((item) => item.split('%')[0])
.join(',')}`
}
addCIRelationView(first_ci_id, ci_id, { ancestor_ids }).then((res) => {
setTimeout(() => { setTimeout(() => {
this.loadData({}, 'refreshNumber') this.loadData({}, 'refreshNumber')
}, 500) }, 500)
@@ -1353,7 +1270,9 @@ export default {
}) })
Promise.all(promises) Promise.all(promises)
.then((res) => { .then((res) => {
that.$message.success('删除成功') that.$message.success({
message: '删除成功',
})
}) })
.catch((e) => { .catch((e) => {
console.log(e) console.log(e)

View File

@@ -86,7 +86,6 @@ export default {
isFocusExpression: false, isFocusExpression: false,
type: 'children', type: 'children',
preferenceAttrList: [], preferenceAttrList: [],
ancestor_ids: undefined,
} }
}, },
computed: { computed: {
@@ -102,13 +101,13 @@ export default {
}, },
watch: {}, watch: {},
methods: { methods: {
async openModal(ciObj, ciId, addTypeId, type, ancestor_ids = undefined) { async openModal(ciObj, ciId, addTypeId, type) {
console.log(ciObj, addTypeId)
this.visible = true this.visible = true
this.ciObj = ciObj this.ciObj = ciObj
this.ciId = ciId this.ciId = ciId
this.addTypeId = addTypeId this.addTypeId = addTypeId
this.type = type this.type = type
this.ancestor_ids = ancestor_ids
await getSubscribeAttributes(addTypeId).then((res) => { await getSubscribeAttributes(addTypeId).then((res) => {
this.preferenceAttrList = res.attributes // 已经订阅的全部列 this.preferenceAttrList = res.attributes // 已经订阅的全部列
}) })
@@ -169,15 +168,13 @@ export default {
const ciIds = [...selectRecordsCurrent, ...selectRecordsReserved].map((record) => record._id) const ciIds = [...selectRecordsCurrent, ...selectRecordsReserved].map((record) => record._id)
if (ciIds.length) { if (ciIds.length) {
if (this.type === 'children') { if (this.type === 'children') {
await batchUpdateCIRelationChildren(ciIds, [this.ciId], this.ancestor_ids) await batchUpdateCIRelationChildren(ciIds, [this.ciId])
} else { } else {
await batchUpdateCIRelationParents(ciIds, [this.ciId]) await batchUpdateCIRelationParents(ciIds, [this.ciId])
} }
setTimeout(() => { this.$message.success('添加成功!')
this.$message.success('添加成功!') this.handleClose()
this.handleClose() this.$emit('reload')
this.$emit('reload')
}, 500)
} }
}, },
handleSearch() { handleSearch() {

View File

@@ -24,15 +24,13 @@ services:
cmdb-cache: cmdb-cache:
image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-cache:3.0 image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-cache:3.0
container_name: cmdb-cache container_name: cmdb-cache
environment:
TZ: Asia/Shanghai
networks: networks:
new: new:
aliases: aliases:
- redis - redis
cmdb-api: cmdb-api:
image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-api:2.3.7 image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-api:2.3.6
# build: # build:
# context: . # context: .
# target: cmdb-api # target: cmdb-api
@@ -67,7 +65,7 @@ services:
- cmdb-api - cmdb-api
cmdb-ui: cmdb-ui:
image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-ui:2.3.7 image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-ui:2.3.6
# build: # build:
# context: . # context: .
# target: cmdb-ui # target: cmdb-ui

View File

@@ -68,35 +68,20 @@
### One-Click Docker Quick Build ### One-Click Docker Quick Build
> Method 1 - Prepare: install docker and docker-compose
- step 1: **Prepare: install docker and docker-compose** - In directory cmdb
- step 2: copy the repository ```
```shell docker-compose up -d
git clone https://github.com/veops/cmdb.git ```
``` - View: [http://127.0.0.1:8000](http://127.0.0.1:8000)
- step 3: In directory cmdb: - username: demo or admin
``` - password: 123456
docker-compose up -d
```
> Method 2 Usefull for linux os.
- step 1: **Prepare: install docker and docker-compose**
- step 2: directly use the install.sh file in the project's root directory to `install`, `start`, `pause`, `status`, `delete`, and `uninstall` the application.
```shell
curl -so install.sh https://raw.githubusercontent.com/veops/cmdb/master/install.sh
sh install.sh install
```
### [Local Setup](local_en.md) ### [Local Setup](local_en.md)
### [Installation with Makefile](makefile_en.md) ### [Installation with Makefile](makefile_en.md)
## Validation
- View: [http://127.0.0.1:8000](http://127.0.0.1:8000)
- username: demo or admin
- password: 123456
## Contributing ## Contributing
1. Fork it 1. Fork it

View File

@@ -22,7 +22,7 @@ cp cmdb-api/settings.example.py cmdb-api/settings.py
- 后端: `cd cmdb-api && pipenv run pipenv install && cd ..` - 后端: `cd cmdb-api && pipenv run pipenv install && cd ..`
- 前端: `cd cmdb-ui && yarn install && cd ..` - 前端: `cd cmdb-ui && yarn install && cd ..`
- 可以将 docs/cmdb.sql 导入到数据库里,登录用户和密码分别是:demo/123456 - 可以将 docs/cmdb.sql 导入到数据库里,登录用户和密码分别是:demo/123456
- 创建数据库表: 进入**cmdb-api**目录执行 `pipenv run flask db-setup && pipenv run flask common-check-new-columns && pipenv run flask cmdb-init-cache` - 创建数据库表: 进入**cmdb-api**目录执行 `pipenv run flask db-setup && pipenv run flask cmdb-init-cache`
- 启动服务 - 启动服务
- 后端: 进入**cmdb-api**目录执行 `pipenv run flask run -h 0.0.0.0` - 后端: 进入**cmdb-api**目录执行 `pipenv run flask run -h 0.0.0.0`

View File

@@ -17,7 +17,7 @@
- frontend: `cd cmdb-ui && yarn install && cd ..` - frontend: `cd cmdb-ui && yarn install && cd ..`
- Suggest step: (default: user:demo,password:123456) - Suggest step: (default: user:demo,password:123456)
- Create tables of cmdb database: - Create tables of cmdb database:
in **cmdb-api** directory: `pipenv run flask db-setup && pipenv run flask common-check-new-columns && pipenv run flask cmdb-init-cache` in **cmdb-api** directory: `pipenv run flask db-setup && pipenv run flask cmdb-init-cache`
` source docs/cmdb.sql` ` source docs/cmdb.sql`

View File

@@ -46,11 +46,10 @@ read_rnd_buffer_size=512K
skip-name-resolve skip-name-resolve
max_connections=1000 max_connections=1000
slow_query_log = ON slow_query_log = ON
slow_query_log_file = /tmp/mysql_slow.log slow_query_log_file = /var/log/mysql_slow.log
long_query_time = 1 long_query_time = 1
sql_mode="STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION" sql_mode="STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"
#log-error = /var/log/mysql/error.log #log-error = /var/log/mysql/error.log
# By default we only accept connections from localhost # By default we only accept connections from localhost
#bind-address = 127.0.0.1 #bind-address = 127.0.0.1
# Disabling symbolic-links is recommended to prevent assorted security risks # Disabling symbolic-links is recommended to prevent assorted security risks
log_timestamps = SYSTEM

View File

@@ -1,157 +0,0 @@
#!/bin/bash
current_path=$(pwd)
cmdb_dir=$(cd ~ && pwd)/apps
check_docker() {
docker info >/dev/null 2>&1
if ! [ $? -eq 0 ]; then
echo "error: please install and start docker firstly"
exit 1
fi
}
check_docker_compose() {
docker-compose --version >/dev/null 2>&1
if ! [ $? -eq 0 ]; then
echo "error: please install docker-compose firstly"
exit 1
fi
}
clone_repo() {
local repo_url=$1
git clone $repo_url || {
echo "error: failed to clone $repo_url"
exit 1
}
}
change_directory() {
local new_dir=$1
if ! mkdir -p "$new_dir"; then
echo "error: failed to create directory $new_dir"
exit 1
fi
cd "$new_dir" || exit 1
}
install_service() {
echo ""
echo "Installing the service $1..."
change_directory "$cmdb_dir"
if [ -d "${cmdb_dir}/cmdb" ]; then
echo "directory ${cmdb_dir}/cmdb already exist"
exit 1
fi
clone_repo "https://githubfast.com/veops/cmdb.git" || clone_repo "https://github.com/veops/cmdb.git"
cd ${cmdb_dir}/cmdb || exit 1
docker-compose pull
if [ $? -eq 0 ]; then
echo "successfully install package in directory: ${cmdb_dir}/cmdb"
fi
cd $current_path || exit 1
}
start_service() {
echo "Starting the service $1..."
cd ${cmdb_dir}/cmdb
docker-compose up -d
cd $current_path
}
pause_service() {
case $2 in
"" | cmdb-api | cmdb-ui | cmdb-db | cmdb-cache)
echo "Pausing the service ..."
cd ${cmdb_dir}/cmdb || exit 1
docker-compose stop $2
cd $current_path || exit 1
;;
*)
echo "Please input invalid service name: [cmdb-api|cmdb-ui|cmdb-db|cmdb-cache]"
;;
esac
}
delete_service() {
echo "Deleting the service ..."
cd ${cmdb_dir}/cmdb || exit 1
docker-compose down
cd $current_path || exit 1
}
status_service() {
cd ${cmdb_dir}/cmdb || exit 1
docker-compose ps
cd $current_path || exit 1
}
uninstall_service() {
if ! [ -d "${cmdb_dir}/cmdb" ]; then
echo "directory ${cmdb_dir}/cmdb already not exist"
exit 0
fi
read -p "Are you sure to uninstall the all the application and data? y/n:" input
if [ $input = "y" ]; then
echo "Uninstalling the service ..."
cd ${cmdb_dir}/cmdb || exit 1
docker-compose down -v
if [ $? -eq 0 ]; then
rm -fr ${cmdb_dir}/cmdb
fi
cd $current_path || exit 1
fi
}
echo "Welcome to the CMDB service management script!"
echo ""
check_depend() {
check_docker
check_docker_compose
}
case $1 in
install)
check_depend
install_service $2
;;
start)
check_depend
start_service $2
;;
status)
check_depend
status_service $2
;;
pause)
check_depend
pause_service $2
;;
delete)
check_depend
delete_service $2
;;
uninstall)
check_depend
uninstall_service $2
;;
*)
echo "Usage: $0 [install|start|pause|uninstall]"
echo "install Used to install the application"
echo "start Used to start the application"
echo "status Used to show status of the application"
echo "pause Used to pause the application"
echo "delete Used to delete the application"
echo "uninstall Used to uninstall the application, include all data"
;;
esac