mirror of
https://github.com/veops/cmdb.git
synced 2025-09-08 14:31:07 +08:00
Compare commits
16 Commits
dev_common
...
v2.4.7
Author | SHA1 | Date | |
---|---|---|---|
|
c1813f525d | ||
|
b405e28498 | ||
|
fa32758462 | ||
|
29995b660a | ||
|
b96fc06a62 | ||
|
c7f30b63ff | ||
|
5bff69a8a8 | ||
|
d5e60fab88 | ||
|
190f452118 | ||
|
98a4824364 | ||
|
c0f9baea79 | ||
|
d4b661c77f | ||
|
75cd7bde77 | ||
|
ec912d3a65 | ||
|
42f02b4986 | ||
|
a13b999820 |
32
.github/workflows/docker-build-and-release.yaml
vendored
32
.github/workflows/docker-build-and-release.yaml
vendored
@@ -1,4 +1,4 @@
|
||||
name: api-docker-images-build-and-release
|
||||
name: docker-images-build-and-release
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -12,31 +12,25 @@ on:
|
||||
env:
|
||||
# Use docker.io for Docker Hub if empty
|
||||
REGISTRY_SERVER_ADDRESS: ghcr.io/veops
|
||||
TAG: ${{ github.sha }}
|
||||
|
||||
jobs:
|
||||
setup-environment:
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.actor != 'dependabot[bot]' }}
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v4
|
||||
release-images:
|
||||
release-api-images:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [setup-environment]
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
TAG: ${{ github.sha }}
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.21.8"
|
||||
cache: false
|
||||
- name: Login to GitHub Package Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
@@ -55,6 +49,26 @@ jobs:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ env.REGISTRY_SERVER_ADDRESS }}/cmdb-api:${{ env.TAG }}
|
||||
release-ui-images:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [setup-environment]
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v4
|
||||
- name: Login to GitHub Package Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Build and push CMDB-UI Docker image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
|
@@ -515,31 +515,47 @@ def cmdb_patch(version):
|
||||
|
||||
version = version[1:] if version.lower().startswith("v") else version
|
||||
|
||||
if version >= '2.4.6':
|
||||
try:
|
||||
if version >= '2.4.6':
|
||||
|
||||
from api.models.cmdb import CITypeRelation
|
||||
for cr in CITypeRelation.get_by(to_dict=False):
|
||||
if hasattr(cr, 'parent_attr_id') and cr.parent_attr_id and not cr.parent_attr_ids:
|
||||
parent_attr_ids, child_attr_ids = [cr.parent_attr_id], [cr.child_attr_id]
|
||||
cr.update(parent_attr_ids=parent_attr_ids, child_attr_ids=child_attr_ids, commit=False)
|
||||
db.session.commit()
|
||||
from api.models.cmdb import CITypeRelation
|
||||
for cr in CITypeRelation.get_by(to_dict=False):
|
||||
if hasattr(cr, 'parent_attr_id') and cr.parent_attr_id and not cr.parent_attr_ids:
|
||||
parent_attr_ids, child_attr_ids = [cr.parent_attr_id], [cr.child_attr_id]
|
||||
cr.update(parent_attr_ids=parent_attr_ids, child_attr_ids=child_attr_ids, commit=False)
|
||||
db.session.commit()
|
||||
|
||||
from api.models.cmdb import AutoDiscoveryCIType, AutoDiscoveryCITypeRelation
|
||||
from api.lib.cmdb.cache import CITypeCache, AttributeCache
|
||||
for adt in AutoDiscoveryCIType.get_by(to_dict=False):
|
||||
if adt.relation:
|
||||
if not AutoDiscoveryCITypeRelation.get_by(ad_type_id=adt.type_id):
|
||||
peer_type = CITypeCache.get(list(adt.relation.values())['type_name'])
|
||||
peer_type_id = peer_type and peer_type.id
|
||||
peer_attr = AttributeCache.get(list(adt.relation.values())['attr_name'])
|
||||
peer_attr_id = peer_attr and peer_attr.id
|
||||
if peer_type_id and peer_attr_id:
|
||||
AutoDiscoveryCITypeRelation.create(ad_type_id=adt.type_id,
|
||||
ad_key=list(adt.relation.keys())[0],
|
||||
peer_type_id=peer_type_id,
|
||||
peer_attr_id=peer_attr_id,
|
||||
commit=False)
|
||||
if hasattr(adt, 'interval') and adt.interval and not adt.cron:
|
||||
adt.cron = "*/{} * * * *".format(adt.interval // 60)
|
||||
from api.models.cmdb import AutoDiscoveryCIType, AutoDiscoveryCITypeRelation
|
||||
from api.lib.cmdb.cache import CITypeCache, AttributeCache
|
||||
for adt in AutoDiscoveryCIType.get_by(to_dict=False):
|
||||
if adt.relation:
|
||||
if not AutoDiscoveryCITypeRelation.get_by(ad_type_id=adt.type_id):
|
||||
peer_type = CITypeCache.get(list(adt.relation.values())[0]['type_name'])
|
||||
peer_type_id = peer_type and peer_type.id
|
||||
peer_attr = AttributeCache.get(list(adt.relation.values())[0]['attr_name'])
|
||||
peer_attr_id = peer_attr and peer_attr.id
|
||||
if peer_type_id and peer_attr_id:
|
||||
AutoDiscoveryCITypeRelation.create(ad_type_id=adt.type_id,
|
||||
ad_key=list(adt.relation.keys())[0],
|
||||
peer_type_id=peer_type_id,
|
||||
peer_attr_id=peer_attr_id,
|
||||
commit=False)
|
||||
if hasattr(adt, 'interval') and adt.interval and not adt.cron:
|
||||
adt.cron = "*/{} * * * *".format(adt.interval // 60 or 1)
|
||||
|
||||
db.session.commit()
|
||||
db.session.commit()
|
||||
|
||||
if version >= "2.4.7":
|
||||
from api.lib.cmdb.auto_discovery.const import DEFAULT_INNER
|
||||
from api.models.cmdb import AutoDiscoveryRule
|
||||
for i in DEFAULT_INNER:
|
||||
existed = AutoDiscoveryRule.get_by(name=i['name'], first=True, to_dict=False)
|
||||
if existed is not None:
|
||||
if "en" in i['option'] and 'en' not in (existed.option or {}):
|
||||
option = copy.deepcopy(existed.option)
|
||||
option['en'] = i['option']['en']
|
||||
existed.update(option=option, commit=False)
|
||||
|
||||
db.session.commit()
|
||||
except Exception as e:
|
||||
print("cmdb patch failed: {}".format(e))
|
||||
|
@@ -3,7 +3,6 @@ import copy
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
|
||||
from flask import abort
|
||||
from flask import current_app
|
||||
from flask_login import current_user
|
||||
@@ -11,7 +10,8 @@ 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_HTTP
|
||||
from api.lib.cmdb.auto_discovery.const import DEFAULT_INNER
|
||||
from api.lib.cmdb.auto_discovery.const import PRIVILEGED_USERS
|
||||
from api.lib.cmdb.cache import AttributeCache
|
||||
from api.lib.cmdb.cache import CITypeAttributeCache
|
||||
from api.lib.cmdb.cache import CITypeCache
|
||||
@@ -22,6 +22,7 @@ from api.lib.cmdb.const import AutoDiscoveryType
|
||||
from api.lib.cmdb.const import CMDB_QUEUE
|
||||
from api.lib.cmdb.const import PermEnum
|
||||
from api.lib.cmdb.const import ResourceTypeEnum
|
||||
from api.lib.cmdb.custom_dashboard import SystemConfigManager
|
||||
from api.lib.cmdb.resp_format import ErrFormat
|
||||
from api.lib.cmdb.search import SearchError
|
||||
from api.lib.cmdb.search.ci import search as ci_search
|
||||
@@ -109,14 +110,22 @@ class AutoDiscoveryRuleCRUD(DBMixin):
|
||||
else:
|
||||
self.cls.create(**rule)
|
||||
|
||||
def _can_add(self, **kwargs):
|
||||
def _can_add(self, valid=True, **kwargs):
|
||||
self.cls.get_by(name=kwargs['name']) and abort(400, ErrFormat.adr_duplicate.format(kwargs['name']))
|
||||
if kwargs.get('is_plugin') and kwargs.get('plugin_script'):
|
||||
if kwargs.get('is_plugin') and kwargs.get('plugin_script') and valid:
|
||||
kwargs = check_plugin_script(**kwargs)
|
||||
acl = ACLManager(app_cli.app_name)
|
||||
if not acl.has_permission(app_cli.op.Auto_Discovery,
|
||||
app_cli.resource_type_name,
|
||||
app_cli.op.create_plugin) and not is_app_admin(app_cli.app_name):
|
||||
has_perm = True
|
||||
try:
|
||||
if not acl.has_permission(app_cli.op.Auto_Discovery,
|
||||
app_cli.resource_type_name,
|
||||
app_cli.op.create_plugin) and not is_app_admin(app_cli.app_name):
|
||||
has_perm = False
|
||||
except Exception:
|
||||
if not is_app_admin(app_cli.app_name):
|
||||
return abort(403, ErrFormat.role_required.format(app_cli.admin_name))
|
||||
|
||||
if not has_perm:
|
||||
return abort(403, ErrFormat.no_permission.format(
|
||||
app_cli.op.Auto_Discovery, app_cli.op.create_plugin))
|
||||
|
||||
@@ -124,7 +133,7 @@ class AutoDiscoveryRuleCRUD(DBMixin):
|
||||
|
||||
return kwargs
|
||||
|
||||
def _can_update(self, **kwargs):
|
||||
def _can_update(self, valid=True, **kwargs):
|
||||
existed = self.cls.get_by_id(kwargs['_id']) or abort(
|
||||
404, ErrFormat.adr_not_found.format("id={}".format(kwargs['_id'])))
|
||||
|
||||
@@ -136,11 +145,19 @@ class AutoDiscoveryRuleCRUD(DBMixin):
|
||||
if other and other.id != existed.id:
|
||||
return abort(400, ErrFormat.adr_duplicate.format(kwargs['name']))
|
||||
|
||||
if existed.is_plugin:
|
||||
if existed.is_plugin and valid:
|
||||
acl = ACLManager(app_cli.app_name)
|
||||
if not acl.has_permission(app_cli.op.Auto_Discovery,
|
||||
app_cli.resource_type_name,
|
||||
app_cli.op.update_plugin) and not is_app_admin(app_cli.app_name):
|
||||
has_perm = True
|
||||
try:
|
||||
if not acl.has_permission(app_cli.op.Auto_Discovery,
|
||||
app_cli.resource_type_name,
|
||||
app_cli.op.update_plugin) and not is_app_admin(app_cli.app_name):
|
||||
has_perm = False
|
||||
except Exception:
|
||||
if not is_app_admin(app_cli.app_name):
|
||||
return abort(403, ErrFormat.role_required.format(app_cli.admin_name))
|
||||
|
||||
if not has_perm:
|
||||
return abort(403, ErrFormat.no_permission.format(
|
||||
app_cli.op.Auto_Discovery, app_cli.op.update_plugin))
|
||||
|
||||
@@ -165,9 +182,17 @@ class AutoDiscoveryRuleCRUD(DBMixin):
|
||||
|
||||
if existed.is_plugin:
|
||||
acl = ACLManager(app_cli.app_name)
|
||||
if not acl.has_permission(app_cli.op.Auto_Discovery,
|
||||
app_cli.resource_type_name,
|
||||
app_cli.op.delete_plugin) and not is_app_admin(app_cli.app_name):
|
||||
has_perm = True
|
||||
try:
|
||||
if not acl.has_permission(app_cli.op.Auto_Discovery,
|
||||
app_cli.resource_type_name,
|
||||
app_cli.op.delete_plugin) and not is_app_admin(app_cli.app_name):
|
||||
has_perm = False
|
||||
except Exception:
|
||||
if not is_app_admin(app_cli.app_name):
|
||||
return abort(403, ErrFormat.role_required.format(app_cli.admin_name))
|
||||
|
||||
if not has_perm:
|
||||
return abort(403, ErrFormat.no_permission.format(
|
||||
app_cli.op.Auto_Discovery, app_cli.op.delete_plugin))
|
||||
|
||||
@@ -178,8 +203,9 @@ class AutoDiscoveryCITypeCRUD(DBMixin):
|
||||
cls = AutoDiscoveryCIType
|
||||
|
||||
@classmethod
|
||||
def get_all(cls):
|
||||
return cls.cls.get_by(to_dict=False)
|
||||
def get_all(cls, type_ids=None):
|
||||
res = cls.cls.get_by(to_dict=False)
|
||||
return [i for i in res if type_ids is None or i.type_id in type_ids]
|
||||
|
||||
@classmethod
|
||||
def get_by_id(cls, _id):
|
||||
@@ -198,7 +224,7 @@ class AutoDiscoveryCITypeCRUD(DBMixin):
|
||||
if not adr:
|
||||
continue
|
||||
if adr.type == "http":
|
||||
for i in DEFAULT_HTTP:
|
||||
for i in DEFAULT_INNER:
|
||||
if adr.name == i['name']:
|
||||
attrs = AutoDiscoveryHTTPManager.get_attributes(
|
||||
i['en'], (adt.extra_option or {}).get('category')) or []
|
||||
@@ -219,11 +245,17 @@ class AutoDiscoveryCITypeCRUD(DBMixin):
|
||||
|
||||
for rule in rules:
|
||||
if isinstance(rule.get("extra_option"), dict) and rule['extra_option'].get('secret'):
|
||||
if not (current_user.username == "cmdb_agent" or current_user.uid == rule['uid']):
|
||||
if not (current_user.username in PRIVILEGED_USERS or current_user.uid == rule['uid']):
|
||||
rule['extra_option'].pop('secret', None)
|
||||
else:
|
||||
rule['extra_option']['secret'] = AESCrypto.decrypt(rule['extra_option']['secret'])
|
||||
|
||||
if isinstance(rule.get("extra_option"), dict) and rule['extra_option'].get('password'):
|
||||
if not (current_user.username in PRIVILEGED_USERS or current_user.uid == rule['uid']):
|
||||
rule['extra_option'].pop('password', None)
|
||||
else:
|
||||
rule['extra_option']['password'] = AESCrypto.decrypt(rule['extra_option']['password'])
|
||||
|
||||
if oneagent_id and rule['agent_id'] == oneagent_id:
|
||||
result.append(rule)
|
||||
elif rule['query_expr']:
|
||||
@@ -247,17 +279,19 @@ class AutoDiscoveryCITypeCRUD(DBMixin):
|
||||
|
||||
result.append(rule)
|
||||
|
||||
|
||||
ad_rules_updated_at = (SystemConfigManager.get('ad_rules_updated_at') or {}).get('option', {}).get('v') or ""
|
||||
new_last_update_at = ""
|
||||
for i in result:
|
||||
i['adr'] = AutoDiscoveryRule.get_by_id(i['adr_id']).to_dict()
|
||||
i['adr'].pop("attributes", None)
|
||||
__last_update_at = max([i['updated_at'] or "", i['created_at'] or "",
|
||||
i['adr']['created_at'] or "", i['adr']['updated_at'] or ""])
|
||||
i['adr']['created_at'] or "", i['adr']['updated_at'] or "", ad_rules_updated_at])
|
||||
if new_last_update_at < __last_update_at:
|
||||
new_last_update_at = __last_update_at
|
||||
|
||||
write_ad_rule_sync_history.apply_async(args=(result, oneagent_id, oneagent_name, datetime.datetime.now()),
|
||||
queue=CMDB_QUEUE)
|
||||
|
||||
if not last_update_at or new_last_update_at > last_update_at:
|
||||
return result, new_last_update_at
|
||||
else:
|
||||
@@ -322,7 +356,7 @@ class AutoDiscoveryCITypeCRUD(DBMixin):
|
||||
if adr.type == "http":
|
||||
kwargs.setdefault('extra_option', dict)
|
||||
en_name = None
|
||||
for i in DEFAULT_HTTP:
|
||||
for i in DEFAULT_INNER:
|
||||
if i['name'] == adr.name:
|
||||
en_name = i['en']
|
||||
break
|
||||
@@ -338,10 +372,13 @@ class AutoDiscoveryCITypeCRUD(DBMixin):
|
||||
|
||||
if isinstance(kwargs.get('extra_option'), dict) and kwargs['extra_option'].get('secret'):
|
||||
kwargs['extra_option']['secret'] = AESCrypto.encrypt(kwargs['extra_option']['secret'])
|
||||
if isinstance(kwargs.get('extra_option'), dict) and kwargs['extra_option'].get('password'):
|
||||
kwargs['extra_option']['password'] = AESCrypto.encrypt(kwargs['extra_option']['password'])
|
||||
|
||||
ci_type = CITypeCache.get(kwargs['type_id'])
|
||||
unique = AttributeCache.get(ci_type.unique_id)
|
||||
if unique and unique.name not in (kwargs.get('attributes') or {}).values():
|
||||
current_app.logger.warning((unique.name, kwargs.get('attributes'), ci_type.alias))
|
||||
return abort(400, ErrFormat.ad_not_unique_key.format(unique.name))
|
||||
|
||||
kwargs['uid'] = current_user.uid
|
||||
@@ -357,7 +394,7 @@ class AutoDiscoveryCITypeCRUD(DBMixin):
|
||||
if adr.type == "http":
|
||||
kwargs.setdefault('extra_option', dict)
|
||||
en_name = None
|
||||
for i in DEFAULT_HTTP:
|
||||
for i in DEFAULT_INNER:
|
||||
if i['name'] == adr.name:
|
||||
en_name = i['en']
|
||||
break
|
||||
@@ -374,11 +411,15 @@ class AutoDiscoveryCITypeCRUD(DBMixin):
|
||||
ci_type = CITypeCache.get(existed.type_id)
|
||||
unique = AttributeCache.get(ci_type.unique_id)
|
||||
if unique and unique.name not in (kwargs.get('attributes') or {}).values():
|
||||
current_app.logger.warning((unique.name, kwargs.get('attributes'), ci_type.alias))
|
||||
return abort(400, ErrFormat.ad_not_unique_key.format(unique.name))
|
||||
|
||||
if isinstance(kwargs.get('extra_option'), dict) and kwargs['extra_option'].get('secret'):
|
||||
if current_user.uid != existed.uid:
|
||||
return abort(403, ErrFormat.adt_secret_no_permission)
|
||||
if isinstance(kwargs.get('extra_option'), dict) and kwargs['extra_option'].get('password'):
|
||||
if current_user.uid != existed.uid:
|
||||
return abort(403, ErrFormat.adt_secret_no_permission)
|
||||
|
||||
return existed
|
||||
|
||||
@@ -389,6 +430,8 @@ class AutoDiscoveryCITypeCRUD(DBMixin):
|
||||
|
||||
if isinstance(kwargs.get('extra_option'), dict) and kwargs['extra_option'].get('secret'):
|
||||
kwargs['extra_option']['secret'] = AESCrypto.encrypt(kwargs['extra_option']['secret'])
|
||||
if isinstance(kwargs.get('extra_option'), dict) and kwargs['extra_option'].get('password'):
|
||||
kwargs['extra_option']['password'] = AESCrypto.encrypt(kwargs['extra_option']['password'])
|
||||
|
||||
inst = self._can_update(_id=_id, **kwargs)
|
||||
if inst.agent_id != kwargs.get('agent_id') or inst.query_expr != kwargs.get('query_expr'):
|
||||
@@ -396,6 +439,9 @@ class AutoDiscoveryCITypeCRUD(DBMixin):
|
||||
item.delete(commit=False)
|
||||
db.session.commit()
|
||||
|
||||
SystemConfigManager.create_or_update("ad_rules_updated_at",
|
||||
dict(v=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
|
||||
|
||||
obj = inst.update(_id=_id, filter_none=False, **kwargs)
|
||||
|
||||
return obj
|
||||
@@ -429,6 +475,10 @@ class AutoDiscoveryCITypeCRUD(DBMixin):
|
||||
class AutoDiscoveryCITypeRelationCRUD(DBMixin):
|
||||
cls = AutoDiscoveryCITypeRelation
|
||||
|
||||
@classmethod
|
||||
def get_all(cls, type_ids=None):
|
||||
res = cls.cls.get_by(to_dict=False)
|
||||
return [i for i in res if type_ids is None or i.ad_type_id in type_ids]
|
||||
@classmethod
|
||||
def get_by_type_id(cls, type_id, to_dict=False):
|
||||
return cls.cls.get_by(ad_type_id=type_id, to_dict=to_dict)
|
||||
@@ -668,12 +718,13 @@ class AutoDiscoveryHTTPManager(object):
|
||||
categories = (ClOUD_MAP.get(name) or {}) or []
|
||||
for item in copy.deepcopy(categories):
|
||||
item.pop('map', None)
|
||||
item.pop('collect_key_map', None)
|
||||
|
||||
return categories
|
||||
|
||||
def get_resources(self, name):
|
||||
en_name = None
|
||||
for i in DEFAULT_HTTP:
|
||||
for i in DEFAULT_INNER:
|
||||
if i['name'] == name:
|
||||
en_name = i['en']
|
||||
break
|
||||
@@ -709,6 +760,17 @@ class AutoDiscoverySNMPManager(object):
|
||||
return []
|
||||
|
||||
|
||||
class AutoDiscoveryComponentsManager(object):
|
||||
|
||||
@staticmethod
|
||||
def get_attributes(name):
|
||||
if os.path.exists(os.path.join(PWD, "templates/{}.json".format(name))):
|
||||
with open(os.path.join(PWD, "templates/{}.json".format(name))) as f:
|
||||
return json.loads(f.read())
|
||||
|
||||
return []
|
||||
|
||||
|
||||
class AutoDiscoveryRuleSyncHistoryCRUD(DBMixin):
|
||||
cls = AutoDiscoveryRuleSyncHistory
|
||||
|
||||
|
@@ -2,16 +2,28 @@
|
||||
|
||||
from api.lib.cmdb.const import AutoDiscoveryType
|
||||
|
||||
DEFAULT_HTTP = [
|
||||
PRIVILEGED_USERS = ("cmdb_agent", "worker", "admin")
|
||||
|
||||
DEFAULT_INNER = [
|
||||
dict(name="阿里云", en="aliyun", type=AutoDiscoveryType.HTTP, is_inner=True, is_plugin=False,
|
||||
option={'icon': {'name': 'caise-aliyun'}}),
|
||||
option={'icon': {'name': 'caise-aliyun'}, "en": "aliyun"}),
|
||||
dict(name="腾讯云", en="tencentcloud", type=AutoDiscoveryType.HTTP, is_inner=True, is_plugin=False,
|
||||
option={'icon': {'name': 'caise-tengxunyun'}}),
|
||||
option={'icon': {'name': 'caise-tengxunyun'}, "en": "tencentcloud"}),
|
||||
dict(name="华为云", en="huaweicloud", type=AutoDiscoveryType.HTTP, is_inner=True, is_plugin=False,
|
||||
option={'icon': {'name': 'caise-huaweiyun'}}),
|
||||
option={'icon': {'name': 'caise-huaweiyun'}, "en": "huaweicloud"}),
|
||||
dict(name="AWS", en="aws", type=AutoDiscoveryType.HTTP, is_inner=True, is_plugin=False,
|
||||
option={'icon': {'name': 'caise-aws'}}),
|
||||
|
||||
dict(name="VCenter", en="vcenter", type=AutoDiscoveryType.HTTP, is_inner=True, is_plugin=False,
|
||||
option={'icon': {'name': 'cmdb-vcenter'}, "category": "private_cloud", "en": "vcenter"}),
|
||||
dict(name="KVM", en="kvm", type=AutoDiscoveryType.HTTP, is_inner=True, is_plugin=False,
|
||||
option={'icon': {'name': 'ops-KVM'}, "category": "private_cloud", "en": "kvm"}),
|
||||
|
||||
dict(name="Nginx", en="nginx", type=AutoDiscoveryType.COMPONENTS, is_inner=True, is_plugin=False,
|
||||
option={'icon': {'name': 'caise-nginx'}, "en": "nginx"}),
|
||||
dict(name="Redis", en="redis", type=AutoDiscoveryType.COMPONENTS, is_inner=True, is_plugin=False,
|
||||
option={'icon': {'name': 'caise-redis'}, "en": "redis"}),
|
||||
|
||||
dict(name="交换机", type=AutoDiscoveryType.SNMP, is_inner=True, is_plugin=False,
|
||||
option={'icon': {'name': 'caise-jiaohuanji'}}),
|
||||
dict(name="路由器", type=AutoDiscoveryType.SNMP, is_inner=True, is_plugin=False,
|
||||
@@ -23,47 +35,306 @@ DEFAULT_HTTP = [
|
||||
]
|
||||
|
||||
ClOUD_MAP = {
|
||||
"aliyun": [{
|
||||
"category": "计算",
|
||||
"items": ["云服务器 ECS"],
|
||||
"map": {
|
||||
"云服务器 ECS": "templates/aliyun_ecs.json",
|
||||
"aliyun": [
|
||||
{
|
||||
"category": "计算",
|
||||
"items": ["云服务器 ECS", "云服务器 Disk"],
|
||||
"map": {
|
||||
"云服务器 ECS": "templates/aliyun_ecs.json",
|
||||
"云服务器 Disk": "templates/aliyun_ecs_disk2.json",
|
||||
},
|
||||
"collect_key_map": {
|
||||
"云服务器 ECS": "ali.ecs",
|
||||
"云服务器 Disk": "ali.ecs_disk",
|
||||
},
|
||||
},
|
||||
"collect_key_map": {
|
||||
"云服务器 ECS": "ali.ecs",
|
||||
}
|
||||
}],
|
||||
|
||||
"tencentcloud": [{
|
||||
"category": "计算",
|
||||
"items": ["云服务器 CVM"],
|
||||
"map": {
|
||||
"云服务器 CVM": "templates/tencent_cvm.json",
|
||||
{
|
||||
"category": "网络与CDN",
|
||||
"items": [
|
||||
"内容分发CDN",
|
||||
"负载均衡SLB",
|
||||
"专有网络VPC",
|
||||
"交换机Switch",
|
||||
],
|
||||
"map": {
|
||||
"内容分发CDN": "templates/aliyun_cdn.json",
|
||||
"负载均衡SLB": "templates/aliyun_slb.json",
|
||||
"专有网络VPC": "templates/aliyun_vpc.json",
|
||||
"交换机Switch": "templates/aliyun_switch.json",
|
||||
},
|
||||
"collect_key_map": {
|
||||
"内容分发CDN": "ali.cdn",
|
||||
"负载均衡SLB": "ali.slb",
|
||||
"专有网络VPC": "ali.vpc",
|
||||
"交换机Switch": "ali.switch",
|
||||
},
|
||||
},
|
||||
"collect_key_map": {
|
||||
"云服务器 CVM": "tencent.cvm",
|
||||
}
|
||||
}],
|
||||
|
||||
"huaweicloud": [{
|
||||
"category": "计算",
|
||||
"items": ["云服务器 ECS"],
|
||||
"map": {
|
||||
"云服务器 ECS": "templates/huaweicloud_ecs.json",
|
||||
{
|
||||
"category": "存储",
|
||||
"items": ["块存储EBS", "对象存储OSS"],
|
||||
"map": {
|
||||
"块存储EBS": "templates/aliyun_ebs.json",
|
||||
"对象存储OSS": "templates/aliyun_oss.json",
|
||||
},
|
||||
"collect_key_map": {
|
||||
"块存储EBS": "ali.ebs",
|
||||
"对象存储OSS": "ali.oss",
|
||||
},
|
||||
},
|
||||
"collect_key_map": {
|
||||
"云服务器 ECS": "huawei.ecs",
|
||||
}
|
||||
}],
|
||||
|
||||
"aws": [{
|
||||
"category": "计算",
|
||||
"items": ["云服务器 EC2"],
|
||||
"map": {
|
||||
"云服务器 EC2": "templates/aws_ec2.json",
|
||||
{
|
||||
"category": "数据库",
|
||||
"items": ["云数据库RDS MySQL", "云数据库RDS PostgreSQL", "云数据库 Redis"],
|
||||
"map": {
|
||||
"云数据库RDS MySQL": "templates/aliyun_rds_mysql.json",
|
||||
"云数据库RDS PostgreSQL": "templates/aliyun_rds_postgre.json",
|
||||
"云数据库 Redis": "templates/aliyun_redis.json",
|
||||
},
|
||||
"collect_key_map": {
|
||||
"云数据库RDS MySQL": "ali.rds_mysql",
|
||||
"云数据库RDS PostgreSQL": "ali.rds_postgre",
|
||||
"云数据库 Redis": "ali.redis",
|
||||
},
|
||||
},
|
||||
"collect_key_map": {
|
||||
"云服务器 EC2": "aws.ec2",
|
||||
}
|
||||
}],
|
||||
],
|
||||
"tencentcloud": [
|
||||
{
|
||||
"category": "计算",
|
||||
"items": ["云服务器 CVM"],
|
||||
"map": {
|
||||
"云服务器 CVM": "templates/tencent_cvm.json",
|
||||
},
|
||||
"collect_key_map": {
|
||||
"云服务器 CVM": "tencent.cvm",
|
||||
},
|
||||
},
|
||||
{
|
||||
"category": "CDN与边缘",
|
||||
"items": ["内容分发CDN"],
|
||||
"map": {
|
||||
"内容分发CDN": "templates/tencent_cdn.json",
|
||||
},
|
||||
"collect_key_map": {
|
||||
"内容分发CDN": "tencent.cdn",
|
||||
},
|
||||
},
|
||||
{
|
||||
"category": "网络",
|
||||
"items": ["负载均衡CLB", "私有网络VPC", "子网"],
|
||||
"map": {
|
||||
"负载均衡CLB": "templates/tencent_clb.json",
|
||||
"私有网络VPC": "templates/tencent_vpc.json",
|
||||
"子网": "templates/tencent_subnet.json",
|
||||
},
|
||||
"collect_key_map": {
|
||||
"负载均衡CLB": "tencent.clb",
|
||||
"私有网络VPC": "tencent.vpc",
|
||||
"子网": "tencent.subnet",
|
||||
},
|
||||
},
|
||||
{
|
||||
"category": "存储",
|
||||
"items": ["云硬盘CBS", "对象存储COS"],
|
||||
"map": {
|
||||
"云硬盘CBS": "templates/tencent_cbs.json",
|
||||
"对象存储OSS": "templates/tencent_cos.json",
|
||||
},
|
||||
"collect_key_map": {
|
||||
"云硬盘CBS": "tencent.cbs",
|
||||
"对象存储OSS": "tencent.cos",
|
||||
},
|
||||
},
|
||||
{
|
||||
"category": "数据库",
|
||||
"items": ["云数据库 MySQL", "云数据库 PostgreSQL", "云数据库 Redis"],
|
||||
"map": {
|
||||
"云数据库 MySQL": "templates/tencent_rdb.json",
|
||||
"云数据库 PostgreSQL": "templates/tencent_postgres.json",
|
||||
"云数据库 Redis": "templates/tencent_redis.json",
|
||||
},
|
||||
"collect_key_map": {
|
||||
"云数据库 MySQL": "tencent.rdb",
|
||||
"云数据库 PostgreSQL": "tencent.rds_postgres",
|
||||
"云数据库 Redis": "tencent.redis",
|
||||
},
|
||||
},
|
||||
],
|
||||
"huaweicloud": [
|
||||
{
|
||||
"category": "计算",
|
||||
"items": ["云服务器 ECS"],
|
||||
"map": {
|
||||
"云服务器 ECS": "templates/huaweicloud_ecs.json",
|
||||
},
|
||||
"collect_key_map": {
|
||||
"云服务器 ECS": "huawei.ecs",
|
||||
},
|
||||
},
|
||||
{
|
||||
"category": "CDN与智能边缘",
|
||||
"items": ["内容分发网络CDN"],
|
||||
"map": {
|
||||
"内容分发网络CDN": "templates/huawei_cdn.json",
|
||||
},
|
||||
"collect_key_map": {
|
||||
"内容分发网络CDN": "huawei.cdn",
|
||||
},
|
||||
},
|
||||
{
|
||||
"category": "网络",
|
||||
"items": ["弹性负载均衡ELB", "虚拟私有云VPC", "子网"],
|
||||
"map": {
|
||||
"弹性负载均衡ELB": "templates/huawei_elb.json",
|
||||
"虚拟私有云VPC": "templates/huawei_vpc.json",
|
||||
"子网": "templates/huawei_subnet.json",
|
||||
},
|
||||
"collect_key_map": {
|
||||
"弹性负载均衡ELB": "huawei.elb",
|
||||
"虚拟私有云VPC": "huawei.vpc",
|
||||
"子网": "huawei.subnet",
|
||||
},
|
||||
},
|
||||
{
|
||||
"category": "存储",
|
||||
"items": ["云硬盘EVS", "对象存储OBS"],
|
||||
"map": {
|
||||
"云硬盘EVS": "templates/huawei_evs.json",
|
||||
"对象存储OBS": "templates/huawei_obs.json",
|
||||
},
|
||||
"collect_key_map": {
|
||||
"云硬盘EVS": "huawei.evs",
|
||||
"对象存储OBS": "huawei.obs",
|
||||
},
|
||||
},
|
||||
{
|
||||
"category": "数据库",
|
||||
"items": ["云数据库RDS MySQL", "云数据库RDS PostgreSQL"],
|
||||
"map": {
|
||||
"云数据库RDS MySQL": "templates/huawei_rds_mysql.json",
|
||||
"云数据库RDSPostgreSQL": "templates/huaweirds_postgre.json",
|
||||
},
|
||||
"collect_key_map": {
|
||||
"云数据库RDS MySQL": "huawei.rds_mysql",
|
||||
"云数据库RDS PostgreSQL": "huawei.rds_postgre",
|
||||
},
|
||||
},
|
||||
{
|
||||
"category": "应用中间件",
|
||||
"items": ["分布式缓存Redis"],
|
||||
"map": {
|
||||
"分布式缓存Redis": "templates/huawei_dcs.json",
|
||||
},
|
||||
"collect_key_map": {
|
||||
"分布式缓存Redis": "huawei.dcs",
|
||||
},
|
||||
},
|
||||
],
|
||||
"aws": [
|
||||
{
|
||||
"category": "计算",
|
||||
"items": ["云服务器 EC2"],
|
||||
"map": {
|
||||
"云服务器 EC2": "templates/aws_ec2.json",
|
||||
},
|
||||
"collect_key_map": {
|
||||
"云服务器 EC2": "aws.ec2",
|
||||
},
|
||||
},
|
||||
{"category": "网络与CDN", "items": [], "map": {}, "collect_key_map": {}},
|
||||
],
|
||||
"vcenter": [
|
||||
{
|
||||
"category": "计算",
|
||||
"items": [
|
||||
"主机",
|
||||
"虚拟机",
|
||||
"主机集群"
|
||||
],
|
||||
"map": {
|
||||
"主机": "templates/vsphere_host.json",
|
||||
"虚拟机": "templates/vsphere_vm.json",
|
||||
"主机集群": "templates/vsphere_cluster.json",
|
||||
},
|
||||
"collect_key_map": {
|
||||
"主机": "vsphere.host",
|
||||
"虚拟机": "vsphere.vm",
|
||||
"主机集群": "vsphere.cluster",
|
||||
},
|
||||
},
|
||||
{
|
||||
"category": "网络",
|
||||
"items": [
|
||||
"网络",
|
||||
"标准交换机",
|
||||
"分布式交换机",
|
||||
],
|
||||
"map": {
|
||||
"网络": "templates/vsphere_network.json",
|
||||
"标准交换机": "templates/vsphere_standard_switch.json",
|
||||
"分布式交换机": "templates/vsphere_distributed_switch.json",
|
||||
},
|
||||
"collect_key_map": {
|
||||
"网络": "vsphere.network",
|
||||
"标准交换机": "vsphere.standard_switch",
|
||||
"分布式交换机": "vsphere.distributed_switch",
|
||||
},
|
||||
},
|
||||
{
|
||||
"category": "存储",
|
||||
"items": ["数据存储", "数据存储集群"],
|
||||
"map": {
|
||||
"数据存储": "templates/vsphere_datastore.json",
|
||||
"数据存储集群": "templates/vsphere.storage_pod.json",
|
||||
},
|
||||
"collect_key_map": {
|
||||
"数据存储": "vsphere.datastore",
|
||||
"数据存储集群": "vsphere.storage_pod",
|
||||
},
|
||||
},
|
||||
{
|
||||
"category": "其他",
|
||||
"items": ["资源池", "数据中心", "文件夹"],
|
||||
"map": {
|
||||
"资源池": "templates/vsphere_datastore.json",
|
||||
"数据中心": "templates/vsphere_datacenter.json",
|
||||
"文件夹": "templates/vsphere_folder.json",
|
||||
},
|
||||
"collect_key_map": {
|
||||
"资源池": "vsphere.pool",
|
||||
"数据中心": "vsphere.datacenter",
|
||||
"文件夹": "vsphere.folder",
|
||||
},
|
||||
},
|
||||
],
|
||||
"kvm": [
|
||||
{
|
||||
"category": "计算",
|
||||
"items": ["虚拟机"],
|
||||
"map": {
|
||||
"虚拟机": "templates/kvm_vm.json",
|
||||
},
|
||||
"collect_key_map": {
|
||||
"虚拟机": "kvm.vm",
|
||||
},
|
||||
},
|
||||
{
|
||||
"category": "存储",
|
||||
"items": ["存储"],
|
||||
"map": {
|
||||
"存储": "templates/kvm_storage.json",
|
||||
},
|
||||
"collect_key_map": {
|
||||
"存储": "kvm.storage",
|
||||
},
|
||||
},
|
||||
{
|
||||
"category": "network",
|
||||
"items": ["网络"],
|
||||
"map": {
|
||||
"网络": "templates/kvm_network.json",
|
||||
},
|
||||
"collect_key_map": {
|
||||
"网络": "kvm.network",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
@@ -1,7 +1,6 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
import copy
|
||||
|
||||
import toposort
|
||||
from flask import abort
|
||||
from flask import current_app
|
||||
@@ -84,7 +83,7 @@ class CITypeManager(object):
|
||||
self.cls.id, self.cls.icon, self.cls.name).filter(self.cls.deleted.is_(False))}
|
||||
|
||||
@staticmethod
|
||||
def get_ci_types(type_name=None, like=True):
|
||||
def get_ci_types(type_name=None, like=True, type_ids=None):
|
||||
resources = None
|
||||
if current_app.config.get('USE_ACL') and not is_app_admin('cmdb'):
|
||||
resources = set([i.get('name') for i in ACLManager().get_resources(ResourceTypeEnum.CI_TYPE)])
|
||||
@@ -93,6 +92,9 @@ class CITypeManager(object):
|
||||
CIType.get_by_like(name=type_name) if like else CIType.get_by(name=type_name))
|
||||
res = list()
|
||||
for type_dict in ci_types:
|
||||
if type_ids is not None and type_dict['id'] not in type_ids:
|
||||
continue
|
||||
|
||||
attr = AttributeCache.get(type_dict["unique_id"])
|
||||
type_dict["unique_key"] = attr and attr.name
|
||||
if type_dict.get('show_id'):
|
||||
@@ -292,6 +294,12 @@ class CITypeManager(object):
|
||||
class CITypeInheritanceManager(object):
|
||||
cls = CITypeInheritance
|
||||
|
||||
@classmethod
|
||||
def get_all(cls, type_ids=None):
|
||||
res = cls.cls.get_by(to_dict=True)
|
||||
|
||||
return [i for i in res if type_ids is None or (i['parent_id'] in type_ids and i['child_id'] in type_ids)]
|
||||
|
||||
@classmethod
|
||||
def get_parents(cls, type_id):
|
||||
return [i.parent_id for i in cls.cls.get_by(child_id=type_id, to_dict=False)]
|
||||
@@ -387,7 +395,7 @@ class CITypeGroupManager(object):
|
||||
cls = CITypeGroup
|
||||
|
||||
@staticmethod
|
||||
def get(need_other=None, config_required=True):
|
||||
def get(need_other=None, config_required=True, type_ids=None, ci_types=None):
|
||||
resources = None
|
||||
if current_app.config.get('USE_ACL'):
|
||||
resources = ACLManager('cmdb').get_resources(ResourceTypeEnum.CI)
|
||||
@@ -401,6 +409,8 @@ class CITypeGroupManager(object):
|
||||
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']).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):
|
||||
ci_type['permissions'] = resources[ci_type['name']] if resources is not None else None
|
||||
ci_type['inherited'] = True if CITypeInheritanceManager.get_parents(ci_type['id']) else False
|
||||
@@ -408,7 +418,7 @@ class CITypeGroupManager(object):
|
||||
group_types.add(t["type_id"])
|
||||
|
||||
if need_other:
|
||||
ci_types = CITypeManager.get_ci_types()
|
||||
ci_types = CITypeManager.get_ci_types(type_ids=type_ids) if ci_types is None else ci_types
|
||||
other_types = dict(ci_types=[])
|
||||
for ci_type in ci_types:
|
||||
if ci_type["id"] not in group_types and (resources is None or ci_type['name'] in resources):
|
||||
@@ -529,6 +539,8 @@ class CITypeAttributeManager(object):
|
||||
attrs = CITypeAttributesCache.get(_type_id)
|
||||
for attr in sorted(attrs, key=lambda x: (x.order, x.id)):
|
||||
attr_dict = AttributeManager().get_attribute(attr.attr_id, choice_web_hook_parse, choice_other_parse)
|
||||
if not attr_dict:
|
||||
continue
|
||||
attr_dict["is_required"] = attr.is_required
|
||||
attr_dict["order"] = attr.order
|
||||
attr_dict["default_show"] = attr.default_show
|
||||
@@ -537,7 +549,6 @@ class CITypeAttributeManager(object):
|
||||
if not has_config_perm:
|
||||
attr_dict.pop('choice_web_hook', None)
|
||||
attr_dict.pop('choice_other', None)
|
||||
|
||||
if attr_dict['id'] not in id2pos:
|
||||
id2pos[attr_dict['id']] = len(result)
|
||||
result.append(attr_dict)
|
||||
@@ -602,7 +613,6 @@ class CITypeAttributeManager(object):
|
||||
if existed is not None:
|
||||
continue
|
||||
|
||||
current_app.logger.debug(attr_id)
|
||||
CITypeAttribute.create(type_id=type_id, attr_id=attr_id, **kwargs)
|
||||
|
||||
attr = AttributeCache.get(attr_id)
|
||||
@@ -769,11 +779,15 @@ class CITypeRelationManager(object):
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def get():
|
||||
def get(type_ids=None):
|
||||
res = CITypeRelation.get_by(to_dict=False)
|
||||
type2attributes = dict()
|
||||
result = []
|
||||
for idx, item in enumerate(res):
|
||||
_item = item.to_dict()
|
||||
if type_ids is not None and _item['parent_id'] not in type_ids and _item['child_id'] not in type_ids:
|
||||
continue
|
||||
|
||||
res[idx] = _item
|
||||
res[idx]['parent'] = item.parent.to_dict()
|
||||
if item.parent_id not in type2attributes:
|
||||
@@ -785,7 +799,9 @@ class CITypeRelationManager(object):
|
||||
CITypeAttributeManager.get_all_attributes(item.child_id)]
|
||||
res[idx]['relation_type'] = item.relation_type.to_dict()
|
||||
|
||||
return res, type2attributes
|
||||
result.append(res[idx])
|
||||
|
||||
return result, type2attributes
|
||||
|
||||
@staticmethod
|
||||
def get_child_type_ids(type_id, level):
|
||||
@@ -1034,7 +1050,8 @@ class CITypeAttributeGroupManager(object):
|
||||
parent_ids = CITypeInheritanceManager.base(type_id)
|
||||
|
||||
groups = []
|
||||
id2type = {i: CITypeCache.get(i).alias for i in parent_ids}
|
||||
id2type = {i: CITypeCache.get(i) for i in parent_ids}
|
||||
id2type = {k: v.alias for k, v in id2type.items() if v}
|
||||
for _type_id in parent_ids + [type_id]:
|
||||
_groups = CITypeAttributeGroup.get_by(type_id=_type_id)
|
||||
_groups = sorted(_groups, key=lambda x: x["order"] or 0)
|
||||
@@ -1308,13 +1325,14 @@ class CITypeTemplateManager(object):
|
||||
attributes = [attr for type_id in type2attributes for attr in type2attributes[type_id]]
|
||||
attrs = []
|
||||
for i in copy.deepcopy(attributes):
|
||||
if i.pop('inherited', None):
|
||||
continue
|
||||
i.pop('default_show', None)
|
||||
i.pop('is_required', None)
|
||||
i.pop('order', None)
|
||||
i.pop('choice_web_hook', None)
|
||||
i.pop('choice_other', None)
|
||||
i.pop('order', None)
|
||||
i.pop('inherited', None)
|
||||
i.pop('inherited_from', None)
|
||||
choice_value = i.pop('choice_value', None)
|
||||
if not choice_value:
|
||||
@@ -1334,6 +1352,7 @@ class CITypeTemplateManager(object):
|
||||
for i in ci_types:
|
||||
i.pop("unique_key", None)
|
||||
i.pop("show_name", None)
|
||||
i.pop("parent_ids", None)
|
||||
i['unique_id'] = attr_id_map.get(i['unique_id'], i['unique_id'])
|
||||
if i.get('show_id'):
|
||||
i['show_id'] = attr_id_map.get(i['show_id'], i['show_id'])
|
||||
@@ -1371,7 +1390,7 @@ class CITypeTemplateManager(object):
|
||||
return self.__import(RelationType, relation_types)
|
||||
|
||||
@staticmethod
|
||||
def _import_ci_type_relations(ci_type_relations, type_id_map, relation_type_id_map):
|
||||
def _import_ci_type_relations(ci_type_relations, type_id_map, relation_type_id_map, attr_id_map):
|
||||
for i in ci_type_relations:
|
||||
i.pop('parent', None)
|
||||
i.pop('child', None)
|
||||
@@ -1381,15 +1400,32 @@ class CITypeTemplateManager(object):
|
||||
i['child_id'] = type_id_map.get(i['child_id'], i['child_id'])
|
||||
i['relation_type_id'] = relation_type_id_map.get(i['relation_type_id'], i['relation_type_id'])
|
||||
|
||||
i['parent_attr_ids'] = [attr_id_map.get(attr_id, attr_id) for attr_id in i.get('parent_attr_ids') or []]
|
||||
i['child_attr_ids'] = [attr_id_map.get(attr_id, attr_id) for attr_id in i.get('child_attr_ids') or []]
|
||||
try:
|
||||
CITypeRelationManager.add(i.get('parent_id'),
|
||||
i.get('child_id'),
|
||||
i.get('relation_type_id'),
|
||||
i.get('constraint'),
|
||||
parent_attr_ids=i.get('parent_attr_ids', []),
|
||||
child_attr_ids=i.get('child_attr_ids', []),
|
||||
)
|
||||
except BadRequest:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def _import_ci_type_inheritance(ci_type_inheritance, type_id_map):
|
||||
|
||||
for i in ci_type_inheritance:
|
||||
i['parent_id'] = type_id_map.get(i['parent_id'])
|
||||
i['child_id'] = type_id_map.get(i['child_id'])
|
||||
|
||||
if i['parent_id'] and i['child_id']:
|
||||
try:
|
||||
CITypeInheritanceManager.add([i.get('parent_id')], i.get('child_id'))
|
||||
except BadRequest:
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def _import_type_attributes(type2attributes, type_id_map, attr_id_map):
|
||||
for type_id in type2attributes:
|
||||
@@ -1401,6 +1437,9 @@ class CITypeTemplateManager(object):
|
||||
|
||||
handled = set()
|
||||
for attr in type2attributes[type_id]:
|
||||
if attr.get('inherited'):
|
||||
continue
|
||||
|
||||
payload = dict(type_id=type_id_map.get(int(type_id), type_id),
|
||||
attr_id=attr_id_map.get(attr['id'], attr['id']),
|
||||
default_show=attr['default_show'],
|
||||
@@ -1464,6 +1503,9 @@ class CITypeTemplateManager(object):
|
||||
|
||||
for rule in rules:
|
||||
ci_type = CITypeCache.get(rule.pop('type_name', None))
|
||||
if ci_type is None:
|
||||
continue
|
||||
|
||||
adr = rule.pop('adr', {}) or {}
|
||||
|
||||
if ci_type:
|
||||
@@ -1476,10 +1518,10 @@ class CITypeTemplateManager(object):
|
||||
|
||||
if ad_rule:
|
||||
rule['adr_id'] = ad_rule.id
|
||||
ad_rule.update(**adr)
|
||||
ad_rule.update(valid=False, **adr)
|
||||
|
||||
elif adr:
|
||||
ad_rule = AutoDiscoveryRuleCRUD().add(**adr)
|
||||
ad_rule = AutoDiscoveryRuleCRUD().add(valid=False, **adr)
|
||||
rule['adr_id'] = ad_rule.id
|
||||
else:
|
||||
continue
|
||||
@@ -1508,6 +1550,23 @@ class CITypeTemplateManager(object):
|
||||
except Exception as e:
|
||||
current_app.logger.warning("import auto discovery rules failed: {}".format(e))
|
||||
|
||||
@staticmethod
|
||||
def _import_auto_discovery_relation_rules(rules):
|
||||
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryCITypeRelationCRUD
|
||||
|
||||
for rule in rules:
|
||||
ad_ci_type = CITypeCache.get(rule.pop('ad_type_name', None))
|
||||
peer_ci_type = CITypeCache.get(rule.pop('peer_type_name', None))
|
||||
peer_attr = AttributeCache.get(rule.pop('peer_attr_name', None))
|
||||
if ad_ci_type and peer_attr and peer_ci_type:
|
||||
if not AutoDiscoveryCITypeRelation.get_by(
|
||||
ad_type_id=ad_ci_type.id, ad_key=rule.get('ad_key'),
|
||||
peer_attr_id=peer_attr.id, peer_type_id=peer_ci_type.id):
|
||||
AutoDiscoveryCITypeRelationCRUD().add(ad_type_id=ad_ci_type.id,
|
||||
ad_key=rule.get('ad_key'),
|
||||
peer_attr_id=peer_attr.id,
|
||||
peer_type_id=peer_ci_type.id)
|
||||
|
||||
@staticmethod
|
||||
def _import_icons(icons):
|
||||
from api.lib.common_setting.upload_file import CommonFileCRUD
|
||||
@@ -1519,6 +1578,8 @@ class CITypeTemplateManager(object):
|
||||
current_app.logger.warning("save icon failed: {}".format(e))
|
||||
|
||||
def import_template(self, tpt):
|
||||
db.session.commit()
|
||||
|
||||
import time
|
||||
s = time.time()
|
||||
attr_id_map = self._import_attributes(tpt.get('type2attributes') or {})
|
||||
@@ -1537,9 +1598,14 @@ class CITypeTemplateManager(object):
|
||||
current_app.logger.info('import relation_types cost: {}'.format(time.time() - s))
|
||||
|
||||
s = time.time()
|
||||
self._import_ci_type_relations(tpt.get('ci_type_relations') or [], ci_type_id_map, relation_type_id_map)
|
||||
self._import_ci_type_relations(tpt.get('ci_type_relations') or [],
|
||||
ci_type_id_map, relation_type_id_map, attr_id_map)
|
||||
current_app.logger.info('import ci_type_relations cost: {}'.format(time.time() - s))
|
||||
|
||||
s = time.time()
|
||||
self._import_ci_type_inheritance(tpt.get('ci_type_inheritance') or [], ci_type_id_map)
|
||||
current_app.logger.info('import ci_type_inheritance cost: {}'.format(time.time() - s))
|
||||
|
||||
s = time.time()
|
||||
self._import_type_attributes(tpt.get('type2attributes') or {}, ci_type_id_map, attr_id_map)
|
||||
current_app.logger.info('import type2attributes cost: {}'.format(time.time() - s))
|
||||
@@ -1552,26 +1618,73 @@ class CITypeTemplateManager(object):
|
||||
self._import_auto_discovery_rules(tpt.get('ci_type_auto_discovery_rules') or [])
|
||||
current_app.logger.info('import ci_type_auto_discovery_rules cost: {}'.format(time.time() - s))
|
||||
|
||||
s = time.time()
|
||||
self._import_auto_discovery_relation_rules(tpt.get('ci_type_auto_discovery_relation_rules') or [])
|
||||
current_app.logger.info('import ci_type_auto_discovery_relation_rules cost: {}'.format(time.time() - s))
|
||||
|
||||
s = time.time()
|
||||
self._import_icons(tpt.get('icons') or {})
|
||||
current_app.logger.info('import icons cost: {}'.format(time.time() - s))
|
||||
|
||||
@staticmethod
|
||||
def export_template():
|
||||
def export_template(type_ids=None):
|
||||
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryCITypeCRUD
|
||||
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryCITypeRelationCRUD
|
||||
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryRuleCRUD
|
||||
from api.lib.common_setting.upload_file import CommonFileCRUD
|
||||
|
||||
ci_types = CITypeManager.get_ci_types(type_ids=type_ids)
|
||||
extend_type_ids = []
|
||||
for ci_type in ci_types:
|
||||
if ci_type.get('parent_ids'):
|
||||
extend_type_ids.extend(CITypeInheritanceManager.base(ci_type['id']))
|
||||
extend_type_ids = list(set(extend_type_ids) - set(type_ids))
|
||||
|
||||
ci_type_relations = CITypeRelationManager.get(type_ids=type_ids)[0]
|
||||
for i in ci_type_relations:
|
||||
if i['parent_id'] not in type_ids:
|
||||
extend_type_ids.append(i['parent_id'])
|
||||
if i['child_id'] not in type_ids:
|
||||
extend_type_ids.append(i['child_id'])
|
||||
|
||||
ad_relation_rules = AutoDiscoveryCITypeRelationCRUD.get_all(type_ids=type_ids)
|
||||
rules = []
|
||||
for r in ad_relation_rules:
|
||||
if r.peer_type_id not in type_ids:
|
||||
extend_type_ids.append(r.peer_type_id)
|
||||
|
||||
r = r.to_dict()
|
||||
r['ad_type_name'] = CITypeCache.get(r.pop('ad_type_id')).name
|
||||
peer_type_id = r.pop("peer_type_id")
|
||||
peer_type_name = CITypeCache.get(peer_type_id).name
|
||||
if not peer_type_name:
|
||||
peer_type = CITypeCache.get(peer_type_id)
|
||||
peer_type_name = peer_type and peer_type.name
|
||||
r['peer_type_name'] = peer_type_name
|
||||
peer_attr_id = r.pop("peer_attr_id")
|
||||
peer_attr = AttributeCache.get(peer_attr_id)
|
||||
r['peer_attr_name'] = peer_attr and peer_attr.name
|
||||
|
||||
rules.append(r)
|
||||
ci_type_auto_discovery_relation_rules = rules
|
||||
|
||||
if extend_type_ids:
|
||||
extend_type_ids = list(set(extend_type_ids))
|
||||
type_ids.extend(extend_type_ids)
|
||||
ci_types.extend(CITypeManager.get_ci_types(type_ids=extend_type_ids))
|
||||
|
||||
tpt = dict(
|
||||
ci_types=CITypeManager.get_ci_types(),
|
||||
ci_type_groups=CITypeGroupManager.get(),
|
||||
ci_types=ci_types,
|
||||
relation_types=[i.to_dict() for i in RelationTypeManager.get_all()],
|
||||
ci_type_relations=CITypeRelationManager.get()[0],
|
||||
ci_type_relations=ci_type_relations,
|
||||
ci_type_inheritance=CITypeInheritanceManager.get_all(type_ids=type_ids),
|
||||
ci_type_auto_discovery_rules=list(),
|
||||
ci_type_auto_discovery_relation_rules=ci_type_auto_discovery_relation_rules,
|
||||
type2attributes=dict(),
|
||||
type2attribute_group=dict(),
|
||||
icons=dict()
|
||||
)
|
||||
tpt['ci_type_groups'] = CITypeGroupManager.get(ci_types=tpt['ci_types'], type_ids=type_ids)
|
||||
|
||||
def get_icon_value(icon):
|
||||
try:
|
||||
@@ -1579,12 +1692,13 @@ class CITypeTemplateManager(object):
|
||||
except:
|
||||
return ""
|
||||
|
||||
ad_rules = AutoDiscoveryCITypeCRUD.get_all()
|
||||
type_id2name = {i['id']: i['name'] for i in tpt['ci_types']}
|
||||
ad_rules = AutoDiscoveryCITypeCRUD.get_all(type_ids=type_ids)
|
||||
rules = []
|
||||
for r in ad_rules:
|
||||
r = r.to_dict()
|
||||
ci_type = CITypeCache.get(r.pop('type_id'))
|
||||
r['type_name'] = ci_type and ci_type.name
|
||||
|
||||
r['type_name'] = type_id2name.get(r.pop('type_id'))
|
||||
if r.get('adr_id'):
|
||||
adr = AutoDiscoveryRuleCRUD.get_by_id(r.pop('adr_id'))
|
||||
r['adr_name'] = adr and adr.name
|
||||
@@ -1618,65 +1732,6 @@ class CITypeTemplateManager(object):
|
||||
|
||||
return tpt
|
||||
|
||||
@staticmethod
|
||||
def export_template_by_type(type_id):
|
||||
ci_type = CITypeCache.get(type_id) or abort(404, ErrFormat.ci_type_not_found2.format("id={}".format(type_id)))
|
||||
|
||||
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryCITypeCRUD
|
||||
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryRuleCRUD
|
||||
from api.lib.common_setting.upload_file import CommonFileCRUD
|
||||
|
||||
tpt = dict(
|
||||
ci_types=CITypeManager.get_ci_types(type_name=ci_type.name, like=False),
|
||||
ci_type_auto_discovery_rules=list(),
|
||||
type2attributes=dict(),
|
||||
type2attribute_group=dict(),
|
||||
icons=dict()
|
||||
)
|
||||
|
||||
def get_icon_value(icon):
|
||||
try:
|
||||
return CommonFileCRUD().get_file_binary_str(icon)
|
||||
except:
|
||||
return ""
|
||||
|
||||
ad_rules = AutoDiscoveryCITypeCRUD.get_by_type_id(ci_type.id)
|
||||
rules = []
|
||||
for r in ad_rules:
|
||||
r = r.to_dict()
|
||||
r['type_name'] = ci_type and ci_type.name
|
||||
if r.get('adr_id'):
|
||||
adr = AutoDiscoveryRuleCRUD.get_by_id(r.pop('adr_id'))
|
||||
r['adr_name'] = adr and adr.name
|
||||
r['adr'] = adr and adr.to_dict() or {}
|
||||
|
||||
icon_url = r['adr'].get('option', {}).get('icon', {}).get('url')
|
||||
if icon_url and icon_url not in tpt['icons']:
|
||||
tpt['icons'][icon_url] = get_icon_value(icon_url)
|
||||
|
||||
rules.append(r)
|
||||
tpt['ci_type_auto_discovery_rules'] = rules
|
||||
|
||||
for ci_type in tpt['ci_types']:
|
||||
if ci_type['icon'] and len(ci_type['icon'].split('$$')) > 3:
|
||||
icon_url = ci_type['icon'].split('$$')[3]
|
||||
if icon_url not in tpt['icons']:
|
||||
tpt['icons'][icon_url] = get_icon_value(icon_url)
|
||||
|
||||
tpt['type2attributes'][ci_type['id']] = CITypeAttributeManager.get_attributes_by_type_id(
|
||||
ci_type['id'], choice_web_hook_parse=False, choice_other_parse=False)
|
||||
|
||||
for attr in tpt['type2attributes'][ci_type['id']]:
|
||||
for i in (attr.get('choice_value') or []):
|
||||
if (i[1] or {}).get('icon', {}).get('url') and len(i[1]['icon']['url'].split('$$')) > 3:
|
||||
icon_url = i[1]['icon']['url'].split('$$')[3]
|
||||
if icon_url not in tpt['icons']:
|
||||
tpt['icons'][icon_url] = get_icon_value(icon_url)
|
||||
|
||||
tpt['type2attribute_group'][ci_type['id']] = CITypeAttributeGroupManager.get_by_type_id(ci_type['id'])
|
||||
|
||||
return tpt
|
||||
|
||||
|
||||
class CITypeUniqueConstraintManager(object):
|
||||
@staticmethod
|
||||
|
@@ -93,7 +93,8 @@ class RoleEnum(BaseEnum):
|
||||
class AutoDiscoveryType(BaseEnum):
|
||||
AGENT = "agent"
|
||||
SNMP = "snmp"
|
||||
HTTP = "http"
|
||||
HTTP = "http" # cloud
|
||||
COMPONENTS = "components"
|
||||
|
||||
|
||||
class AttributeDefaultValueEnum(BaseEnum):
|
||||
|
@@ -2,23 +2,24 @@
|
||||
import copy
|
||||
import json
|
||||
import uuid
|
||||
from io import BytesIO
|
||||
|
||||
from flask import abort
|
||||
from flask import current_app
|
||||
from flask import request
|
||||
from flask_login import current_user
|
||||
from io import BytesIO
|
||||
|
||||
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryCICRUD
|
||||
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryCITypeCRUD
|
||||
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryCITypeRelationCRUD
|
||||
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryComponentsManager
|
||||
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryCounterCRUD
|
||||
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryExecHistoryCRUD
|
||||
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryHTTPManager
|
||||
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryRuleCRUD
|
||||
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryRuleSyncHistoryCRUD
|
||||
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoverySNMPManager
|
||||
from api.lib.cmdb.auto_discovery.const import DEFAULT_HTTP
|
||||
from api.lib.cmdb.auto_discovery.const import DEFAULT_INNER
|
||||
from api.lib.cmdb.auto_discovery.const import PRIVILEGED_USERS
|
||||
from api.lib.cmdb.const import PermEnum
|
||||
from api.lib.cmdb.const import ResourceTypeEnum
|
||||
from api.lib.cmdb.resp_format import ErrFormat
|
||||
@@ -42,7 +43,7 @@ class AutoDiscoveryRuleView(APIView):
|
||||
|
||||
rebuild = False
|
||||
exists = {i['name'] for i in res}
|
||||
for i in copy.deepcopy(DEFAULT_HTTP):
|
||||
for i in copy.deepcopy(DEFAULT_INNER):
|
||||
if i['name'] not in exists:
|
||||
i.pop('en', None)
|
||||
AutoDiscoveryRuleCRUD().add(**i)
|
||||
@@ -110,12 +111,16 @@ class AutoDiscoveryRuleTemplateFileView(APIView):
|
||||
class AutoDiscoveryRuleHTTPView(APIView):
|
||||
url_prefix = ("/adr/http/<string:name>/categories",
|
||||
"/adr/http/<string:name>/attributes",
|
||||
"/adr/snmp/<string:name>/attributes")
|
||||
"/adr/snmp/<string:name>/attributes",
|
||||
"/adr/components/<string:name>/attributes",)
|
||||
|
||||
def get(self, name):
|
||||
if "snmp" in request.url:
|
||||
return self.jsonify(AutoDiscoverySNMPManager.get_attributes())
|
||||
|
||||
if "components" in request.url:
|
||||
return self.jsonify(AutoDiscoveryComponentsManager.get_attributes(name))
|
||||
|
||||
if "attributes" in request.url:
|
||||
resource = request.values.get('resource')
|
||||
return self.jsonify(AutoDiscoveryHTTPManager.get_attributes(name, resource))
|
||||
@@ -250,7 +255,7 @@ class AutoDiscoveryRuleSyncView(APIView):
|
||||
url_prefix = ("/adt/sync",)
|
||||
|
||||
def get(self):
|
||||
if current_user.username not in ("cmdb_agent", "worker", "admin"):
|
||||
if current_user.username not in PRIVILEGED_USERS:
|
||||
return abort(403)
|
||||
|
||||
oneagent_name = request.values.get('oneagent_name')
|
||||
|
@@ -268,6 +268,7 @@ class CIBaselineView(APIView):
|
||||
return self.jsonify(CIManager().baseline(list(map(int, ci_ids)), before_date))
|
||||
|
||||
@args_required("before_date")
|
||||
@has_perm_for_ci("ci_id", ResourceTypeEnum.CI, PermEnum.UPDATE, CIManager.get_type)
|
||||
def post(self, ci_id):
|
||||
if 'rollback' in request.url:
|
||||
before_date = request.values.get('before_date')
|
||||
|
@@ -51,7 +51,11 @@ class CITypeView(APIView):
|
||||
q = request.args.get("type_name")
|
||||
|
||||
if type_id is not None:
|
||||
ci_type = CITypeCache.get(type_id).to_dict()
|
||||
ci_type = CITypeCache.get(type_id)
|
||||
if ci_type is None:
|
||||
return abort(404, ErrFormat.ci_type_not_found)
|
||||
|
||||
ci_type = ci_type.to_dict()
|
||||
ci_type['parent_ids'] = CITypeInheritanceManager.get_parents(type_id)
|
||||
ci_types = [ci_type]
|
||||
elif type_name is not None:
|
||||
@@ -357,15 +361,13 @@ class CITypeAttributeGroupView(APIView):
|
||||
|
||||
|
||||
class CITypeTemplateView(APIView):
|
||||
url_prefix = ("/ci_types/template/import", "/ci_types/template/export", "/ci_types/<int:type_id>/template/export")
|
||||
url_prefix = ("/ci_types/template/import", "/ci_types/template/export")
|
||||
|
||||
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Model_Configuration,
|
||||
app_cli.op.download_CIType, app_cli.admin_name)
|
||||
def get(self, type_id=None): # export
|
||||
if type_id is not None:
|
||||
return self.jsonify(dict(ci_type_template=CITypeTemplateManager.export_template_by_type(type_id)))
|
||||
|
||||
return self.jsonify(dict(ci_type_template=CITypeTemplateManager.export_template()))
|
||||
def get(self): # export
|
||||
type_ids = list(map(int, handle_arg_list(request.values.get('type_ids')))) or None
|
||||
return self.jsonify(dict(ci_type_template=CITypeTemplateManager.export_template(type_ids=type_ids)))
|
||||
|
||||
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Model_Configuration,
|
||||
app_cli.op.download_CIType, app_cli.admin_name)
|
||||
|
@@ -59,7 +59,7 @@
|
||||
"vue-template-compiler": "2.6.11",
|
||||
"vuedraggable": "^2.23.0",
|
||||
"vuex": "^3.1.1",
|
||||
"vxe-table": "3.6.9",
|
||||
"vxe-table": "3.7.10",
|
||||
"vxe-table-plugin-export-xlsx": "2.0.0",
|
||||
"xe-utils": "3",
|
||||
"xlsx": "0.15.0",
|
||||
|
@@ -54,6 +54,78 @@
|
||||
<div class="content unicode" style="display: block;">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">veops-markdown</div>
|
||||
<div class="code-name">&#xe96a;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">veops-bar_horizontal</div>
|
||||
<div class="code-name">&#xe860;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">veops-gauge</div>
|
||||
<div class="code-name">&#xe965;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">veops-heatmap</div>
|
||||
<div class="code-name">&#xe966;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">veops-treemap</div>
|
||||
<div class="code-name">&#xe967;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">veops-radar</div>
|
||||
<div class="code-name">&#xe968;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">veops-data</div>
|
||||
<div class="code-name">&#xe969;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">veops-import</div>
|
||||
<div class="code-name">&#xe963;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">veops-batch_operation</div>
|
||||
<div class="code-name">&#xe964;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">cmdb-enterprise_edition</div>
|
||||
<div class="code-name">&#xe962;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">ops-KVM</div>
|
||||
<div class="code-name">&#xe961;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">cmdb-vcenter</div>
|
||||
<div class="code-name">&#xe960;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">cmdb-manual_warehousing</div>
|
||||
@@ -534,12 +606,6 @@
|
||||
<div class="code-name">&#xe862;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">veops-import</div>
|
||||
<div class="code-name">&#xe860;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">monitor-ip (1)</div>
|
||||
@@ -5178,9 +5244,9 @@
|
||||
<pre><code class="language-css"
|
||||
>@font-face {
|
||||
font-family: 'iconfont';
|
||||
src: url('iconfont.woff2?t=1718872392430') format('woff2'),
|
||||
url('iconfont.woff?t=1718872392430') format('woff'),
|
||||
url('iconfont.ttf?t=1718872392430') format('truetype');
|
||||
src: url('iconfont.woff2?t=1719487341033') format('woff2'),
|
||||
url('iconfont.woff?t=1719487341033') format('woff'),
|
||||
url('iconfont.ttf?t=1719487341033') format('truetype');
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
|
||||
@@ -5206,6 +5272,114 @@
|
||||
<div class="content font-class">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont veops-markdown"></span>
|
||||
<div class="name">
|
||||
veops-markdown
|
||||
</div>
|
||||
<div class="code-name">.veops-markdown
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont veops-bar_horizontal"></span>
|
||||
<div class="name">
|
||||
veops-bar_horizontal
|
||||
</div>
|
||||
<div class="code-name">.veops-bar_horizontal
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont veops-gauge"></span>
|
||||
<div class="name">
|
||||
veops-gauge
|
||||
</div>
|
||||
<div class="code-name">.veops-gauge
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont veops-heatmap"></span>
|
||||
<div class="name">
|
||||
veops-heatmap
|
||||
</div>
|
||||
<div class="code-name">.veops-heatmap
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont veops-treemap"></span>
|
||||
<div class="name">
|
||||
veops-treemap
|
||||
</div>
|
||||
<div class="code-name">.veops-treemap
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont veops-radar"></span>
|
||||
<div class="name">
|
||||
veops-radar
|
||||
</div>
|
||||
<div class="code-name">.veops-radar
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont veops-data"></span>
|
||||
<div class="name">
|
||||
veops-data
|
||||
</div>
|
||||
<div class="code-name">.veops-data
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont veops-import"></span>
|
||||
<div class="name">
|
||||
veops-import
|
||||
</div>
|
||||
<div class="code-name">.veops-import
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont veops-batch_operation"></span>
|
||||
<div class="name">
|
||||
veops-batch_operation
|
||||
</div>
|
||||
<div class="code-name">.veops-batch_operation
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont cmdb-enterprise_edition"></span>
|
||||
<div class="name">
|
||||
cmdb-enterprise_edition
|
||||
</div>
|
||||
<div class="code-name">.cmdb-enterprise_edition
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont ops-KVM"></span>
|
||||
<div class="name">
|
||||
ops-KVM
|
||||
</div>
|
||||
<div class="code-name">.ops-KVM
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont cmdb-vcenter"></span>
|
||||
<div class="name">
|
||||
cmdb-vcenter
|
||||
</div>
|
||||
<div class="code-name">.cmdb-vcenter
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont cmdb-manual_warehousing"></span>
|
||||
<div class="name">
|
||||
@@ -5693,11 +5867,11 @@
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont a-Group427319324"></span>
|
||||
<span class="icon iconfont monitor-add2"></span>
|
||||
<div class="name">
|
||||
monitor-add2
|
||||
</div>
|
||||
<div class="code-name">.a-Group427319324
|
||||
<div class="code-name">.monitor-add2
|
||||
</div>
|
||||
</li>
|
||||
|
||||
@@ -5926,15 +6100,6 @@
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont a-veops-import1"></span>
|
||||
<div class="name">
|
||||
veops-import
|
||||
</div>
|
||||
<div class="code-name">.a-veops-import1
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont monitor-ip"></span>
|
||||
<div class="name">
|
||||
@@ -12892,6 +13057,102 @@
|
||||
<div class="content symbol">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#veops-markdown"></use>
|
||||
</svg>
|
||||
<div class="name">veops-markdown</div>
|
||||
<div class="code-name">#veops-markdown</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#veops-bar_horizontal"></use>
|
||||
</svg>
|
||||
<div class="name">veops-bar_horizontal</div>
|
||||
<div class="code-name">#veops-bar_horizontal</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#veops-gauge"></use>
|
||||
</svg>
|
||||
<div class="name">veops-gauge</div>
|
||||
<div class="code-name">#veops-gauge</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#veops-heatmap"></use>
|
||||
</svg>
|
||||
<div class="name">veops-heatmap</div>
|
||||
<div class="code-name">#veops-heatmap</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#veops-treemap"></use>
|
||||
</svg>
|
||||
<div class="name">veops-treemap</div>
|
||||
<div class="code-name">#veops-treemap</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#veops-radar"></use>
|
||||
</svg>
|
||||
<div class="name">veops-radar</div>
|
||||
<div class="code-name">#veops-radar</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#veops-data"></use>
|
||||
</svg>
|
||||
<div class="name">veops-data</div>
|
||||
<div class="code-name">#veops-data</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#veops-import"></use>
|
||||
</svg>
|
||||
<div class="name">veops-import</div>
|
||||
<div class="code-name">#veops-import</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#veops-batch_operation"></use>
|
||||
</svg>
|
||||
<div class="name">veops-batch_operation</div>
|
||||
<div class="code-name">#veops-batch_operation</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#cmdb-enterprise_edition"></use>
|
||||
</svg>
|
||||
<div class="name">cmdb-enterprise_edition</div>
|
||||
<div class="code-name">#cmdb-enterprise_edition</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#ops-KVM"></use>
|
||||
</svg>
|
||||
<div class="name">ops-KVM</div>
|
||||
<div class="code-name">#ops-KVM</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#cmdb-vcenter"></use>
|
||||
</svg>
|
||||
<div class="name">cmdb-vcenter</div>
|
||||
<div class="code-name">#cmdb-vcenter</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#cmdb-manual_warehousing"></use>
|
||||
@@ -13326,10 +13587,10 @@
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#a-Group427319324"></use>
|
||||
<use xlink:href="#monitor-add2"></use>
|
||||
</svg>
|
||||
<div class="name">monitor-add2</div>
|
||||
<div class="code-name">#a-Group427319324</div>
|
||||
<div class="code-name">#monitor-add2</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
@@ -13532,14 +13793,6 @@
|
||||
<div class="code-name">#veops-export</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#a-veops-import1"></use>
|
||||
</svg>
|
||||
<div class="name">veops-import</div>
|
||||
<div class="code-name">#a-veops-import1</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#monitor-ip"></use>
|
||||
|
@@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 3857903 */
|
||||
src: url('iconfont.woff2?t=1718872392430') format('woff2'),
|
||||
url('iconfont.woff?t=1718872392430') format('woff'),
|
||||
url('iconfont.ttf?t=1718872392430') format('truetype');
|
||||
src: url('iconfont.woff2?t=1719487341033') format('woff2'),
|
||||
url('iconfont.woff?t=1719487341033') format('woff'),
|
||||
url('iconfont.ttf?t=1719487341033') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@@ -13,6 +13,54 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.veops-markdown:before {
|
||||
content: "\e96a";
|
||||
}
|
||||
|
||||
.veops-bar_horizontal:before {
|
||||
content: "\e860";
|
||||
}
|
||||
|
||||
.veops-gauge:before {
|
||||
content: "\e965";
|
||||
}
|
||||
|
||||
.veops-heatmap:before {
|
||||
content: "\e966";
|
||||
}
|
||||
|
||||
.veops-treemap:before {
|
||||
content: "\e967";
|
||||
}
|
||||
|
||||
.veops-radar:before {
|
||||
content: "\e968";
|
||||
}
|
||||
|
||||
.veops-data:before {
|
||||
content: "\e969";
|
||||
}
|
||||
|
||||
.veops-import:before {
|
||||
content: "\e963";
|
||||
}
|
||||
|
||||
.veops-batch_operation:before {
|
||||
content: "\e964";
|
||||
}
|
||||
|
||||
.cmdb-enterprise_edition:before {
|
||||
content: "\e962";
|
||||
}
|
||||
|
||||
.ops-KVM:before {
|
||||
content: "\e961";
|
||||
}
|
||||
|
||||
.cmdb-vcenter:before {
|
||||
content: "\e960";
|
||||
}
|
||||
|
||||
.cmdb-manual_warehousing:before {
|
||||
content: "\e95f";
|
||||
}
|
||||
@@ -229,7 +277,7 @@
|
||||
content: "\e92a";
|
||||
}
|
||||
|
||||
.a-Group427319324:before {
|
||||
.monitor-add2:before {
|
||||
content: "\e929";
|
||||
}
|
||||
|
||||
@@ -333,10 +381,6 @@
|
||||
content: "\e862";
|
||||
}
|
||||
|
||||
.a-veops-import1:before {
|
||||
content: "\e860";
|
||||
}
|
||||
|
||||
.monitor-ip:before {
|
||||
content: "\e807";
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@@ -5,6 +5,90 @@
|
||||
"css_prefix_text": "",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "40896913",
|
||||
"name": "veops-markdown",
|
||||
"font_class": "veops-markdown",
|
||||
"unicode": "e96a",
|
||||
"unicode_decimal": 59754
|
||||
},
|
||||
{
|
||||
"icon_id": "40896859",
|
||||
"name": "veops-bar_horizontal",
|
||||
"font_class": "veops-bar_horizontal",
|
||||
"unicode": "e860",
|
||||
"unicode_decimal": 59488
|
||||
},
|
||||
{
|
||||
"icon_id": "40896881",
|
||||
"name": "veops-gauge",
|
||||
"font_class": "veops-gauge",
|
||||
"unicode": "e965",
|
||||
"unicode_decimal": 59749
|
||||
},
|
||||
{
|
||||
"icon_id": "40896882",
|
||||
"name": "veops-heatmap",
|
||||
"font_class": "veops-heatmap",
|
||||
"unicode": "e966",
|
||||
"unicode_decimal": 59750
|
||||
},
|
||||
{
|
||||
"icon_id": "40896884",
|
||||
"name": "veops-treemap",
|
||||
"font_class": "veops-treemap",
|
||||
"unicode": "e967",
|
||||
"unicode_decimal": 59751
|
||||
},
|
||||
{
|
||||
"icon_id": "40896887",
|
||||
"name": "veops-radar",
|
||||
"font_class": "veops-radar",
|
||||
"unicode": "e968",
|
||||
"unicode_decimal": 59752
|
||||
},
|
||||
{
|
||||
"icon_id": "40896905",
|
||||
"name": "veops-data",
|
||||
"font_class": "veops-data",
|
||||
"unicode": "e969",
|
||||
"unicode_decimal": 59753
|
||||
},
|
||||
{
|
||||
"icon_id": "40872369",
|
||||
"name": "veops-import",
|
||||
"font_class": "veops-import",
|
||||
"unicode": "e963",
|
||||
"unicode_decimal": 59747
|
||||
},
|
||||
{
|
||||
"icon_id": "40872361",
|
||||
"name": "veops-batch_operation",
|
||||
"font_class": "veops-batch_operation",
|
||||
"unicode": "e964",
|
||||
"unicode_decimal": 59748
|
||||
},
|
||||
{
|
||||
"icon_id": "40834860",
|
||||
"name": "cmdb-enterprise_edition",
|
||||
"font_class": "cmdb-enterprise_edition",
|
||||
"unicode": "e962",
|
||||
"unicode_decimal": 59746
|
||||
},
|
||||
{
|
||||
"icon_id": "40832458",
|
||||
"name": "ops-KVM",
|
||||
"font_class": "ops-KVM",
|
||||
"unicode": "e961",
|
||||
"unicode_decimal": 59745
|
||||
},
|
||||
{
|
||||
"icon_id": "40822644",
|
||||
"name": "cmdb-vcenter",
|
||||
"font_class": "cmdb-vcenter",
|
||||
"unicode": "e960",
|
||||
"unicode_decimal": 59744
|
||||
},
|
||||
{
|
||||
"icon_id": "40795271",
|
||||
"name": "cmdb-manual_warehousing",
|
||||
@@ -386,7 +470,7 @@
|
||||
{
|
||||
"icon_id": "40372105",
|
||||
"name": "monitor-add2",
|
||||
"font_class": "a-Group427319324",
|
||||
"font_class": "monitor-add2",
|
||||
"unicode": "e929",
|
||||
"unicode_decimal": 59689
|
||||
},
|
||||
@@ -565,13 +649,6 @@
|
||||
"unicode": "e862",
|
||||
"unicode_decimal": 59490
|
||||
},
|
||||
{
|
||||
"icon_id": "40306881",
|
||||
"name": "veops-import",
|
||||
"font_class": "a-veops-import1",
|
||||
"unicode": "e860",
|
||||
"unicode_decimal": 59488
|
||||
},
|
||||
{
|
||||
"icon_id": "40262335",
|
||||
"name": "monitor-ip (1)",
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -50,3 +50,13 @@ export const putCITypeGroups = (data) => {
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 导出模型分组
|
||||
export function exportCITypeGroups(params) {
|
||||
return axios({
|
||||
url: `${urlPrefix}/ci_types/template/export`,
|
||||
method: 'GET',
|
||||
params: params,
|
||||
timeout: 30 * 1000,
|
||||
})
|
||||
}
|
||||
|
@@ -45,9 +45,9 @@ export function getHttpAttributes(name, params) {
|
||||
})
|
||||
}
|
||||
|
||||
export function getSnmpAttributes(name) {
|
||||
export function getSnmpAttributes(type, name) {
|
||||
return axios({
|
||||
url: `/v0.1/adr/snmp/${name}/attributes`,
|
||||
url: `/v0.1/adr/${type}/${name}/attributes`,
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
|
@@ -1,21 +1,36 @@
|
||||
<template>
|
||||
<div class="http-ad-category">
|
||||
<div class="http-ad-category-preview" v-if="currentCate">
|
||||
<div class="http-ad-category-preview" v-if="currentCate && isPreviewDetail">
|
||||
<div class="category-side">
|
||||
<div
|
||||
v-for="category in categories"
|
||||
v-for="(category, categoryIndex) in categories"
|
||||
:key="category.category"
|
||||
class="category-side-item"
|
||||
>
|
||||
<div class="category-side-title">{{ category.category }}</div>
|
||||
<div class="category-side-title">
|
||||
<div class="category-side-title">
|
||||
<a-icon
|
||||
v-if="categoryIndex === 0"
|
||||
type="left"
|
||||
@click="clickBack"
|
||||
/>
|
||||
{{ category.category }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="category-side-children">
|
||||
<div
|
||||
v-for="item in category.items"
|
||||
v-for="(item, itemIndex) in category.items"
|
||||
:key="item"
|
||||
:class="['category-side-children-item', item === currentCate ? 'category-side-children-item_active' : '']"
|
||||
@click="clickCategory(item)"
|
||||
>
|
||||
{{ item }}
|
||||
<span
|
||||
class="category-side-children-item-corporate"
|
||||
v-if="ruleType === 'private_cloud' || (ruleType === 'http' && (categoryIndex !== 0 || itemIndex !== 0))"
|
||||
>
|
||||
企
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -35,19 +50,25 @@
|
||||
/>
|
||||
<div class="category-main">
|
||||
<div
|
||||
v-for="category in filterCategories"
|
||||
v-for="(category, categoryIndex) in filterCategories"
|
||||
:key="category.category"
|
||||
class="category-item"
|
||||
>
|
||||
<div class="category-title">{{ category.category }}</div>
|
||||
<div class="category-children">
|
||||
<div
|
||||
v-for="item in category.items"
|
||||
v-for="(item, itemIndex) in category.items"
|
||||
:key="item"
|
||||
:class="['category-children-item', item === currentCate ? 'category-children-item_active' : '']"
|
||||
@click="clickCategory(item)"
|
||||
>
|
||||
{{ item }}
|
||||
<div
|
||||
class="corporate-flag"
|
||||
v-if="ruleType === 'private_cloud' || (ruleType === 'http' && (categoryIndex !== 0 || itemIndex !== 0))"
|
||||
>
|
||||
<span class="corporate-flag-text">企</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -81,10 +102,15 @@ export default {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
ruleType: {
|
||||
type: String,
|
||||
default: 'http',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
searchValue: ''
|
||||
searchValue: '',
|
||||
isPreviewDetail: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -105,6 +131,10 @@ export default {
|
||||
},
|
||||
clickCategory(item) {
|
||||
this.$emit('clickCategory', item)
|
||||
this.isPreviewDetail = true
|
||||
},
|
||||
clickBack() {
|
||||
this.isPreviewDetail = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,8 +157,8 @@ export default {
|
||||
padding-right: 10px;
|
||||
|
||||
&-item {
|
||||
&:not(:first-child) {
|
||||
margin-top: 24px;
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.category-side-title {
|
||||
@@ -150,6 +180,12 @@ export default {
|
||||
font-weight: 400;
|
||||
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
margin-top: 5px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
&:hover {
|
||||
background-color: @layout-sidebar-selected-color;
|
||||
@@ -160,6 +196,20 @@ export default {
|
||||
background-color: @layout-sidebar-selected-color;
|
||||
color: @layout-header-font-selected-color;
|
||||
}
|
||||
|
||||
&-corporate {
|
||||
flex-shrink: 0;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background-color: #E1EFFF;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
|
||||
color: #2F54EB;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -201,6 +251,7 @@ export default {
|
||||
font-weight: 400;
|
||||
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
background-color: @layout-sidebar-selected-color;
|
||||
@@ -214,9 +265,33 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.corporate-tip {
|
||||
margin-top: 20px;
|
||||
.corporate-tip {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.corporate-flag {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 4;
|
||||
|
||||
width: 38px;
|
||||
height: 28px;
|
||||
border-left: 38px solid transparent;
|
||||
border-top: 28px solid @primary-color_4;
|
||||
|
||||
&-text {
|
||||
width: 37px;
|
||||
position: absolute;
|
||||
top: -28px;
|
||||
right: 3px;
|
||||
text-align: right;
|
||||
|
||||
color: @primary-color;
|
||||
font-size: 10px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,14 +1,15 @@
|
||||
<template>
|
||||
<div class="http-snmp-ad">
|
||||
<HttpADCategory
|
||||
v-if="!isEdit && ruleType === 'http'"
|
||||
v-if="!isEdit && isCloud"
|
||||
:categories="categories"
|
||||
:currentCate="currentCate"
|
||||
:tableData="tableData"
|
||||
:ruleType="ruleType"
|
||||
@clickCategory="setCurrentCate"
|
||||
/>
|
||||
<template v-else>
|
||||
<a-select v-if="ruleType === 'http'" :style="{ marginBottom: '10px' }" v-model="currentCate">
|
||||
<a-select v-if="isCloud" :style="{ marginBottom: '10px', minWidth: '120px' }" v-model="currentCate">
|
||||
<a-select-option v-for="cate in categoriesSelect" :key="cate" :value="cate">{{ cate }}</a-select-option>
|
||||
</a-select>
|
||||
<AttrMapTable
|
||||
@@ -89,15 +90,20 @@ export default {
|
||||
腾讯云: { name: 'tencentcloud' },
|
||||
华为云: { name: 'huaweicloud' },
|
||||
AWS: { name: 'aws' },
|
||||
VCenter: { name: 'vcenter' },
|
||||
KVM: { name: 'kvm' },
|
||||
}
|
||||
},
|
||||
isCloud() {
|
||||
return ['http', 'private_cloud'].includes(this.ruleType)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
currentCate: {
|
||||
immediate: true,
|
||||
handler(newVal) {
|
||||
if (newVal) {
|
||||
getHttpAttributes(this.httpMap[`${this.ruleName}`].name, { resource: newVal }).then((res) => {
|
||||
getHttpAttributes(this.ruleName, { resource: newVal }).then((res) => {
|
||||
if (this.isEdit) {
|
||||
this.formatTableData(res)
|
||||
} else {
|
||||
@@ -113,8 +119,8 @@ export default {
|
||||
this.currentCate = ''
|
||||
this.$nextTick(() => {
|
||||
const { ruleType, ruleName } = newVal
|
||||
if (['snmp'].includes(ruleType) && ruleName) {
|
||||
getSnmpAttributes(ruleName).then((res) => {
|
||||
if (['snmp', 'components'].includes(ruleType) && ruleName) {
|
||||
getSnmpAttributes(ruleType, ruleName).then((res) => {
|
||||
if (this.isEdit) {
|
||||
this.formatTableData(res)
|
||||
} else {
|
||||
@@ -122,8 +128,9 @@ export default {
|
||||
}
|
||||
})
|
||||
}
|
||||
if (ruleType === 'http' && ruleName) {
|
||||
getHttpCategories(this.httpMap[`${this.ruleName}`].name).then((res) => {
|
||||
|
||||
if (this.isCloud && ruleName) {
|
||||
getHttpCategories(this.ruleName).then((res) => {
|
||||
this.categories = res
|
||||
const categoriesSelect = []
|
||||
res.forEach((category) => {
|
||||
|
@@ -37,6 +37,14 @@ const cmdb_en = {
|
||||
editGroup: 'Edit Group',
|
||||
group: 'Group',
|
||||
attributeLibray: 'Attribute Library',
|
||||
viewAttributeLibray: 'Attribute Library',
|
||||
addGroup2: 'Add Group',
|
||||
modelExport: 'Model Export',
|
||||
filename: 'Filename',
|
||||
filenameInputTips: 'Please enter filename',
|
||||
selectModel: 'Select Model',
|
||||
unselectModel: 'Unselected',
|
||||
selectedModel: 'Selected',
|
||||
addCITypeInGroup: 'Add a new CIType to the group',
|
||||
addCIType: 'Add CIType',
|
||||
editGroupName: 'Edit group name',
|
||||
@@ -76,6 +84,8 @@ const cmdb_en = {
|
||||
byInterval: 'by interval',
|
||||
allNodes: 'All machines',
|
||||
specifyNodes: 'Specify machine',
|
||||
masterNode: 'Master machine',
|
||||
masterNodeTip: 'The machine where OneMaster is installed',
|
||||
specifyNodesTips: 'Please fill in the specify machine!',
|
||||
username: 'Username',
|
||||
password: 'Password',
|
||||
@@ -243,6 +253,12 @@ const cmdb_en = {
|
||||
relationADTip2: 'When an auto-discovered attribute matches an associated model attribute, the two instance models are automatically associated',
|
||||
relationADTip3: 'If the value of the auto-discovered attribute is a list, multiple relationships are established with the association model',
|
||||
deleteRelationAdTip: 'Cannot be deleted again',
|
||||
cronTips: 'The format is the same as crontab, for example: 0 15 * * 1-5',
|
||||
privateCloud: 'vSphere API Configuration',
|
||||
host: 'Host',
|
||||
account: 'Account',
|
||||
insecure: 'Certificate Validation',
|
||||
vcenterName: 'Platform Name',
|
||||
},
|
||||
components: {
|
||||
unselectAttributes: 'Unselected',
|
||||
@@ -479,6 +495,8 @@ const cmdb_en = {
|
||||
snmp: 'Network Devices',
|
||||
http: 'Public Clouds',
|
||||
plugin: 'Plugin',
|
||||
component: 'Databases & Middleware',
|
||||
privateCloud: 'Private Clouds',
|
||||
rule: 'AutoDiscovery Rules',
|
||||
timeout: 'Timeout error',
|
||||
mode: 'Mode',
|
||||
@@ -648,7 +666,8 @@ if __name__ == "__main__":
|
||||
confirmDeleteView: 'Are you sure you want to delete this view ?',
|
||||
noInstancePerm: 'You do not have read permissions for this instance',
|
||||
noPreferenceAttributes: 'This instance has no subscription attributes or no default displayed attributes',
|
||||
topoViewSearchPlaceholder: 'Please enter the node name.'
|
||||
topoViewSearchPlaceholder: 'Please enter the node name.',
|
||||
moreBtn: 'Show more({count})'
|
||||
},
|
||||
}
|
||||
export default cmdb_en
|
||||
|
@@ -37,6 +37,14 @@ const cmdb_zh = {
|
||||
editGroup: '修改分组',
|
||||
group: '分组',
|
||||
attributeLibray: '属性库',
|
||||
viewAttributeLibray: '查看属性库',
|
||||
addGroup2: '添加分组',
|
||||
modelExport: '模型导出',
|
||||
filename: '文件名',
|
||||
filenameInputTips: '请输入文件名',
|
||||
selectModel: '请选择模型',
|
||||
unselectModel: '未选',
|
||||
selectedModel: '已选',
|
||||
addCITypeInGroup: '在该组中新增CI模型',
|
||||
addCIType: '新增CI模型',
|
||||
editGroupName: '重命名分组',
|
||||
@@ -76,6 +84,8 @@ const cmdb_zh = {
|
||||
byInterval: '按间隔',
|
||||
allNodes: '所有机器',
|
||||
specifyNodes: '指定机器',
|
||||
masterNode: 'Master机器',
|
||||
masterNodeTip: '安装OneMaster的所在机器',
|
||||
specifyNodesTips: '请填写指定机器!',
|
||||
username: '用户名',
|
||||
password: '密码',
|
||||
@@ -243,6 +253,12 @@ const cmdb_zh = {
|
||||
relationADTip2: '当自动发现属性与关联模型属性一致时,两实例模型则自动关联',
|
||||
relationADTip3: '如果自动发现的属性值是列表,则会和关联模型建立多个关系',
|
||||
deleteRelationAdTip: '不可再删除',
|
||||
cronTips: '格式同crontab, 例如:0 15 * * 1-5',
|
||||
privateCloud: 'vSphere API配置',
|
||||
host: '地址',
|
||||
account: '账号',
|
||||
insecure: '是否证书验证',
|
||||
vcenterName: '虚拟平台名',
|
||||
},
|
||||
components: {
|
||||
unselectAttributes: '未选属性',
|
||||
@@ -478,7 +494,8 @@ const cmdb_zh = {
|
||||
agent: '服务器',
|
||||
snmp: '网络设备',
|
||||
http: '公有云',
|
||||
plugin: '插件',
|
||||
component: '数据库 & 中间件',
|
||||
privateCloud: '私有云',
|
||||
rule: '自动发现规则',
|
||||
timeout: '超时错误',
|
||||
mode: '模式',
|
||||
@@ -648,7 +665,8 @@ if __name__ == "__main__":
|
||||
confirmDeleteView: '您确定要删除该视图吗?',
|
||||
noInstancePerm: '您没有该实例的查看权限',
|
||||
noPreferenceAttributes: '该实例没有订阅属性或者没有默认展示的属性',
|
||||
topoViewSearchPlaceholder: '请输入节点名字'
|
||||
topoViewSearchPlaceholder: '请输入节点名字',
|
||||
moreBtn: '展示更多({count})'
|
||||
},
|
||||
}
|
||||
export default cmdb_zh
|
||||
|
@@ -101,10 +101,14 @@
|
||||
:cell-style="getCellStyle"
|
||||
:scroll-y="{ enabled: true, gt: 20 }"
|
||||
:scroll-x="{ enabled: true, gt: 0 }"
|
||||
class="ops-unstripe-table"
|
||||
class="ops-unstripe-table checkbox-hover-table"
|
||||
:custom-config="{ storage: true }"
|
||||
>
|
||||
<vxe-column align="center" type="checkbox" width="60" :fixed="isCheckboxFixed ? 'left' : ''"></vxe-column>
|
||||
<vxe-column align="center" type="checkbox" width="60" :fixed="isCheckboxFixed ? 'left' : ''">
|
||||
<template #default="{row}">
|
||||
{{ getRowSeq(row) }}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-table-column
|
||||
v-for="(col, index) in columns"
|
||||
:key="`${col.field}_${index}`"
|
||||
@@ -1048,6 +1052,9 @@ export default {
|
||||
this.visible = false
|
||||
}
|
||||
},
|
||||
getRowSeq(row) {
|
||||
return this.$refs.xTable.getVxetableRef().getRowSeq(row)
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -1065,4 +1072,33 @@ export default {
|
||||
overflow: auto;
|
||||
margin-bottom: -24px;
|
||||
}
|
||||
.checkbox-hover-table {
|
||||
/deep/ .vxe-table--body-wrapper {
|
||||
.vxe-checkbox--label {
|
||||
display: inline;
|
||||
padding-left: 0px !important;
|
||||
color: #bfbfbf;
|
||||
}
|
||||
|
||||
.vxe-icon-checkbox-unchecked {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vxe-icon-checkbox-checked ~ .vxe-checkbox--label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vxe-cell--checkbox {
|
||||
&:hover {
|
||||
.vxe-icon-checkbox-unchecked {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.vxe-checkbox--label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -273,8 +273,12 @@ export default {
|
||||
const adtIndex = this.clientCITypeList.findIndex((item) => item.id === id)
|
||||
this.clientCITypeList[adtIndex].extra_option.alias = value
|
||||
} else {
|
||||
const adtIndex = this.adCITypeList.findIndex((item) => item.id === id)
|
||||
const oldExtraOption = this.adCITypeList?.[adtIndex]?.extra_option
|
||||
|
||||
const params = {
|
||||
extra_option: {
|
||||
...(oldExtraOption || {}),
|
||||
alias: value
|
||||
}
|
||||
}
|
||||
|
@@ -51,7 +51,7 @@
|
||||
:wrapperCol="{ span: 14 }"
|
||||
class="attr-ad-form"
|
||||
>
|
||||
<a-form-model-item :label="$t('cmdb.ciType.adExecTarget')">
|
||||
<a-form-model-item :required="true" :label="$t('cmdb.ciType.adExecTarget')">
|
||||
<CustomRadio v-model="agent_type" :radioList="agentTypeRadioList">
|
||||
<a-input
|
||||
:style="{ width: '300px' }"
|
||||
@@ -69,6 +69,13 @@
|
||||
>
|
||||
<a @click="handleOpenCmdb" slot="suffix"><a-icon type="menu"/></a>
|
||||
</a-input>
|
||||
<span
|
||||
v-show="agent_type === 'master'"
|
||||
slot="extra_master"
|
||||
class="radio-master-tip"
|
||||
>
|
||||
{{ $t('cmdb.ciType.masterNodeTip') }}
|
||||
</span>
|
||||
</CustomRadio>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
@@ -82,6 +89,7 @@
|
||||
:labelCol="labelCol"
|
||||
:wrapperCol="{ span: 6 }"
|
||||
:label="$t('cmdb.ciType.adInterval')"
|
||||
:required="true"
|
||||
>
|
||||
<el-popover v-model="cronVisible" trigger="click">
|
||||
<template slot>
|
||||
@@ -98,28 +106,59 @@
|
||||
<a-input
|
||||
v-model="cron"
|
||||
slot="reference"
|
||||
:placeholder="$t('cmdb.reconciliation.cronTips')"
|
||||
:placeholder="$t('cmdb.ciType.cronTips')"
|
||||
/>
|
||||
</el-popover>
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
<template v-if="adrType === 'http'">
|
||||
<div class="attr-ad-header attr-ad-header-margin">{{ $t('cmdb.ciType.cloudAccessKey') }}</div>
|
||||
<div class="public-cloud-info">{{ $t('cmdb.ciType.cloudAccessKeyTip') }}</div>
|
||||
<a-form-model
|
||||
:model="form2"
|
||||
labelAlign="left"
|
||||
:labelCol="labelCol"
|
||||
:wrapperCol="{ span: 6 }"
|
||||
class="attr-ad-form"
|
||||
>
|
||||
<a-form-model-item label="key">
|
||||
<a-input-password v-model="form2.key" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="secret">
|
||||
<a-input-password v-model="form2.secret" />
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
<template v-if="isPrivateCloud">
|
||||
<template v-if="privateCloudName === PRIVATE_CLOUD_NAME.VCenter">
|
||||
<div class="attr-ad-header">{{ $t('cmdb.ciType.privateCloud') }}</div>
|
||||
<a-form-model
|
||||
:model="privateCloudForm"
|
||||
labelAlign="left"
|
||||
:labelCol="labelCol"
|
||||
:wrapperCol="{ span: 6 }"
|
||||
class="attr-ad-form"
|
||||
>
|
||||
<a-form-model-item :required="true" :label="$t('cmdb.ciType.host')">
|
||||
<a-input v-model="privateCloudForm.host" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item :required="true" :label="$t('cmdb.ciType.account')">
|
||||
<a-input v-model="privateCloudForm.account" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item :required="true" :label="$t('cmdb.ciType.password')">
|
||||
<a-input-password v-model="privateCloudForm.password" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item :label="$t('cmdb.ciType.insecure')">
|
||||
<a-switch v-model="privateCloudForm.insecure" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item :label="$t('cmdb.ciType.vcenterName')">
|
||||
<a-input v-model="privateCloudForm.vcenterName" />
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<div class="attr-ad-header">{{ $t('cmdb.ciType.cloudAccessKey') }}</div>
|
||||
<!-- <div class="public-cloud-info">{{ $t('cmdb.ciType.cloudAccessKeyTip') }}</div> -->
|
||||
<a-form-model
|
||||
:model="form2"
|
||||
labelAlign="left"
|
||||
:labelCol="labelCol"
|
||||
:wrapperCol="{ span: 6 }"
|
||||
class="attr-ad-form"
|
||||
>
|
||||
<a-form-model-item :required="true" label="key">
|
||||
<a-input-password v-model="form2.key" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item :required="true" label="secret">
|
||||
<a-input-password v-model="form2.secret" />
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<AttrADTest
|
||||
@@ -138,6 +177,7 @@ import { v4 as uuidv4 } from 'uuid'
|
||||
import { mapState } from 'vuex'
|
||||
import Vcrontab from '@/components/Crontab'
|
||||
import { putCITypeDiscovery, postCITypeDiscovery } from '../../api/discovery'
|
||||
import { PRIVATE_CLOUD_NAME } from '@/modules/cmdb/views/discovery/constants.js'
|
||||
|
||||
import HttpSnmpAD from '../../components/httpSnmpAD'
|
||||
import AttrMapTable from '@/modules/cmdb/components/attrMapTable/index.vue'
|
||||
@@ -199,6 +239,13 @@ export default {
|
||||
key: '',
|
||||
secret: '',
|
||||
},
|
||||
privateCloudForm: {
|
||||
host: '',
|
||||
account: '',
|
||||
password: '',
|
||||
insecure: false,
|
||||
vcenterName: '',
|
||||
},
|
||||
interval: 'cron', // interval cron
|
||||
cron: '',
|
||||
intervalValue: 3,
|
||||
@@ -214,6 +261,9 @@ export default {
|
||||
form3: this.$form.createForm(this, { name: 'snmp_form' }),
|
||||
cronVisible: false,
|
||||
uniqueKey: '',
|
||||
isPrivateCloud: false,
|
||||
privateCloudName: '',
|
||||
PRIVATE_CLOUD_NAME
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -225,7 +275,7 @@ export default {
|
||||
return this.currentAdr?.type || ''
|
||||
},
|
||||
adrName() {
|
||||
return this.currentAdr?.name || ''
|
||||
return this?.currentAdr?.option?.en || this.currentAdr?.name || ''
|
||||
},
|
||||
adrIsInner() {
|
||||
return this.currentAdr?.is_inner || ''
|
||||
@@ -241,6 +291,10 @@ export default {
|
||||
radios.unshift({ value: 'all', label: this.$t('cmdb.ciType.allNodes') })
|
||||
}
|
||||
|
||||
if (this.adrType !== 'agent' || this?.currentAdr?.is_plugin) {
|
||||
radios.unshift({ value: 'master', label: this.$t('cmdb.ciType.masterNode') })
|
||||
}
|
||||
|
||||
return radios
|
||||
},
|
||||
radioList() {
|
||||
@@ -250,7 +304,7 @@ export default {
|
||||
]
|
||||
},
|
||||
labelCol() {
|
||||
const span = this.$i18n.locale === 'en' ? 4 : 2
|
||||
const span = this.$i18n.locale === 'en' ? 5 : 3
|
||||
return {
|
||||
span
|
||||
}
|
||||
@@ -264,11 +318,38 @@ export default {
|
||||
this.uniqueKey = _find?.unique_key ?? ''
|
||||
|
||||
if (this.adrType === 'http') {
|
||||
const { category = undefined, key = '', secret = '' } = _findADT?.extra_option ?? {}
|
||||
this.form2 = {
|
||||
key,
|
||||
secret,
|
||||
const {
|
||||
category = undefined,
|
||||
key = '',
|
||||
secret = '',
|
||||
host = '',
|
||||
account = '',
|
||||
password = '',
|
||||
insecure = false,
|
||||
vcenterName = ''
|
||||
} = _findADT?.extra_option ?? {}
|
||||
|
||||
if (_find?.option?.category === 'private_cloud') {
|
||||
this.isPrivateCloud = true
|
||||
this.privateCloudName = _find?.option?.en || ''
|
||||
|
||||
if (this.privateCloudName === PRIVATE_CLOUD_NAME.VCenter) {
|
||||
this.privateCloudForm = {
|
||||
host,
|
||||
account,
|
||||
password,
|
||||
insecure,
|
||||
vcenterName,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.isPrivateCloud = false
|
||||
this.form2 = {
|
||||
key,
|
||||
secret,
|
||||
}
|
||||
}
|
||||
|
||||
this.$refs.httpSnmpAd.setCurrentCate(category)
|
||||
}
|
||||
if (this.adrType === 'snmp') {
|
||||
@@ -308,13 +389,13 @@ export default {
|
||||
}
|
||||
this.form = {
|
||||
auto_accept: _findADT?.auto_accept || false,
|
||||
agent_id: _findADT.agent_id || '',
|
||||
agent_id: _findADT?.agent_id && _findADT?.agent_id !== '0x0000' ? _findADT.agent_id : '',
|
||||
query_expr: _findADT.query_expr || '',
|
||||
}
|
||||
if (_findADT.query_expr) {
|
||||
this.agent_type = 'query_expr'
|
||||
} else if (_findADT.agent_id) {
|
||||
this.agent_type = 'agent_id'
|
||||
this.agent_type = _findADT.agent_id === '0x0000' ? 'master' : 'agent_id'
|
||||
} else {
|
||||
this.agent_type = this.agentTypeRadioList[0].value
|
||||
}
|
||||
@@ -329,10 +410,25 @@ export default {
|
||||
handleSave() {
|
||||
const { currentAdt } = this
|
||||
let params
|
||||
|
||||
const isError = this.validateForm()
|
||||
if (isError) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.adrType === 'http') {
|
||||
let cloudOption = {}
|
||||
if (this.isPrivateCloud) {
|
||||
if (this.privateCloudName === PRIVATE_CLOUD_NAME.VCenter) {
|
||||
cloudOption = this.privateCloudForm
|
||||
}
|
||||
} else {
|
||||
cloudOption = this.form2
|
||||
}
|
||||
|
||||
params = {
|
||||
extra_option: {
|
||||
...this.form2,
|
||||
...cloudOption,
|
||||
category: this.$refs.httpSnmpAd.currentCate,
|
||||
},
|
||||
}
|
||||
@@ -392,18 +488,23 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.agent_type === 'master') {
|
||||
params.agent_id = '0x0000'
|
||||
}
|
||||
|
||||
if (!this.cron) {
|
||||
this.$message.error(this.$t('cmdb.ciType.cronRequiredTip'))
|
||||
return
|
||||
}
|
||||
|
||||
if (currentAdt?.isClient) {
|
||||
if (currentAdt?.extra_option) {
|
||||
params.extra_option = {
|
||||
...(params?.extra_option || {}),
|
||||
...(currentAdt?.extra_option || {})
|
||||
}
|
||||
if (currentAdt?.extra_option) {
|
||||
params.extra_option = {
|
||||
...(currentAdt?.extra_option || {}),
|
||||
...(params?.extra_option || {})
|
||||
}
|
||||
}
|
||||
|
||||
if (currentAdt?.isClient) {
|
||||
postCITypeDiscovery(this.CITypeId, params).then((res) => {
|
||||
this.$message.success(this.$t('saveSuccess'))
|
||||
this.$emit('handleSave', res.id)
|
||||
@@ -415,6 +516,40 @@ export default {
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
validateForm() {
|
||||
let isError = false
|
||||
|
||||
if (this.adrType === 'http') {
|
||||
if (this.isPrivateCloud) {
|
||||
if (this.privateCloudName === PRIVATE_CLOUD_NAME.VCenter) {
|
||||
const vcenterErros = {
|
||||
'host': `${this.$t('placeholder1')} ${this.$t('cmdb.ciType.host')}`,
|
||||
'account': `${this.$t('placeholder1')} ${this.$t('cmdb.ciType.account')}`,
|
||||
'password': `${this.$t('placeholder1')} ${this.$t('cmdb.ciType.password')}`
|
||||
}
|
||||
const findError = Object.keys(this.privateCloudForm).find((key) => !this.privateCloudForm[key] && vcenterErros[key])
|
||||
if (findError) {
|
||||
isError = true
|
||||
this.$message.error(this.$t(vcenterErros[findError]))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const publicCloudErros = {
|
||||
'key': `${this.$t('placeholder1')} key`,
|
||||
'secret': `${this.$t('placeholder1')} secret`
|
||||
}
|
||||
const findError = Object.keys(this.form2).find((key) => !this.form2[key] && publicCloudErros[key])
|
||||
if (findError) {
|
||||
isError = true
|
||||
this.$message.error(this.$t(publicCloudErros[findError]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isError
|
||||
},
|
||||
|
||||
handleOpenCmdb() {
|
||||
this.$refs.cmdbDrawer.open()
|
||||
},
|
||||
@@ -458,6 +593,11 @@ export default {
|
||||
margin-left: 17px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.radio-master-tip {
|
||||
font-size: 12px;
|
||||
color: #86909c;
|
||||
}
|
||||
}
|
||||
.attr-ad-snmp-form {
|
||||
.ant-form-item {
|
||||
|
@@ -241,8 +241,8 @@ export default {
|
||||
|
||||
<style lang="less" scoped>
|
||||
.attribute-card {
|
||||
width: 182px;
|
||||
height: 80px;
|
||||
width: 172px;
|
||||
height: 75px;
|
||||
background: @primary-color_6;
|
||||
border-radius: 2px;
|
||||
position: relative;
|
||||
@@ -308,7 +308,7 @@ export default {
|
||||
}
|
||||
}
|
||||
.attribute-card-footer {
|
||||
width: 182px;
|
||||
width: 172px;
|
||||
height: 30px;
|
||||
padding: 0 8px;
|
||||
position: absolute;
|
||||
|
@@ -104,7 +104,7 @@
|
||||
:filter="'.filter-empty'"
|
||||
:animation="300"
|
||||
tag="div"
|
||||
style="width: 100%; display: flex;flex-flow: wrap"
|
||||
style="width: 100%; display: flex; flex-flow: wrap; column-gap: 10px;"
|
||||
handle=".handle"
|
||||
>
|
||||
<AttributeCard
|
||||
@@ -146,7 +146,7 @@
|
||||
}
|
||||
"
|
||||
:animation="300"
|
||||
style="min-height: 2rem; width: 100%; display: flex; flex-flow: wrap"
|
||||
style="min-height: 2rem; width: 100%; display: flex; flex-flow: wrap; column-gap: 10px;"
|
||||
handle=".handle"
|
||||
>
|
||||
<AttributeCard
|
||||
|
File diff suppressed because it is too large
Load Diff
200
cmdb-ui/src/modules/cmdb/views/ci_types/modelExport.vue
Normal file
200
cmdb-ui/src/modules/cmdb/views/ci_types/modelExport.vue
Normal file
@@ -0,0 +1,200 @@
|
||||
<template>
|
||||
<a-modal
|
||||
:visible="visible"
|
||||
:title="$t('cmdb.ciType.modelExport')"
|
||||
:width="560"
|
||||
@cancel="handleCancel"
|
||||
@ok="handleOK"
|
||||
>
|
||||
<a-form
|
||||
:form="form"
|
||||
:labelCol="{ span: 5 }"
|
||||
:wrapperCol="{ span: 19 }"
|
||||
>
|
||||
<a-form-item
|
||||
:label="$t('cmdb.ciType.filename')"
|
||||
>
|
||||
<a-input v-decorator="['name', { rules: [{ required: true, message: $t('cmdb.ciType.filenameInputTips') }], initialValue: 'cmdb_template' }]" />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
:label="$t('cmdb.ciType.selectModel')"
|
||||
>
|
||||
<a-transfer
|
||||
class="model-export-transfer"
|
||||
:dataSource="transferDataSource"
|
||||
:targetKeys="targetKeys"
|
||||
:render="item => item.title"
|
||||
:titles="[$t('cmdb.ciType.unselectModel'), $t('cmdb.ciType.selectedModel')]"
|
||||
:listStyle="{
|
||||
width: '180px',
|
||||
height: `262px`,
|
||||
}"
|
||||
@change="onChange"
|
||||
>
|
||||
<template
|
||||
slot="children"
|
||||
slot-scope="{ props: { direction, selectedKeys }, on: { itemSelect } }"
|
||||
>
|
||||
<a-tree
|
||||
v-if="direction === 'left'"
|
||||
blockNode
|
||||
checkable
|
||||
:checkedKeys="[...selectedKeys, ...targetKeys]"
|
||||
:treeData="treeData"
|
||||
:checkStrictly="true"
|
||||
@check="
|
||||
(_, props) => {
|
||||
onChecked(_, props, [...selectedKeys, ...targetKeys], itemSelect);
|
||||
}
|
||||
"
|
||||
@select="
|
||||
(_, props) => {
|
||||
onChecked(_, props, [...selectedKeys, ...targetKeys], itemSelect);
|
||||
}
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
</a-transfer>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
import { exportCITypeGroups } from '@/modules/cmdb/api/ciTypeGroup'
|
||||
|
||||
export default {
|
||||
name: 'ModelExport',
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
CITypeGroups: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
form: this.$form.createForm(this, { name: 'model-export' }),
|
||||
targetKeys: [],
|
||||
btnLoading: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
transferDataSource() {
|
||||
const dataSource = this.CITypeGroups.reduce((acc, item) => {
|
||||
const types = _.cloneDeep(item?.ci_types || [])
|
||||
types.forEach((item) => {
|
||||
item.key = String(item.id)
|
||||
item.title = item?.alias || item?.name || this.$t('other')
|
||||
})
|
||||
return acc.concat(types)
|
||||
}, [])
|
||||
return dataSource
|
||||
},
|
||||
treeData() {
|
||||
const treeData = _.cloneDeep(this.CITypeGroups)
|
||||
let newTreeData = treeData.map((item) => {
|
||||
const childrenKeys = []
|
||||
const children = (item.ci_types || []).map((child) => {
|
||||
const key = String(child?.id)
|
||||
const disabled = this.targetKeys.includes(key)
|
||||
childrenKeys.push(key)
|
||||
|
||||
return {
|
||||
key,
|
||||
title: child?.alias || child?.name || this.$t('other'),
|
||||
disabled,
|
||||
checkable: true,
|
||||
children: []
|
||||
}
|
||||
})
|
||||
return {
|
||||
key: String(item?.id),
|
||||
title: item?.name || this.$t('other'),
|
||||
children,
|
||||
childrenKeys,
|
||||
disabled: children.every((item) => item.disabled),
|
||||
checkable: false,
|
||||
selectable: false
|
||||
}
|
||||
})
|
||||
console.log('treeData', newTreeData)
|
||||
newTreeData = newTreeData.filter((item) => item.children.length > 0)
|
||||
|
||||
return newTreeData
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onChange(targetKeys, direction, moveKeys) {
|
||||
this.targetKeys = targetKeys
|
||||
},
|
||||
onChecked(_, e, checkedKeys, itemSelect) {
|
||||
const { eventKey } = e.node
|
||||
const selected = checkedKeys.indexOf(eventKey) === -1
|
||||
|
||||
itemSelect(eventKey, selected)
|
||||
},
|
||||
handleCancel() {
|
||||
this.$emit('cancel')
|
||||
this.form.resetFields()
|
||||
this.targetKeys = []
|
||||
},
|
||||
handleOK() {
|
||||
this.form.validateFields(async (err, values) => {
|
||||
if (err || !this.targetKeys.length || this.btnLoading) {
|
||||
return
|
||||
}
|
||||
this.btnLoading = true
|
||||
const hide = this.$message.loading(this.$t('loading'), 0)
|
||||
|
||||
try {
|
||||
const typeIds = this.targetKeys.join(',')
|
||||
const res = await exportCITypeGroups({
|
||||
type_ids: typeIds
|
||||
})
|
||||
console.log('exportCITypeGroups res', res)
|
||||
|
||||
if (res) {
|
||||
const jsonStr = JSON.stringify(res)
|
||||
const blob = new Blob([jsonStr], { type: 'application/json' })
|
||||
|
||||
const url = URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
|
||||
const fileName = values.name
|
||||
a.download = fileName
|
||||
|
||||
a.click()
|
||||
URL.revokeObjectURL(url)
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('exportCITypeGroups fail', error)
|
||||
hide()
|
||||
this.btnLoading = false
|
||||
}
|
||||
hide()
|
||||
this.btnLoading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.model-export-transfer {
|
||||
/deep/ .ant-transfer-list-body {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/deep/ .ant-transfer-list-header-title {
|
||||
color: @primary-color;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -2,5 +2,11 @@ export const DISCOVERY_CATEGORY_TYPE = {
|
||||
AGENT: 'agent',
|
||||
SNMP: 'snmp',
|
||||
HTTP: 'http',
|
||||
PLUGIN: 'plugin'
|
||||
PLUGIN: 'plugin',
|
||||
COMPONENT: 'components',
|
||||
PRIVATE_CLOUD: 'private_cloud'
|
||||
}
|
||||
|
||||
export const PRIVATE_CLOUD_NAME = {
|
||||
VCenter: 'vcenter'
|
||||
}
|
||||
|
@@ -53,11 +53,12 @@
|
||||
:key="index"
|
||||
v-if="index < 2"
|
||||
class="discovery-resources-item"
|
||||
:style="{ maxWidth: rule.resources.length >= 2 ? '70px' : '160px' }"
|
||||
>
|
||||
{{ item }}
|
||||
</span>
|
||||
</template>
|
||||
<span v-if="rule.resources.length >= 2" class="discovery-resources-item">
|
||||
<span v-if="rule.resources.length > 2" class="discovery-resources-item">
|
||||
<ops-icon type="veops-more" />
|
||||
</span>
|
||||
</div>
|
||||
@@ -234,6 +235,7 @@ export default {
|
||||
color: @text-color_3;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&-right {
|
||||
@@ -249,7 +251,6 @@ export default {
|
||||
color: @text-color_3;
|
||||
font-size: 11px;
|
||||
font-weight: 400;
|
||||
max-width: 95px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
|
@@ -112,7 +112,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<HttpSnmpAD ref="httpSnmpAd" :ruleType="adType" :ruleName="ruleData.name" />
|
||||
<HttpSnmpAD ref="httpSnmpAd" :ruleType="adType" :ruleName="ruleName" />
|
||||
</template>
|
||||
</CustomDrawer>
|
||||
</template>
|
||||
@@ -171,7 +171,7 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
if ([DISCOVERY_CATEGORY_TYPE.HTTP, DISCOVERY_CATEGORY_TYPE.SNMP, DISCOVERY_CATEGORY_TYPE.AGENT].includes(this.adType)) {
|
||||
if ([DISCOVERY_CATEGORY_TYPE.HTTP, DISCOVERY_CATEGORY_TYPE.SNMP, DISCOVERY_CATEGORY_TYPE.AGENT, DISCOVERY_CATEGORY_TYPE.PRIVATE_CLOUD, DISCOVERY_CATEGORY_TYPE.COMPONENT].includes(this.adType)) {
|
||||
return this.ruleData.name
|
||||
}
|
||||
if (this.type === 'edit') {
|
||||
@@ -179,6 +179,9 @@ export default {
|
||||
}
|
||||
return this.$t('new')
|
||||
},
|
||||
ruleName() {
|
||||
return this?.ruleData?.option?.en || this?.ruleData?.name || ''
|
||||
}
|
||||
},
|
||||
inject: {
|
||||
getDiscovery: {
|
||||
@@ -202,7 +205,7 @@ export default {
|
||||
return
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
if (adType === DISCOVERY_CATEGORY_TYPE.AGENT) {
|
||||
if ([DISCOVERY_CATEGORY_TYPE.HTTP, DISCOVERY_CATEGORY_TYPE.SNMP, DISCOVERY_CATEGORY_TYPE.PRIVATE_CLOUD].includes(adType)) {
|
||||
this.tableData = data?.attributes ?? []
|
||||
return
|
||||
}
|
||||
|
@@ -39,7 +39,10 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-discovery-body">
|
||||
<div
|
||||
class="setting-discovery-body"
|
||||
:style="{ height: !isSelected ? `${windowHeight - 155}px` : '' }"
|
||||
>
|
||||
<template v-if="!showNullData">
|
||||
<div v-for="{ type, label } in typeCategory" :key="type">
|
||||
<template v-if="filterCategoryChildren[type] && (filterCategoryChildren[type].children.length || (showAddPlugin && type === DISCOVERY_CATEGORY_TYPE.PLUGIN))">
|
||||
@@ -79,6 +82,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import _ from 'lodash'
|
||||
import { getDiscovery, deleteDiscovery } from '../../api/discovery'
|
||||
import { DISCOVERY_CATEGORY_TYPE } from './constants.js'
|
||||
@@ -103,16 +107,27 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
windowHeight: (state) => state.windowHeight,
|
||||
}),
|
||||
typeCategory() {
|
||||
return [
|
||||
{
|
||||
type: DISCOVERY_CATEGORY_TYPE.HTTP,
|
||||
label: this.$t('cmdb.ad.http'),
|
||||
},
|
||||
{
|
||||
type: DISCOVERY_CATEGORY_TYPE.PRIVATE_CLOUD,
|
||||
label: this.$t('cmdb.ad.privateCloud'),
|
||||
},
|
||||
{
|
||||
type: DISCOVERY_CATEGORY_TYPE.AGENT,
|
||||
label: this.$t('cmdb.ad.agent'),
|
||||
},
|
||||
{
|
||||
type: DISCOVERY_CATEGORY_TYPE.COMPONENT,
|
||||
label: this.$t('cmdb.ad.component'),
|
||||
},
|
||||
{
|
||||
type: DISCOVERY_CATEGORY_TYPE.SNMP,
|
||||
label: this.$t('cmdb.ad.snmp'),
|
||||
@@ -162,10 +177,18 @@ export default {
|
||||
type: DISCOVERY_CATEGORY_TYPE.HTTP,
|
||||
children: []
|
||||
},
|
||||
[DISCOVERY_CATEGORY_TYPE.PRIVATE_CLOUD]: {
|
||||
type: DISCOVERY_CATEGORY_TYPE.PRIVATE_CLOUD,
|
||||
children: []
|
||||
},
|
||||
[DISCOVERY_CATEGORY_TYPE.AGENT]: {
|
||||
type: DISCOVERY_CATEGORY_TYPE.AGENT,
|
||||
children: []
|
||||
},
|
||||
[DISCOVERY_CATEGORY_TYPE.COMPONENT]: {
|
||||
type: DISCOVERY_CATEGORY_TYPE.COMPONENT,
|
||||
children: []
|
||||
},
|
||||
[DISCOVERY_CATEGORY_TYPE.SNMP]: {
|
||||
type: DISCOVERY_CATEGORY_TYPE.SNMP,
|
||||
children: []
|
||||
@@ -179,6 +202,12 @@ export default {
|
||||
this.typeCategory.forEach(({ type }) => {
|
||||
let categoryChildren = []
|
||||
switch (type) {
|
||||
case DISCOVERY_CATEGORY_TYPE.PRIVATE_CLOUD:
|
||||
categoryChildren = res.filter((list) => list?.option?.category === 'private_cloud' && list?.type === 'http')
|
||||
break
|
||||
case DISCOVERY_CATEGORY_TYPE.HTTP:
|
||||
categoryChildren = res.filter((list) => list?.option?.category !== 'private_cloud' && list?.type === 'http')
|
||||
break
|
||||
case DISCOVERY_CATEGORY_TYPE.PLUGIN:
|
||||
categoryChildren = res.filter((list) => list.is_plugin)
|
||||
break
|
||||
@@ -269,6 +298,7 @@ export default {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
flex-shrink: 0;
|
||||
|
||||
&-btn {
|
||||
display: flex;
|
||||
@@ -284,6 +314,7 @@ export default {
|
||||
|
||||
&-search {
|
||||
width: 254px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&-radio {
|
||||
@@ -291,6 +322,7 @@ export default {
|
||||
align-items: center;
|
||||
margin-left: 15px;
|
||||
gap: 15px;
|
||||
overflow: auto;
|
||||
|
||||
&-item {
|
||||
padding: 4px 14px;
|
||||
@@ -298,6 +330,7 @@ export default {
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
|
||||
&_active {
|
||||
background-color: @primary-color_3;
|
||||
@@ -312,6 +345,7 @@ export default {
|
||||
box-shadow: 0px 0px 4px 0px rgba(158, 171, 190, 0.25);
|
||||
padding: 20px;
|
||||
margin-top: 24px;
|
||||
overflow: auto;
|
||||
|
||||
.setting-discovery-add {
|
||||
height: 105px;
|
||||
@@ -339,6 +373,10 @@ export default {
|
||||
&-text {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
&-img {
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -58,7 +58,7 @@
|
||||
ref="xTable"
|
||||
size="mini"
|
||||
stripe
|
||||
class="ops-stripe-table"
|
||||
class="ops-stripe-table checkbox-hover-table"
|
||||
:data="filterTableData"
|
||||
:height="tableHeight"
|
||||
:scroll-y="{ enabled: true, gt: 50 }"
|
||||
@@ -74,7 +74,11 @@
|
||||
type="checkbox"
|
||||
width="60"
|
||||
fixed="left"
|
||||
></vxe-column>
|
||||
>
|
||||
<template #default="{row}">
|
||||
{{ getRowSeq(row) }}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
v-for="(col, index) in columns"
|
||||
:key="`${col.field}_${index}`"
|
||||
@@ -399,6 +403,9 @@ export default {
|
||||
textEl.scrollTop = textEl.scrollHeight
|
||||
}
|
||||
})
|
||||
},
|
||||
getRowSeq(row) {
|
||||
return this.$refs.xTable.getVxetableRef().getRowSeq(row)
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -462,6 +469,36 @@ export default {
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.checkbox-hover-table {
|
||||
/deep/ .vxe-table--body-wrapper {
|
||||
.vxe-checkbox--label {
|
||||
display: inline;
|
||||
padding-left: 0px !important;
|
||||
color: #bfbfbf;
|
||||
}
|
||||
|
||||
.vxe-icon-checkbox-unchecked {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vxe-icon-checkbox-checked ~ .vxe-checkbox--label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vxe-cell--checkbox {
|
||||
&:hover {
|
||||
.vxe-icon-checkbox-unchecked {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.vxe-checkbox--label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.log-modal-title {
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -32,8 +32,11 @@
|
||||
ghost
|
||||
@click="handleClickAddGroup"
|
||||
class="ops-button-ghost"
|
||||
><ops-icon type="veops-increase" />{{ $t('cmdb.ciType.group') }}</a-button
|
||||
v-if="permissions.includes('admin') || permissions.includes('cmdb_admin')"
|
||||
>
|
||||
<ops-icon type="veops-increase" />
|
||||
{{ $t('cmdb.ciType.group') }}
|
||||
</a-button>
|
||||
</div>
|
||||
<draggable class="topo-left-content" :list="computedTopoGroups" @end="handleChangeGroups" filter=".undraggable">
|
||||
<div v-for="group in computedTopoGroups" :key="group.id || group.name">
|
||||
@@ -56,16 +59,16 @@
|
||||
<a-space>
|
||||
<a-tooltip>
|
||||
<template slot="title">{{ $t('cmdb.topo.addTopoViewInGroup') }}</template>
|
||||
<a><ops-icon type="veops-increase" @click="handleCreate(group)"/></a>
|
||||
<a v-if="permissions.includes('admin') || permissions.includes('cmdb_admin')"><ops-icon type="veops-increase" @click="handleCreate(group)"/></a>
|
||||
</a-tooltip>
|
||||
<template v-if="group.id">
|
||||
<a-tooltip >
|
||||
<template slot="title">{{ $t('cmdb.ciType.editGroup') }}</template>
|
||||
<a><a-icon type="edit" @click="handleEditGroup(group)"/></a>
|
||||
<a v-if="permissions.includes('admin') || permissions.includes('cmdb_admin')"><a-icon type="edit" @click="handleEditGroup(group)"/></a>
|
||||
</a-tooltip>
|
||||
<a-tooltip>
|
||||
<template slot="title">{{ $t('cmdb.ciType.deleteGroup') }}</template>
|
||||
<a :style="{color: 'red'}"><a-icon type="delete" @click="handleDeleteGroup(group)"/></a>
|
||||
<a v-if="permissions.includes('admin') || permissions.includes('cmdb_admin')" :style="{color: 'red'}"><a-icon type="delete" @click="handleDeleteGroup(group)"/></a>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</a-space>
|
||||
@@ -157,7 +160,9 @@
|
||||
class="relation-graph-node-icon"
|
||||
/>
|
||||
</template>
|
||||
<span class="relation-graph-node-text">{{ node.text }}</span>
|
||||
<span class="relation-graph-node-text">
|
||||
{{ node.data.btnType === 'more' ? $t('cmdb.topo.moreBtn', { count: node.text }) : node.text }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #graph-plug>
|
||||
@@ -957,7 +962,7 @@ export default {
|
||||
const id = uuidv4()
|
||||
jsonData.nodes.set(id, {
|
||||
id,
|
||||
text: `展示更多(${childs.length - showNodeCount})`,
|
||||
text: childs.length - showNodeCount,
|
||||
data: {
|
||||
btnType: 'more'
|
||||
},
|
||||
@@ -1005,7 +1010,7 @@ export default {
|
||||
if (showNodeCount === childs.length) {
|
||||
moreBtnNode.isHide = true
|
||||
} else {
|
||||
moreBtnNode.text = `展示更多(${childs.length - showNodeCount})`
|
||||
moreBtnNode.text = childs.length - showNodeCount
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -172,10 +172,14 @@
|
||||
@edit-closed="handleEditClose"
|
||||
@edit-actived="handleEditActived"
|
||||
:edit-config="{ trigger: 'dblclick', mode: 'row', showIcon: false }"
|
||||
class="ops-unstripe-table"
|
||||
class="ops-unstripe-table checkbox-hover-table"
|
||||
:custom-config="{ storage: true }"
|
||||
>
|
||||
<vxe-column align="center" type="checkbox" width="60" :fixed="isCheckboxFixed ? 'left' : ''"></vxe-column>
|
||||
<vxe-column align="center" type="checkbox" width="60" :fixed="isCheckboxFixed ? 'left' : ''">
|
||||
<template #default="{row}">
|
||||
{{ getRowSeq(row) }}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-table-column
|
||||
v-for="(col, index) in columns"
|
||||
:key="`${col.field}_${index}`"
|
||||
@@ -1229,6 +1233,9 @@ export default {
|
||||
}
|
||||
)
|
||||
},
|
||||
getRowSeq(row) {
|
||||
return this.$refs.xTable.getVxetableRef().getRowSeq(row)
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -1337,6 +1344,36 @@ export default {
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
border-radius: @border-radius-box;
|
||||
|
||||
.checkbox-hover-table {
|
||||
.vxe-table--body-wrapper {
|
||||
.vxe-checkbox--label {
|
||||
display: inline;
|
||||
padding-left: 0px !important;
|
||||
color: #bfbfbf;
|
||||
}
|
||||
|
||||
.vxe-icon-checkbox-unchecked {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vxe-icon-checkbox-checked ~ .vxe-checkbox--label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vxe-cell--checkbox {
|
||||
&:hover {
|
||||
.vxe-icon-checkbox-unchecked {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.vxe-checkbox--label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -41,7 +41,7 @@ services:
|
||||
- redis
|
||||
|
||||
cmdb-api:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-api:2.4.6
|
||||
image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-api:2.4.7
|
||||
container_name: cmdb-api
|
||||
env_file:
|
||||
- .env
|
||||
@@ -71,6 +71,7 @@ services:
|
||||
flask cmdb-init-acl
|
||||
flask init-import-user-from-acl
|
||||
flask init-department
|
||||
flask cmdb-patch -v 2.4.7
|
||||
flask cmdb-counter > counter.log 2>&1
|
||||
networks:
|
||||
new:
|
||||
@@ -83,7 +84,7 @@ services:
|
||||
test: "ps aux|grep -v grep|grep -v '1 root'|grep gunicorn || exit 1"
|
||||
|
||||
cmdb-ui:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-ui:2.4.6
|
||||
image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-ui:2.4.7
|
||||
container_name: cmdb-ui
|
||||
depends_on:
|
||||
cmdb-api:
|
||||
|
@@ -1,5 +1,5 @@
|
||||
# ================================= UI ================================
|
||||
FROM node:14.17.3-alpine AS builder
|
||||
FROM node:16.20 AS builder
|
||||
|
||||
LABEL description="cmdb-ui"
|
||||
|
||||
@@ -7,10 +7,10 @@ COPY cmdb-ui /data/apps/cmdb-ui
|
||||
|
||||
WORKDIR /data/apps/cmdb-ui
|
||||
|
||||
RUN sed -i "s#http://127.0.0.1:5000##g" .env && yarn install --ignore-engines && yarn build
|
||||
RUN sed -i "s#http://127.0.0.1:5000##g" .env && yarn install --ignore-engines --network-timeout 1000000 && yarn build
|
||||
|
||||
FROM nginx:alpine AS cmdb-ui
|
||||
|
||||
RUN mkdir /etc/nginx/html && rm -f /etc/nginx/conf.d/default.conf
|
||||
|
||||
COPY --from=builder /data/apps/cmdb-ui/dist /etc/nginx/html/
|
||||
COPY --from=builder /data/apps/cmdb-ui/dist /etc/nginx/html/
|
||||
|
Reference in New Issue
Block a user