Compare commits

..

651 Commits

Author SHA1 Message Date
pycook
03fdf5c004 chore: release v2.4.14 2024-11-11 19:02:05 +08:00
pycook
f277cf088e fix(api): ipam assign address 2024-11-11 18:56:09 +08:00
pycook
b1f8a0024b Dev api ipam (#637)
* feat: ipam api

* fix: ipam
2024-11-11 18:17:37 +08:00
Leo Song
aae43a53b5 Merge pull request #636 from veops/dev_ui_ipam
feat(ui): add ipam
2024-11-11 16:50:35 +08:00
songlh
8c2cdb1ca4 feat(ui): add ipam 2024-11-11 16:49:53 +08:00
thexqn
57d4bf5548 fix(search): correct type_id usage in CI relation filtering (#633) 2024-11-11 15:53:24 +08:00
dagongren
5e7c6199bf feat:add employee work_region (#634)
* feat:add employee work_region

* env
2024-11-07 11:52:55 +08:00
Zhuohao Li
28dca7f086 fix permission bug (#632)
不同的appid下可能有相同的resource type name.
2024-10-27 14:04:34 +08:00
pycook
4a3c21eec4 feat(api): add builtin attributes (#631) 2024-10-22 18:21:07 +08:00
Leo Song
5d28c28023 Merge pull request #630 from veops/dev_ui_241022
fix(ui): update userPanel style
2024-10-22 14:12:14 +08:00
songlh
ba6edb3abe fix(ui): update userPanel style 2024-10-22 14:11:36 +08:00
Leo Song
2f1d57cee1 Merge pull request #629 from veops/dev_ui_241022
feat(ui): add userPanel component
2024-10-22 14:01:02 +08:00
songlh
4111c634d9 feat(ui): add userPanel component 2024-10-22 13:59:38 +08:00
pycook
f1fababa3d feat(api): save relation search option 2024-10-18 11:03:31 +08:00
pycook
fe6373422e chore: release v2.4.13 2024-10-18 09:52:26 +08:00
pycook
b3ea776886 Merge pull request #628 from veops/dev_api_relation_path_search
feat(api): relation path search
2024-10-17 19:47:34 +08:00
pycook
c4d2ce313d feat(api): relation path search 2024-10-17 19:46:39 +08:00
Leo Song
20103a0fe6 Merge pull request #627 from veops/dev_ui_241017
feat(ui): add relation search
2024-10-17 17:56:13 +08:00
songlh
394e2aeac6 feat(ui): add relation search 2024-10-17 17:55:36 +08:00
pycook
8f7d78c26c Merge pull request #623 from veops/dev_api_relation_path_search
Dev api relation path search
2024-09-30 17:33:45 +08:00
pycook
7eecf3cec3 feat(api): add api /ci_type_relations/path 2024-09-26 20:32:21 +08:00
pycook
f6e9c443f7 Merge pull request #622 from novohool/master
Update cache support for environment variables in settings.example.py
2024-09-26 18:09:54 +08:00
pycook
857cbd82fd feat(api): add relation path search 2024-09-26 17:59:08 +08:00
novohool
9a14296e02 Update settings.example.py 2024-09-26 17:00:51 +08:00
pycook
f638b52759 fix(api): change records of attribute values for date and datetime 2024-09-25 19:37:08 +08:00
pycook
78da728105 fix(api): search for multiple CIType 2024-09-24 17:46:27 +08:00
pycook
eb69029a51 fix(api): ci relations search 2024-09-23 19:46:43 +08:00
Leo Song
07a097eba2 Merge pull request #619 from veops/dev_ui_240920
feat: update computed attr tip
2024-09-20 15:36:55 +08:00
songlh
e843e3eac9 feat: update computed attr tip 2024-09-20 15:36:19 +08:00
Leo Song
7308cfa6c2 Merge pull request #617 from veops/dev_ui_240914
dev_ui_240914
2024-09-14 17:28:42 +08:00
songlh
64ea4fb21f fix(ui): operation history search expand error 2024-09-14 17:27:57 +08:00
songlh
e15cefaa38 fix(ui): employeeTreeSelect display error 2024-09-14 17:26:33 +08:00
pycook
f32339b969 Merge pull request #616 from thexqn/optimize_history
feat: Add show_attr value column to operation history table
2024-09-14 11:55:01 +08:00
thexqn
131d213a73 优化CITypeCache的调用方式 2024-09-14 11:30:45 +08:00
thexqn
ff98777689 feat(cmdb): 添加操作历史表的唯一值列 (Add unique value column to operation history table) 2024-09-14 01:13:07 +08:00
thexqn
383d4c88ed feat: Add unique value column to operation history table 2024-09-13 23:44:40 +08:00
Leo Song
bb7157e292 Merge pull request #615 from veops/dev_ui_240913
feat(ui): add employeeTreeSelect otherOptions prop
2024-09-13 18:36:48 +08:00
songlh
b1a82f1a67 feat(ui): add employeeTreeSelect otherOptions prop 2024-09-13 18:36:24 +08:00
pycook
de86ea3852 fix(api): remote ip for login log 2024-09-10 11:41:35 +08:00
pycook
bf05ea240e feat(api): acl supports channel 2024-09-09 15:28:20 +08:00
Leo Song
8ec0d619d7 Merge pull request #613 from veops/dev_ui_240909
feat(ui): add SplitPane calcBasedParent prop
2024-09-09 10:45:27 +08:00
songlh
61f8c463bc feat(ui): add SplitPane calcBasedParent prop 2024-09-09 10:44:58 +08:00
Leo Song
9b4dc3e43b Merge pull request #611 from veops/dev_ui_240903
feat: update icon select
2024-09-03 16:41:18 +08:00
songlh
9e69be8256 feat: update icon select 2024-09-03 16:40:46 +08:00
pycook
251b9e7fd5 chore: release v2.4.12 2024-09-03 14:18:53 +08:00
Leo Song
f3cc12f1f9 Merge pull request #610 from veops/dev_ui_240903
fix(ui): build error
2024-09-03 13:15:43 +08:00
songlh
56f03e1624 fix(ui): build error 2024-09-03 13:14:56 +08:00
Leo Song
42ad2b6dde Merge pull request #609 from veops/dev_ui_240903
feat: update resource search
2024-09-03 11:30:08 +08:00
songlh
5aba1ff257 feat: update resource search 2024-09-03 11:29:32 +08:00
pycook
417e8fe349 perf(api): resource search supports recent searches and my favorites 2024-09-02 16:56:06 +08:00
Leo Song
02235d8cc0 Merge pull request #607 from veops/dev_ui_240828
fix(ui): ci choice attr error
2024-08-28 18:52:43 +08:00
songlh
00c7a644a2 fix(ui): ci choice attr error 2024-08-28 18:52:20 +08:00
pycook
f3e8757450 fix(api): CIType templates import 2024-08-28 17:52:15 +08:00
Leo Song
f0749341ba Merge pull request #606 from veops/dev_ui_240828
feat(ui): update ui
2024-08-28 16:55:45 +08:00
songlh
89da671e46 feat(ui): update ui 2024-08-28 16:55:07 +08:00
Leo Song
0e60aae076 Merge pull request #605 from veops/dev_ui_240827
Dev UI 240827
2024-08-27 10:33:30 +08:00
songlh
4dfa97d404 fix(ui): resource search export error 2024-08-27 10:32:53 +08:00
songlh
9b778f9bc7 fix(ui): update create attr icon 2024-08-27 10:32:25 +08:00
pycook
eafb5f053a fix(api): custom dashboard for enum type 2024-08-26 22:31:58 +08:00
Leo Song
834054e216 Merge pull request #604 from veops/dev_ui_240826
feat: export remove reference attr
2024-08-26 22:22:43 +08:00
LH_R
a97cabbedc feat: export remove reference attr 2024-08-26 22:21:25 +08:00
Leo Song
ae77852d5f Merge pull request #603 from veops/dev_ui_240826
fix(ui): define value filter error
2024-08-26 21:40:03 +08:00
LH_R
611ee40dca fix(ui): define value filter error 2024-08-26 21:38:02 +08:00
pycook
c0d55b2126 Merge branch 'master' of github.com:veops/cmdb 2024-08-26 19:50:44 +08:00
pycook
2cc4499ef9 fix(api): custom dashboard 2024-08-26 19:50:22 +08:00
Leo Song
1268404bca Merge pull request #602 from veops/dev_ui_240826
fix(ui): menu icon display
2024-08-26 19:49:46 +08:00
songlh
570a9203c4 fix(ui): menu icon display 2024-08-26 19:47:23 +08:00
pycook
adae7b5519 chore: release v2.4.11 2024-08-26 18:44:23 +08:00
Leo Song
8a91ec7b11 Merge pull request #601 from veops/dev_ui_240826
fix(ui): some bugs
2024-08-26 18:35:44 +08:00
songlh
92fca65383 fix(ui): some bugs 2024-08-26 18:34:42 +08:00
Leo Song
4b8e6c2841 Merge pull request #600 from veops/dev_ui_240826
fix(ui): update builtIn params
2024-08-26 16:03:37 +08:00
songlh
ab240cb003 fix(ui): update builtIn params 2024-08-26 16:02:05 +08:00
Leo Song
61e62e4740 Merge pull request #599 from veops/dev_ui_240826
feat(ui) update CMDBFilterComp label
2024-08-26 15:16:07 +08:00
songlh
1fd72d6c78 feat(ui) update CMDBFilterComp label 2024-08-26 15:14:52 +08:00
Leo Song
51e16f6b23 Merge pull request #598 from veops/dev_ui_240826
Dev UI 240826
2024-08-26 15:09:01 +08:00
songlh
037378e384 fix(ui): create ad plugin params 2024-08-26 15:08:19 +08:00
songlh
631871a8cf feat(ui): update ci type choice config 2024-08-26 15:05:11 +08:00
pycook
6e02f6a21f fix(api): in query 2024-08-26 13:29:03 +08:00
pycook
a2224ba2ac Merge pull request #597 from veops/dev_api_0826
feat(api): enum supports
2024-08-26 12:15:05 +08:00
pycook
11a289aac9 feat(api): enum supports 2024-08-26 12:14:14 +08:00
Leo Song
55ab04dd28 Merge pull request #596 from thexqn/fix_order_bug
修复在继承模型的情况下,非继承属性与继承属性的排序以及其他分组的排序提示问题Fix order bug
2024-08-26 11:20:15 +08:00
thexqn
256a4f4844 清理多余的router-view 2024-08-23 16:55:21 +08:00
thexqn
018a349336 feat: 修复在继承模型的情况下,非继承属性与继承属性的排序以及其他分组的排序的问题 2024-08-23 16:50:37 +08:00
thexqn
8f62227adb feat: 修复在继承模型的情况下,非继承属性与继承属性的排序以及其他分组的排序的问题 2024-08-23 16:29:29 +08:00
thexqn
de51cb3e21 Merge branch 'veops:master' into master 2024-08-23 14:56:12 +08:00
Leo Song
ecb069cf14 Merge pull request #594 from veops/dev_ui_240820
feat(ui): add bool and reference type
2024-08-20 15:31:45 +08:00
songlh
937cb84393 feat(ui): add bool and reference type 2024-08-20 15:31:11 +08:00
pycook
40a4db06b5 Merge pull request #593 from veops/dev_api_0820
feat(api): supports bool and reference
2024-08-20 13:51:44 +08:00
pycook
cc98f903ea feat(api): supports bool and reference 2024-08-20 13:49:51 +08:00
kinyXu
fb7471ce04 feat: add attribute sorted tips for non-inherited attributes 2024-08-20 11:48:44 +08:00
Leo Song
e2872f041e Merge pull request #591 from veops/dev_ui_240813
refactor(ui): ci table
2024-08-13 17:15:15 +08:00
songlh
250fde127c refactor(ui): ci table 2024-08-13 17:14:05 +08:00
pycook
73dbb14944 Merge pull request #590 from lgphone/patch-1
bugfix: cmdb-api  auto_discovery add unique_value param
2024-08-07 16:05:56 +08:00
YangEver
73c9a6fa72 bugfix: cmdb-api auto_discovery add unique_value param
自动发现接口需要根据unique_value参数进行数据唯一性校验,此参数为必填项
2024-08-07 15:50:42 +08:00
Leo Song
09d957db79 Merge pull request #589 from veops/dev_ui_240807
Dev UI 240807
2024-08-07 14:42:01 +08:00
songlh
b73d796891 fix(ui): dashboard chart config 2024-08-07 14:41:22 +08:00
songlh
e7cbd0caa9 feat(ui): update common settings btn 2024-08-07 14:40:54 +08:00
pycook
3e4c385d91 fix(api): Dashboard using display attributes 2024-08-06 19:59:16 +08:00
pycook
3aac012ee9 chore: release v2.4.10 2024-07-31 16:42:26 +08:00
Leo Song
78d762cacc Merge pull request #588 from veops/dev_ui_240731
feat(ui): update ci type
2024-07-31 16:01:17 +08:00
songlh
c668ba7d3f feat(ui): update ci type 2024-07-31 16:00:40 +08:00
pycook
542a876ead fix(api): delete item for multi-value attributes 2024-07-30 20:05:21 +08:00
pycook
68b7497bba Merge pull request #587 from thexqn/master
修复在用了计算属性的情况下,批量上传功能可能出现的错误.
2024-07-30 09:18:30 +08:00
thexqn
dfbf3d462d 修复在用了计算属性的情况下,批量上传功能可能出现的错误. 2024-07-30 01:17:53 +08:00
pycook
692708fcba feat(api): Multi-valued attribute values ​​support adding and deleting 2024-07-29 19:55:07 +08:00
pycook
330b64edb3 chore: release v2.4.9 2024-07-26 17:03:00 +08:00
Leo Song
63a3074cb7 Merge pull request #585 from veops/fix_ui_240726
fix: discovery card eye btn
2024-07-26 16:50:43 +08:00
songlh
31b8cf49dc fix: discovery card eye btn 2024-07-26 16:49:28 +08:00
Leo Song
b01c335456 Merge pull request #584 from veops/dev_ui_240726
feat(ui): update auto discovery
2024-07-26 10:41:19 +08:00
songlh
002fef09e2 feat(ui): update auto discovery 2024-07-26 10:40:37 +08:00
pycook
175778a162 perf(api): auto discovery (#582) 2024-07-25 17:45:26 +08:00
Leo Song
5050a1bef5 Merge pull request #581 from veops/dev_ui_240722
feat: add accounts config
2024-07-22 17:39:18 +08:00
songlh
46a6cf67d6 feat: add accounts config 2024-07-22 17:38:48 +08:00
Leo Song
4e857c2775 Merge pull request #580 from veops/dev_ui_240716
feat: add history export
2024-07-16 13:46:40 +08:00
songlh
835df1bdeb feat: add history export 2024-07-16 13:45:31 +08:00
ivonGwy
579339d13c change pic 2024-07-15 16:23:34 +08:00
ivonGwy
629967ce82 change pic 2024-07-15 16:22:20 +08:00
pycook
3a00bfd236 chore: update docker compose 2024-07-11 14:29:34 +08:00
pycook
2e97ebd895 chore: release v2.4.8 2024-07-10 19:43:01 +08:00
Leo Song
eb6a813cbc Merge pull request #578 from veops/dev_ui_24071002
feat(ui): update
2024-07-10 19:19:08 +08:00
songlh
ff78face48 feat(ui): update 2024-07-10 19:18:22 +08:00
pycook
d55433c438 fix(api): computed attributes for multi values (#577) 2024-07-10 19:18:03 +08:00
Leo Song
daf0254616 Merge pull request #575 from veops/dev_ui_240710
fix: topoview search error
2024-07-10 10:12:11 +08:00
songlh
6b32009955 fix: topoview search error 2024-07-10 10:11:40 +08:00
Leo Song
d53288c1fb Merge pull request #574 from veops/dev_ui_240709
feat: update auto discovery
2024-07-09 09:45:25 +08:00
songlh
586d820a08 feat: update auto discovery 2024-07-09 09:44:28 +08:00
pycook
6776be4599 fix(api): auto discovery update
fix(api): auto discovery update
2024-07-08 18:03:21 +08:00
pycook
ff2b8ea198 perf(api): relationships built by attribute values (#572) 2024-07-08 11:42:18 +08:00
Leo Song
ed46a1e1c1 Merge pull request #571 from veops/dev_ui_240703
feat: add http attr mapping
2024-07-03 18:49:23 +08:00
songlh
0dc614fb46 feat: add http attr mapping 2024-07-03 18:47:55 +08:00
pycook
bc66d33ce0 fix(api): auto discovery configuration save password
fix(api): auto discovery configuration save password
2024-07-02 21:32:30 +08:00
pycook
d5db68d7d0 feat(api): auto discovery supports mapping (#569) 2024-07-02 20:19:50 +08:00
Leo Song
b22b8b286b Merge pull request #568 from veops/dev_ui_240628
dev_ui_240628
2024-06-28 17:43:30 +08:00
songlh
dd4f3b0e9c feat: update model export 2024-06-28 17:42:20 +08:00
songlh
688f4e0ea4 fix(ui): load ci type error 2024-06-28 17:42:10 +08:00
pycook
c1813f525d chore: update docker compose 2024-06-27 21:33:19 +08:00
pycook
b405e28498 chore: release v2.4.7 2024-06-27 21:30:02 +08:00
pycook
fa32758462 perf(api): CIType templates download (#567) 2024-06-27 20:54:38 +08:00
Leo Song
29995b660a Merge pull request #566 from veops/dev_ui_0627
feat: update model config
2024-06-27 19:41:58 +08:00
songlh
b96fc06a62 feat: update model config 2024-06-27 19:41:24 +08:00
Leo Song
c7f30b63ff Merge pull request #565 from veops/dev_ui_0625
fix(ui): auto discovery
2024-06-25 17:36:00 +08:00
songlh
5bff69a8a8 fix(ui): auto discovery 2024-06-25 17:35:29 +08:00
pycook
d5e60fab88 chore: update ui dockerfile 2024-06-24 21:40:38 +08:00
Jared Tan
190f452118 chore: fix UI docker build and makes UI/API docker build parallel execution (#563)
* try fix UI docker build

* parallel execution

* polish

* polish

* polish

* update
2024-06-24 21:00:57 +08:00
Leo Song
98a4824364 Merge pull request #564 from veops/dev_ui_ad_0624
feat: update ad ui
2024-06-24 14:26:33 +08:00
songlh
c0f9baea79 feat: update ad ui 2024-06-24 14:25:56 +08:00
pycook
d4b661c77f fix(api): commands cmdb-patch 2024-06-21 18:22:56 +08:00
pycook
75cd7bde77 fix(api): auto discovery permission 2024-06-21 12:47:12 +08:00
Leo Song
ec912d3a65 Merge pull request #562 from veops/fix_ui_2.4.6
fix(ui): some bugs
2024-06-21 11:49:53 +08:00
songlh
42f02b4986 fix(ui): some bugs 2024-06-21 11:49:12 +08:00
simontigers
a13b999820 Merge pull request #561 from veops/dev_common_perm
fix(api): auto_discovery add new perms
2024-06-21 10:25:35 +08:00
simontigers
5f53b0dd0e fix(api): auto_discovery add new perms 2024-06-21 10:25:13 +08:00
Leo Song
df22085ff9 Merge pull request #560 from veops/fix_ui_topology
fix(ui): topology view error
2024-06-20 22:21:27 +08:00
LH_R
06148b402d fix(ui): topology view error 2024-06-20 22:20:25 +08:00
pycook
3fe020505a chore: release v2.4.6 2024-06-20 20:31:10 +08:00
pycook
b34e83124f perf(api): auto discovery has been upgraded (#559) 2024-06-20 20:30:04 +08:00
Leo Song
cdc52d3f80 Merge pull request #558 from veops/dev_ui_ad
fix: build error
2024-06-20 20:03:40 +08:00
LH_R
b3a80d5678 fix: build error 2024-06-20 19:54:15 +08:00
Leo Song
a2e3061bba Merge pull request #557 from veops/dev_ui_ad
feat(ui): auto discovery
2024-06-20 17:29:06 +08:00
songlh
a8eb5126ea feat(ui): auto discovery 2024-06-20 17:28:09 +08:00
pycook
adac2129fc chore: update Dockerfile-UI 2024-06-20 13:20:42 +08:00
pycook
e660c901ce chore: update Dockerfile-UI 2024-06-20 11:07:57 +08:00
pycook
ff002c0a1e chore: update Dockerfile-UI 2024-06-20 09:47:56 +08:00
Leo Song
88593d6da7 Merge pull request #555 from veops/fix_ui_lint
fix(ui): lint error
2024-06-18 11:42:53 +08:00
songlh
6fa0dd5bc5 fix(ui): lint error 2024-06-18 11:42:22 +08:00
Jared Tan
3200942373 polish ci and remove es build (#553) 2024-06-18 10:31:33 +08:00
pycook
4fd705cc59 feat(api): add table c_ad_ci_type_relations 2024-06-18 10:22:04 +08:00
Jared Tan
74827ce187 add workflow (#552) 2024-06-18 09:29:00 +08:00
Leo Song
4ed1eb6062 Merge pull request #551 from veops/fix_bug_538
fix: issue #538
2024-06-17 14:41:51 +08:00
songlh
7792204658 fix: issue #538 2024-06-17 14:41:24 +08:00
Leo Song
8621108906 Merge pull request #550 from veops/fix_bug_operation_history
fix: operation history table
2024-06-14 17:27:49 +08:00
songlh
6437af19b9 fix: operation history table 2024-06-14 17:27:13 +08:00
Leo Song
735ddb334c Merge pull request #542 from veops/fix_issue_540
fix: issue #540
2024-06-12 15:00:08 +08:00
songlh
4a8032202e fix: issue #540 2024-06-12 14:59:14 +08:00
Leo Song
c7acea6422 Merge pull request #539 from veops/fix_computed_code
fix: computed code area tab
2024-06-11 15:03:20 +08:00
songlh
ac4c93de8e fix: computed code area tab 2024-06-11 15:02:37 +08:00
pycook
8d044cf935 chore(docker compose): add api health check 2024-06-09 20:58:27 +08:00
pycook
54747fa789 feat(ui): update iconfont 2024-06-07 10:41:26 +08:00
pycook
545f1bb30b Dev dynamic attribute (#535)
* feat: dynamic attribute

* feat(api): dynamic attribute
2024-06-07 10:39:40 +08:00
pycook
dc77bca17c feat: dynamic attribute (#534) 2024-06-07 10:29:32 +08:00
Leo Song
4973278c5a Merge pull request #532 from veops/fix_bug_530
fix: ci topo expand error
2024-06-06 14:06:26 +08:00
songlh
d1c9361e47 fix: ci topo expand error 2024-06-06 14:05:32 +08:00
Leo Song
28c57cacd9 Merge pull request #531 from veops/dev_ui_240606
feat: update topology view
2024-06-06 11:10:35 +08:00
songlh
711dcc4bd7 feat: update topology view 2024-06-06 11:08:58 +08:00
simontigers
491d3cce00 Merge pull request #529 from veops/fix_decorator_perms_role_required
fix: decorator_perms_role_required
2024-06-04 19:23:58 +08:00
simontigers
27354a3927 fix: decorator_perms_role_required 2024-06-04 19:23:22 +08:00
Leo Song
78495eb976 Merge pull request #528 from veops/feat/dev_ui_240604
feat(ui): update model relation
2024-06-04 12:05:42 +08:00
songlh
ae900c7d3b feat(ui): update model relation 2024-06-04 12:04:26 +08:00
pycook
50134e6a0b feat(api): attribute association supports multiple groups (#527) 2024-06-04 11:34:54 +08:00
pycook
65ef58dea9 feat: update docker-compose 2024-05-30 13:18:28 +08:00
pycook
0a2e7aa99f feat: put the mysql password in .env 2024-05-30 13:08:18 +08:00
pycook
8875e75883 fix(acl): add relation 2024-05-30 09:33:30 +08:00
pycook
2f03639c57 chore: release v2.4.5 2024-05-29 13:32:40 +08:00
pycook
49bc5d94a9 fix(api): topology view read permission 2024-05-29 11:34:02 +08:00
pycook
39354e1293 feat(ui): update components CMDBExprDrawer 2024-05-28 20:16:39 +08:00
pycook
d3714f3ecf feat(ui): relation-graph upgrade to 2.1.42 2024-05-28 20:11:53 +08:00
pycook
729a616282 feat(ui): topology view (#525) 2024-05-28 20:03:10 +08:00
fxiang21
2d3a290aa3 fix: cmdb-inner-secrets-init bug 2024-05-28 19:57:16 +08:00
pycook
9e885a5b12 feat(api): i18n update 2024-05-28 18:08:15 +08:00
simontigers
f5822d7cba Merge pull request #524 from simontigers/common_cmdb_app_perm
fix: cmdb app perms
2024-05-28 17:55:08 +08:00
hu.sima
21ea553e74 fix: cmdb app perms 2024-05-28 17:54:51 +08:00
pycook
e63038d1b6 feat(api): topology view (#523)
* feat(api): topology views crud

* feat(api): topology view

* feat(api): topology view api done

* feat(api): topology view is done
2024-05-28 17:50:09 +08:00
simontigers
d56806f511 Merge pull request #521 from simontigers/common_cmdb_app_perm
feat: CMDB add TopologyView resource
2024-05-28 16:21:25 +08:00
simontigers
7ac7fdc08e feat: CMDB add TopologyView resource 2024-05-28 16:20:56 +08:00
pycook
ba11707146 feat(ui): resource views router 2024-05-21 17:58:06 +08:00
pycook
d49dc8a067 fix(api): hot loading is blocked in development mode 2024-05-21 13:14:40 +08:00
pycook
6bfb34fe2a Dev UI 240520 (#517)
* feat(ui): Model configuration supports search models

* fix(ui): Jump to the first subscription by default
2024-05-20 14:10:33 +08:00
thexqn
2c7ed8c32d chore: Update local.md with instructions for setting up MySQL and Redis services (#515) 2024-05-20 13:39:05 +08:00
pycook
5b275af54e fix(api): exception when calling webhook (#516) 2024-05-20 13:22:54 +08:00
pycook
dde7ec6246 feat(ui): Resources and Preference support grouping 2024-05-19 21:55:36 +08:00
pycook
9181817e96 feat(api): my preference support grouping (#513) 2024-05-18 22:55:01 +08:00
pycook
46b54bb7f2 fix(ui): some bugs (#512) 2024-05-17 12:07:56 +08:00
pycook
fe63310c4e Dev api 240517 (#511)
* fix(api): list values delete

* fix(acl): role rebuild cache
2024-05-17 11:20:53 +08:00
pycook
27c733aa2c docs: update sql 2024-05-16 20:59:30 +08:00
pycook
2a8e9e684e fix(ui): issue#490 2024-05-02 21:28:06 +08:00
pycook
095190a785 fix(api): unique constraint (#505) 2024-05-02 21:22:40 +08:00
pycook
ef25c94b5d fix(api): permissions for CIType group editing 2024-04-29 15:18:47 +08:00
pycook
06ae1bcf13 docs: update build_api_key 2024-04-29 15:11:12 +08:00
pycook
9ead4e7d8d chore: release v2.4.4 2024-04-29 14:44:33 +08:00
pycook
994a28dd25 feat(ui): baseline rollback (#502) 2024-04-29 10:10:07 +08:00
simontigers
74b587e46c Merge pull request #501 from simontigers/common_decorator_perms
fix: role base app perm
2024-04-29 09:27:36 +08:00
hu.sima
091cd882bd fix: role base app perm 2024-04-29 09:26:23 +08:00
simontigers
73093db467 Merge pull request #500 from simontigers/common_decorator_perms
fix(api): decorator_perms_role_required
2024-04-28 19:43:22 +08:00
hu.sima
66e268ce68 fix(api): decorator_perms_role_required 2024-04-28 19:41:50 +08:00
simontigers
a41d1a5e97 Merge pull request #499 from simontigers/common_decorator_perms
feat(api): role perm
2024-04-28 19:22:43 +08:00
hu.sima
b4b728fe28 feat(api): role perm 2024-04-28 19:22:10 +08:00
pycook
d16462d8b7 feat(api): ci baseline rollback (#498) 2024-04-28 19:19:14 +08:00
kdyq007
de7d98c0b4 feat(api): Add sorting function to ci list attribute (#495)
Co-authored-by: sherlock <sherlock@gmail.com>
2024-04-27 09:20:24 +08:00
pycook
51332c7236 feat(ui): CI change logs related itsm 2024-04-24 20:09:59 +08:00
dagongren
bf1076fe4a feat:update cs && update style (#488) 2024-04-23 12:20:27 +08:00
dagongren
3454a98cfb fix(cmdb-ui):service tree search (#487) 2024-04-19 13:32:12 +08:00
dagongren
506dcbb40e fix(cmdb-ui):fix service tree change table page (#486) 2024-04-19 11:46:51 +08:00
dagongren
5ac4517187 style (#482) 2024-04-18 10:49:39 +08:00
pycook
761e98884b chore: add volumes cmdb_cache-data in docker-compose 2024-04-18 10:02:57 +08:00
pycook
073654624e fix(api): commands cmdb-init-cache 2024-04-17 21:37:18 +08:00
dagongren
df54244ff1 fix(cmdb-ui):service tree key (#480) 2024-04-17 20:42:16 +08:00
pycook
27e9919198 chore: release v2.4.3 2024-04-17 19:35:35 +08:00
dagongren
dc8b1a5de2 feat(cmdb-ui):citype show attr && service tree search (#479) 2024-04-17 17:59:21 +08:00
pycook
d8a7728f1d feat(api): custom attribute display (#478) 2024-04-17 17:50:46 +08:00
simontigers
82881965fb Merge pull request #474 from simontigers/common_check_new_columns
Common check new columns
2024-04-16 15:35:15 +08:00
hu.sima
1bb62022f1 fix(api): check new column support enum change 2024-04-16 15:34:03 +08:00
hu.sima
ed445a8d82 fix(api): secrets_shares Import ERROR 2024-04-16 15:33:36 +08:00
pycook
3626b1a97e feat(api): service tree search by keywords (#471) 2024-04-15 20:04:56 +08:00
loveiwei
32529fba9b fix: support sealing and unsealing secret in multiple process(more than one workers started by gunicorn) (#469)
* fix: 解决在麒麟系统上使用docker安装时使用celery -D启动 celery 可能出现的问题

* fix: 解决在麒麟系统上使用docker安装时使用celery -D启动 celery 可能出现的问题

* fix: NoneType happend while unsealing the secret funtion, cancel the address check while unseal and seal

* fix: unseal secret function

* fix: remove depens_on in docker-compose

* fix: support sealing and unsealing secret in multiple process(more than one workers started by gunicorn)
2024-04-15 18:08:47 +08:00
dagongren
a042b4fe39 fix(cmdb-ui):ci detail relation repeatly ciid (#468) 2024-04-15 13:50:50 +08:00
dagongren
a0631414dc style: global static.less (#467) 2024-04-12 15:18:52 +08:00
pycook
5266cb5b88 release: 2.4.2 2024-04-03 15:55:13 +08:00
dagongren
c7d4bec988 feat(cmdb-ui): attributes relation (#463) 2024-04-03 15:27:54 +08:00
pycook
099ddd6ca9 feat(api): rebuild relation by attribute (#462) 2024-04-03 15:13:43 +08:00
pycook
bd813174b1 feat(api): build relation by attributes (#461) 2024-04-02 09:19:51 +08:00
dagongren
0a43680d6e feat:add icons (#460) 2024-04-01 17:37:00 +08:00
dagongren
976c6cfe91 fix:topmenu shake & change logo (#459) 2024-04-01 15:11:24 +08:00
pycook
cf594f04ba fix(api): import CIType 2024-03-29 15:50:07 +08:00
ivonGwy
4232094aed fix: discover scripts (#458)
Co-authored-by: wang-liang0615 <dhuwl0615@163.com>
2024-03-29 15:48:46 +08:00
dagongren
d08827d086 style and 文案变更 (#457) 2024-03-29 15:02:18 +08:00
pycook
d25ae532cd fix(api): CIType template import 2024-03-29 14:20:56 +08:00
pycook
9fbb6ee64d docs: docker-compose changed to docker compose 2024-03-29 13:27:23 +08:00
pycook
b62f0e96fd Merge branch 'master' of github.com:veops/cmdb 2024-03-29 13:14:00 +08:00
pycook
c1bcd0ce45 fix(acl): del resource 2024-03-29 13:13:38 +08:00
dagongren
c8b55c34eb i18n (#456) 2024-03-29 13:11:52 +08:00
pycook
4b5906770f release: v2.4.1 2024-03-29 12:47:23 +08:00
dagongren
4188ac7252 i18n (#455) 2024-03-29 12:23:23 +08:00
dagongren
2efbc6474a style && service tree define (#454) 2024-03-29 11:53:43 +08:00
pycook
03eac0c4d2 pref(api): error tips for out of range value (#453) 2024-03-29 11:46:50 +08:00
dagongren
2a861250eb fix:icon/filter/router...and some bugs (#451) 2024-03-29 10:50:14 +08:00
dagongren
8fc19d8b7c icon font && opsTable (#450) 2024-03-28 20:59:32 +08:00
dagongren
430d2ff6d0 fix:cmdbgrant (#449) 2024-03-28 20:16:47 +08:00
dagongren
2517009d70 fix:Login (#448) 2024-03-28 19:56:54 +08:00
dagongren
67da360d80 Dev UI 240328 (#447)
* feat:ui 全面升级

* feat:ui全面升级
2024-03-28 19:53:54 +08:00
dagongren
24c56fb259 feat:ui 全面升级 (#446)
* feat:ui 全面升级

* feat:ui全面升级
2024-03-28 19:47:46 +08:00
pycook
37d5da65de Dev api 240328 (#445)
* feat(api): login api supports parameter auth_with_ldap

* fix(api): transfer attribute
2024-03-28 19:12:47 +08:00
dagongren
2224ebd533 feat:ui 全面升级 (#444) 2024-03-28 18:38:15 +08:00
pycook
bf6331d215 fix(api): batch import ci relation 2024-03-26 20:38:39 +08:00
pycook
b18b90ab4e fix(api): import CIType
fix(api): import CIType
2024-03-26 16:53:10 +08:00
pycook
702e17a7a4 fix(api): revoke service tree node permissions
fix(api): revoke service tree node permissions
2024-03-26 12:05:22 +08:00
pycook
a7586aa140 feat(api): support service tree editing (#437) 2024-03-26 10:58:11 +08:00
simontigers
ad3f96431c Merge pull request #431 from simontigers/common_employee_edit_department_in_acl
fix(api): common_employee_edit department in acl role
2024-03-25 11:46:30 +08:00
hu.sima
1515820713 fix(api): common_employee_edit department in acl role 2024-03-25 11:46:04 +08:00
simontigers
7728b57878 Merge pull request #430 from simontigers/common_file_ext_check
fix(api): check file ext with magic
2024-03-25 11:17:36 +08:00
hu.sima
a419eefd72 fix(api): check file ext with magic 2024-03-25 11:16:04 +08:00
simontigers
a44e5f6cf1 Merge pull request #429 from simontigers/common_check_new_columns
fix(api): common check new columns
2024-03-22 17:52:19 +08:00
simontigers
7d46e92c2d fix(api): common check new columns 2024-03-22 16:48:16 +08:00
pycook
4117cf87ec Merge branch 'master' of github.com:veops/cmdb 2024-03-20 11:56:49 +08:00
pycook
9e0fe0b818 fix: custom dashboard 2024-03-20 11:56:39 +08:00
dagongren
2a8f1ab9a4 style:update global.less (#426) 2024-03-19 10:01:38 +08:00
pycook
c0fe99b8c7 release: 2.3.13 2024-03-18 20:35:51 +08:00
dagongren
42feb4b862 feat(cmdb-ui):service tree grant (#425) 2024-03-18 19:59:16 +08:00
pycook
482d34993b Dev api 0308 (#424)
* feat(api): grant by node in relation view

* fix(api): When removing attributes, remove the unique constraint

* feat(api): grant by service tree
2024-03-18 19:57:25 +08:00
simontigers
7ff309b8b8 fix(api): edit employee depart with rid=0 (#420) 2024-03-12 17:46:50 +08:00
rustrover
98eb47d44f fix: some typos (#415)
Signed-off-by: gcmutator <329964069@qq.com>
Co-authored-by: gcmutator <329964069@qq.com>
2024-03-11 15:04:38 +08:00
pycook
9ab0f624ef fix(api): remove ACL resources when deleting CIType (#414) 2024-03-08 16:31:03 +08:00
pycook
3f3eda8b3c fix(api): issule #412, unique value restrictions (#413) 2024-03-05 16:21:27 +08:00
pycook
f788adc8cf feat(api): multi-id search (#411)
_id:(id1;id2)
2024-03-04 15:15:34 +08:00
simontigers
693ae4ff05 fix: deploy init common (#407) 2024-03-01 17:21:32 +08:00
pycook
a1a9d99eb4 release: v2.3.12 2024-03-01 17:04:38 +08:00
dagongren
e045e0fb43 fix(cmdb-ui):to lowercase (#406) 2024-03-01 13:52:30 +08:00
pycook
09376dbd2b feat(api): CIType inheritance (#405) 2024-03-01 13:51:13 +08:00
dagongren
7fda5a1e7b feat(cmdb-ui):ci type inherit (#404) 2024-03-01 13:39:20 +08:00
dagongren
113b84763f feat:ci detail share (#403) 2024-02-27 16:13:28 +08:00
dagongren
190170acad fix(cmdb-ui):triggers webhook headers (#402) 2024-02-26 13:46:40 +08:00
pycook
513d2af4b8 feat(api): Remove many-to-many restrictions (#401) 2024-02-26 10:17:53 +08:00
pycook
4588bd8996 fix(api): db-setup commands (#399)
fix(api): db-setup commands
2024-02-23 11:05:11 +08:00
dagongren
082da5fade fix(cmdb-ui):resource search common attrs (#397) 2024-02-22 16:19:12 +08:00
pycook
013b116eb5 feat(acl): login channel add ssh options (#396) 2024-02-21 18:10:44 +08:00
simontigers
208d29165b fix: grant common perm after create new employee (#394) 2024-02-04 13:48:02 +08:00
dagongren
d510330cde fix(cmdb-ui):fix multiple default value (#395) 2024-02-04 11:49:44 +08:00
wang-liang0615
ea4f0fc2a5 fix(ui):login email-》username (#393) 2024-01-31 15:52:27 +08:00
pycook
9bcdaacdc4 docs: update init sql
docs: update init sql
2024-01-26 13:57:36 +08:00
pycook
5045581ddf feat(api): Auto-increment id can be used as primary key (#391) 2024-01-26 13:12:17 +08:00
simontigers
232913172c fix: change common_setting task queue (#390) 2024-01-25 17:39:52 +08:00
pycook
157e1809ed release: 2.3.11 2024-01-13 15:06:51 +08:00
pycook
ab8ccf7d1b ui: lint 2024-01-12 18:21:47 +08:00
wang-liang0615
ae1f0f6b4f lint regexSelect (#382) 2024-01-12 18:09:03 +08:00
wang-liang0615
ff47e0ade6 feat:citype regex check & pref:edit is_list (#380) 2024-01-12 17:09:44 +08:00
pycook
0dd272fb04 feat(api): password supports regular check 2024-01-12 16:56:10 +08:00
pycook
6bc5c1516d feat(api): Attributes support regular check (#379)
feat(api): Attributes support regular check
2024-01-12 13:05:37 +08:00
wang-liang0615
4a4b9e6ef0 feat(cmdb-ui):preference citype order (#378) 2024-01-12 11:14:53 +08:00
pycook
6e3f9478b3 Dev api 240111 (#377)
* feat(api): My subscription supports CIType sorting

* feat(api): db change
2024-01-11 18:01:37 +08:00
pycook
691051c254 perf(api): /api/v0.1/ci/adc/statistics
perf(api): /api/v0.1/ci/adc/statistics
2024-01-11 10:10:01 +08:00
pycook
8f066e95a6 fix(api): grant by attr (#373) 2024-01-10 16:52:27 +08:00
pycook
75bca39bf6 fix(api): commands add-user 2024-01-10 11:56:39 +08:00
pycook
3360e4d0fe feat(db): set variable sql_mode
feat(db): set variable sql_mode
2024-01-10 10:28:15 +08:00
wang-liang0615
521fcd0ba2 fix(ui):some fix (#370)
* pref(ui):some bugfix & some style

* fix(ui):some fix
2024-01-10 09:46:02 +08:00
wang-liang0615
fc113425cb pref(ui):some bugfix & some style (#369) 2024-01-09 14:48:13 +08:00
pycook
81a76a9632 fix(api): cmdb-init-acl commands (#368) 2024-01-09 10:04:53 +08:00
wang-liang0615
9ec105ca37 fix(ui):logout (#365) 2024-01-04 16:12:20 +08:00
wang-liang0615
1e1c92a3ef refactor(ui):extract pager components (#364) 2024-01-04 13:41:07 +08:00
pycook
0b3cad8215 feat: update docker-compose 2024-01-04 10:26:47 +08:00
pycook
ed41a72900 release: v2.3.10 2024-01-03 19:23:42 +08:00
wang-liang0615
27854d4e0a fix(acl-ui):operation history (#363) 2024-01-03 17:41:42 +08:00
wang-liang0615
0372459f9d fix(ui) (#362) 2024-01-03 17:09:25 +08:00
simontigers
f56378e2b5 fix(api): common edit department return (#359)
fix(api): common edit department return (#359)
2024-01-03 16:42:14 +08:00
wang-liang0615
00b276d5e7 format(ui) (#361) 2024-01-03 16:35:50 +08:00
wang-liang0615
3faefbe8d3 feat(ui):i18n (#360)
* feat(ui):i18n

* i18n

* feat(ui):i18n
2024-01-03 16:03:15 +08:00
simontigers
4261f6fb57 fix(api): common department allow parent (#358)
* fix(api): common department edit method

* fix(api): common department edit method

* fix(api): common department allow parent
2024-01-03 15:19:08 +08:00
wang-liang0615
c98199e98e feat(ui):i18n (#357)
* feat(ui):i18n

* i18n
2024-01-03 14:58:47 +08:00
simontigers
82ea1ddc79 fix(api): common department edit method (#356)
* fix(api): common department edit method

* fix(api): common department edit method
2024-01-03 14:31:54 +08:00
simontigers
995e581315 fix(api): common department edit method (#355) 2024-01-03 14:26:40 +08:00
pycook
ec8f626b8f docs: update install (#354) 2024-01-03 13:58:35 +08:00
wang-liang0615
ec884c92e1 feat(ui):i18n (#352) 2024-01-03 13:29:38 +08:00
simontigers
9ee2776bdd fix(api): common i18n wide (#351)
* fix(api): common_i18n wide

* fix(api): department i18n
2024-01-03 13:26:58 +08:00
simontigers
7186bdac9c fix(api): common_i18n wide (#350) 2024-01-03 12:29:49 +08:00
wang-liang0615
0a9964375a feat(ui):add packages (#349) 2024-01-03 09:42:32 +08:00
wang-liang0615
bff45d00b6 i18n (#348) 2024-01-02 18:04:53 +08:00
wang-liang0615
e429ad59ff feat(ui):i18n (#347)
* feat(acl-ui):i18n

* feat(base-ui):i18n

* feat(cmdb-ui):i18n
2024-01-02 17:53:07 +08:00
pycook
ace160ae19 Dev api 231229 (#345)
* fix(api): predefined value for float

* feat(api): update public clouds config

* feat(api): commands add-user support is_admin
2023-12-29 13:44:23 +08:00
wang-liang0615
7036fe023c style(ui):global.less (#344) 2023-12-28 11:00:00 +08:00
wang-liang0615
341f5dba53 fix(cmdb-ui):model relation table (#343) 2023-12-27 13:24:01 +08:00
wang-liang0615
5883c1616e feat:Batch import and download templates support predefined values (#342) 2023-12-27 13:18:05 +08:00
simontigers
bee0a3a3e5 feat(api): common i18n (#340) 2023-12-26 10:06:15 +08:00
pycook
7a79a8bbf7 feat(api): i18n
feat(api): i18n
2023-12-25 21:51:44 +08:00
pycook
100a889cb8 fix(api): CI revoke permission (#337) 2023-12-25 12:15:20 +08:00
kdyq007
b093569453 [更新] 修复 LDAP 登录失败的问题 (#336)
Co-authored-by: sherlock <sherlock@gmail.com>
2023-12-25 09:36:31 +08:00
pycook
3919dfdfbb release: 2.3.9 2023-12-23 12:51:09 +08:00
pycook
ef85ba2542 feat(api): update cmdb-init-acl commands (#335) 2023-12-23 12:44:01 +08:00
pycook
3d5c2ec5bc Merge branch 'master' of github.com:veops/cmdb 2023-12-23 12:31:52 +08:00
pycook
c143d6ae5b fix(api): role grant 2023-12-23 12:30:52 +08:00
simontigers
10b273ee81 feat(api): add update_last_login_by_uid (#333) 2023-12-22 18:43:20 +08:00
wang-liang0615
855cb91b31 feat(cmdb-ui):ci type import&export,pref(cmdb-ui):download ci xlsx name, pref(cmdb-ui):ci detail history merge row method (#331)
* pref(cmdb-ui):download ci xlsx name

* pref(cmdb-ui):ci detail history merge row method

* feat(cmdb-ui):ci type import&export
2023-12-22 15:42:20 +08:00
pycook
ffae57642c fix(api): ci relation search
fix(api): ci relation search
2023-12-22 15:35:02 +08:00
simontigers
9ed1108c20 feat(api): add get_file_binary_str and save (#329) 2023-12-22 15:33:05 +08:00
simontigers
72b2f8b6de fix(api): refresh rid after create and import employee (#328) 2023-12-22 15:24:48 +08:00
pycook
20f3e917fe pref(api): import and export of CIType templates
pref(api): import and export of CIType templates
2023-12-22 14:32:03 +08:00
pycook
c430515377 fix(api): add CI (#326) 2023-12-22 11:19:16 +08:00
simontigers
a6e2aca281 fix(api): svg upload (#321) 2023-12-21 18:58:35 +08:00
wang-liang0615
18313b7bd1 fix(acl_ui):permission (#325) 2023-12-21 17:22:49 +08:00
wang-liang0615
dbc44a8ad6 fix(ui):common double menu (#324) 2023-12-21 14:59:39 +08:00
wang-liang0615
f143e30cf5 feat(ui):批量导入模型根据create权限过滤&&模型配置页面权限 (#323) 2023-12-21 14:23:38 +08:00
wang-liang0615
4beece5a6e fix:open triggerForm from attributeCard (#322) 2023-12-21 14:18:00 +08:00
pycook
920295d955 feat: Fixed db volume name 2023-12-21 10:26:51 +08:00
pycook
6eb8ae1dac fix(api): CAS authentication 2023-12-20 12:10:00 +08:00
wang-liang0615
f1f86ce25a feat(ui):api_host annotation (#320) 2023-12-19 14:22:40 +08:00
pycook
090007487d fix(api): oauth2.0 authentication 2023-12-19 13:07:21 +08:00
wang-liang0615
aa98a304c1 feat:add auth common api_host (#319) 2023-12-19 11:23:27 +08:00
pycook
fe22e363b4 fix(api): ldap authentication 2023-12-19 00:16:56 +08:00
pycook
2d2fb6e1d6 release: v2.3.8 2023-12-18 20:08:19 +08:00
pycook
9894c77300 feat(ui): lint 2023-12-18 19:25:22 +08:00
simontigers
4ea947f741 fix: auth config (#318) 2023-12-18 18:27:06 +08:00
simontigers
e5ab2c2573 fix: auth config (#317) 2023-12-18 16:52:24 +08:00
wang-liang0615
5581fa8d0f lint(ui) (#316) 2023-12-18 16:41:21 +08:00
wang-liang0615
76c939fe5c fix(ui):401 redirect && feat(ui):add auth ldap test (#315) 2023-12-18 16:30:02 +08:00
wang-liang0615
6e9ce08e2c pref(cmdb-ui):change adt key & add adt alias (#314) 2023-12-18 16:07:52 +08:00
pycook
092a8b9b92 pref(api): A CIType allows repeated binding of auto-discovery rules (#313) 2023-12-16 17:56:14 +08:00
wang-liang0615
b45fd0cbbb fix:is_list edit bug (#312)
* feat(ui):auth setting

* fix:is_list edit bug
2023-12-15 13:19:55 +08:00
simontigers
5320ecfd62 fix(api): common_data (#311) 2023-12-15 10:56:04 +08:00
wang-liang0615
1d253d7ad3 feat(ui):auth setting (#310) 2023-12-15 10:33:38 +08:00
pycook
73d53f0440 pref(api): authentication and login log (#308)
* pref(api): authentication and login log

* feat(api): ldap and OAuth2.0
2023-12-14 19:53:08 +08:00
simontigers
d4a37af183 feat(api): auth config api (#309) 2023-12-14 19:39:21 +08:00
wang-liang0615
e03849b054 fix(cmdb-ui):batch upload cancel bug && download error (#306) 2023-12-13 14:50:53 +08:00
simontigers
faed3fe6a2 fix(api): get_employee_notice check data None (#305)
* fix(api): get_employee_notice check data None

* fix(api): remove path when save messager url
2023-12-13 09:45:42 +08:00
pycook
21c9d9accd feat(api): support OAuth2.0 and OIDC authentication, it has been tested with casdoor
feat(api): support OAuth2.0 and OIDC authentication, it has been tested with casdoor
2023-12-12 20:29:57 +08:00
wang-liang0615
a06599ce33 pref(cmdb-ui):batch upload for date type (#301) 2023-12-12 14:53:12 +08:00
pycook
2b69217136 fix(api): time data format
fix(api): time data format
2023-12-12 14:37:21 +08:00
wang-liang0615
03a3b8b169 Revert "pref(cmdb-ui):batch upload for date type (#298)" (#299)
This reverts commit cd319421d5.
2023-12-12 13:51:19 +08:00
wang-liang0615
cd319421d5 pref(cmdb-ui):batch upload for date type (#298)
* fix(cmdb-ui):set localstorage '' after unsubscribe ci

* pref(cmdb-ui):batch upload for date type
2023-12-12 13:38:08 +08:00
pycook
c918d54ea5 perf(api): ci delete (#297) 2023-12-12 11:09:32 +08:00
wang-liang0615
cf0ad7bad6 fix(cmdb-ui):set localstorage '' after unsubscribe ci (#296) 2023-12-12 09:38:50 +08:00
pycook
e0c8263542 feat(api): cas is compatible with casdoor
feat(api): cas is compatible with casdoor
2023-12-11 20:58:18 +08:00
pycook
275e8b15f3 Dev api 231211 (#294)
* fix(api): cas authentication

* feat(api): add lz4 package
2023-12-11 19:30:09 +08:00
simontigers
6ff942c107 feat(api): upload file save db (#292) 2023-12-11 18:22:33 +08:00
gmailnovo
d3c87ee500 feat: Handle '/dev/stdout' in Logger Configuration 2023-12-07 11:27:45 +08:00
simontigers
a4f65e7fc6 fix(api): Common create employee (#287)
* fix(api): add add_from arg in create employee

* fix(api): add check in acl call common after add user
2023-12-06 17:12:37 +08:00
wang-liang0615
10527bf9b8 pref(cmdb-ui):ci upload&delete concurrent 6 (#286) 2023-12-06 14:33:25 +08:00
pycook
0414121c27 docs: update local install 2023-11-30 16:18:33 +08:00
simontigers
edde467c87 fix(api): common_data delele (#282) 2023-11-30 13:03:09 +08:00
pycook
d525e1ec54 feat(api): only the role cmdb_admin can modify the CIType group (#280) 2023-11-29 17:40:12 +08:00
pycook
91c49b690f fix(api): get relation history 2023-11-28 20:37:36 +08:00
pycook
05453becf9 release: 2.3.7 2023-11-24 14:53:53 +08:00
pycook
5fe27f8678 feat(api): issue #212 (#279) 2023-11-24 10:26:48 +08:00
wang-liang0615
0924b8846f feat(cmdb-ui):多对多关系&&仪表盘色卡调整 (#271) 2023-11-24 10:25:56 +08:00
loveiwei
62a669159a doc: change content in readme_en (#278) 2023-11-23 20:10:25 +08:00
loveiwei
2933bf1efa feature: add a new management script, such as install,start,pause,del… (#277)
* feature: add a new management script, such as install,start,pause,delete,uninstall

* doc: add install.sh method in readme

* doc: add install.sh method in readme, change install.sh for macos support

* doc: add install.sh method in readme

* doc: add install.sh method in readme
2023-11-23 17:45:53 +08:00
loveiwei
981f8b0145 Fix deploy 1700028675 (#272)
* fix: Solving the timezone issue in Redis, as well as the problem of MySQL logs always being in UTC timezone.

* fix: change the config path of slow_log into /tmp directory in mysqld.cnf file
2023-11-22 18:34:58 +08:00
pycook
213bda671c docs: local install (#270) 2023-11-16 20:54:08 +08:00
loveiwei
837aabfe77 fix: Solving the timezone issue in Redis, as well as the problem of MySQL logs always being in UTC timezone. (#268) 2023-11-15 20:48:52 +08:00
wang-liang0615
cc599d414a feat(acl-ui):resources table resizable (#267) 2023-11-14 09:37:45 +08:00
pycook
01fcd7f80e Dev api 231108 (#264)
* perf(api): commands add-user

* feat(api): add commands cmdb-agent-init
2023-11-09 11:32:54 +08:00
pycook
af254ddbeb Dev api 1107 (#263)
feat: update cmdb.sql
2023-11-08 16:11:36 +08:00
wang-liang0615
422e89c6c6 ui package update (#261)
* update(cmdb-ui):update packages && delete yarn.lock

* update:gitignore
2023-11-08 16:06:48 +08:00
wang-liang0615
c2c11b38ed Revert "Dev UI 231108 (#256)" (#260)
This reverts commit 45d3f57228.
2023-11-08 14:43:37 +08:00
wang-liang0615
45d3f57228 Dev UI 231108 (#256)
* pref(cmdb-ui):update packages

* pref(cmdb-ui):update packages
2023-11-08 14:32:17 +08:00
pycook
c711c3d3fd feat(api): encrypting webhook configurations (#255) 2023-11-07 17:15:24 +08:00
pycook
2ae4aeee67 fix(api): Code scanning alerts (#254) 2023-11-06 14:27:30 +08:00
pycook
46238b8b51 Dev api 1103 (#252)
feat(api): update requirements
2023-11-06 13:24:08 +08:00
dependabot[bot]
b1528ec511 build(deps): bump browserify-sign from 4.2.1 to 4.2.2 in /cmdb-ui (#241)
Bumps [browserify-sign](https://githubfast.com/crypto-browserify/browserify-sign) from 4.2.1 to 4.2.2.
- [Changelog](https://githubfast.com/browserify/browserify-sign/blob/main/CHANGELOG.md)
- [Commits](https://githubfast.com/crypto-browserify/browserify-sign/compare/v4.2.1...v4.2.2)

---
updated-dependencies:
- dependency-name: browserify-sign
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-06 13:13:29 +08:00
pycook
8ef4edb7d2 fix(api): search sort misses cache (#251) 2023-11-03 12:01:44 +08:00
pycook
0172206c7c fix(api commands): cmdb-password-data-migrate (#249) 2023-11-02 20:43:39 +08:00
simontigers
9d5bdf1b0d style(common-setting): clean warning (#246) 2023-10-30 17:35:34 +08:00
pycook
c0726b228d fix(api): secrets 2023-10-30 17:23:42 +08:00
pycook
5b314aa907 feat: add inner password storage and optimize flask command about inner cmdb (#248)
Co-authored-by: fxiang21 <fxiang21@126.com>
2023-10-30 16:48:53 +08:00
wang-liang0615
c9f0de9838 Dev UI 231030 (#247)
* config(ui):useEncryption default false

* fix(cmdb-ui):ident 4

* fix(cmdb-ui):relation views
2023-10-30 12:38:05 +08:00
pycook
2db41dd992 Dev api password (#244)
* fix: delete CI password data

* fix(api): update CI password to flush cache
2023-10-29 11:42:07 +08:00
pycook
89e492c1f3 fix: delete CI password data (#243) 2023-10-29 10:53:29 +08:00
pycook
1aeb9a2702 fix(cmdb-ui): CI update password 2023-10-28 23:07:01 +08:00
pycook
b090a88b76 release: v2.3.6 2023-10-28 17:51:13 +08:00
pycook
d5c479f7e5 feat(cmdb-api): ci password 2023-10-28 16:55:02 +08:00
pycook
b342258e75 feat(cmdb-api): CI password data store (#242)
* add secrets,for test

* feat: vault SDK (#238)

* feat: vault SDK

* docs: i18n

* perf(vault): format code

* feat(secrets): support vault

* feat: add inner password storage

* feat: secrets

* feat: add inner password storage

* feat: add secrets feature

* perf(secrets): review

---------

Co-authored-by: fxiang21 <fxiang21@126.com>
Co-authored-by: Mimo <osatmnzn@gmail.com>
2023-10-28 16:19:00 +08:00
wang-liang0615
3d716eff3e feat:预定义值支持脚本&&密码存储&&一些bugfix (#239) 2023-10-27 11:10:43 +08:00
ivonGwy
9791a184e3 Doc (#235)
* change assignees
2023-10-25 16:00:04 +08:00
ivonGwy
142b8c95c5 Doc (#234)
* final template
2023-10-25 15:52:12 +08:00
ivonGwy
363b89011f Doc (#232)
* fix bugs
2023-10-25 14:54:23 +08:00
ivonGwy
321372fa88 Update issue templates 2023-10-25 14:46:44 +08:00
ivonGwy
fc27dc6e64 Doc (#231)
* add issue template
2023-10-25 14:36:36 +08:00
ivonGwy
8ad98f4ba4 Update issue templates 2023-10-25 14:06:24 +08:00
ivonGwy
9b050fa7fa Update issue templates 2023-10-25 14:04:53 +08:00
pycook
a30826827a fix(api): add ci (#230) 2023-10-25 13:51:29 +08:00
ivonGwy
691b50aec4 Doc (#229)
* change reandme
2023-10-25 13:19:30 +08:00
kdyq007
55ca2d5eb5 关闭前端密码加密;加强 ldap 用户验证 (#216)
* [更新] python-ldap 更新到 ldap3

* [更新] 关闭前端密码加密;加强 ldap 用户验证

* Update app.js

---------

Co-authored-by: sherlock <sherlock@gmail.com>
Co-authored-by: pycook <pycook@126.com>
2023-10-24 19:47:46 +08:00
pycook
779cd1ea9e feat: Predefined values support executing scripts (#227) 2023-10-24 19:32:43 +08:00
simontigers
7d53180a2f fix: add_employee_from_acl (#225) 2023-10-24 14:20:40 +08:00
wang-liang0615
b6c41c00dd fix:关系视图删除关系接口传参修改 (#224)
* fix:acl新增用户展示异常问题

* fix:关系视图删除关系接口传参修改
2023-10-24 06:04:21 +08:00
wang-liang0615
fff5679f6e fix:acl新增用户展示异常问题 (#223) 2023-10-24 05:59:08 +08:00
pycook
7556dfe56b feat: add cryptography to requirements 2023-10-23 14:37:01 +08:00
pycook
44b6f2b2ad fix: acl cache 2023-10-23 13:57:06 +08:00
Evan Sung
f5607d96f3 fix(common): fix 'ACLManager' object has no attribute 'create_app' (#217) 2023-10-21 11:38:19 +08:00
Evan Sung
2e85a9971b fix(ci_cache): ci cache async args (#215) 2023-10-20 12:05:19 +08:00
kdyq007
8d177266dc feat: python-ldap 更新到 ldap3 (#214)
Co-authored-by: sherlock <sherlock@gmail.com>
2023-10-20 09:36:38 +08:00
pycook
42d870ea4e Dev api 20231019 (#210)
* fix(acl): get resources

* fix(celery worker): db server has gone away
2023-10-19 11:51:34 +08:00
wang-liang0615
424cad2130 fix:ci relation add type filter (#208) 2023-10-18 14:06:28 +08:00
pycook
430308a679 fix: ci relation statistics 2023-10-18 13:35:01 +08:00
pycook
cc7570a4b2 docs: api doc 2023-10-17 12:06:37 +08:00
wang-liang0615
413c0dfdfe feat:webhook body 支持非json (#203) 2023-10-17 10:44:38 +08:00
Evan Sung
f8fee771c4 Feature db migrate 20231013 (#202)
* feat(db): support flask migrate

* minor

---------

Co-authored-by: s01249 <songbing@smyfinancial.com>
2023-10-13 16:24:49 +08:00
Evan Sung
3d05cef7cd feat(db): support flask migrate (#201)
Co-authored-by: s01249 <songbing@smyfinancial.com>
2023-10-13 15:55:26 +08:00
simontigers
30477f736e fix: common perms (#200) 2023-10-12 16:02:35 +08:00
wang-liang0615
0cd5c0277b pref:批量上传&资源管理小优化 (#199) 2023-10-12 15:06:39 +08:00
ivonGwy
4fcddd1010 Merge pull request #197 from veops/doc
Doc
2023-10-11 14:34:05 +08:00
ivonGwy
b269ef894f change wechat pic size 2023-10-11 14:33:19 +08:00
ivonGwy
ea13432e4c change wechat pic 2023-10-11 14:28:49 +08:00
pycook
c8a68bd185 Dev api (#196)
* docs: update

* docs: README & Makefile
2023-10-11 13:40:15 +08:00
wang-liang0615
f9ee895f58 Dev UI 231009 (#195)
* pref:用户密钥非必填

* fix:chartColor undefined
2023-10-11 09:12:04 +08:00
pycook
59d9f2c79a docs: update (#194) 2023-10-10 16:53:30 +08:00
wang-liang0615
f629558d60 pref:用户密钥非必填 (#193) 2023-10-10 09:25:24 +08:00
pycook
4cbf70e756 release: v2.3.5 2023-10-09 20:55:30 +08:00
pycook
40b9bec122 feat: get messenger url from common setting 2023-10-09 20:25:27 +08:00
wang-liang0615
17b27d492a 前端更新 (#192)
* fix:add package

* fix:notice_info为null的情况

* fix:2 bugs

* feat:1.common增加通知配置 2.cmdb预定义值webhook&其他模型

* fix:json 不支持预定义值

* fix:json 不支持预定义值

* fix:删除代码
2023-10-09 19:52:19 +08:00
simontigers
dd8f66a3fa fix: init company structure resource (#191)
* fix: init company structure resource

* fix: notice_info null
2023-10-09 19:25:49 +08:00
wang-liang0615
d501436e3d 前端更新 (#189)
* fix:add package

* fix:notice_info为null的情况

* fix:2 bugs

* feat:1.common增加通知配置 2.cmdb预定义值webhook&其他模型

* fix:json 不支持预定义值

* fix:json 不支持预定义值
2023-10-09 17:43:34 +08:00
simontigers
8c6389b4f8 feat: notice_config access messenger (#190) 2023-10-09 17:32:20 +08:00
pycook
63ed73fa14 fix: delete user role 2023-10-09 15:40:18 +08:00
pycook
ef90506916 feat: The definition of attribute choice values supports webhook and other model attribute values. 2023-10-09 15:33:18 +08:00
wang-liang0615
c0541b7e50 Dev UI (#186)
* fix:add package

* fix:notice_info为null的情况

* fix:2 bugs
2023-09-28 09:45:11 +08:00
pycook
388134b38c release 2.3.4 2023-09-27 11:37:18 +08:00
simontigers
f57d640c5d feat: add api get_notice_by_ids (#184) 2023-09-27 09:54:30 +08:00
wang-liang0615
42c506ded8 前端更新 (#183)
* fix:add package

* fix:notice_info为null的情况
2023-09-27 09:18:33 +08:00
pycook
373346e71e feat: ci triggers 2023-09-26 21:18:34 +08:00
wang-liang0615
983cb4041c fix:add package (#182) 2023-09-26 21:12:10 +08:00
wang-liang0615
718fca57d2 Merge pull request #181 from veops/dev_ui
前端更新
2023-09-26 20:34:27 +08:00
wang-liang0615
2b1b0333ff Merge branch 'master' into dev_ui 2023-09-26 20:34:14 +08:00
wang-liang0615
9bb787d3f4 fix:topo图相同节点出现两次的bug 2023-09-26 20:13:12 +08:00
wang-liang0615
9f5979c1b1 feat:wangeditor 注册自定义组件 2023-09-26 20:07:00 +08:00
wang-liang0615
b58230fd56 delete:删除getwx 2023-09-26 20:04:38 +08:00
simontigers
8e6ce05c04 feat: common notice config (#180) 2023-09-26 19:44:20 +08:00
wang-liang0615
b8d93bc9eb feat: UI更新 触发器 (#179)
* feat:新增api&适配

* feat:触发器

* add packages & 注释代码

* feat: webhook tips
2023-09-26 18:25:04 +08:00
wang-liang0615
968ef93153 feat: webhook tips 2023-09-26 18:17:23 +08:00
wang-liang0615
e0a0113e69 add packages & 注释代码 2023-09-26 17:35:41 +08:00
wang-liang0615
7181f2879a feat:触发器 2023-09-26 17:01:31 +08:00
wang-liang0615
ff1626ff07 feat:新增api&适配 2023-09-26 16:26:25 +08:00
pycook
dca8781a03 fix: ci_cache 2023-09-25 15:46:07 +08:00
wang-liang0615
43c5d74661 Merge pull request #178 from veops/dev_ui
前端更新:仪表盘优化
2023-09-25 14:52:09 +08:00
wang-liang0615
b1d0bdb536 pref:仪表盘优化 2023-09-25 14:50:08 +08:00
wang-liang0615
2420631ed5 Merge branch 'master' of github.com:veops/cmdb into dev_ui 2023-09-25 14:43:34 +08:00
pycook
deabebb922 refactor: CI triggers 2023-09-22 17:39:54 +08:00
simontigers
cd451060e3 fix: icon svg support (#177) 2023-09-20 15:56:57 +08:00
pycook
82d4c4f961 fix date search 2023-09-18 18:15:02 +08:00
pycook
79f5dac661 fix dashboard compute 2023-09-18 13:04:50 +08:00
pycook
1a7c3fcd21 release v2.3.3 2023-09-15 17:57:39 +08:00
pycook
606be7f8e8 dashboard ui update 2023-09-15 17:36:10 +08:00
simontigers
7f66f7688b feat: init resource for backend (#176) 2023-09-15 15:30:30 +08:00
pycook
25ad2192fd enhance dashboard 2023-09-15 15:26:20 +08:00
pycook
881b8cc1fa cmdb-api/api/lib/resp_format.py 2023-09-12 20:01:30 +08:00
pycook
68905281f2 Detect circular dependencies when adding CIType relationships 2023-09-12 20:00:56 +08:00
wang-liang0615
3cb78f30d2 计算属性 触发计算 (#174) 2023-09-11 19:16:05 +08:00
pycook
ebce839eaa fix upload template and add /api/v0.1/attributes/<int:attr_id>/calc_computed_attribute 2023-09-11 19:15:31 +08:00
wang-liang0615
3170048ea9 Merge branch 'master' of github.com:veops/cmdb into dev_ui 2023-09-11 17:35:44 +08:00
wang-liang0615
976cac6742 计算属性 触发计算 2023-09-11 17:34:51 +08:00
pycook
99e5a932ae release 2.3.2 2023-09-07 13:44:51 +08:00
pycook
058585504f Merge pull request #172 from veops/dev_ui
新建ci及批量导入时,新建关系
2023-09-07 11:04:49 +08:00
wang-liang0615
6d50a13cf0 新建ci及批量导入时,新建关系 2023-09-07 10:25:18 +08:00
pycook
198ecad13a Merge branch 'master' of github.com:veops/cmdb 2023-09-07 10:12:55 +08:00
pycook
1660139b27 Add CI relationship when creating CI, the text value removes the escape 2023-09-07 10:12:42 +08:00
pycook
0155eb98a2 Merge pull request #171 from ronething/fix/makefile
optimize: makefile help
2023-09-05 20:34:16 +08:00
ashing
c0c05bca86 fix: review
Signed-off-by: ashing <axingfly@gmail.com>
2023-09-05 20:33:07 +08:00
ashing
32227d375a fix: review
Signed-off-by: ashing <axingfly@gmail.com>
2023-09-05 20:29:29 +08:00
ashing
eb9c20a295 fix: review
Signed-off-by: ashing <axingfly@gmail.com>
2023-09-05 20:21:20 +08:00
ashing
b826de4195 optimize: makefile help
Signed-off-by: ashing <axingfly@gmail.com>
2023-09-05 20:06:31 +08:00
pycook
c3b55c2850 Merge pull request #170 from ronething/feat/xx
feat: support docker deploy mysql and redis
2023-09-05 19:28:47 +08:00
ivonGwy
33313b6206 Merge pull request #169 from veops/doc
add document link
2023-09-05 15:41:52 +08:00
ivonGwy
00fbe15a19 add document link 2023-09-05 15:40:31 +08:00
ashing
9e5c926bfd feat: support docker deploy mysql and redis
Signed-off-by: ashing <axingfly@gmail.com>
2023-09-05 15:26:50 +08:00
wang-liang0615
f0467c3d3b Merge pull request #168 from veops/dev_ui
UI更新
2023-09-05 15:23:43 +08:00
wang-liang0615
6758b994f8 Merge branch 'master' of github.com:veops/cmdb into dev_ui 2023-09-05 15:22:18 +08:00
wang-liang0615
d8646f8e7c 模型关联 展示反向关系 2023-09-05 15:22:08 +08:00
pycook
0fa95aed36 Merge branch 'master' of github.com:veops/cmdb 2023-09-05 14:49:53 +08:00
pycook
d82356444a move Dockerfile to docs 2023-09-05 14:49:34 +08:00
wang-liang0615
b8fa68ec8d Merge pull request #167 from veops/dev_ui
sub menu color
2023-09-04 16:34:26 +08:00
wang-liang0615
78c542e71d sub menu color 2023-09-04 16:33:35 +08:00
wang-liang0615
6594863934 Merge pull request #166 from veops/dev_ui
ui更新
2023-09-04 13:15:27 +08:00
wang-liang0615
61e8c61fc6 Merge branch 'master' of github.com:veops/cmdb into dev_ui 2023-09-04 13:14:35 +08:00
wang-liang0615
ec9b5a0fa0 sidebar 2023-09-04 13:14:11 +08:00
pycook
3664994cc5 import format 2023-09-02 12:09:41 +08:00
pycook
366311e59b format 2023-09-01 18:07:44 +08:00
pycook
03f2ff912d fix delete choice values 2023-08-31 16:02:24 +08:00
pycook
ff9af51465 fix delete choice values 2023-08-31 15:18:15 +08:00
wang-liang0615
04fc588935 Merge pull request #165 from veops/dev_ui
proxy
2023-08-31 13:31:26 +08:00
wang-liang0615
f215e887c9 proxy 2023-08-31 13:28:15 +08:00
pycook
3e6955fc7a Merge pull request #162 from simontigers/cmdb_icon_manage
feat: add cmdb custom icon manage
2023-08-31 11:15:09 +08:00
pycook
b1cfb88308 Merge pull request #163 from veops/dev_ui
支持上传自定义图标
2023-08-31 11:14:42 +08:00
hu.sima
d1af0eba79 feat: add cmdb custom icon manage 2023-08-31 10:49:56 +08:00
wang-liang0615
f41873dc8c 支持上传自定义图标 2023-08-31 10:05:11 +08:00
pycook
63562007df fix update attribute 2023-08-30 13:34:10 +08:00
pycook
a99ecb9ea5 Merge branch 'master' of github.com:veops/cmdb 2023-08-29 14:49:21 +08:00
pycook
08f9bd9071 The default value of USE_ACL is set to True 2023-08-29 14:49:09 +08:00
pycook
17c851f354 Merge pull request #161 from simontigers/common_setting_format
fix: company info create
2023-08-29 11:01:25 +08:00
hu.sima
3382195a25 fix: company info create 2023-08-29 10:56:48 +08:00
pycook
cd70b16eb3 Merge branch 'master' of github.com:veops/cmdb 2023-08-25 11:01:24 +08:00
pycook
2a98c00d97 update ad_ci when deleting ci 2023-08-25 10:59:38 +08:00
wang-liang0615
5044af5490 Merge pull request #160 from veops/dev_ui
前端更新
2023-08-25 10:12:31 +08:00
wang-liang0615
320dc07676 fix 新增类型回车键发送两次请求 2023-08-25 10:11:09 +08:00
wang-liang0615
95d3b0233a fix 新增类型回车键发送两次请求 2023-08-25 10:08:04 +08:00
pycook
093466ef06 Merge pull request #158 from EvanSung/perf_20230824_optimize_ad_ci_relation
perf(ad_ci_relation): optimize ad_ci relation
2023-08-24 16:26:43 +08:00
EvanSung
ceacc0ab15 perf(ad_ci_relation): optimize ad_ci relation 2023-08-24 14:16:12 +08:00
pycook
cb3a9d9432 docker-compose add flask db-setup 2023-08-24 11:32:09 +08:00
pycook
23fd56cf28 vxe-table-plugin-export-xlsx==2.0.0 2023-08-24 11:06:28 +08:00
pycook
dd1c783919 add config CACHE_REDIS_PASSWORD and fix delete ci_type 2023-08-23 18:05:28 +08:00
pycook
8296e9a552 fix update ci 2023-08-22 11:34:40 +08:00
pycook
590565bdf0 Register api and commands with absolute paths 2023-08-21 20:08:23 +08:00
pycook
9e2bf3d1f0 fix merge conflict 2023-08-21 11:55:49 +08:00
pycook
7547b67805 fix g.user 2023-08-21 11:54:33 +08:00
pycook
4abe4d7e8f version: 2.3.1 2023-08-20 11:24:53 +08:00
pycook
0ebd52f3fd lint 2023-08-20 11:23:55 +08:00
pycook
4cc2e47f02 Merge pull request #157 from EvanSung/fix_20230817_guser_issue
fix(acl): g user issue
2023-08-17 22:12:45 +08:00
EvanSung
b16bfdfa03 fix(acl): g user issue 2023-08-17 18:40:45 +08:00
pycook
ece24080d5 fix MyJSONEncoder 2023-08-16 21:28:27 +08:00
pycook
a0ea8a193b Merge pull request #155 from veops/dev_ui
前端更新
2023-08-16 13:01:13 +08:00
pycook
d8e09d449e Merge branch 'master' of github.com:veops/cmdb 2023-08-16 13:00:44 +08:00
pycook
44c99e0f2e Delete user without soft delete 2023-08-16 13:00:30 +08:00
wang-liang0615
7110b4bcb3 Merge branch 'master' of github.com:veops/cmdb into dev_ui 2023-08-16 10:09:47 +08:00
wang-liang0615
3989859945 delete user 2023-08-16 10:09:25 +08:00
pycook
29ed4dd708 Merge pull request #154 from simontigers/common_setting_format
fix: init-import-user-from-acl
2023-08-15 20:47:36 +08:00
hu.sima
dbcbe33ba0 fix: init-import-user-from-acl 2023-08-15 20:45:28 +08:00
pycook
ed340a1c33 Merge pull request #153 from simontigers/common_setting_format
fix: import_user_from_acl
2023-08-15 20:25:09 +08:00
hu.sima
17ee7622b4 fix: import_user_from_acl 2023-08-15 20:19:45 +08:00
pycook
51877778bf Merge branch 'master' of github.com:veops/cmdb 2023-08-15 19:48:11 +08:00
pycook
1de8b492ea [update] delete roles, users, attributes 2023-08-15 19:47:59 +08:00
wang-liang0615
1fe38c4ec3 Merge pull request #152 from veops/dev_ui
前端更新
2023-08-15 19:47:03 +08:00
wang-liang0615
92bff08b84 属性库 2023-08-15 19:34:17 +08:00
wang-liang0615
37d0dacd2e 属性库 2023-08-15 19:26:49 +08:00
wang-liang0615
e22f45fd25 属性库 2023-08-15 19:21:09 +08:00
wang-liang0615
56ef0a055a 属性库 2023-08-15 19:10:26 +08:00
wang-liang0615
522991e23b 后台管理-模型关联 关系删除&&筛选 2023-08-15 15:02:46 +08:00
pycook
ff061d4d2e update gitattributes 2023-08-15 13:41:45 +08:00
wang-liang0615
5b84a704b1 Merge pull request #150 from EvanSung/optimize_20230810_acl_resource_fe
refactor(fe): reduce the width of resource mgt table
2023-08-10 19:32:49 +08:00
pycook
3ba83821f2 Merge pull request #148 from simontigers/common_setting_format
fix: default arg value
2023-08-10 19:31:18 +08:00
pycook
45b82aa52d Merge pull request #149 from veops/dev_ui
ui更新:password
2023-08-10 19:28:24 +08:00
EvanSung
c99790bda2 refactor(fe): reduce the width of resource mgt table 2023-08-10 19:23:41 +08:00
wang-liang0615
7272880062 Merge branch 'master' of github.com:veops/cmdb into dev_ui 2023-08-10 19:21:28 +08:00
wang-liang0615
8130dd92ab 增加密码明文传输 2023-08-10 19:21:10 +08:00
hu.sima
9ca2d38307 fix: default arg value 2023-08-10 19:05:56 +08:00
pycook
e61bbdbcb7 Merge pull request #147 from simontigers/common_setting_format
fix: remove useless
2023-08-10 19:01:25 +08:00
hu.sima
88960c3082 fix: remove useless 2023-08-10 18:55:32 +08:00
pycook
0aa433882e Merge pull request #146 from simontigers/common_setting_format
Common setting format
2023-08-10 18:23:24 +08:00
hu.sima
d4f5713e0a fix: remove unused column 2023-08-10 16:29:52 +08:00
hu.sima
790204ea28 style: format common setting 2023-08-10 15:30:01 +08:00
pycook
927ed393d3 Merge pull request #145 from EvanSung/optimize_20230810_auth_require
optimize(auth): auth request json
2023-08-10 11:24:23 +08:00
EvanSung
bf92b89a5f optimize(auth): auth request json 2023-08-10 10:43:59 +08:00
pycook
7ddaa143da fix celery config 2023-08-08 16:33:24 +08:00
pycook
157361cc7a Merge branch 'master' of github.com:veops/cmdb 2023-08-08 13:16:14 +08:00
pycook
dcf768376a upgrade celery 2023-08-08 13:16:07 +08:00
pycook
e339ad2c23 Merge pull request #144 from veops/dev_ui
UI更新:fix preferenceList=>attrList
2023-08-08 09:21:10 +08:00
wang-liang0615
8b10c5c72d Merge branch 'master' of github.com:veops/cmdb into dev_ui 2023-08-08 09:11:24 +08:00
wang-liang0615
bd13f6b744 fix preferenceList=>attrList 2023-08-08 09:11:03 +08:00
pycook
934d00e87d upgrade flask to 2.3.2 and replace g.user with current_user 2023-08-06 21:54:18 +08:00
pycook
9d421993a0 Merge pull request #138 from lovvvve/fix_ldap
fix ldap login
2023-08-04 11:31:58 +08:00
pycook
6751f673d5 Merge pull request #142 from veops/dev_ui
ci 批量更新和删除的异步处理
2023-08-04 09:27:55 +08:00
wang-liang0615
c420a2195a Merge branch 'master' of github.com:veops/cmdb into dev_ui 2023-08-03 16:54:47 +08:00
wang-liang0615
ec3a6e0b6e ci 批量更新和删除的异步处理 2023-08-03 16:54:27 +08:00
pycook
ab0a5399f7 Merge pull request #139 from EvanSung/fix-post-acltrigger-session-invalid
fix(trigger): session invalid issue
2023-08-02 19:33:05 +08:00
songbing01249
33e63c762f fix(trigger): session invalid issue 2023-08-02 18:22:42 +08:00
lovvvve
53b2a228ae fix ldap login 2023-08-01 11:27:29 +00:00
pycook
c687e7ad9b Merge pull request #134 from veops/dependabot/pip/cmdb-api/pillow-9.3.0
Bump pillow from 9.2.0 to 9.3.0 in /cmdb-api
2023-08-01 15:57:02 +08:00
pycook
ea3cdbd5f5 Merge pull request #135 from simontigers/remove_pandas
fix: remove pandas
2023-08-01 15:55:15 +08:00
hu.sima
1ebe74d966 fix: remove pandas 2023-08-01 15:32:44 +08:00
dependabot[bot]
1f935d25a2 Bump pillow from 9.2.0 to 9.3.0 in /cmdb-api
Bumps [pillow](https://github.com/python-pillow/Pillow) from 9.2.0 to 9.3.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/9.2.0...9.3.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-01 05:51:16 +00:00
pycook
14e4dbacac Merge branch 'master' of github.com:veops/cmdb 2023-08-01 13:47:34 +08:00
pycook
f7a9257e16 fix dependabot alerts 2023-08-01 13:47:11 +08:00
pycook
99562df6cd fix dependabot alerts 2023-08-01 13:46:47 +08:00
pycook
630013cb85 Merge pull request #130 from veops/dependabot/pip/cmdb-api/certifi-2023.7.22
Bump certifi from 2023.5.7 to 2023.7.22 in /cmdb-api
2023-08-01 13:14:25 +08:00
pycook
6351d3af64 Merge pull request #132 from veops/dev_ui
删除角色相关
2023-07-31 19:54:19 +08:00
wang-liang0615
2d96048828 删除角色相关 2023-07-31 19:52:06 +08:00
pycook
3e67bb94bc Merge branch 'master' of github.com:veops/cmdb 2023-07-31 18:39:46 +08:00
pycook
a26c9ff542 fix delete ci_type 2023-07-31 18:39:33 +08:00
pycook
252003f76d Merge pull request #131 from veops/dev_ui
前端acl
2023-07-28 18:03:36 +08:00
wang-liang0615
928149f2a0 common-setting 2023-07-27 15:47:13 +08:00
wang-liang0615
0283af812c acl 2023-07-27 15:30:27 +08:00
wang-liang0615
6ccb2e74e0 fix acl change page size 2023-07-27 15:08:25 +08:00
dependabot[bot]
5016464016 Bump certifi from 2023.5.7 to 2023.7.22 in /cmdb-api
Bumps [certifi](https://github.com/certifi/python-certifi) from 2023.5.7 to 2023.7.22.
- [Commits](https://github.com/certifi/python-certifi/compare/2023.05.07...2023.07.22)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-25 23:35:30 +00:00
pycook
b787bd9b7b Merge pull request #129 from veops/dev_ui
前端更新
2023-07-25 18:19:47 +08:00
wang-liang0615
11f69d8679 Merge branch 'master' of github.com:veops/cmdb into dev_ui 2023-07-25 13:11:03 +08:00
wang-liang0615
c3168035ed 授权高亮提示 2023-07-25 13:10:45 +08:00
pycook
5140c0a58f add command cmdb-index-table-upgrade 2023-07-25 10:31:30 +08:00
wang-liang0615
ee97582579 style 新建属性行错乱 2023-07-25 10:18:22 +08:00
pycook
83e9172722 废弃3个表: c_value_datetime c_value_floats c_value_integers, time类型属性值增加写入校验 2023-07-24 21:55:00 +08:00
pycook
341e687987 禁止删除唯一标识的属性 2023-07-21 15:58:41 +08:00
pycook
93d9804127 Merge branch 'master' of github.com:veops/cmdb 2023-07-20 18:37:14 +08:00
pycook
f3d42cb356 fix docker-compose 2023-07-20 18:36:32 +08:00
pycook
04b9a3c929 Merge pull request #127 from veops/dev_ui
fix currentValueType
2023-07-20 15:39:11 +08:00
wang-liang0615
e98160b84a fix currentValueType 2023-07-20 15:30:12 +08:00
pycook
7c769a53da 更新架构图 2023-07-20 11:01:25 +08:00
pycook
08dc343083 update readme 2023-07-20 11:01:25 +08:00
pycook
52e654ff60 update readme 2023-07-20 11:01:25 +08:00
237 changed files with 3366 additions and 13490 deletions

1
.gitignore vendored
View File

@@ -78,3 +78,4 @@ cmdb-ui/npm-debug.log*
cmdb-ui/yarn-debug.log*
cmdb-ui/yarn-error.log*
cmdb-ui/package-lock.json
start.sh

View File

@@ -1,13 +0,0 @@
# Contributor Code of Conduct
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
We are committed to making participation in this project a harassment-free experience for everyone, regardless of the level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
This Code of Conduct is adapted from the [Contributor Covenant](https://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)

195
README.md
View File

@@ -1,138 +1,119 @@
<p align="center">
<a href="https://veops.cn">
<img src="https://github.com/user-attachments/assets/c5cfb272-899b-418d-9e69-8e1dd07db0f6" alt="维易CMDB"/>
</a>
<a href="https://veops.cn"><img src="docs/images/logo.png" alt="维易CMDB" width="300"/></a>
</p>
<h4 align="center">简单、轻量、通用的运维配置管理数据库</h4>
<h3 align="center">简单、轻量、通用的运维配置管理数据库</h3>
<p align="center">
<a href="https://github.com/veops/cmdb/blob/master/LICENSE"><img src="https://img.shields.io/badge/License-AGPLv3-brightgreen" alt="License: GPLv3"></a>
<a href="https://github.com/veops/cmdb/releases"><img alt="the latest release version" src="https://img.shields.io/github/v/release/veops/cmdb?color=75C1C4&include_prereleases&label=Release&logo=github&logoColor=white"></a>
<a href="https:https://github.com/sendya/ant-design-pro-vue"><img src="https://img.shields.io/badge/UI-Ant%20Design%20Pro%20Vue-green" alt="UI"></a>
<a href="https://github.com/pallets/flask"><img src="https://img.shields.io/badge/API-Flask-bright" alt="API"></a>
<a href="https://github.com/veops/cmdb/stargazers"><img src="https://img.shields.io/github/stars/veops/cmdb" alt="Stars Badge"/></a>
<a href="https://github.com/veops/cmdb"><img src="https://img.shields.io/github/forks/veops/cmdb" alt="Forks Badge"/></a>
</p>
<p align="center">
中文(简体) · <a href="docs/README_en.md">English</a>
<a href="https:https://github.com/sendya/ant-design-pro-vue"><img src="https://img.shields.io/badge/UI-Ant%20Design%20Pro%20Vue-brightgreen" alt="UI"></a>
<a href="https://github.com/pallets/flask"><img src="https://img.shields.io/badge/API-Flask-brightgreen" alt="API"></a>
</p>
------------------------------
[English](docs/README_en.md) / [中文](README.md)
- 产品文档https://veops.cn/docs/
- 在线体验:<a href="https://cmdb.veops.cn" target="_blank">CMDB</a>
- username: demo 或者 admin
- password: 123456
> **重要提示**: `master` 分支在开发过程中可能处于 _不稳定的状态_ 。
> 请通过[releases](https://github.com/veops/cmdb/releases)获取
## 系统介绍
维易CMDB是一个简洁、轻量且高度可定制的运维配置管理数据库CMDB。它支持灵活的模型配置和资源自动发现旨在为企业提供便捷的资产管理解决方案帮助运维团队高效地管理 IT 基础设施和服务。
### 系统概览
- 产品文档:[https://veops.cn/docs/](https://veops.cn/docs/)
- 在线体验:[https://cmdb.veops.cn](https://cmdb.veops.cn)
- 用户名demo 或者 admin
- 密码123456
- **重要提示**`master` 分支在开发过程中可能处于**不稳定的状态**。请通过 [releases](https://github.com/veops/cmdb/releases) 获取最新稳定版本。
<img src=docs/images/dashboard.png />
[查看更多展示](docs/screenshot.md)
### 相关文章
- <a href="https://mp.weixin.qq.com/s/v3eANth64UBW5xdyOkK3tg" target="_blank">概要设计</a>
- <a href="https://github.com/veops/cmdb/tree/master/docs/cmdb_api.md" target="_blank">API 文档</a>
- <a href="https://mp.weixin.qq.com/s/rQaf4AES7YJsyNQG_MKOLg" target="_blank">自动发现</a>
- 更多文章可以在公众号 **维易科技OneOps** 里查看
### 特点
- 灵活性
1. 配置灵活,不设定任何运维场景,有内置模板
2. 自动发现、入库 IT 资产
- 安全性
1. 细粒度权限控制
2. 完备操作日志
- 多应用
1. 丰富视图展示维度
2. API简单强大
3. 支持定义属性触发器、计算属性
### 主要功能
- **自定义模型和模型关系**:支持模型属性的自定义,包括下拉列表、字体颜色计算属性等高级功能,满足不同业务需求。
- **自动发现资源**:支持计算机、网络设备、存储设备、数据库、中间件、公有云资源等自动发现。
- **多维度视图展示**:包括资源视图、层级视图、关系视图等,帮助运维人员全面管理资源。
- **细粒度权限控制**:通过精确的访问控制和完备的操作日志保障系统的安全性。
- **全面的资源搜索功能**:支持灵活的资源和关系搜索,快速定位和操作资源。
- **集成 IP 地址管理IPAM和数据中心基础设施管理DCIM**:简化网络资源和数据中心设备的管理。
- 模型属性支持索引、多值、默认排序、字体颜色,支持计算属性
- 支持自动发现、定时巡检、文件导入
- 支持资源、层级、关系视图展示
- 支持模型间关系配置和展示
- 细粒度访问控制,完备的操作日志
- 支持跨模型搜索
更多详细功能,请移步 [维易科技官网](https://veops.cn) 进行了解。
### 系统优势
- 灵活性
+ 无需指定固定运维场景,支持自由配置并内置多种模板
+ 支持自动发现和入库 IT 资产,快速搭建资产管理系统
- 安全性
+ 细粒度的权限控制机制,确保资源管理的安全性
+ 完整的操作日志记录,便于审计和问题追踪
- 多应用
+ 提供多种视图展示方式,满足不同场景的需求
+ 强大的 API 接口,支持深度集成
+ 支持定义属性触发器和计算属性,增强数据处理能力
### 技术栈
+ 后端Python [3.8-3.11]
+ 数据存储MySQL、Redis
+ 前端Vue.js
+ UI组件库Ant Design Vue
### 更多功能
### 系统概览
<table style="border-collapse: collapse;">
<tr>
<td style="padding: 5px;background-color:#fff;">
<img width="400" src="https://github.com/user-attachments/assets/6d2df835-ae93-4d91-9bd9-213c270eca7a"/>
</td>
<td style="padding: 5px;background-color:#fff;">
<img width="400" src="https://github.com/user-attachments/assets/cb8b598a-a1f9-4c74-adf1-6e59aea2c9b3"/>
</td>
</tr>
<tr>
<td style="padding: 5px;background-color:#fff;">
<img width="400" src="https://github.com/user-attachments/assets/b440224f-53c3-4b7f-a9be-285d7a4b848f"/>
</td>
<td style="padding: 5px;background-color:#fff;">
<img width="400" src="https://github.com/user-attachments/assets/f457d5a0-b60b-4949-b94e-020f4c61444b"/>
</td>
</tr>
</table>
## 关注我们
欢迎 Star 加关注,第一时间获取更新动态!
![star us](https://github.com/user-attachments/assets/f9056d5a-171c-4f53-9fec-d40c9e5ff94d)
## 快速开始
### 1. 搭建
+ 方案一Docker 一键快速构建
- 第1步: 安装 Docker 环境和 Docker Composev2
- 第2步: 拷贝项目代码, `git clone https://github.com/veops/cmdb.git`
- 第3步进入主目录并启动, `docker compose up -d`
+ 方案二:[本地开发环境搭建](docs/local.md)
+ 方案三:[Makefile 安装](docs/makefile.md)
### 2. 访问
- 打开浏览器并访问: [http://127.0.0.1:8000](http://127.0.0.1:8000)
- 用户名: demo 或者 admin
- 密码: 123456
> 也欢迎移步[维易科技官网](https://veops.cn),发现更多免费运维系统。
## 接入公司
+ 欢迎使用开源CMDB的公司和团队,在 [#112](https://github.com/veops/cmdb/issues/112) 登记
> 欢迎使用开源CMDB的公司在 [#112](https://github.com/veops/cmdb/issues/112) 登记
## 代码贡献
我们欢迎所有开发者贡献代码,改善和扩展这个项目。请先阅读我们的[贡献指南](docs/CONTRIBUTING.md)。此外,您还可以通过社交媒体、活动和分享来支持 Veops 的开源。
## 安装
<a href="https://github.com/veops/cmdb/graphs/contributors">
<img src="https://contrib.rocks/image?repo=veops/cmdb" />
</a>
### Docker 一键快速构建
## 更多开源
[//]: # (> 方法一)
- 第一步: 先安装 Docker 环境, 以及Docker Compose (v2)
- 第二步: 拷贝项目
```shell
git clone https://github.com/veops/cmdb.git
```
- 第三步:进入主目录,执行:
```
docker compose up -d
```
- [OneTerm](https://github.com/veops/oneterm): 一款简单、轻量、灵活的堡垒机服务。
- [messenger](https://github.com/veops/messenger): 一个简单轻量的消息发送服务。
- [ACL](https://github.com/veops/acl): 一个简单通用的权限管理系统设计与实践。
[//]: # (> 方法二, 该方法适用于linux系统)
## 相关文章
[//]: # (- 第一步: 先安装 Docker 环境, 以及Docker Compose &#40;v2&#41;)
- <a href="https://mp.weixin.qq.com/s/v3eANth64UBW5xdyOkK3tg" target="_blank">尽可能通用的运维CMDB的设计与实践() - 概览</a>
- <a href="https://mp.weixin.qq.com/s/rQaf4AES7YJsyNQG_MKOLg" target="_blank">尽可能通用的运维CMDB的设计与实践() - 自动发现</a>
- <a href="https://github.com/veops/cmdb/tree/master/docs/cmdb_api.md" target="_blank">CMDB接口文档</a>
[//]: # (- 第二步: 直接使用项目根目录下的install.sh 文件进行 `安装`、`启动`、`暂停`、`查状态`、`删除`、`卸载`)
更多文章可以在公众号 **维易科技OneOps** 里查看
[//]: # (```shell)
## 与我联系
[//]: # (curl -so install.sh https://raw.githubusercontent.com/veops/cmdb/deploy_on_kylin_docker/install.sh)
+ 邮箱: <a href="mailto:bd@veops.cn">bd@veops.cn</a>
+ 公众号:**维易科技OneOps**。关注后可以加入微信群,参与产品和技术交流
<img src="docs/images/wechat.png" alt="公众号: 维易科技OneOps" />
[//]: # (sh install.sh install)
[//]: # (```)
### [本地开发环境搭建](docs/local.md)
### [Makefile 安装](docs/makefile.md)
## 验证
- 浏览器打开: [http://127.0.0.1:8000](http://127.0.0.1:8000)
- username: demo 或者 admin
- password: 123456
---
_**欢迎关注公众号(维易科技OneOps),关注后可加入微信群,进行产品和技术交流。**_
<p align="center">
<img src="docs/images/wechat.png" alt="公众号: 维易科技OneOps" />
</p>

View File

@@ -1,79 +0,0 @@
line-length = 120
cache-dir = ".ruff_cache"
target-version = "py310"
unsafe-fixes = true
show-fixes = true
[lint]
select = [
"E",
"F",
"I",
"TCH",
# W
"W505",
# PT
"PT018",
# SIM
"SIM101",
"SIM114",
# PGH
"PGH004",
# PL
"PLE1142",
# RUF
"RUF100",
# UP
"UP007"
]
preview = true
ignore = ["FURB101"]
[lint.flake8-pytest-style]
mark-parentheses = false
parametrize-names-type = "list"
parametrize-values-row-type = "list"
parametrize-values-type = "tuple"
[lint.flake8-unused-arguments]
ignore-variadic-names = true
[lint.isort]
lines-between-types = 1
order-by-type = true
[lint.per-file-ignores]
"**/api/v1/*.py" = ["TCH"]
"**/model/*.py" = ["TCH003"]
"**/models/__init__.py" = ["F401", "F403"]
"**/tests/*.py" = ["E402"]
"celery_worker.py" = ["F401"]
"api/views/entry.py" = ["I001"]
"migrations/*.py" = ["I001", "E402"]
"*.py" = ["I001"]
"api/views/common_setting/department.py" = ["F841"]
"api/lib/common_setting/upload_file.py" = ["F841"]
"api/lib/common_setting/acl.py" = ["F841"]
"**/__init__.py" = ["F822"]
"api/tasks/*.py" = ["E722"]
"api/views/cmdb/*.py" = ["E722"]
"api/views/acl/*.py" = ["E722"]
"api/lib/secrets/*.py" = ["E722", "F841"]
"api/lib/utils.py" = ["E722", "E731"]
"api/lib/perm/authentication/cas/*" = ["E113", "F841"]
"api/lib/perm/acl/*" = ["E722"]
"api/lib/*" = ["E721", "F722"]
"api/lib/cmdb/*" = ["F722", "E722"]
"api/lib/cmdb/search/ci/es/search.py" = ["F841", "SIM114"]
"api/lib/cmdb/search/ci/db/search.py" = ["F841"]
"api/lib/cmdb/value.py" = ["F841"]
"api/lib/cmdb/history.py" = ["E501"]
"api/commands/common.py" = ["E722"]
"api/commands/click_cmdb.py" = ["E722"]
"api/lib/perm/auth.py" = ["SIM114"]
[format]
preview = true
quote-style = "single"
docstring-code-format = true
skip-magic-trailing-comma = false

View File

@@ -23,7 +23,6 @@ from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION2
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
@@ -196,7 +195,7 @@ def cmdb_counter():
today = datetime.date.today()
while True:
try:
db.session.commit()
db.session.remove()
CMDBCounterCache.reset()
@@ -210,8 +209,6 @@ def cmdb_counter():
CMDBCounterCache.flush_sub_counter()
RackManager().check_u_slot()
i += 1
except:
import traceback
@@ -228,12 +225,6 @@ def cmdb_trigger():
"""
from api.lib.cmdb.ci import CITriggerManager
current_app.test_request_context().push()
if not UserCache.get('worker'):
from api.lib.perm.acl.user import UserCRUD
UserCRUD.add(username='worker', password=uuid.uuid4().hex, email='worker@xxx.com')
login_user(UserCache.get('worker'))
current_day = datetime.datetime.today().strftime("%Y-%m-%d")
trigger2cis = dict()
trigger2completed = dict()
@@ -262,10 +253,10 @@ def cmdb_trigger():
trigger2cis[trigger.id] = (trigger, ready_cis)
else:
cur = trigger2cis[trigger.id]
cur_ci_ids = {_ci.ci_id for _ci in cur[1]}
cur_ci_ids = {i.ci_id for i in cur[1]}
trigger2cis[trigger.id] = (
trigger, cur[1] + [_ci for _ci in ready_cis if _ci.ci_id not in cur_ci_ids
and _ci.ci_id not in trigger2completed.get(trigger.id, {})])
trigger, cur[1] + [i for i in ready_cis if i.ci_id not in cur_ci_ids
and i.ci_id not in trigger2completed.get(trigger.id, {})])
for tid in trigger2cis:
trigger, cis = trigger2cis[tid]
@@ -352,7 +343,7 @@ def cmdb_inner_secrets_init(address):
if valid_address(address):
token = current_app.config.get("INNER_TRIGGER_TOKEN", "") if not token else token
if not token:
token = click.prompt('Enter root token', hide_input=True, confirmation_prompt=False)
token = click.prompt(f'Enter root token', hide_input=True, confirmation_prompt=False)
assert token is not None
resp = requests.post("{}/api/v0.1/secrets/auto_seal".format(address.strip("/")),
headers={"Inner-Token": token})

View File

@@ -415,7 +415,7 @@ class AttributeManager(object):
db.session.rollback()
current_app.logger.error("update attribute error, {0}".format(str(e)))
return abort(400, ErrFormat.update_attribute_failed.format(("id={}".format(_id))))
return abort(400, ErrFormat.update_attribute_failed.format(("id=".format(_id))))
new = attr.to_dict()
if not new['choice_web_hook'] and new['is_choice']:

View File

@@ -12,7 +12,6 @@ from sqlalchemy import func
from api.extensions import db
from api.lib.cmdb.auto_discovery.const import CLOUD_MAP
from api.lib.cmdb.auto_discovery.const import DEFAULT_INNER
from api.lib.cmdb.auto_discovery.const import NET_DEVICE_NAMES
from api.lib.cmdb.auto_discovery.const import PRIVILEGED_USERS
from api.lib.cmdb.cache import AttributeCache
from api.lib.cmdb.cache import AutoDiscoveryMappingCache
@@ -253,7 +252,6 @@ class AutoDiscoveryCITypeCRUD(DBMixin):
:return:
"""
result = []
db.session.commit()
rules = cls.cls.get_by(to_dict=True)
for rule in rules:
@@ -720,12 +718,6 @@ class AutoDiscoveryCICRUD(DBMixin):
build_relations_for_ad_accept.apply_async(args=(adc.to_dict(), ci_id, ad_key2attr), queue=CMDB_QUEUE)
ci_type = CITypeCache.get(adc.type_id)
if ci_type and ci_type.name in NET_DEVICE_NAMES and 'ports' in adc.instance:
from api.tasks.cmdb import add_net_device_ports
add_net_device_ports.apply_async(args=(ci_id, adc.instance['ports']),
queue=CMDB_QUEUE)
adc.update(is_accept=True,
accept_by=nickname or current_user.nickname,
accept_time=datetime.datetime.now(),

View File

@@ -4,8 +4,6 @@ from api.lib.cmdb.const import AutoDiscoveryType
PRIVILEGED_USERS = ("cmdb_agent", "worker", "admin")
NET_DEVICE_NAMES = {"switch", 'router', 'firewall', 'printer'}
DEFAULT_INNER = [
dict(name="阿里云", en="aliyun", type=AutoDiscoveryType.HTTP, is_inner=True, is_plugin=False,
option={'icon': {'name': 'caise-aliyun'}, "en": "aliyun"}),
@@ -43,12 +41,8 @@ DEFAULT_INNER = [
option={'icon': {'name': 'caise-luyouqi'}}),
dict(name="防火墙", type=AutoDiscoveryType.SNMP, is_inner=True, is_plugin=False,
option={'icon': {'name': 'caise-fanghuoqiang'}}),
# dict(name="打印机", type=AutoDiscoveryType.SNMP, is_inner=True, is_plugin=False,
# option={'icon': {'name': 'caise-dayinji'}}),
dict(name="光纤交换机", type=AutoDiscoveryType.SNMP, is_inner=True, is_plugin=False,
option={'icon': {'name': 'caise-fiber'}}),
dict(name="F5", type=AutoDiscoveryType.SNMP, is_inner=True, is_plugin=False,
option={'icon': {'name': 'caise-F5'}}),
dict(name="打印机", type=AutoDiscoveryType.SNMP, is_inner=True, is_plugin=False,
option={'icon': {'name': 'caise-dayinji'}}),
]
CLOUD_MAP = {

View File

@@ -1,74 +1,37 @@
[
{
"name": "manufacturer",
"type": "文本",
"example": "Huawei",
"desc": "制造产商"
},
{
"name": "sn",
"type": "文本",
"example": "102030059898",
"desc": "设备序列号"
},
{
"name": "name",
"type": "文本",
"example": "USG6525E",
"desc": "设备名称"
},
{
"name": "model",
"type": "文本",
"example": "2011.2.321.1.205",
"desc": "设备细分类型 结合相关产商获取相应的产品类型"
},
{
"name": "description",
"type": "文本",
"example": "Huawei Vwersatile Routing Platform Software",
"desc": "设备描述"
},
{
"name": "manager_ip",
"type": "文本",
"example": "192.168.1.1",
"desc": "管理ip"
},
{
"name": "ips",
"type": "文本、多值",
"example": "192.168.1.1, 192.168.1.2",
"desc": "ips"
},
{
"name": "uptime",
"type": "文本",
"example": "2023-04-15 10:00:00",
"desc": "启动时间"
},
{
"name": "snmp_version",
"type": "文本",
"example": "v2c",
"desc": "SNMP版本"
},
{
"name": "port_num",
"type": "整数",
"example": 24,
"desc": "端口数量"
},
{
"name": "ports",
"type": "json",
"example": "",
"desc": "设备的端口列表"
},
{
"name": "neighbors",
"type": "json",
"example": "",
"desc": "设备的邻居列表"
}
[{
"name":"manufacturer",
"type": "文本",
"example":"HUAWEI Technology Co.,Ltd",
"desc":"制造产商"
},{
"name":"sn",
"type": "文本",
"example":"102030059898",
"desc":"设备序列号"
},{
"name":"device_name",
"type": "文本",
"example":"USG6525E",
"desc":"设备名称"
},{
"name":"device_model",
"type": "文本",
"example":"2011.2.321.1.205",
"desc":"设备细分类型 结合相关产商获取相应的产品类型"
},{
"name":"description",
"type": "文本",
"example":"Huawei Vwersatile Routing Platform Software",
"desc":"设备描述"
},{
"name":"manager_ip",
"type": "文本",
"example":"192.168.1.1",
"desc":"管理ip"
}, {
"name":"ips",
"type": "文本、多值",
"example":"192.168.1.1, 192.168.1.2",
"desc":"ips"
}
]

View File

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

View File

@@ -295,7 +295,7 @@ class CIManager(object):
db.session.commit()
value_table = TableMap(attr_name=attr.name).table
with redis_lock.Lock(rd.r, "auto_inc_id_{}".format(attr.name), expire=10):
with redis_lock.Lock(rd.r, "auto_inc_id_{}".format(attr.name)):
max_v = value_table.get_by(attr_id=attr.id, only_query=True).order_by(
getattr(value_table, 'value').desc()).first()
if max_v is not None:
@@ -357,7 +357,6 @@ class CIManager(object):
is_auto_discovery=False,
_is_admin=False,
ticket_id=None,
_sync=False,
**ci_dict):
"""
add ci
@@ -367,7 +366,6 @@ class CIManager(object):
:param is_auto_discovery: default is False
:param _is_admin: default is False
:param ticket_id:
:param _sync:
:param ci_dict:
:return:
"""
@@ -393,7 +391,7 @@ class CIManager(object):
ci = None
record_id = None
password_dict = {}
with redis_lock.Lock(rd.r, ci_type.name, expire=10):
with redis_lock.Lock(rd.r, ci_type.name):
db.session.commit()
if (unique_key.default and unique_key.default.get('default') == AttributeDefaultValueEnum.AUTO_INC_ID and
@@ -498,16 +496,10 @@ class CIManager(object):
record_id = cls.save_password(ci.id, attr_id, password_dict[attr_id], record_id, ci_type.id)
if record_id or has_dynamic: # has changed
if not _sync:
ci_cache.apply_async(args=(ci.id, operate_type, record_id), queue=CMDB_QUEUE)
else:
ci_cache(ci.id, operate_type, record_id)
ci_cache.apply_async(args=(ci.id, operate_type, record_id), queue=CMDB_QUEUE)
if ref_ci_dict: # add relations
if not _sync:
ci_relation_add.apply_async(args=(ref_ci_dict, ci.id, current_user.uid), queue=CMDB_QUEUE)
else:
ci_relation_add(ref_ci_dict, ci.id, current_user.uid)
ci_relation_add.apply_async(args=(ref_ci_dict, ci.id, current_user.uid), queue=CMDB_QUEUE)
return ci.id
@@ -524,14 +516,10 @@ class CIManager(object):
raw_dict = copy.deepcopy(ci_dict)
ci_attr2type_attr = {type_attr.attr_id: type_attr for type_attr, _ in attrs}
unique_name = None
for _, attr in attrs:
if attr.default and attr.default.get('default') == AttributeDefaultValueEnum.UPDATED_AT:
ci_dict[attr.name] = now
if attr.id == ci_type.unique_id:
unique_name = attr.name
value_manager = AttributeValueManager()
password_dict = dict()
@@ -554,15 +542,14 @@ class CIManager(object):
limit_attrs = self._valid_ci_for_no_read(ci) if not _is_admin else {}
record_id = None
with redis_lock.Lock(rd.r, ci_type.name, expire=10):
with redis_lock.Lock(rd.r, ci_type.name):
db.session.commit()
self._valid_unique_constraint(ci.type_id, ci_dict, ci_id)
ci_dict = {k: v for k, v in ci_dict.items() if k in ci_type_attrs_name}
key2attr = value_manager.valid_attr_value(ci_dict, ci.type_id, ci.id, ci_type_attrs_name,
ci_attr2type_attr=ci_attr2type_attr,
unique_name=unique_name)
ci_attr2type_attr=ci_attr2type_attr)
if computed_attrs:
value_manager.handle_ci_compute_attributes(ci_dict, computed_attrs, ci)
@@ -1273,9 +1260,7 @@ class CIRelationManager(object):
else:
type_relation = CITypeRelation.get_by_id(relation_type_id)
with redis_lock.Lock(rd.r,
"ci_relation_add_{}_{}".format(first_ci.type_id, second_ci.type_id),
expire=10):
with redis_lock.Lock(rd.r, "ci_relation_add_{}_{}".format(first_ci.type_id, second_ci.type_id)):
cls._check_constraint(first_ci_id, first_ci.type_id, second_ci_id, second_ci.type_id, type_relation)
@@ -1296,10 +1281,10 @@ class CIRelationManager(object):
return existed.id
@staticmethod
def delete(cr_id, apply_async=True, valid=True):
def delete(cr_id, apply_async=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' and valid:
if current_app.config.get('USE_ACL') and current_user.username != 'worker':
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,
@@ -1338,7 +1323,7 @@ class CIRelationManager(object):
return cr
@classmethod
def delete_3(cls, first_ci_id, second_ci_id, apply_async=True, valid=True):
def delete_3(cls, first_ci_id, second_ci_id, apply_async=True):
cr = CIRelation.get_by(first_ci_id=first_ci_id,
second_ci_id=second_ci_id,
to_dict=False,
@@ -1348,7 +1333,7 @@ class CIRelationManager(object):
# 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, valid=valid)
cls.delete(cr.id, apply_async=apply_async)
return cr
@@ -1541,8 +1526,7 @@ class CITriggerManager(object):
ci_dict.update(attr_dict)
@classmethod
def _exec_webhook(cls, operate_type, webhook, ci_dict, trigger_id, trigger_name, record_id,
ci_id=None, app=None, record_history=True):
def _exec_webhook(cls, operate_type, webhook, ci_dict, trigger_id, trigger_name, record_id, ci_id=None, app=None):
app = app or current_app
with app.app_context():
@@ -1560,20 +1544,19 @@ class CITriggerManager(object):
current_app.logger.warning("exec webhook failed: {}".format(e))
response = e
is_ok = False
if record_history:
CITriggerHistoryManager.add(operate_type,
record_id,
ci_dict.get('_id'),
trigger_id,
trigger_name,
is_ok=is_ok,
webhook=response)
CITriggerHistoryManager.add(operate_type,
record_id,
ci_dict.get('_id'),
trigger_id,
trigger_name,
is_ok=is_ok,
webhook=response)
return is_ok
@classmethod
def _exec_notify(cls, operate_type, notify, ci_dict, trigger_id, trigger_name, record_id,
ci_id=None, app=None, record_history=True):
def _exec_notify(cls, operate_type, notify, ci_dict, trigger_id, trigger_name, record_id, ci_id=None, app=None):
app = app or current_app
with app.app_context():
@@ -1597,14 +1580,13 @@ class CITriggerManager(object):
response = "{}\n{}".format(response, e)
is_ok = False
if record_history:
CITriggerHistoryManager.add(operate_type,
record_id,
ci_dict.get('_id'),
trigger_id,
trigger_name,
is_ok=is_ok,
notify=response.strip())
CITriggerHistoryManager.add(operate_type,
record_id,
ci_dict.get('_id'),
trigger_id,
trigger_name,
is_ok=is_ok,
notify=response.strip())
return is_ok
@@ -1681,47 +1663,25 @@ class CITriggerManager(object):
return result
@classmethod
def trigger_notify(cls, trigger, ci, only_test=False):
def trigger_notify(cls, trigger, ci):
"""
only for date attribute
:param trigger:
:param ci:
:param only_test:
:return:
"""
if (trigger.option.get('notifies', {}).get('notify_at') == datetime.datetime.now().strftime("%H:%M") or
not trigger.option.get('notifies', {}).get('notify_at')) or only_test:
not trigger.option.get('notifies', {}).get('notify_at')):
if trigger.option.get('webhooks'):
threading.Thread(
target=cls._exec_webhook,
args=(None, trigger.option['webhooks'], None, trigger.id,
trigger.option.get('name'), None,
ci and ci.ci_id,
current_app._get_current_object(), not only_test)).start()
threading.Thread(target=cls._exec_webhook, args=(
None, trigger.option['webhooks'], None, trigger.id, trigger.option.get('name'), None, ci.ci_id,
current_app._get_current_object())).start()
elif trigger.option.get('notifies'):
threading.Thread(target=cls._exec_notify, args=(
None, trigger.option['notifies'], None, trigger.id,
trigger.option.get('name'), None,
ci and ci.ci_id,
current_app._get_current_object(), not only_test)).start()
None, trigger.option['notifies'], None, trigger.id, trigger.option.get('name'), None, ci.ci_id,
current_app._get_current_object())).start()
return True
return False
@classmethod
def trigger_notify_test(cls, type_id, trigger_id):
trigger = CITypeTrigger.get_by_id(trigger_id) or abort(
404, ErrFormat.ci_type_trigger_not_found.format(trigger_id))
ci_type = CITypeCache.get(type_id) or abort(404, ErrFormat.ci_type_not_found.format(type_id))
attr = AttributeCache.get(ci_type.unique_id)
value_table = TableMap(attr=attr).table
if not value_table:
return
value = value_table.get_by(attr_id=attr.id, only_query=True).join(
CI, value_table.ci_id == CI.id).filter(CI.type_id == type_id).first()
cls.trigger_notify(trigger, value, only_test=True)

View File

@@ -421,10 +421,7 @@ class CITypeGroupManager(object):
group_types = set()
for group in groups:
for t in sorted(CITypeGroupItem.get_by(group_id=group['id']), key=lambda x: x['order'] or 0):
ci_type = CITypeCache.get(t['type_id'])
if ci_type is None:
continue
ci_type = ci_type.to_dict()
ci_type = CITypeCache.get(t['type_id']).to_dict()
if type_ids is not None and ci_type['id'] not in type_ids:
continue
if resources is None or (ci_type and ci_type['name'] in resources):
@@ -722,6 +719,9 @@ class CITypeAttributeManager(object):
ci_cache.apply_async(args=(ci.id, None, None), queue=CMDB_QUEUE)
for item in PreferenceShowAttributes.get_by(type_id=type_id, attr_id=attr_id, to_dict=False):
item.soft_delete(commit=False)
child_ids = CITypeInheritanceManager.recursive_children(type_id)
for _type_id in [type_id] + child_ids:
for item in CITypeUniqueConstraint.get_by(type_id=_type_id, to_dict=False):
@@ -737,9 +737,6 @@ class CITypeAttributeManager(object):
item = CITypeTrigger.get_by(type_id=_type_id, attr_id=attr_id, to_dict=False, first=True)
item and item.soft_delete(commit=False)
for item in PreferenceShowAttributes.get_by(type_id=_type_id, attr_id=attr_id, to_dict=False):
item.soft_delete(commit=False)
for item in (CITypeRelation.get_by(parent_id=type_id, to_dict=False) +
CITypeRelation.get_by(child_id=type_id, to_dict=False)):
if item.parent_id == type_id and attr_id in (item.parent_attr_ids or []):
@@ -862,15 +859,15 @@ class CITypeRelationManager(object):
graph = nx.DiGraph()
def get_children(_id, _graph):
def get_children(_id):
children = CITypeRelation.get_by(parent_id=_id, to_dict=False)
for i in children:
if i.child_id != _id:
_graph.add_edge(i.parent_id, i.child_id)
get_children(i.child_id, _graph)
graph.add_edge(i.parent_id, i.child_id)
get_children(i.child_id)
get_children(source_type_id, graph)
get_children(source_type_id)
paths = list(nx.all_simple_paths(graph, source_type_id, target_type_ids))
@@ -882,8 +879,6 @@ class CITypeRelationManager(object):
def _wrap_relation_type_dict(type_id, relation_inst):
ci_type_dict = CITypeCache.get(type_id).to_dict()
ci_type_dict["ctr_id"] = relation_inst.id
show_key = AttributeCache.get(ci_type_dict.get('show_id') or ci_type_dict['unique_id'])
ci_type_dict["show_key"] = show_key and show_key.name
ci_type_dict["attributes"] = CITypeAttributeManager.get_attributes_by_type_id(ci_type_dict["id"])
attr_filter = CIFilterPermsCRUD.get_attr_filter(type_id)
if attr_filter:
@@ -1145,14 +1140,13 @@ class CITypeAttributeGroupManager(object):
else:
group_pos = group2pos[group['name']]
attr = None
for i in items:
if i.attr_id in id2attr:
attr = id2attr[i.attr_id]
attr['inherited'] = group['inherited']
attr['inherited_from'] = group.get('inherited_from')
result[group_pos]['attributes'].append(attr)
else:
continue
if i.attr_id in attr2pos:
result[attr2pos[i.attr_id][0]]['attributes'].remove(attr2pos[i.attr_id][1])
@@ -1399,7 +1393,6 @@ class CITypeTemplateManager(object):
i.pop('order', None)
i.pop('choice_web_hook', None)
i.pop('choice_other', None)
i.pop('choice_builtin', None)
i.pop('order', None)
i.pop('inherited_from', None)
choice_value = i.pop('choice_value', None)
@@ -1558,10 +1551,7 @@ class CITypeTemplateManager(object):
if existed is None:
_group['type_id'] = type_id_map.get(_group['type_id'], _group['type_id'])
try:
existed = CITypeAttributeGroup.create(flush=True, **_group)
except:
continue
existed = CITypeAttributeGroup.create(flush=True, **_group)
for order, attr in enumerate(group['attributes'] or []):
item_existed = CITypeAttributeGroupItem.get_by(group_id=existed.id,

View File

@@ -123,11 +123,6 @@ 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"),

View File

@@ -1 +0,0 @@
# -*- coding:utf-8 -*-

View File

@@ -1,33 +0,0 @@
# -*- 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)

View File

@@ -1,17 +0,0 @@
# -*- 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"

View File

@@ -1,40 +0,0 @@
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

View File

@@ -1,19 +0,0 @@
# -*- 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

View File

@@ -1,182 +0,0 @@
# -*- 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:
u_slot_abnormal = False
for a, b in itertools.combinations(u_slot_sets, 2):
if a.intersection(b):
u_slot_abnormal = True
break
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), expire=10)):
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), expire=10)):
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), expire=10)):
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), expire=10)):
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)

View File

@@ -1,29 +0,0 @@
# -*- 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)

View File

@@ -1,56 +0,0 @@
# -*- 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

View File

@@ -1,85 +0,0 @@
# -*- 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

View File

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

View File

@@ -43,17 +43,13 @@ class ScanHistoryManager(DBMixin):
if kwargs.get('ips'):
from api.lib.cmdb.ipam.address import IpAddressManager
IpAddressManager().assign_ips(kwargs['ips'], ci_id, kwargs.get('cidr'),
IpAddressManager().assign_ips(kwargs['ips'], None, kwargs.get('cidr'),
**{IPAddressBuiltinAttributes.IS_USED: 1})
scan_rule = IPAMSubnetScan.get_by(ci_id=ci_id, first=True, to_dict=False)
if scan_rule is not None:
scan_rule.update(last_scan_time=kwargs.get('start_at'))
for i in self.cls.get_by(subnet_scan_id=kwargs.get('subnet_scan_id'), only_query=True).order_by(
self.cls.id.desc()).offset(100):
i.delete()
def _can_update(self, **kwargs):
pass

View File

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

View File

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

View File

@@ -163,7 +163,7 @@ class CIFilterPermsCRUD(DBMixin):
def add(self, **kwargs):
kwargs = self._can_add(**kwargs) or kwargs
with redis_lock.Lock(rd.r, 'CMDB_FILTER_{}_{}'.format(kwargs['type_id'], kwargs['rid']), expire=10):
with redis_lock.Lock(rd.r, 'CMDB_FILTER_{}_{}'.format(kwargs['type_id'], kwargs['rid'])):
request_id_filter = {}
if kwargs.get('id_filter'):
obj = self.cls.get_by(type_id=kwargs.get('type_id'),
@@ -232,7 +232,7 @@ class CIFilterPermsCRUD(DBMixin):
pass
def delete(self, **kwargs):
with redis_lock.Lock(rd.r, 'CMDB_FILTER_{}_{}'.format(kwargs['type_id'], kwargs['rid']), expire=10):
with redis_lock.Lock(rd.r, 'CMDB_FILTER_{}_{}'.format(kwargs['type_id'], kwargs['rid'])):
obj = self.cls.get_by(type_id=kwargs.get('type_id'),
rid=kwargs.get('rid'),
id_filter=None,
@@ -249,7 +249,7 @@ class CIFilterPermsCRUD(DBMixin):
def delete2(self, **kwargs):
with redis_lock.Lock(rd.r, 'CMDB_FILTER_{}_{}'.format(kwargs['type_id'], kwargs['rid']), expire=10):
with redis_lock.Lock(rd.r, 'CMDB_FILTER_{}_{}'.format(kwargs['type_id'], kwargs['rid'])):
obj = self.cls.get_by(type_id=kwargs.get('type_id'),
rid=kwargs.get('rid'),
ci_filter=None,

View File

@@ -16,9 +16,8 @@ class ErrFormat(CommonErrFormat):
argument_file_not_found = _l("The file doesn't seem to be uploaded") # 文件似乎并未上传
attribute_not_found = _l("Attribute {} does not exist!") # 属性 {} 不存在!
# 该属性是模型的唯一标识,不能被删除!
attribute_is_unique_id = _l(
"This attribute is the unique identifier of the model and cannot be deleted!")
"This attribute is the unique identifier of the model and cannot be deleted!") # 该属性是模型的唯一标识,不能被删除!
attribute_is_ref_by_type = _l(
"This attribute is referenced by model {} and cannot be deleted!") # 该属性被模型 {} 引用, 不能删除!
attribute_value_type_cannot_change = _l(
@@ -130,8 +129,7 @@ class ErrFormat(CommonErrFormat):
adr_default_ref_once = _l("The default auto-discovery rule is already referenced by model {}!")
# unique_key方法必须返回非空字符串!
adr_unique_key_required = _l("The unique_key method must return a non-empty string!")
# attributes方法必须返回的是list
adr_plugin_attributes_list_required = _l("The attributes method must return a list")
adr_plugin_attributes_list_required = _l("The attributes method must return a list") # attributes方法必须返回的是list
# attributes方法返回的list不能为空!
adr_plugin_attributes_list_no_empty = _l("The list returned by the attributes method cannot be empty!")
# 只有管理员才可以定义执行机器为: 所有节点!
@@ -171,8 +169,3 @@ 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")

View File

@@ -107,12 +107,3 @@ FROM
WHERE c_value_index_datetime.value LIKE "{0}") AS {1}
GROUP BY {1}.ci_id
"""
QUERY_CI_BY_NO_ATTR_IN = """
SELECT *
FROM
(SELECT c_value_index_texts.ci_id
FROM c_value_index_texts
WHERE c_value_index_texts.value in ({0})) AS {1}
GROUP BY {1}.ci_id
"""

View File

@@ -6,7 +6,6 @@ from __future__ import unicode_literals
import copy
import six
import time
from flask import abort
from flask import current_app
from flask_login import current_user
from jinja2 import Template
@@ -28,7 +27,6 @@ from api.lib.cmdb.search.ci.db.query_sql import FACET_QUERY
from api.lib.cmdb.search.ci.db.query_sql import QUERY_CI_BY_ATTR_NAME
from api.lib.cmdb.search.ci.db.query_sql import QUERY_CI_BY_ID
from api.lib.cmdb.search.ci.db.query_sql import QUERY_CI_BY_NO_ATTR
from api.lib.cmdb.search.ci.db.query_sql import QUERY_CI_BY_NO_ATTR_IN
from api.lib.cmdb.search.ci.db.query_sql import QUERY_CI_BY_TYPE
from api.lib.cmdb.search.ci.db.query_sql import QUERY_UNION_CI_ATTRIBUTE_IS_NULL
from api.lib.cmdb.utils import TableMap
@@ -143,7 +141,7 @@ class Search(object):
if str(ci_type.id) in self.type_id_list:
self.type_id_list.remove(str(ci_type.id))
type_id_list.remove(str(ci_type.id))
sub.extend([i for i in queries[1:] if isinstance(i, (six.string_types, list))])
sub.extend([i for i in queries[1:] if isinstance(i, six.string_types)])
sub.insert(0, "_type:{}".format(ci_type.id))
queries.append(dict(operator="|", queries=sub))
@@ -153,9 +151,7 @@ class Search(object):
if not self.fl:
self.fl = set(self.type2filter_perms[ci_type.id]['attr_filter'])
else:
fl = set(self.fl) & set(self.type2filter_perms[ci_type.id]['attr_filter'])
not fl and abort(400, ErrFormat.ci_filter_perm_attr_no_permission.format(self.fl))
self.fl = fl
self.fl = set(self.fl) & set(self.type2filter_perms[ci_type.id]['attr_filter'])
else:
self.fl = self.fl or {}
if not self.fl or isinstance(self.fl, dict):
@@ -437,14 +433,11 @@ class Search(object):
if not q.startswith("("):
raise SearchError(ErrFormat.ci_search_Parentheses_invalid)
if ":" not in q: # multi-line search
result.append(q[1:-1].split(';'))
else:
operator, q = self._operator_proc(q)
if q.endswith(")"):
result.append(dict(operator=operator, queries=[q[1:-1]]))
operator, q = self._operator_proc(q)
if q.endswith(")"):
result.append(dict(operator=operator, queries=[q[1:-1]]))
sub = dict(operator=operator, queries=[q[1:]])
sub = dict(operator=operator, queries=[q[1:]])
elif q.endswith(")") and sub:
sub['queries'].append(q[:-1])
result.append(copy.deepcopy(sub))
@@ -532,31 +525,22 @@ class Search(object):
query_sql = ""
for q in queries:
# current_app.logger.debug(q)
_query_sql = ""
if isinstance(q, dict):
if len(q['queries']) == 1 and ";" in q['queries'][0]:
values = q['queries'][0].split(";")
in_values = ",".join("'{0}'".format(v) for v in values)
_query_sql = QUERY_CI_BY_NO_ATTR_IN.format(in_values, alias)
operator = q['operator']
else:
alias, _query_sql, operator = self.__query_build_by_field(q['queries'], True, True, alias,
is_sub=True)
operator = q['operator']
alias, _query_sql, operator = self.__query_build_by_field(q['queries'], True, True, alias, is_sub=True)
# current_app.logger.info(_query_sql)
# current_app.logger.info((operator, is_first, alias))
operator = q['operator']
elif ":" in q and not q.startswith("*"):
alias, _query_sql, operator = self.__query_by_attr(q, queries, alias, is_sub)
elif q == "*":
continue
elif q:
if not isinstance(q, list):
q = q.replace("'", "\\'")
q = q.replace('"', '\\"')
q = q.replace("*", "%").replace('\\n', '%')
_query_sql = QUERY_CI_BY_NO_ATTR.format(q, alias)
else:
_query_sql = QUERY_CI_BY_NO_ATTR_IN.format(",".join("'{0}'".format(v) for v in q), alias)
q = q.replace("'", "\\'")
q = q.replace('"', '\\"')
q = q.replace("*", "%").replace('\\n', '%')
_query_sql = QUERY_CI_BY_NO_ATTR.format(q, alias)
if is_first and _query_sql and not self.only_type_query:
query_sql = "SELECT * FROM ({0}) AS {1}".format(_query_sql, alias)

View File

@@ -64,7 +64,7 @@ class Search(object):
self.ancestor_ids = ancestor_ids
self.descendant_ids = descendant_ids
self.root_parent_path = root_parent_path or []
self.root_parent_path = root_parent_path
self.has_m2m = has_m2m or False
if not self.has_m2m:
if self.ancestor_ids:

View File

@@ -55,6 +55,7 @@ def str2datetime(x):
return datetime.datetime.strptime(x, "%Y-%m-%d %H:%M")
class ValueTypeMap(object):
deserialize = {
ValueTypeEnum.INT: string2int,

View File

@@ -3,7 +3,7 @@
from __future__ import unicode_literals
import importlib.util
import imp
import copy
import jinja2
@@ -136,7 +136,7 @@ class AttributeValueManager(object):
if not re.compile(expr).match(str(value)):
return abort(400, ErrFormat.attribute_value_invalid2.format(alias, value))
def _validate(self, attr, value, value_table, ci=None, type_id=None, ci_id=None, type_attr=None, unique_name=None):
def _validate(self, attr, value, value_table, ci=None, type_id=None, ci_id=None, type_attr=None):
if not attr.is_reference:
ci = ci or {}
v = self._deserialize_value(attr.alias, attr.value_type, value)
@@ -146,7 +146,7 @@ class AttributeValueManager(object):
else:
v = value or None
(attr.is_unique or attr.name == unique_name) and self._check_is_unique(
attr.is_unique and self._check_is_unique(
value_table, attr, ci and ci.id or ci_id, ci and ci.type_id or type_id, v)
self._check_is_required(ci and ci.type_id or type_id, attr, v, type_attr=type_attr)
if attr.is_reference:
@@ -180,15 +180,14 @@ class AttributeValueManager(object):
@staticmethod
def _compute_attr_value_from_expr(expr, ci_dict):
t = jinja2.Template(expr).render(ci_dict)
try:
result = jinja2.Template(expr).render(ci_dict)
return result
return eval(t)
except Exception as e:
current_app.logger.warning(
f"Expression evaluation error - Expression: '{expr}'"
f"Input parameters: {ci_dict}, Error type: {type(e).__name__}, Error message: {str(e)}"
)
return None
current_app.logger.warning(str(e))
return t
@staticmethod
def _compute_attr_value_from_script(script, ci_dict):
script = jinja2.Template(script).render(ci_dict)
@@ -199,11 +198,11 @@ class AttributeValueManager(object):
try:
path = script_f.name
name = os.path.basename(path)[:-3]
dir_name, name = os.path.dirname(path), os.path.basename(path)[:-3]
spec = importlib.util.spec_from_file_location(name, path)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
fp, path, desc = imp.find_module(name, [dir_name])
mod = imp.load_module(name, fp, path, desc)
if hasattr(mod, 'computed'):
return mod.computed()
@@ -238,10 +237,7 @@ class AttributeValueManager(object):
if computed_value is not None:
ci_dict[attr['name']] = computed_value
def valid_attr_value(self, ci_dict, type_id, ci_id, name2attr,
alias2attr=None,
ci_attr2type_attr=None,
unique_name=None):
def valid_attr_value(self, ci_dict, type_id, ci_id, name2attr, alias2attr=None, ci_attr2type_attr=None):
key2attr = dict()
alias2attr = alias2attr or {}
ci_attr2type_attr = ci_attr2type_attr or {}
@@ -272,8 +268,7 @@ class AttributeValueManager(object):
else:
value = self._validate(attr, value, value_table, ci=None, type_id=type_id, ci_id=ci_id,
type_attr=ci_attr2type_attr.get(attr.id),
unique_name=unique_name)
type_attr=ci_attr2type_attr.get(attr.id))
ci_dict[key] = value
except BadRequest as e:
raise

View File

@@ -1,6 +1,6 @@
import functools
from flask import abort, session, current_app
from flask import abort, session
from api.lib.common_setting.acl import ACLManager
from api.lib.common_setting.resp_format import ErrFormat
from api.lib.perm.acl.acl import is_app_admin
@@ -15,7 +15,6 @@ def perms_role_required(app_name, resource_type_name, resource_name, perm, role_
try:
has_perms = acl.role_has_perms(session["acl"]['rid'], resource_name, resource_type_name, perm)
except Exception as e:
current_app.logger.error(f"acl role_has_perms err: {e}")
# resource_type not exist, continue check role
if role_name:
if role_name not in session.get("acl", {}).get("parentRoles", []) and not is_app_admin(app_name):

View File

@@ -476,7 +476,7 @@ class EditDepartmentInACL(object):
for employee in e_list:
employee_acl_rid = employee.get('e_acl_rid')
if employee_acl_rid == 0:
result.append("employee_acl_rid == 0")
result.append(f"employee_acl_rid == 0")
continue
cls.remove_single_employee_from_old_department(acl, employee, result)
@@ -501,8 +501,8 @@ class EditDepartmentInACL(object):
acl.remove_user_from_role(employee.get('e_acl_rid'), payload)
current_app.logger.info(f"remove {employee.get('e_acl_rid')} from {d_acl_rid}")
except Exception as e:
err = f"remove_user_from_role e_acl_rid: {employee.get('e_acl_rid')}, parent_id: {d_acl_rid}, err: {e}"
result.append(err)
result.append(
f"remove_user_from_role employee_acl_rid: {employee.get('e_acl_rid')}, parent_id: {d_acl_rid}, err: {e}")
return True
@@ -548,7 +548,7 @@ class EditDepartmentInACL(object):
for employee in e_list:
employee_acl_rid = employee.get('e_acl_rid')
if employee_acl_rid == 0:
result.append("employee_acl_rid == 0")
result.append(f"employee_acl_rid == 0")
continue
cls.remove_single_employee_from_old_department(acl, employee, result)

View File

@@ -4,7 +4,7 @@ import traceback
from datetime import datetime
import requests
from flask import abort, current_app
from flask import abort
from flask_login import current_user
from sqlalchemy import or_, literal_column, func, not_, and_
from werkzeug.datastructures import MultiDict
@@ -478,7 +478,7 @@ class EmployeeCRUD(object):
Employee.deleted == 0,
Employee.block == block,
]
if isinstance(department_id, list):
if type(department_id) == list:
if len(department_id) == 0:
return []
else:
@@ -702,7 +702,6 @@ class EmployeeCRUD(object):
try:
last_login = datetime.strptime(last_login, '%Y-%m-%d %H:%M:%S')
except Exception as e:
current_app.logger.error(f"strptime {last_login} err: {e}")
last_login = datetime.now()
else:
last_login = datetime.now()
@@ -713,7 +712,6 @@ class EmployeeCRUD(object):
)
return last_login
except Exception as e:
current_app.logger.error(f"update last_login err: {e}")
return

View File

@@ -2,7 +2,7 @@ import requests
from api.lib.common_setting.const import BotNameMap
from api.lib.common_setting.resp_format import ErrFormat
from api.models.common_setting import NoticeConfig
from api.models.common_setting import CompanyInfo, NoticeConfig
from wtforms import Form
from wtforms import StringField
from wtforms import validators

View File

@@ -48,15 +48,12 @@ class CMDBApp(BaseApp):
{"page": "Model_Relationships", "page_cn": "模型关系", "perms": ["read"]},
{"page": "Operation_Audit", "page_cn": "操作审计", "perms": ["read"]},
{"page": "Relationship_Types", "page_cn": "关系类型", "perms": ["read"]},
{"page": "Auto_Discovery", "page_cn": "自动发现",
"perms": ["read", "create_plugin", "update_plugin", "delete_plugin"]
},
{"page": "Auto_Discovery", "page_cn": "自动发现", "perms": ["read", "create_plugin", "update_plugin", "delete_plugin"]},
{"page": "TopologyView", "page_cn": "拓扑视图",
"perms": ["read", "create_topology_group", "update_topology_group", "delete_topology_group",
"create_topology_view"],
},
{"page": "IPAM", "page_cn": "IPAM", "perms": ["read"]},
{"page": "DCIM", "page_cn": "数据中心", "perms": ["read"]},
]
def __init__(self):

View File

@@ -6,7 +6,7 @@ from functools import wraps
from flask import abort
from flask import request
from api.lib.perm.acl.cache import AppCache
from api.lib.perm.acl.cache import AppCache, AppAccessTokenCache
from api.lib.perm.acl.resp_format import ErrFormat

View File

@@ -138,14 +138,14 @@ class HasResourceRoleCache(object):
@classmethod
def add(cls, rid, app_id):
with redis_lock.Lock(rd.r, 'HasResourceRoleCache', expire=10):
with redis_lock.Lock(rd.r, 'HasResourceRoleCache'):
c = cls.get(app_id)
c[rid] = 1
cache.set(cls.PREFIX_KEY.format(app_id), c, timeout=0)
@classmethod
def remove(cls, rid, app_id):
with redis_lock.Lock(rd.r, 'HasResourceRoleCache', expire=10):
with redis_lock.Lock(rd.r, 'HasResourceRoleCache'):
c = cls.get(app_id)
c.pop(rid, None)
cache.set(cls.PREFIX_KEY.format(app_id), c, timeout=0)

View File

@@ -1,5 +1,8 @@
# -*- coding:utf-8 -*-
import time
import redis_lock
import six
from flask import abort
@@ -142,7 +145,7 @@ class RoleRelationCRUD(object):
@classmethod
def add(cls, role, parent_id, child_ids, app_id):
with redis_lock.Lock(rd.r, "ROLE_RELATION_ADD", expire=10):
with redis_lock.Lock(rd.r, "ROLE_RELATION_ADD"):
db.session.commit()
result = []

View File

@@ -1,6 +1,7 @@
# -*- coding:utf-8 -*-
import base64
from typing import Set
import elasticsearch
import redis

View File

@@ -105,8 +105,8 @@ class User(CRUDModel, SoftDeleteMixin):
_password = db.Column("password", db.String(80))
key = db.Column(db.String(32), nullable=False)
secret = db.Column(db.String(32), nullable=False)
date_joined = db.Column(db.DateTime, default=datetime.now)
last_login = db.Column(db.DateTime, default=datetime.now)
date_joined = db.Column(db.DateTime, default=datetime.utcnow)
last_login = db.Column(db.DateTime, default=datetime.utcnow)
block = db.Column(db.Boolean, default=False)
has_logined = db.Column(db.Boolean, default=False)
wx_id = db.Column(db.String(32))

View File

@@ -676,7 +676,6 @@ class IPAMSubnetScan(Model):
ci_id = db.Column(db.Integer, index=True, nullable=False)
scan_enabled = db.Column(db.Boolean, default=True)
rule_updated_at = db.Column(db.DateTime)
last_scan_time = db.Column(db.DateTime)
# scan rules
@@ -707,14 +706,3 @@ 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()))

View File

@@ -32,7 +32,7 @@ from api.models.acl import Trigger
def role_rebuild(rids, app_id):
rids = rids if isinstance(rids, list) else [rids]
for rid in rids:
with redis_lock.Lock(rd.r, "ROLE_REBUILD_{}_{}".format(rid, app_id), expire=10):
with redis_lock.Lock(rd.r, "ROLE_REBUILD_{}_{}".format(rid, app_id)):
RoleRelationCache.rebuild(rid, app_id)
current_app.logger.info("Role {0} App {1} rebuild..........".format(rids, app_id))

View File

@@ -1,4 +1,4 @@
# -*- coding:utf-8 -*-
# -*- coding:utf-8 -*-
import datetime
@@ -145,7 +145,7 @@ def ci_delete_trigger(trigger, operate_type, ci_dict):
@flush_db
@reconnect_db
def ci_relation_cache(parent_id, child_id, ancestor_ids):
with redis_lock.Lock(rd.r, "CIRelation_{}".format(parent_id), expire=10):
with redis_lock.Lock(rd.r, "CIRelation_{}".format(parent_id)):
children = rd.get([parent_id], REDIS_PREFIX_CI_RELATION)[0]
children = json.loads(children) if children is not None else {}
@@ -223,7 +223,7 @@ def ci_relation_add(parent_dict, child_id, uid):
@celery.task(name="cmdb.ci_relation_delete", queue=CMDB_QUEUE)
@reconnect_db
def ci_relation_delete(parent_id, child_id, ancestor_ids):
with redis_lock.Lock(rd.r, "CIRelation_{}".format(parent_id), expire=10):
with redis_lock.Lock(rd.r, "CIRelation_{}".format(parent_id)):
children = rd.get([parent_id], REDIS_PREFIX_CI_RELATION)[0]
children = json.loads(children) if children is not None else {}
@@ -374,48 +374,3 @@ def build_relations_for_ad_accept(adc, ci_id, ad_key2attr):
source=RelationSourceEnum.AUTO_DISCOVERY)
except:
pass
@celery.task(name="cmdb.add_net_device_ports", queue=CMDB_QUEUE)
@reconnect_db
def add_net_device_ports(ci_id, ports):
from api.lib.cmdb.ci import CIRelationManager
from api.lib.cmdb.ci import CIManager
from api.lib.cmdb.cache import CITypeCache
port_type = CITypeCache.get("net_port")
if not port_type:
current_app.logger.warning("CIType net port is not found")
return
for port in ports:
try:
port_id = CIManager.add(port_type.id, is_auto_discovery=True, _is_admin=True, **port)
CIRelationManager.add(ci_id, port_id,
valid=False,
source=RelationSourceEnum.AUTO_DISCOVERY)
except Exception as e:
current_app.logger.warning("add_net_device_ports failed: {}".format(e))
@celery.task(name="cmdb.dcim_calc_u_free_count", queue=CMDB_QUEUE)
@reconnect_db
def dcim_calc_u_free_count():
from api.lib.cmdb.ci import CIManager
from api.lib.cmdb.dcim.rack import RackManager
from api.lib.cmdb.dcim.const import RackBuiltinAttributes
if not has_request_context():
current_app.test_request_context().push()
login_user(UserCache.get('worker'))
try:
rack_m = RackManager()
except Exception:
return
racks = CI.get_by(type_id=rack_m.type_id, to_dict=False)
for rack in racks:
payload = {RackBuiltinAttributes.FREE_U_COUNT: rack_m.calc_u_free_count(rack.id)}
CIManager().update(rack.id, **payload)

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-11-26 18:54+0800\n"
"POT-Creation-Date: 2024-11-11 17:40+0800\n"
"PO-Revision-Date: 2023-12-25 20:21+0800\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: zh\n"
@@ -92,11 +92,11 @@ msgstr "您没有操作权限!"
msgid "Only the creator or administrator has permission!"
msgstr "只有创建人或者管理员才有权限!"
#: api/lib/cmdb/const.py:133
#: api/lib/cmdb/const.py:128
msgid "Update Time"
msgstr "更新时间"
#: api/lib/cmdb/const.py:134
#: api/lib/cmdb/const.py:129
msgid "Updated By"
msgstr "更新人"
@@ -544,18 +544,6 @@ 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 "公司信息已存在,无法创建!"

View File

@@ -30,7 +30,6 @@ from api.lib.cmdb.search import SearchError
from api.lib.cmdb.search.ci import search as ci_search
from api.lib.decorator import args_required
from api.lib.decorator import args_validate
from api.lib.exception import AbortException
from api.lib.perm.acl.acl import has_perm_from_args
from api.lib.utils import AESCrypto
from api.lib.utils import get_page
@@ -297,10 +296,7 @@ class AutoDiscoveryRuleSyncView(APIView):
rules, last_update_at1 = AutoDiscoveryCITypeCRUD.get(None, oneagent_id, oneagent_name, last_update_at)
try:
subnet_scan_rules, last_update_at2 = SubnetManager().scan_rules(oneagent_id, last_update_at)
except AbortException:
subnet_scan_rules, last_update_at2 = [], ""
subnet_scan_rules, last_update_at2 = SubnetManager().scan_rules(oneagent_id, last_update_at)
return self.jsonify(rules=rules,
subnet_scan_rules=subnet_scan_rules,

View File

@@ -2,14 +2,14 @@
import json
from io import BytesIO
from flask import abort
from flask import current_app
from flask import request
from io import BytesIO
from api.lib.cmdb.cache import AttributeCache
from api.lib.cmdb.cache import CITypeCache
from api.lib.cmdb.ci import CITriggerManager
from api.lib.cmdb.ci_type import CITypeAttributeGroupManager
from api.lib.cmdb.ci_type import CITypeAttributeManager
from api.lib.cmdb.ci_type import CITypeGroupManager
@@ -64,13 +64,9 @@ class CITypeView(APIView):
ci_type['unique_name'] = ci_type['unique_id'] and AttributeCache.get(ci_type['unique_id']).name
ci_types.append(ci_type)
elif type_name is not None:
ci_type = CITypeCache.get(type_name)
if ci_type is not None:
ci_type = ci_type.to_dict()
ci_type['parent_ids'] = CITypeInheritanceManager.get_parents(ci_type['id'])
ci_types = [ci_type]
else:
ci_types = []
ci_type = CITypeCache.get(type_name).to_dict()
ci_type['parent_ids'] = CITypeInheritanceManager.get_parents(ci_type['id'])
ci_types = [ci_type]
else:
ci_types = CITypeManager().get_ci_types(q)
count = len(ci_types)
@@ -501,16 +497,6 @@ class CITypeTriggerView(APIView):
return self.jsonify(code=200)
class CITypeTriggerTestView(APIView):
url_prefix = ("/ci_types/<int:type_id>/triggers/<int:_id>/test_notify",)
@has_perm_from_args("type_id", ResourceTypeEnum.CI, PermEnum.CONFIG, CITypeManager.get_name_by_id)
def post(self, type_id, _id):
CITriggerManager().trigger_notify_test(type_id, _id)
return self.jsonify(code=200)
class CITypeGrantView(APIView):
url_prefix = "/ci_types/<int:type_id>/roles/<int:rid>/grant"

View File

@@ -1 +0,0 @@
# -*- coding:utf-8 -*-

View File

@@ -1,30 +0,0 @@
# -*- 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)

View File

@@ -1,35 +0,0 @@
# -*- 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/<int:_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 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)

View File

@@ -1,102 +0,0 @@
# -*- coding:utf-8 -*-
from flask import request
from api.lib.cmdb.const import CMDB_QUEUE
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
from api.tasks.cmdb import dcim_calc_u_free_count
app_cli = CMDBApp()
class RackView(APIView):
url_prefix = ("/dcim/rack", "/dcim/rack/<int:_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("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/<int:rack_id>/device/<int: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(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/<int:rack_id>/device/<int:device_id>/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)
class RackCalcUFreeCountView(APIView):
url_prefix = ("/dcim/rack/calc_u_free_count",)
@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):
dcim_calc_u_free_count.apply_async(queue=CMDB_QUEUE)
return self.jsonify(code=200)

View File

@@ -1,33 +0,0 @@
# -*- 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/<int:_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 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)

View File

@@ -1,43 +0,0 @@
# -*- 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/<int:_id>", "/dcim/server_room/<int:_id>/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)

View File

@@ -1,19 +0,0 @@
# -*- 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)

View File

@@ -131,7 +131,7 @@ class EmployeeChangePasswordWithACLID(APIView):
if not password:
abort(400, ErrFormat.password_is_required)
EmployeeCRUD.change_password_by_uid(_uid, password)
data = EmployeeCRUD.change_password_by_uid(_uid, password)
return self.jsonify(200)

View File

@@ -6,7 +6,7 @@ import magic
from api.lib.common_setting.const import MIMEExtMap
from api.lib.common_setting.resp_format import ErrFormat
from api.lib.common_setting.upload_file import generate_new_file_name, CommonFileCRUD
from api.lib.common_setting.upload_file import allowed_file, generate_new_file_name, CommonFileCRUD
from api.resource import APIView
prefix = '/file'

View File

@@ -1,37 +0,0 @@
import os
from api.resource import APIView
from api.lib.perm.auth import auth_abandoned
prefix = "/system"
class SystemLanguageView(APIView):
url_prefix = (f"{prefix}/language",)
method_decorators = []
@auth_abandoned
def get(self):
"""Get system default language
Read from environment variable SYSTEM_DEFAULT_LANGUAGE, default to Chinese if not set
"""
default_language = os.environ.get("SYSTEM_DEFAULT_LANGUAGE", "")
return self.jsonify(
{
"language": default_language,
"language_name": self._get_language_name(default_language),
}
)
def _get_language_name(self, language_code):
"""Return language name based on language code"""
language_mapping = {
"zh-CN": "中文(简体)",
"zh-TW": "中文(繁体)",
"en-US": "English",
"ja-JP": "日本語",
"ko-KR": "한국어",
}
return language_mapping.get(language_code, "")

View File

@@ -58,4 +58,3 @@ python-magic==0.4.27
jsonpath==0.82.2
networkx>=3.1
ipaddress>=1.0.23
ruff==0.8.3

View File

@@ -54,258 +54,6 @@
<div class="content unicode" style="display: block;">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont">&#xea23;</span>
<div class="name">onterm-symbolic_link</div>
<div class="code-name">&amp;#xea23;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea20;</span>
<div class="name">oneterm-batch_execution</div>
<div class="code-name">&amp;#xea20;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea21;</span>
<div class="name">oneterm-file_log-selected</div>
<div class="code-name">&amp;#xea21;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea22;</span>
<div class="name">oneterm-file_log</div>
<div class="code-name">&amp;#xea22;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea1f;</span>
<div class="name">file</div>
<div class="code-name">&amp;#xea1f;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea1e;</span>
<div class="name">folder</div>
<div class="code-name">&amp;#xea1e;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea1b;</span>
<div class="name">mongoDB (1)</div>
<div class="code-name">&amp;#xea1b;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea1c;</span>
<div class="name">postgreSQL (1)</div>
<div class="code-name">&amp;#xea1c;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea1d;</span>
<div class="name">telnet (1)</div>
<div class="code-name">&amp;#xea1d;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea17;</span>
<div class="name">command_interception (1)</div>
<div class="code-name">&amp;#xea17;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea18;</span>
<div class="name">quick_commands</div>
<div class="code-name">&amp;#xea18;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea19;</span>
<div class="name">terminal_settings</div>
<div class="code-name">&amp;#xea19;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea1a;</span>
<div class="name">basic_settings</div>
<div class="code-name">&amp;#xea1a;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea16;</span>
<div class="name">asset_management</div>
<div class="code-name">&amp;#xea16;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea15;</span>
<div class="name">ai-seek</div>
<div class="code-name">&amp;#xea15;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea13;</span>
<div class="name">ai-hate1</div>
<div class="code-name">&amp;#xea13;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea14;</span>
<div class="name">ai-like1</div>
<div class="code-name">&amp;#xea14;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea11;</span>
<div class="name">ai-like2</div>
<div class="code-name">&amp;#xea11;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea12;</span>
<div class="name">ai-hate2</div>
<div class="code-name">&amp;#xea12;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea10;</span>
<div class="name">ai-top_up</div>
<div class="code-name">&amp;#xea10;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea0f;</span>
<div class="name">ai-top_down</div>
<div class="code-name">&amp;#xea0f;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea0d;</span>
<div class="name">autoflow-script</div>
<div class="code-name">&amp;#xea0d;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea0e;</span>
<div class="name">autoflow-dag</div>
<div class="code-name">&amp;#xea0e;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea0c;</span>
<div class="name">itsm-default_line</div>
<div class="code-name">&amp;#xea0c;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea0b;</span>
<div class="name">veops-servicetree</div>
<div class="code-name">&amp;#xea0b;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea0a;</span>
<div class="name">veops-switch (1)</div>
<div class="code-name">&amp;#xea0a;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea09;</span>
<div class="name">veops-label</div>
<div class="code-name">&amp;#xea09;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea08;</span>
<div class="name">top_acl</div>
<div class="code-name">&amp;#xea08;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea06;</span>
<div class="name">top_ticket</div>
<div class="code-name">&amp;#xea06;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea07;</span>
<div class="name">top_agent</div>
<div class="code-name">&amp;#xea07;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea05;</span>
<div class="name">itsm-table_download</div>
<div class="code-name">&amp;#xea05;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea04;</span>
<div class="name">itsm-image_download</div>
<div class="code-name">&amp;#xea04;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea02;</span>
<div class="name">veops-rear</div>
<div class="code-name">&amp;#xea02;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea03;</span>
<div class="name">veops-front</div>
<div class="code-name">&amp;#xea03;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea01;</span>
<div class="name">veops-xianggang</div>
<div class="code-name">&amp;#xea01;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea00;</span>
<div class="name">veops-device (2)</div>
<div class="code-name">&amp;#xea00;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe9ff;</span>
<div class="name">veops-room (1)</div>
<div class="code-name">&amp;#xe9ff;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe9fe;</span>
<div class="name">veops-IDC</div>
<div class="code-name">&amp;#xe9fe;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe9fd;</span>
<div class="name">veops-region</div>
<div class="code-name">&amp;#xe9fd;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe9fb;</span>
<div class="name">veops-device</div>
<div class="code-name">&amp;#xe9fb;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe9fc;</span>
<div class="name">veops-cabinet</div>
<div class="code-name">&amp;#xe9fc;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe9f9;</span>
<div class="name">veops-data_center</div>
<div class="code-name">&amp;#xe9f9;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe9fa;</span>
<div class="name">ops-setting-holiday_management-copy</div>
@@ -6354,9 +6102,9 @@
<pre><code class="language-css"
>@font-face {
font-family: 'iconfont';
src: url('iconfont.woff2?t=1749393321370') format('woff2'),
url('iconfont.woff?t=1749393321370') format('woff'),
url('iconfont.ttf?t=1749393321370') format('truetype');
src: url('iconfont.woff2?t=1731312848138') format('woff2'),
url('iconfont.woff?t=1731312848138') format('woff'),
url('iconfont.ttf?t=1731312848138') format('truetype');
}
</code></pre>
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@@ -6382,384 +6130,6 @@
<div class="content font-class">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont onterm-symbolic_link"></span>
<div class="name">
onterm-symbolic_link
</div>
<div class="code-name">.onterm-symbolic_link
</div>
</li>
<li class="dib">
<span class="icon iconfont oneterm-batch_execution"></span>
<div class="name">
oneterm-batch_execution
</div>
<div class="code-name">.oneterm-batch_execution
</div>
</li>
<li class="dib">
<span class="icon iconfont ops-oneterm-file_log-selected"></span>
<div class="name">
oneterm-file_log-selected
</div>
<div class="code-name">.ops-oneterm-file_log-selected
</div>
</li>
<li class="dib">
<span class="icon iconfont ops-oneterm-file_log"></span>
<div class="name">
oneterm-file_log
</div>
<div class="code-name">.ops-oneterm-file_log
</div>
</li>
<li class="dib">
<span class="icon iconfont file"></span>
<div class="name">
file
</div>
<div class="code-name">.file
</div>
</li>
<li class="dib">
<span class="icon iconfont folder1"></span>
<div class="name">
folder
</div>
<div class="code-name">.folder1
</div>
</li>
<li class="dib">
<span class="icon iconfont a-mongoDB1"></span>
<div class="name">
mongoDB (1)
</div>
<div class="code-name">.a-mongoDB1
</div>
</li>
<li class="dib">
<span class="icon iconfont a-postgreSQL1"></span>
<div class="name">
postgreSQL (1)
</div>
<div class="code-name">.a-postgreSQL1
</div>
</li>
<li class="dib">
<span class="icon iconfont a-telnet1"></span>
<div class="name">
telnet (1)
</div>
<div class="code-name">.a-telnet1
</div>
</li>
<li class="dib">
<span class="icon iconfont a-command_interception1"></span>
<div class="name">
command_interception (1)
</div>
<div class="code-name">.a-command_interception1
</div>
</li>
<li class="dib">
<span class="icon iconfont quick_commands"></span>
<div class="name">
quick_commands
</div>
<div class="code-name">.quick_commands
</div>
</li>
<li class="dib">
<span class="icon iconfont terminal_settings"></span>
<div class="name">
terminal_settings
</div>
<div class="code-name">.terminal_settings
</div>
</li>
<li class="dib">
<span class="icon iconfont basic_settings"></span>
<div class="name">
basic_settings
</div>
<div class="code-name">.basic_settings
</div>
</li>
<li class="dib">
<span class="icon iconfont ops-oneterm-asset-management"></span>
<div class="name">
asset_management
</div>
<div class="code-name">.ops-oneterm-asset-management
</div>
</li>
<li class="dib">
<span class="icon iconfont ai-seek"></span>
<div class="name">
ai-seek
</div>
<div class="code-name">.ai-seek
</div>
</li>
<li class="dib">
<span class="icon iconfont ai-hate1"></span>
<div class="name">
ai-hate1
</div>
<div class="code-name">.ai-hate1
</div>
</li>
<li class="dib">
<span class="icon iconfont ai-like1"></span>
<div class="name">
ai-like1
</div>
<div class="code-name">.ai-like1
</div>
</li>
<li class="dib">
<span class="icon iconfont ai-like2"></span>
<div class="name">
ai-like2
</div>
<div class="code-name">.ai-like2
</div>
</li>
<li class="dib">
<span class="icon iconfont ai-hate2"></span>
<div class="name">
ai-hate2
</div>
<div class="code-name">.ai-hate2
</div>
</li>
<li class="dib">
<span class="icon iconfont ai-top_up"></span>
<div class="name">
ai-top_up
</div>
<div class="code-name">.ai-top_up
</div>
</li>
<li class="dib">
<span class="icon iconfont ai-top_down"></span>
<div class="name">
ai-top_down
</div>
<div class="code-name">.ai-top_down
</div>
</li>
<li class="dib">
<span class="icon iconfont autoflow-script"></span>
<div class="name">
autoflow-script
</div>
<div class="code-name">.autoflow-script
</div>
</li>
<li class="dib">
<span class="icon iconfont autoflow-dag"></span>
<div class="name">
autoflow-dag
</div>
<div class="code-name">.autoflow-dag
</div>
</li>
<li class="dib">
<span class="icon iconfont itsm-default_line"></span>
<div class="name">
itsm-default_line
</div>
<div class="code-name">.itsm-default_line
</div>
</li>
<li class="dib">
<span class="icon iconfont veops-servicetree"></span>
<div class="name">
veops-servicetree
</div>
<div class="code-name">.veops-servicetree
</div>
</li>
<li class="dib">
<span class="icon iconfont veops-switch1"></span>
<div class="name">
veops-switch (1)
</div>
<div class="code-name">.veops-switch1
</div>
</li>
<li class="dib">
<span class="icon iconfont veops-label"></span>
<div class="name">
veops-label
</div>
<div class="code-name">.veops-label
</div>
</li>
<li class="dib">
<span class="icon iconfont top_acl"></span>
<div class="name">
top_acl
</div>
<div class="code-name">.top_acl
</div>
</li>
<li class="dib">
<span class="icon iconfont top_ticket"></span>
<div class="name">
top_ticket
</div>
<div class="code-name">.top_ticket
</div>
</li>
<li class="dib">
<span class="icon iconfont top_agent"></span>
<div class="name">
top_agent
</div>
<div class="code-name">.top_agent
</div>
</li>
<li class="dib">
<span class="icon iconfont itsm-table_download"></span>
<div class="name">
itsm-table_download
</div>
<div class="code-name">.itsm-table_download
</div>
</li>
<li class="dib">
<span class="icon iconfont itsm-image_download"></span>
<div class="name">
itsm-image_download
</div>
<div class="code-name">.itsm-image_download
</div>
</li>
<li class="dib">
<span class="icon iconfont veops-rear"></span>
<div class="name">
veops-rear
</div>
<div class="code-name">.veops-rear
</div>
</li>
<li class="dib">
<span class="icon iconfont veops-front"></span>
<div class="name">
veops-front
</div>
<div class="code-name">.veops-front
</div>
</li>
<li class="dib">
<span class="icon iconfont veops-xianggang"></span>
<div class="name">
veops-xianggang
</div>
<div class="code-name">.veops-xianggang
</div>
</li>
<li class="dib">
<span class="icon iconfont a-veops-device2"></span>
<div class="name">
veops-device (2)
</div>
<div class="code-name">.a-veops-device2
</div>
</li>
<li class="dib">
<span class="icon iconfont a-veops-room1"></span>
<div class="name">
veops-room (1)
</div>
<div class="code-name">.a-veops-room1
</div>
</li>
<li class="dib">
<span class="icon iconfont veops-IDC"></span>
<div class="name">
veops-IDC
</div>
<div class="code-name">.veops-IDC
</div>
</li>
<li class="dib">
<span class="icon iconfont veops-region"></span>
<div class="name">
veops-region
</div>
<div class="code-name">.veops-region
</div>
</li>
<li class="dib">
<span class="icon iconfont veops-device"></span>
<div class="name">
veops-device
</div>
<div class="code-name">.veops-device
</div>
</li>
<li class="dib">
<span class="icon iconfont veops-cabinet"></span>
<div class="name">
veops-cabinet
</div>
<div class="code-name">.veops-cabinet
</div>
</li>
<li class="dib">
<span class="icon iconfont veops-data_center"></span>
<div class="name">
veops-data_center
</div>
<div class="code-name">.veops-data_center
</div>
</li>
<li class="dib">
<span class="icon iconfont ops-setting-holidays"></span>
<div class="name">
@@ -15832,342 +15202,6 @@
<div class="content symbol">
<ul class="icon_lists dib-box">
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#onterm-symbolic_link"></use>
</svg>
<div class="name">onterm-symbolic_link</div>
<div class="code-name">#onterm-symbolic_link</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#oneterm-batch_execution"></use>
</svg>
<div class="name">oneterm-batch_execution</div>
<div class="code-name">#oneterm-batch_execution</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#ops-oneterm-file_log-selected"></use>
</svg>
<div class="name">oneterm-file_log-selected</div>
<div class="code-name">#ops-oneterm-file_log-selected</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#ops-oneterm-file_log"></use>
</svg>
<div class="name">oneterm-file_log</div>
<div class="code-name">#ops-oneterm-file_log</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#file"></use>
</svg>
<div class="name">file</div>
<div class="code-name">#file</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#folder1"></use>
</svg>
<div class="name">folder</div>
<div class="code-name">#folder1</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#a-mongoDB1"></use>
</svg>
<div class="name">mongoDB (1)</div>
<div class="code-name">#a-mongoDB1</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#a-postgreSQL1"></use>
</svg>
<div class="name">postgreSQL (1)</div>
<div class="code-name">#a-postgreSQL1</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#a-telnet1"></use>
</svg>
<div class="name">telnet (1)</div>
<div class="code-name">#a-telnet1</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#a-command_interception1"></use>
</svg>
<div class="name">command_interception (1)</div>
<div class="code-name">#a-command_interception1</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#quick_commands"></use>
</svg>
<div class="name">quick_commands</div>
<div class="code-name">#quick_commands</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#terminal_settings"></use>
</svg>
<div class="name">terminal_settings</div>
<div class="code-name">#terminal_settings</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#basic_settings"></use>
</svg>
<div class="name">basic_settings</div>
<div class="code-name">#basic_settings</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#ops-oneterm-asset-management"></use>
</svg>
<div class="name">asset_management</div>
<div class="code-name">#ops-oneterm-asset-management</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#ai-seek"></use>
</svg>
<div class="name">ai-seek</div>
<div class="code-name">#ai-seek</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#ai-hate1"></use>
</svg>
<div class="name">ai-hate1</div>
<div class="code-name">#ai-hate1</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#ai-like1"></use>
</svg>
<div class="name">ai-like1</div>
<div class="code-name">#ai-like1</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#ai-like2"></use>
</svg>
<div class="name">ai-like2</div>
<div class="code-name">#ai-like2</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#ai-hate2"></use>
</svg>
<div class="name">ai-hate2</div>
<div class="code-name">#ai-hate2</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#ai-top_up"></use>
</svg>
<div class="name">ai-top_up</div>
<div class="code-name">#ai-top_up</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#ai-top_down"></use>
</svg>
<div class="name">ai-top_down</div>
<div class="code-name">#ai-top_down</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#autoflow-script"></use>
</svg>
<div class="name">autoflow-script</div>
<div class="code-name">#autoflow-script</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#autoflow-dag"></use>
</svg>
<div class="name">autoflow-dag</div>
<div class="code-name">#autoflow-dag</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#itsm-default_line"></use>
</svg>
<div class="name">itsm-default_line</div>
<div class="code-name">#itsm-default_line</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#veops-servicetree"></use>
</svg>
<div class="name">veops-servicetree</div>
<div class="code-name">#veops-servicetree</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#veops-switch1"></use>
</svg>
<div class="name">veops-switch (1)</div>
<div class="code-name">#veops-switch1</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#veops-label"></use>
</svg>
<div class="name">veops-label</div>
<div class="code-name">#veops-label</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#top_acl"></use>
</svg>
<div class="name">top_acl</div>
<div class="code-name">#top_acl</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#top_ticket"></use>
</svg>
<div class="name">top_ticket</div>
<div class="code-name">#top_ticket</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#top_agent"></use>
</svg>
<div class="name">top_agent</div>
<div class="code-name">#top_agent</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#itsm-table_download"></use>
</svg>
<div class="name">itsm-table_download</div>
<div class="code-name">#itsm-table_download</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#itsm-image_download"></use>
</svg>
<div class="name">itsm-image_download</div>
<div class="code-name">#itsm-image_download</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#veops-rear"></use>
</svg>
<div class="name">veops-rear</div>
<div class="code-name">#veops-rear</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#veops-front"></use>
</svg>
<div class="name">veops-front</div>
<div class="code-name">#veops-front</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#veops-xianggang"></use>
</svg>
<div class="name">veops-xianggang</div>
<div class="code-name">#veops-xianggang</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#a-veops-device2"></use>
</svg>
<div class="name">veops-device (2)</div>
<div class="code-name">#a-veops-device2</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#a-veops-room1"></use>
</svg>
<div class="name">veops-room (1)</div>
<div class="code-name">#a-veops-room1</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#veops-IDC"></use>
</svg>
<div class="name">veops-IDC</div>
<div class="code-name">#veops-IDC</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#veops-region"></use>
</svg>
<div class="name">veops-region</div>
<div class="code-name">#veops-region</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#veops-device"></use>
</svg>
<div class="name">veops-device</div>
<div class="code-name">#veops-device</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#veops-cabinet"></use>
</svg>
<div class="name">veops-cabinet</div>
<div class="code-name">#veops-cabinet</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#veops-data_center"></use>
</svg>
<div class="name">veops-data_center</div>
<div class="code-name">#veops-data_center</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#ops-setting-holidays"></use>

View File

@@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 3857903 */
src: url('iconfont.woff2?t=1749393321370') format('woff2'),
url('iconfont.woff?t=1749393321370') format('woff'),
url('iconfont.ttf?t=1749393321370') format('truetype');
src: url('iconfont.woff2?t=1731312848138') format('woff2'),
url('iconfont.woff?t=1731312848138') format('woff'),
url('iconfont.ttf?t=1731312848138') format('truetype');
}
.iconfont {
@@ -13,174 +13,6 @@
-moz-osx-font-smoothing: grayscale;
}
.onterm-symbolic_link:before {
content: "\ea23";
}
.oneterm-batch_execution:before {
content: "\ea20";
}
.ops-oneterm-file_log-selected:before {
content: "\ea21";
}
.ops-oneterm-file_log:before {
content: "\ea22";
}
.file:before {
content: "\ea1f";
}
.folder1:before {
content: "\ea1e";
}
.a-mongoDB1:before {
content: "\ea1b";
}
.a-postgreSQL1:before {
content: "\ea1c";
}
.a-telnet1:before {
content: "\ea1d";
}
.a-command_interception1:before {
content: "\ea17";
}
.quick_commands:before {
content: "\ea18";
}
.terminal_settings:before {
content: "\ea19";
}
.basic_settings:before {
content: "\ea1a";
}
.ops-oneterm-asset-management:before {
content: "\ea16";
}
.ai-seek:before {
content: "\ea15";
}
.ai-hate1:before {
content: "\ea13";
}
.ai-like1:before {
content: "\ea14";
}
.ai-like2:before {
content: "\ea11";
}
.ai-hate2:before {
content: "\ea12";
}
.ai-top_up:before {
content: "\ea10";
}
.ai-top_down:before {
content: "\ea0f";
}
.autoflow-script:before {
content: "\ea0d";
}
.autoflow-dag:before {
content: "\ea0e";
}
.itsm-default_line:before {
content: "\ea0c";
}
.veops-servicetree:before {
content: "\ea0b";
}
.veops-switch1:before {
content: "\ea0a";
}
.veops-label:before {
content: "\ea09";
}
.top_acl:before {
content: "\ea08";
}
.top_ticket:before {
content: "\ea06";
}
.top_agent:before {
content: "\ea07";
}
.itsm-table_download:before {
content: "\ea05";
}
.itsm-image_download:before {
content: "\ea04";
}
.veops-rear:before {
content: "\ea02";
}
.veops-front:before {
content: "\ea03";
}
.veops-xianggang:before {
content: "\ea01";
}
.a-veops-device2:before {
content: "\ea00";
}
.a-veops-room1:before {
content: "\e9ff";
}
.veops-IDC:before {
content: "\e9fe";
}
.veops-region:before {
content: "\e9fd";
}
.veops-device:before {
content: "\e9fb";
}
.veops-cabinet:before {
content: "\e9fc";
}
.veops-data_center:before {
content: "\e9f9";
}
.ops-setting-holidays:before {
content: "\e9fa";
}

File diff suppressed because one or more lines are too long

View File

@@ -5,300 +5,6 @@
"css_prefix_text": "",
"description": "",
"glyphs": [
{
"icon_id": "44501032",
"name": "onterm-symbolic_link",
"font_class": "onterm-symbolic_link",
"unicode": "ea23",
"unicode_decimal": 59939
},
{
"icon_id": "44497221",
"name": "oneterm-batch_execution",
"font_class": "oneterm-batch_execution",
"unicode": "ea20",
"unicode_decimal": 59936
},
{
"icon_id": "44497220",
"name": "oneterm-file_log-selected",
"font_class": "ops-oneterm-file_log-selected",
"unicode": "ea21",
"unicode_decimal": 59937
},
{
"icon_id": "44497219",
"name": "oneterm-file_log",
"font_class": "ops-oneterm-file_log",
"unicode": "ea22",
"unicode_decimal": 59938
},
{
"icon_id": "44455092",
"name": "file",
"font_class": "file",
"unicode": "ea1f",
"unicode_decimal": 59935
},
{
"icon_id": "44455100",
"name": "folder",
"font_class": "folder1",
"unicode": "ea1e",
"unicode_decimal": 59934
},
{
"icon_id": "44315758",
"name": "mongoDB (1)",
"font_class": "a-mongoDB1",
"unicode": "ea1b",
"unicode_decimal": 59931
},
{
"icon_id": "44315757",
"name": "postgreSQL (1)",
"font_class": "a-postgreSQL1",
"unicode": "ea1c",
"unicode_decimal": 59932
},
{
"icon_id": "44315755",
"name": "telnet (1)",
"font_class": "a-telnet1",
"unicode": "ea1d",
"unicode_decimal": 59933
},
{
"icon_id": "44276353",
"name": "command_interception (1)",
"font_class": "a-command_interception1",
"unicode": "ea17",
"unicode_decimal": 59927
},
{
"icon_id": "44276352",
"name": "quick_commands",
"font_class": "quick_commands",
"unicode": "ea18",
"unicode_decimal": 59928
},
{
"icon_id": "44276351",
"name": "terminal_settings",
"font_class": "terminal_settings",
"unicode": "ea19",
"unicode_decimal": 59929
},
{
"icon_id": "44276350",
"name": "basic_settings",
"font_class": "basic_settings",
"unicode": "ea1a",
"unicode_decimal": 59930
},
{
"icon_id": "44276278",
"name": "asset_management",
"font_class": "ops-oneterm-asset-management",
"unicode": "ea16",
"unicode_decimal": 59926
},
{
"icon_id": "43267802",
"name": "ai-seek",
"font_class": "ai-seek",
"unicode": "ea15",
"unicode_decimal": 59925
},
{
"icon_id": "43213714",
"name": "ai-hate1",
"font_class": "ai-hate1",
"unicode": "ea13",
"unicode_decimal": 59923
},
{
"icon_id": "43213712",
"name": "ai-like1",
"font_class": "ai-like1",
"unicode": "ea14",
"unicode_decimal": 59924
},
{
"icon_id": "43213717",
"name": "ai-like2",
"font_class": "ai-like2",
"unicode": "ea11",
"unicode_decimal": 59921
},
{
"icon_id": "43213716",
"name": "ai-hate2",
"font_class": "ai-hate2",
"unicode": "ea12",
"unicode_decimal": 59922
},
{
"icon_id": "43139007",
"name": "ai-top_up",
"font_class": "ai-top_up",
"unicode": "ea10",
"unicode_decimal": 59920
},
{
"icon_id": "43139017",
"name": "ai-top_down",
"font_class": "ai-top_down",
"unicode": "ea0f",
"unicode_decimal": 59919
},
{
"icon_id": "43029539",
"name": "autoflow-script",
"font_class": "autoflow-script",
"unicode": "ea0d",
"unicode_decimal": 59917
},
{
"icon_id": "43029538",
"name": "autoflow-dag",
"font_class": "autoflow-dag",
"unicode": "ea0e",
"unicode_decimal": 59918
},
{
"icon_id": "42960865",
"name": "itsm-default_line",
"font_class": "itsm-default_line",
"unicode": "ea0c",
"unicode_decimal": 59916
},
{
"icon_id": "42930714",
"name": "veops-servicetree",
"font_class": "veops-servicetree",
"unicode": "ea0b",
"unicode_decimal": 59915
},
{
"icon_id": "42921461",
"name": "veops-switch (1)",
"font_class": "veops-switch1",
"unicode": "ea0a",
"unicode_decimal": 59914
},
{
"icon_id": "42857659",
"name": "veops-label",
"font_class": "veops-label",
"unicode": "ea09",
"unicode_decimal": 59913
},
{
"icon_id": "42790685",
"name": "top_acl",
"font_class": "top_acl",
"unicode": "ea08",
"unicode_decimal": 59912
},
{
"icon_id": "42790687",
"name": "top_ticket",
"font_class": "top_ticket",
"unicode": "ea06",
"unicode_decimal": 59910
},
{
"icon_id": "42790686",
"name": "top_agent",
"font_class": "top_agent",
"unicode": "ea07",
"unicode_decimal": 59911
},
{
"icon_id": "42732510",
"name": "itsm-table_download",
"font_class": "itsm-table_download",
"unicode": "ea05",
"unicode_decimal": 59909
},
{
"icon_id": "42732515",
"name": "itsm-image_download",
"font_class": "itsm-image_download",
"unicode": "ea04",
"unicode_decimal": 59908
},
{
"icon_id": "42510712",
"name": "veops-rear",
"font_class": "veops-rear",
"unicode": "ea02",
"unicode_decimal": 59906
},
{
"icon_id": "42510708",
"name": "veops-front",
"font_class": "veops-front",
"unicode": "ea03",
"unicode_decimal": 59907
},
{
"icon_id": "42497603",
"name": "veops-xianggang",
"font_class": "veops-xianggang",
"unicode": "ea01",
"unicode_decimal": 59905
},
{
"icon_id": "42485038",
"name": "veops-device (2)",
"font_class": "a-veops-device2",
"unicode": "ea00",
"unicode_decimal": 59904
},
{
"icon_id": "42455620",
"name": "veops-room (1)",
"font_class": "a-veops-room1",
"unicode": "e9ff",
"unicode_decimal": 59903
},
{
"icon_id": "42455607",
"name": "veops-IDC",
"font_class": "veops-IDC",
"unicode": "e9fe",
"unicode_decimal": 59902
},
{
"icon_id": "42455609",
"name": "veops-region",
"font_class": "veops-region",
"unicode": "e9fd",
"unicode_decimal": 59901
},
{
"icon_id": "42448953",
"name": "veops-device",
"font_class": "veops-device",
"unicode": "e9fb",
"unicode_decimal": 59899
},
{
"icon_id": "42448948",
"name": "veops-cabinet",
"font_class": "veops-cabinet",
"unicode": "e9fc",
"unicode_decimal": 59900
},
{
"icon_id": "42433324",
"name": "veops-data_center",
"font_class": "veops-data_center",
"unicode": "e9f9",
"unicode_decimal": 59897
},
{
"icon_id": "42337844",
"name": "ops-setting-holiday_management-copy",

Binary file not shown.

View File

@@ -12,7 +12,6 @@ import zhCN from 'ant-design-vue/lib/locale-provider/zh_CN'
import enUS from 'ant-design-vue/lib/locale-provider/en_US'
import { AppDeviceEnquire } from '@/utils/mixin'
import { debounce } from './utils/util'
import { getSystemLanguage } from '@/api/system.js'
import { h } from 'snabbdom'
import { DomEditor, Boot } from '@wangeditor/editor'
@@ -46,7 +45,8 @@ export default {
},
},
created() {
this.initLanguage()
this.SET_LOCALE(localStorage.getItem('ops_locale') || 'zh')
this.$i18n.locale = localStorage.getItem('ops_locale') || 'zh'
this.timer = setInterval(() => {
this.setTime(new Date().getTime())
}, 1000)
@@ -200,28 +200,6 @@ export default {
this.alive = true
})
},
async initLanguage() {
let saveLocale = localStorage.getItem('ops_locale')
if (!saveLocale) {
let requestLanguage = ''
try {
const languageRes = await getSystemLanguage()
requestLanguage = languageRes?.language || ''
} catch (e) {
console.error('getSystemLanguage error:', e)
}
// request language variable || user local system language
const userLanguage = requestLanguage || navigator.language || navigator.userLanguage
if (userLanguage.includes('zh')) {
saveLocale = 'zh'
} else {
saveLocale = 'en'
}
}
this.SET_LOCALE(saveLocale)
this.$i18n.locale = saveLocale
}
},
}
</script>

View File

@@ -1,8 +0,0 @@
import { axios } from '@/utils/request'
export function getSystemLanguage() {
return axios({
url: '/common-setting/v1/system/language',
method: 'get',
})
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 249 KiB

View File

@@ -106,7 +106,7 @@
<CIReferenceAttr
v-if="getAttr(item.property).is_reference && (item.exp === 'is' || item.exp === '~is')"
:style="{ width: '175px' }"
class="select-filter-component ops-select-bg"
class="select-filter-component"
:referenceTypeId="getAttr(item.property).reference_type_id"
:disabled="disabled"
v-model="item.value"
@@ -114,7 +114,7 @@
<a-select
v-else-if="getAttr(item.property).is_bool && (item.exp === 'is' || item.exp === '~is')"
v-model="item.value"
class="select-filter-component ops-select-bg"
class="select-filter-component"
:style="{ width: '175px' }"
:disabled="disabled"
:placeholder="$t('placeholder2')"
@@ -139,9 +139,9 @@
:normalizer="
(node) => {
return {
id: node.id,
label: node.label,
children: node.children,
id: String(node[0] || ''),
label: node[1] ? node[1].label || node[0] : node[0],
children: node.children && node.children.length ? node.children : undefined,
}
}
"
@@ -352,12 +352,8 @@ export default {
},
getChoiceValueByProperty(property) {
const _find = this.canSearchPreferenceAttrList.find((item) => item.name === property)
if (_find?.choice_value?.length) {
return _find.choice_value.map((node) => ({
id: String(node?.[0] ?? ''),
label: node?.[1]?.label || node?.[0] || '',
children: node?.children?.length ? node.children : undefined
}))
if (_find) {
return _find.choice_value
}
return []
},
@@ -398,6 +394,7 @@ export default {
/deep/ .ant-select-selection {
height: 24px;
background: #f7f8fa;
line-height: 24px;
border: none;

View File

@@ -1,302 +1,302 @@
<template>
<div>
<a-popover
v-if="isDropdown"
v-model="visible"
trigger="click"
:placement="placement"
overlayClassName="table-filter"
@visibleChange="visibleChange"
>
<slot name="popover_item">
<a-button type="primary" ghost>{{ $t('cmdbFilterComp.conditionFilter') }}<a-icon type="filter"/></a-button>
</slot>
<template slot="content">
<Expression
:needAddHere="needAddHere"
v-model="ruleList"
:canSearchPreferenceAttrList="canSearchPreferenceAttrList.filter((attr) => !attr.is_password)"
:disabled="disabled"
/>
<a-divider :style="{ margin: '10px 0' }" />
<div style="width:554px">
<a-space :style="{ display: 'flex', justifyContent: 'flex-end' }">
<a-button type="primary" size="small" @click="handleSubmit">{{ $t('confirm') }}</a-button>
<a-button size="small" @click="handleClear">{{ $t('clear') }}</a-button>
</a-space>
</div>
</template>
</a-popover>
<Expression
:needAddHere="needAddHere"
v-else
v-model="ruleList"
:canSearchPreferenceAttrList="canSearchPreferenceAttrList.filter((attr) => !attr.is_password)"
:disabled="disabled"
/>
</div>
</template>
<script>
import { v4 as uuidv4 } from 'uuid'
import Expression from './expression.vue'
import { advancedExpList, compareTypeList } from './constants'
export default {
name: 'FilterComp',
components: { Expression },
props: {
canSearchPreferenceAttrList: {
type: Array,
required: true,
default: () => [],
},
expression: {
type: String,
default: '',
},
regQ: {
type: String,
default: '(?<=q=).+(?=&)|(?<=q=).+$',
},
placement: {
type: String,
default: 'bottomRight',
},
isDropdown: {
type: Boolean,
default: true,
},
needAddHere: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
},
data() {
return {
advancedExpList,
compareTypeList,
visible: false,
ruleList: [],
filterExp: '',
}
},
methods: {
visibleChange(open, isInitOne = true) {
// isInitOne 初始化exp为空时ruleList是否默认给一条
// const regQ = /(?<=q=).+(?=&)|(?<=q=).+$/g
const exp = this.expression.match(new RegExp(this.regQ, 'g'))
? this.expression.match(new RegExp(this.regQ, 'g'))[0]
: null
if (open && exp) {
const expArray = exp.split(',').map((item) => {
let has_not = ''
const key = item.split(':')[0]
const val = item
.split(':')
.slice(1)
.join(':')
let type, property, exp, value, min, max, compareType
if (key.includes('-')) {
type = 'or'
if (key.includes('~')) {
property = key.substring(2)
has_not = '~'
} else {
property = key.substring(1)
}
} else {
type = 'and'
if (key.includes('~')) {
property = key.substring(1)
has_not = '~'
} else {
property = key
}
}
const in_reg = /(?<=\().+(?=\))/g
const range_reg = /(?<=\[).+(?=\])/g
const compare_reg = /(?<=>=|<=|>(?!=)|<(?!=)).+/
if (val === '*') {
exp = has_not + 'value'
value = ''
} else if (in_reg.test(val)) {
exp = has_not + 'in'
value = val.match(in_reg)[0]
} else if (range_reg.test(val)) {
exp = has_not + 'range'
value = val.match(range_reg)[0]
min = value.split('_TO_')[0]
max = value.split('_TO_')[1]
} else if (compare_reg.test(val)) {
exp = has_not + 'compare'
value = val.match(compare_reg)[0]
const _compareType = val.substring(0, val.match(compare_reg)['index'])
const idx = compareTypeList.findIndex((item) => item.label === _compareType)
compareType = compareTypeList[idx].value
} else if (!val.includes('*')) {
exp = has_not + 'is'
value = val
} else {
const resList = [
['contain', /(?<=\*).*(?=\*)/g],
['end_with', /(?<=\*).+/g],
['start_with', /.+(?=\*)/g],
]
for (let i = 0; i < 3; i++) {
const reg = resList[i]
if (reg[1].test(val)) {
exp = has_not + reg[0]
value = val.match(reg[1])[0]
break
}
}
}
return {
id: uuidv4(),
type,
property,
exp,
value,
min,
max,
compareType,
}
})
this.ruleList = [...expArray]
} else if (open) {
const _canSearchPreferenceAttrList = this.canSearchPreferenceAttrList.filter((attr) => !attr.is_password)
this.ruleList = isInitOne
? [
{
id: uuidv4(),
type: 'and',
property:
_canSearchPreferenceAttrList && _canSearchPreferenceAttrList.length
? _canSearchPreferenceAttrList[0].name
: undefined,
exp: 'is',
value: null,
},
]
: []
}
},
handleClear() {
this.ruleList = [
{
id: uuidv4(),
type: 'and',
property: this.canSearchPreferenceAttrList[0].name,
exp: 'is',
value: null,
},
]
this.filterExp = ''
this.visible = false
this.$emit('setExpFromFilter', this.filterExp)
},
handleSubmit() {
if (this.ruleList && this.ruleList.length) {
this.ruleList[0].type = 'and' // 增删后以防万一第一个不是and
this.filterExp = ''
const expList = this.ruleList.map((rule) => {
let singleRuleExp = ''
let _exp = rule.exp
if (rule.type === 'or') {
singleRuleExp += '-'
}
if (rule.exp.includes('~')) {
singleRuleExp += '~'
_exp = rule.exp.split('~')[1]
}
singleRuleExp += `${rule.property}:`
if (_exp === 'is') {
singleRuleExp += `${rule.value ?? ''}`
}
if (_exp === 'contain') {
singleRuleExp += `*${rule.value ?? ''}*`
}
if (_exp === 'start_with') {
singleRuleExp += `${rule.value ?? ''}*`
}
if (_exp === 'end_with') {
singleRuleExp += `*${rule.value ?? ''}`
}
if (_exp === 'value') {
singleRuleExp += `*`
}
if (_exp === 'in') {
singleRuleExp += `(${rule.value ?? ''})`
}
if (_exp === 'range') {
singleRuleExp += `[${rule.min}_TO_${rule.max}]`
}
if (_exp === 'compare') {
const idx = compareTypeList.findIndex((item) => item.value === rule.compareType)
singleRuleExp += `${compareTypeList[idx].label}${rule.value ?? ''}`
}
return singleRuleExp
})
this.filterExp = expList.join(',')
this.$emit('setExpFromFilter', this.filterExp)
} else {
this.$emit('setExpFromFilter', '')
}
this.visible = false
},
},
}
</script>
<style lang="less" scoped>
.table-filter {
.table-filter-add {
margin-top: 10px;
& > a {
padding: 2px 8px;
&:hover {
background-color: @primary-color_5;
border-radius: 5px;
}
}
}
.table-filter-extra-icon {
padding: 0px 2px;
&:hover {
display: inline-block;
border-radius: 5px;
background-color: #f0faff;
}
}
}
</style>
<style lang="less">
.table-filter-extra-operation {
.ant-popover-inner-content {
padding: 3px 4px;
.operation {
cursor: pointer;
width: 90px;
height: 30px;
line-height: 30px;
padding: 3px 4px;
border-radius: 5px;
transition: all 0.3s;
&:hover {
background-color: #f0faff;
}
> .anticon {
margin-right: 10px;
}
}
}
}
</style>
<template>
<div>
<a-popover
v-if="isDropdown"
v-model="visible"
trigger="click"
:placement="placement"
overlayClassName="table-filter"
@visibleChange="visibleChange"
>
<slot name="popover_item">
<a-button type="primary" ghost>{{ $t('cmdbFilterComp.conditionFilter') }}<a-icon type="filter"/></a-button>
</slot>
<template slot="content">
<Expression
:needAddHere="needAddHere"
v-model="ruleList"
:canSearchPreferenceAttrList="canSearchPreferenceAttrList.filter((attr) => !attr.is_password)"
:disabled="disabled"
/>
<a-divider :style="{ margin: '10px 0' }" />
<div style="width:554px">
<a-space :style="{ display: 'flex', justifyContent: 'flex-end' }">
<a-button type="primary" size="small" @click="handleSubmit">{{ $t('confirm') }}</a-button>
<a-button size="small" @click="handleClear">{{ $t('clear') }}</a-button>
</a-space>
</div>
</template>
</a-popover>
<Expression
:needAddHere="needAddHere"
v-else
v-model="ruleList"
:canSearchPreferenceAttrList="canSearchPreferenceAttrList.filter((attr) => !attr.is_password)"
:disabled="disabled"
/>
</div>
</template>
<script>
import { v4 as uuidv4 } from 'uuid'
import Expression from './expression.vue'
import { advancedExpList, compareTypeList } from './constants'
export default {
name: 'FilterComp',
components: { Expression },
props: {
canSearchPreferenceAttrList: {
type: Array,
required: true,
default: () => [],
},
expression: {
type: String,
default: '',
},
regQ: {
type: String,
default: '(?<=q=).+(?=&)|(?<=q=).+$',
},
placement: {
type: String,
default: 'bottomRight',
},
isDropdown: {
type: Boolean,
default: true,
},
needAddHere: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
},
data() {
return {
advancedExpList,
compareTypeList,
visible: false,
ruleList: [],
filterExp: '',
}
},
methods: {
visibleChange(open, isInitOne = true) {
// isInitOne 初始化exp为空时ruleList是否默认给一条
// const regQ = /(?<=q=).+(?=&)|(?<=q=).+$/g
const exp = this.expression.match(new RegExp(this.regQ, 'g'))
? this.expression.match(new RegExp(this.regQ, 'g'))[0]
: null
if (open && exp) {
const expArray = exp.split(',').map((item) => {
let has_not = ''
const key = item.split(':')[0]
const val = item
.split(':')
.slice(1)
.join(':')
let type, property, exp, value, min, max, compareType
if (key.includes('-')) {
type = 'or'
if (key.includes('~')) {
property = key.substring(2)
has_not = '~'
} else {
property = key.substring(1)
}
} else {
type = 'and'
if (key.includes('~')) {
property = key.substring(1)
has_not = '~'
} else {
property = key
}
}
const in_reg = /(?<=\().+(?=\))/g
const range_reg = /(?<=\[).+(?=\])/g
const compare_reg = /(?<=>=|<=|>(?!=)|<(?!=)).+/
if (val === '*') {
exp = has_not + 'value'
value = ''
} else if (in_reg.test(val)) {
exp = has_not + 'in'
value = val.match(in_reg)[0]
} else if (range_reg.test(val)) {
exp = has_not + 'range'
value = val.match(range_reg)[0]
min = value.split('_TO_')[0]
max = value.split('_TO_')[1]
} else if (compare_reg.test(val)) {
exp = has_not + 'compare'
value = val.match(compare_reg)[0]
const _compareType = val.substring(0, val.match(compare_reg)['index'])
const idx = compareTypeList.findIndex((item) => item.label === _compareType)
compareType = compareTypeList[idx].value
} else if (!val.includes('*')) {
exp = has_not + 'is'
value = val
} else {
const resList = [
['contain', /(?<=\*).*(?=\*)/g],
['end_with', /(?<=\*).+/g],
['start_with', /.+(?=\*)/g],
]
for (let i = 0; i < 3; i++) {
const reg = resList[i]
if (reg[1].test(val)) {
exp = has_not + reg[0]
value = val.match(reg[1])[0]
break
}
}
}
return {
id: uuidv4(),
type,
property,
exp,
value,
min,
max,
compareType,
}
})
this.ruleList = [...expArray]
} else if (open) {
const _canSearchPreferenceAttrList = this.canSearchPreferenceAttrList.filter((attr) => !attr.is_password)
this.ruleList = isInitOne
? [
{
id: uuidv4(),
type: 'and',
property:
_canSearchPreferenceAttrList && _canSearchPreferenceAttrList.length
? _canSearchPreferenceAttrList[0].name
: undefined,
exp: 'is',
value: null,
},
]
: []
}
},
handleClear() {
this.ruleList = [
{
id: uuidv4(),
type: 'and',
property: this.canSearchPreferenceAttrList[0].name,
exp: 'is',
value: null,
},
]
this.filterExp = ''
this.visible = false
this.$emit('setExpFromFilter', this.filterExp)
},
handleSubmit() {
if (this.ruleList && this.ruleList.length) {
this.ruleList[0].type = 'and' // 增删后以防万一第一个不是and
this.filterExp = ''
const expList = this.ruleList.map((rule) => {
let singleRuleExp = ''
let _exp = rule.exp
if (rule.type === 'or') {
singleRuleExp += '-'
}
if (rule.exp.includes('~')) {
singleRuleExp += '~'
_exp = rule.exp.split('~')[1]
}
singleRuleExp += `${rule.property}:`
if (_exp === 'is') {
singleRuleExp += `${rule.value ?? ''}`
}
if (_exp === 'contain') {
singleRuleExp += `*${rule.value ?? ''}*`
}
if (_exp === 'start_with') {
singleRuleExp += `${rule.value ?? ''}*`
}
if (_exp === 'end_with') {
singleRuleExp += `*${rule.value ?? ''}`
}
if (_exp === 'value') {
singleRuleExp += `*`
}
if (_exp === 'in') {
singleRuleExp += `(${rule.value ?? ''})`
}
if (_exp === 'range') {
singleRuleExp += `[${rule.min}_TO_${rule.max}]`
}
if (_exp === 'compare') {
const idx = compareTypeList.findIndex((item) => item.value === rule.compareType)
singleRuleExp += `${compareTypeList[idx].label}${rule.value ?? ''}`
}
return singleRuleExp
})
this.filterExp = expList.join(',')
this.$emit('setExpFromFilter', this.filterExp)
} else {
this.$emit('setExpFromFilter', '')
}
this.visible = false
},
},
}
</script>
<style lang="less" scoped>
.table-filter {
.table-filter-add {
margin-top: 10px;
& > a {
padding: 2px 8px;
&:hover {
background-color: #f0faff;
border-radius: 5px;
}
}
}
.table-filter-extra-icon {
padding: 0px 2px;
&:hover {
display: inline-block;
border-radius: 5px;
background-color: #f0faff;
}
}
}
</style>
<style lang="less">
.table-filter-extra-operation {
.ant-popover-inner-content {
padding: 3px 4px;
.operation {
cursor: pointer;
width: 90px;
height: 30px;
line-height: 30px;
padding: 3px 4px;
border-radius: 5px;
transition: all 0.3s;
&:hover {
background-color: #f0faff;
}
> .anticon {
margin-right: 10px;
}
}
}
}
</style>

View File

@@ -341,7 +341,7 @@ export default {
},
}
</script>
<style lang="less" scoped>
<style scoped>
.pop_btn {
text-align: right;
margin-top: 24px;

View File

@@ -47,7 +47,7 @@ export const commonIconList = ['changyong-ubuntu',
export const linearIconList = [
{
value: 'database',
label: 'components.database',
label: '数据库',
list: [{
value: 'icon-xianxing-DB2',
label: 'DB2'
@@ -81,7 +81,7 @@ export const linearIconList = [
}]
}, {
value: 'system',
label: 'components.system',
label: '操作系统',
list: [{
value: 'icon-xianxing-Windows',
label: 'Windows'
@@ -106,7 +106,7 @@ export const linearIconList = [
}]
}, {
value: 'language',
label: 'components.language',
label: '语言',
list: [{
value: 'icon-xianxing-python',
label: 'python'
@@ -137,7 +137,7 @@ export const linearIconList = [
}]
}, {
value: 'status',
label: 'components.status',
label: '状态',
list: [{
value: 'icon-xianxing-yiwen',
label: '疑问'
@@ -177,7 +177,7 @@ export const linearIconList = [
}]
}, {
value: 'icon-xianxing-application',
label: 'components.commonComponent',
label: '常用组件',
list: [{
value: 'icon-xianxing-yilianjie',
label: '已连接'
@@ -310,7 +310,7 @@ export const linearIconList = [
}]
}, {
value: 'data',
label: 'components.data',
label: '数据',
list: [{
value: 'icon-xianxing-bingzhuangtu',
label: '饼状图'
@@ -387,7 +387,7 @@ export const linearIconList = [
export const fillIconList = [
{
value: 'database',
label: 'components.database',
label: '数据库',
list: [{
value: 'icon-shidi-DB2',
label: 'DB2'
@@ -421,7 +421,7 @@ export const fillIconList = [
}]
}, {
value: 'system',
label: 'components.system',
label: '操作系统',
list: [{
value: 'icon-shidi-Windows',
label: 'Windows'
@@ -446,7 +446,7 @@ export const fillIconList = [
}]
}, {
value: 'language',
label: 'components.language',
label: '语言',
list: [{
value: 'icon-shidi-python',
label: 'python'
@@ -477,7 +477,7 @@ export const fillIconList = [
}]
}, {
value: 'status',
label: 'components.status',
label: '状态',
list: [{
value: 'icon-shidi-yiwen',
label: '疑问'
@@ -517,7 +517,7 @@ export const fillIconList = [
}]
}, {
value: 'icon-shidi-application',
label: 'components.commonComponent',
label: '常用组件',
list: [{
value: 'icon-shidi-yilianjie',
label: '已连接'
@@ -650,7 +650,7 @@ export const fillIconList = [
}]
}, {
value: 'data',
label: 'components.data',
label: '数据',
list: [{
value: 'icon-shidi-bingzhuangtu',
label: '饼状图'
@@ -727,7 +727,7 @@ export const fillIconList = [
export const multicolorIconList = [
{
value: 'database',
label: 'components.database',
label: '数据库',
list: [{
value: 'caise-TIDB',
label: 'TIDB'
@@ -773,7 +773,7 @@ export const multicolorIconList = [
}]
}, {
value: 'cloud',
label: 'components.cloud',
label: '',
list: [{
value: 'AWS',
label: 'AWS'
@@ -819,7 +819,7 @@ export const multicolorIconList = [
}]
}, {
value: 'system',
label: 'components.system',
label: '操作系统',
list: [{
value: 'ciase-aix',
label: 'aix'
@@ -847,7 +847,7 @@ export const multicolorIconList = [
}]
}, {
value: 'language',
label: 'components.language',
label: '语言',
list: [{
value: 'caise-python',
label: 'python'
@@ -878,7 +878,7 @@ export const multicolorIconList = [
}]
}, {
value: 'status',
label: 'components.status',
label: '状态',
list: [{
value: 'caise-yiwen',
label: '疑问'
@@ -918,7 +918,7 @@ export const multicolorIconList = [
}]
}, {
value: 'caise-application',
label: 'components.commonComponent',
label: '常用组件',
list: [{
value: 'caise-websphere',
label: 'WebSphere'
@@ -1180,7 +1180,7 @@ export const multicolorIconList = [
}]
}, {
value: 'data',
label: 'components.data',
label: '数据',
list: [{
value: 'caise-bingzhuangtu',
label: '饼状图'

View File

@@ -33,7 +33,7 @@
<template v-if="iconList && iconList.length">
<template v-if="currentIconType !== '4'">
<div v-for="category in iconList" :key="category.value">
<h4 class="category">{{ $t(category.label) }}</h4>
<h4 class="category">{{ category.label }}</h4>
<div class="custom-icon-select-popover-content-wrapper">
<div
v-for="name in category.list"

View File

@@ -6,9 +6,9 @@
</div>
<div class="content">
<h1>{{ config[type].title }}</h1>
<div class="desc">{{ $t(config[type].desc) }}</div>
<div class="desc">{{ config[type].desc }}</div>
<div class="actions">
<a-button type="primary" @click="handleToHome">{{ $t('exception.backToHome') }}</a-button>
<a-button type="primary" @click="handleToHome">返回首页</a-button>
</div>
</div>
</div>

View File

@@ -2,17 +2,17 @@ const types = {
403: {
img: 'https://gw.alipayobjects.com/zos/rmsportal/wZcnGqRDyhPOEYFcZDnb.svg',
title: '403',
desc: 'exception.desc1'
desc: '抱歉,你无权访问该页面'
},
404: {
img: 'https://gw.alipayobjects.com/zos/rmsportal/KpnpchXsobRgLElEozzI.svg',
title: '404',
desc: 'exception.desc2'
desc: '抱歉,你访问的页面不存在或仍在开发中'
},
500: {
img: 'https://gw.alipayobjects.com/zos/rmsportal/RVRUAYdCGeYNBWoKiIwB.svg',
title: '500',
desc: 'exception.desc3'
desc: '抱歉,服务器出错了'
}
}

View File

@@ -11,19 +11,10 @@
.ant-input {
box-shadow: none;
border: none;
background-color: #F7F8FA;
height: 30px;
line-height: 30px;
border-radius: 30px;
&:focus {
border: solid 1px #B1C9FF;
}
}
.cmdb-side-menu-search-focused {
.ant-input {
border: solid 1px #B1C9FF;
}
}
.ant-input-suffix {

View File

@@ -92,7 +92,7 @@ export default {
e.preventDefault()
e.stopPropagation()
this.$confirm({
title: this.$t('warning'),
title: this.$t('alert'),
content: this.$t('cmdb.preference.confirmcancelSub2', { name: menu.meta.title }),
onOk() {
const citypeId = menu.meta.typeId
@@ -171,6 +171,7 @@ export default {
},
renderMenuItem(menu) {
const isShowDot = menu.path.substr(0, 22) === '/cmdb/instances/types/'
const isShowGrant = menu.path.substr(0, 20) === '/cmdb/relationviews/'
const target = menu.meta.target || null
const tag = target && 'a' || 'router-link'
const props = { to: { name: menu.name } }
@@ -204,9 +205,11 @@ export default {
<a-icon type="menu" ref="extraEllipsis" class="custom-menu-extra-ellipsis"></a-icon>
</a-popover>
}
{isShowGrant && <a-icon class="custom-menu-extra-ellipsis" onClick={e => this.handlePerm(e, menu, 'RelationView')} type="user-add" />}
</span>
</tag>
{isShowDot && <CMDBGrant ref="cmdbGrantCIType" resourceType="CIType" app_id="cmdb" />}
{isShowGrant && <CMDBGrant ref="cmdbGrantRelationView" resourceType="RelationView" app_id="cmdb" />}
</Item>
)
},
@@ -310,7 +313,10 @@ export default {
<Item class={styles['cmdb-side-menu-search']}>
<a-input
ref="cmdbSideMenuSearchInputRef"
class={`ops-input ${this.$route.name === 'cmdb_resource_search' ? 'cmdb-side-menu-search-focused' : ''}`}
class={styles['cmdb-side-menu-search-input']}
style={{
border: this.$route.name === 'cmdb_resource_search' ? 'solid 1px #B1C9FF' : ''
}}
placeholder={this.$t('cmdbSearch')}
onPressEnter={(e) => {
this.jumpCMDBSearch(e.target.value)

View File

@@ -1,121 +1,121 @@
<template>
<vxe-table v-bind="$attrs" v-on="new$listeners" ref="xTable">
<slot></slot>
<template #empty>
<slot name="empty">
<div :style="{ paddingTop: '10px' }">
<img :style="{ width: '140px', height: '120px' }" :src="require('@/assets/data_empty.png')" />
<div>{{ $t('noData') }}</div>
</div>
</slot>
</template>
<template #loading>
<slot name="loading"></slot>
</template>
</vxe-table>
</template>
<script>
import _ from 'lodash'
// 该组件使用方法与vxe-table一致但调用它的方法时需先调用getVxetableRef()获取到vxe-table实体
export default {
name: 'OpsTable',
data() {
return {
// isShifting: false,
// lastIndex: -1,
lastSelected: [],
currentSelected: [],
}
},
computed: {
new$listeners() {
if (!Object.keys(this.$listeners).length) {
return this.$listeners
}
return Object.assign(this.$listeners, {
// 在这里覆盖原有的change事件
// 'checkbox-change': this.selectChangeEvent,
'checkbox-range-change': this.checkboxRangeChange,
'checkbox-range-start': this.checkboxRangeStart,
'checkbox-range-end': this.checkboxRangeEnd,
})
},
},
mounted() {
// window.onkeydown = (e) => {
// if (e.key === 'Shift') {
// this.isShifting = true
// }
// }
// window.onkeyup = (e) => {
// if (e.key === 'Shift') {
// this.isShifting = false
// this.lastIndex = -1
// }
// }
},
beforeDestroy() {
// window.onkeydown = ''
// window.onkeyup = ''
},
methods: {
getVxetableRef() {
return this.$refs.xTable
},
// selectChangeEvent(e) {
// const xTable = this.$refs.xTable
// const { lastIndex } = this
// const currentIndex = e.rowIndex
// const { tableData } = xTable.getTableData()
// if (lastIndex > -1 && this.isShifting) {
// let start = lastIndex
// let end = currentIndex
// if (lastIndex > currentIndex) {
// start = currentIndex
// end = lastIndex
// }
// const rangeData = tableData.slice(start, end + 1)
// xTable.setCheckboxRow(rangeData, true)
// }
// this.lastIndex = currentIndex
// this.$emit('checkbox-change', { ...e, records: xTable.getCheckboxRecords() })
// },
checkboxRangeStart(e) {
const xTable = this.$refs.xTable
const lastSelected = xTable.getCheckboxRecords()
const selectedReserve = xTable.getCheckboxReserveRecords()
this.lastSelected = [...lastSelected, ...selectedReserve]
this.$emit('checkbox-range-start', e)
},
checkboxRangeChange(e) {
const xTable = this.$refs.xTable
xTable.setCheckboxRow(this.lastSelected, true)
this.currentSelected = e.records
// this.lastSelected = [...new Set([...this.lastSelected, ...e.records])]
this.$emit('checkbox-range-change', {
...e,
records: [...xTable.getCheckboxRecords(), ...xTable.getCheckboxReserveRecords()],
})
},
checkboxRangeEnd(e) {
const xTable = this.$refs.xTable
const isAllSelected = this.currentSelected.every((item) => {
const _idx = this.lastSelected.findIndex((ele) => _.isEqual(ele, item))
return _idx > -1
})
if (isAllSelected) {
xTable.setCheckboxRow(this.currentSelected, false)
}
this.currentSelected = []
this.lastSelected = []
this.$emit('checkbox-range-end', {
...e,
records: [...xTable.getCheckboxRecords(), ...xTable.getCheckboxReserveRecords()],
})
},
},
}
</script>
<style lang="less"></style>
<template>
<vxe-table v-bind="$attrs" v-on="new$listeners" ref="xTable">
<slot></slot>
<template #empty>
<slot name="empty">
<div :style="{ paddingTop: '10px' }">
<img :style="{ width: '140px', height: '90px' }" :src="require('@/assets/data_empty.png')" />
<div>{{ $t('noData') }}</div>
</div>
</slot>
</template>
<template #loading>
<slot name="loading"></slot>
</template>
</vxe-table>
</template>
<script>
import _ from 'lodash'
// 该组件使用方法与vxe-table一致但调用它的方法时需先调用getVxetableRef()获取到vxe-table实体
export default {
name: 'OpsTable',
data() {
return {
// isShifting: false,
// lastIndex: -1,
lastSelected: [],
currentSelected: [],
}
},
computed: {
new$listeners() {
if (!Object.keys(this.$listeners).length) {
return this.$listeners
}
return Object.assign(this.$listeners, {
// 在这里覆盖原有的change事件
// 'checkbox-change': this.selectChangeEvent,
'checkbox-range-change': this.checkboxRangeChange,
'checkbox-range-start': this.checkboxRangeStart,
'checkbox-range-end': this.checkboxRangeEnd,
})
},
},
mounted() {
// window.onkeydown = (e) => {
// if (e.key === 'Shift') {
// this.isShifting = true
// }
// }
// window.onkeyup = (e) => {
// if (e.key === 'Shift') {
// this.isShifting = false
// this.lastIndex = -1
// }
// }
},
beforeDestroy() {
// window.onkeydown = ''
// window.onkeyup = ''
},
methods: {
getVxetableRef() {
return this.$refs.xTable
},
// selectChangeEvent(e) {
// const xTable = this.$refs.xTable
// const { lastIndex } = this
// const currentIndex = e.rowIndex
// const { tableData } = xTable.getTableData()
// if (lastIndex > -1 && this.isShifting) {
// let start = lastIndex
// let end = currentIndex
// if (lastIndex > currentIndex) {
// start = currentIndex
// end = lastIndex
// }
// const rangeData = tableData.slice(start, end + 1)
// xTable.setCheckboxRow(rangeData, true)
// }
// this.lastIndex = currentIndex
// this.$emit('checkbox-change', { ...e, records: xTable.getCheckboxRecords() })
// },
checkboxRangeStart(e) {
const xTable = this.$refs.xTable
const lastSelected = xTable.getCheckboxRecords()
const selectedReserve = xTable.getCheckboxReserveRecords()
this.lastSelected = [...lastSelected, ...selectedReserve]
this.$emit('checkbox-range-start', e)
},
checkboxRangeChange(e) {
const xTable = this.$refs.xTable
xTable.setCheckboxRow(this.lastSelected, true)
this.currentSelected = e.records
// this.lastSelected = [...new Set([...this.lastSelected, ...e.records])]
this.$emit('checkbox-range-change', {
...e,
records: [...xTable.getCheckboxRecords(), ...xTable.getCheckboxReserveRecords()],
})
},
checkboxRangeEnd(e) {
const xTable = this.$refs.xTable
const isAllSelected = this.currentSelected.every((item) => {
const _idx = this.lastSelected.findIndex((ele) => _.isEqual(ele, item))
return _idx > -1
})
if (isAllSelected) {
xTable.setCheckboxRow(this.currentSelected, false)
}
this.currentSelected = []
this.lastSelected = []
this.$emit('checkbox-range-end', {
...e,
records: [...xTable.getCheckboxRecords(), ...xTable.getCheckboxReserveRecords()],
})
},
},
}
</script>
<style lang="less"></style>

View File

@@ -109,25 +109,9 @@ export default {
default: 'default',
tip: 'Tip',
cmdbSearch: 'Search',
exception: {
backToHome: 'Back to home page',
desc1: 'Sorry, you are not authorized to access this page',
desc2: 'Sorry, the page you are visiting does not exist or is still under development',
desc3: 'Sorry, server error'
},
pagination: {
total: '{range0}-{range1} of {total} items'
},
components: {
colorTagSelectTip: 'Enter or select tags',
database: 'Database',
system: 'System',
language: 'Language',
status: 'Status',
commonComponent: 'Common Component',
data: 'Data',
cloud: 'Cloud'
},
topMenu: {
personalCenter: 'My Profile',
logout: 'Logout',

View File

@@ -109,25 +109,9 @@ export default {
default: '默认',
tip: '提示',
cmdbSearch: '搜索一下',
exception: {
backToHome: '返回首页',
desc1: '抱歉,你无权访问该页面',
desc2: '抱歉,你访问的页面不存在或仍在开发中',
desc3: '抱歉,服务器出错了'
},
pagination: {
total: '当前展示 {range0}-{range1} 条数据, 共 {total} 条'
},
components: {
colorTagSelectTip: '选择或输入(回车确定)标签',
database: '数据库',
system: '操作系统',
language: '语言',
status: '状态',
commonComponent: '常用组件',
data: '数据',
cloud: '云'
},
topMenu: {
personalCenter: '个人中心',
logout: '退出登录',

View File

@@ -226,16 +226,16 @@ export default {
right: 4px;
display: none;
&:hover {
color: @primary-color;
color: #1f78d1;
}
}
&:hover .ant-transfer-list-icon {
display: inline;
background-color: @primary-color_4;
background-color: #c0eaff;
border-radius: 4px;
}
}
.ant-transfer-list-content-item-selected {
background-color: ~'@{primary-color_8}35';
background-color: #f0faff;
}
</style>

View File

@@ -195,11 +195,11 @@ export default {
background-color: #fff;
height: calc(100vh - 64px);
margin-bottom: -24px;
padding: 20px;
padding: 24px;
.acl-resource-types-header {
width: 100%;
display: inline-flex;
margin-bottom: 20px;
margin-bottom: 15px;
align-items: center;
}
}

View File

@@ -358,11 +358,11 @@ export default {
background-color: #fff;
height: calc(100vh - 64px);
margin-bottom: -24px;
padding: 8px 20px 20px 20px;
padding: 12px 24px 24px 24px;
.acl-resources-header {
width: 100%;
display: inline-flex;
margin-bottom: 20px;
margin-bottom: 15px;
align-items: center;
justify-content: space-between;
.ant-switch {

View File

@@ -58,7 +58,10 @@
:title="$t('acl.visualRole')"
:width="120"
align="center"
:filters="visualRoleFilters"
:filters="[
{ label: $t('yes'), value: 1 },
{ label: $t('no'), value: 0 },
]"
:filterMultiple="false"
:filter-method="
({ value, row }) => {
@@ -152,10 +155,6 @@ export default {
pageSizeOptions: ['20', '50', '100', '200'],
searchName: '',
filterTableValue: { user_role: 1, user_only: 0 },
visualRoleFilters: [
{ label: this.$t('yes'), value: 1 },
{ label: this.$t('no'), value: 0 }
]
}
},
computed: {
@@ -292,11 +291,11 @@ export default {
background-color: #fff;
height: calc(100vh - 64px);
margin-bottom: -24px;
padding: 20px;
padding: 24px;
.acl-roles-header {
width: 100%;
display: inline-flex;
margin-bottom: 20px;
margin-bottom: 15px;
align-items: center;
.ant-checkbox-wrapper {
margin-left: auto;

View File

@@ -326,11 +326,11 @@ export default {
background-color: #fff;
height: calc(100vh - 64px);
margin-bottom: -24px;
padding: 20px;
padding: 24px;
.acl-trigger-header {
width: 100%;
display: inline-flex;
margin-bottom: 20px;
margin-bottom: 15px;
align-items: center;
}
}

View File

@@ -207,13 +207,6 @@ export function deleteTrigger(type_id, id) {
})
}
export function testTrigger(type_id, id) {
return axios({
url: `/v0.1/ci_types/${type_id}/triggers/${id}/test_notify`,
method: 'post',
})
}
// CMDB的模型和实例的授权接口
export function grantCiType(type_id, rid, data) {
return axios({

View File

@@ -1,93 +0,0 @@
import { axios } from '@/utils/request'
export function getDCIMTreeView(params) {
return axios({
url: '/v0.1/dcim/tree_view ',
method: 'GET',
params
})
}
export function getDCIMById(type, id) {
return axios({
url: `/v0.1/dcim/${type}/${id}`,
method: 'GET'
})
}
export function postDCIM(type, data) {
return axios({
url: `/v0.1/dcim/${type}`,
method: 'POST',
data
})
}
export function putDCIM(type, id, data) {
return axios({
url: `/v0.1/dcim/${type}/${id}`,
method: 'PUT',
data
})
}
export function deleteDCIM(type, id) {
return axios({
url: `/v0.1/dcim/${type}/${id}`,
method: 'DELETE',
})
}
export function getDCIMRacks(id, params) {
return axios({
url: `/v0.1/dcim/server_room/${id}/racks`,
method: 'GET',
params
})
}
export function postDevice(rackId, deviceId, data) {
return axios({
url: `/v0.1/dcim/rack/${rackId}/device/${deviceId}`,
method: 'POST',
data
})
}
export function deleteDevice(rackId, deviceId) {
return axios({
url: `/v0.1/dcim/rack/${rackId}/device/${deviceId}`,
method: 'DELETE'
})
}
export function putDevice(rackId, deviceId, data) {
return axios({
url: `/v0.1/dcim/rack/${rackId}/device/${deviceId}`,
method: 'PUT',
data
})
}
export function migrateDevice(rackId, deviceId, data) {
return axios({
url: `/v0.1/dcim/rack/${rackId}/device/${deviceId}/migrate`,
method: 'PUT',
data
})
}
export function getDCIMHistoryOperate(params) {
return axios({
url: `/v0.1/dcim/history/operate`,
method: 'GET',
params
})
}
export function calcUnitFreeCount() {
return axios({
url: `/v0.1/dcim/rack/calc_u_free_count`,
method: 'POST'
})
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Some files were not shown because too many files have changed in this diff Show More