perf: gateway account (#9150)

Co-authored-by: feng <1304903146@qq.com>
pull/9152/head
fit2bot 2022-12-02 17:36:55 +08:00 committed by GitHub
parent 2f5e133558
commit 0e0a9f4654
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 60 additions and 19 deletions

View File

@ -7,7 +7,7 @@ from rest_framework.response import Response
from assets import serializers from assets import serializers
from assets.filters import IpInFilterBackend, LabelFilterBackend, NodeFilterBackend from assets.filters import IpInFilterBackend, LabelFilterBackend, NodeFilterBackend
from assets.models import Asset from assets.models import Asset, Gateway
from assets.tasks import ( from assets.tasks import (
push_accounts_to_assets, test_assets_connectivity_manual, push_accounts_to_assets, test_assets_connectivity_manual,
update_assets_hardware_info_manual, verify_accounts_connectivity, update_assets_hardware_info_manual, verify_accounts_connectivity,
@ -57,7 +57,7 @@ class AssetViewSet(SuggestionMixin, NodeFilterMixin, OrgBulkModelViewSet):
("default", serializers.AssetSerializer), ("default", serializers.AssetSerializer),
("suggestion", serializers.MiniAssetSerializer), ("suggestion", serializers.MiniAssetSerializer),
("platform", serializers.PlatformSerializer), ("platform", serializers.PlatformSerializer),
("gateways", serializers.GatewayWithAuthSerializer), ("gateways", serializers.GatewaySerializer),
) )
rbac_perms = ( rbac_perms = (
("match", "assets.match_asset"), ("match", "assets.match_asset"),
@ -76,9 +76,9 @@ class AssetViewSet(SuggestionMixin, NodeFilterMixin, OrgBulkModelViewSet):
def gateways(self, *args, **kwargs): def gateways(self, *args, **kwargs):
asset = self.get_object() asset = self.get_object()
if not asset.domain: if not asset.domain:
gateways = Asset.objects.none() gateways = Gateway.objects.none()
else: else:
gateways = asset.domain.gateways.filter(protocol="ssh") gateways = asset.domain.gateways
return self.get_paginated_response_from_queryset(gateways) return self.get_paginated_response_from_queryset(gateways)

View File

@ -6,7 +6,6 @@ from hashlib import md5
import sshpubkeys import sshpubkeys
from django.conf import settings from django.conf import settings
from django.db import models from django.db import models
from django.db.models import QuerySet
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -35,7 +34,7 @@ class AbsConnectivity(models.Model):
@classmethod @classmethod
def bulk_set_connectivity(cls, queryset_or_id, connectivity): def bulk_set_connectivity(cls, queryset_or_id, connectivity):
if not isinstance(queryset_or_id, QuerySet): if not isinstance(queryset_or_id, models.QuerySet):
queryset = cls.objects.filter(id__in=queryset_or_id) queryset = cls.objects.filter(id__in=queryset_or_id)
else: else:
queryset = queryset_or_id queryset = queryset_or_id
@ -87,7 +86,7 @@ class BaseAccount(JMSOrgBaseModel):
@lazyproperty @lazyproperty
def public_key(self): def public_key(self):
if self.secret_type == SecretType.SSH_KEY and self.private_key: if self.secret_type == SecretType.SSH_KEY and self.private_key:
return parse_ssh_public_key_str(private_key=self.private_key) return parse_ssh_public_key_str(self.private_key)
return None return None
@property @property

View File

@ -10,7 +10,7 @@ from django.utils.translation import ugettext_lazy as _
from common.utils import get_logger, lazyproperty from common.utils import get_logger, lazyproperty
from orgs.mixins.models import OrgModelMixin from orgs.mixins.models import OrgModelMixin
from assets.models import Host, Platform from assets.models import Host, Platform
from assets.const import GATEWAY_NAME from assets.const import GATEWAY_NAME, SecretType
from orgs.mixins.models import OrgManager from orgs.mixins.models import OrgManager
logger = get_logger(__file__) logger = get_logger(__file__)
@ -80,3 +80,40 @@ class Gateway(Host):
platform = self.default_platform platform = self.default_platform
self.platform_id = platform.id self.platform_id = platform.id
return super().save(*args, **kwargs) return super().save(*args, **kwargs)
@lazyproperty
def select_accounts(self) -> dict:
account_dict = {}
accounts = self.accounts.filter(is_active=True).order_by('-privileged', '-date_updated')
password_account = accounts.filter(secret_type=SecretType.PASSWORD).first()
if password_account:
account_dict[SecretType.PASSWORD] = password_account
ssh_key_account = accounts.filter(secret_type=SecretType.SSH_KEY).first()
if ssh_key_account:
account_dict[SecretType.SSH_KEY] = ssh_key_account
return account_dict
@property
def password(self):
account = self.select_accounts.get(SecretType.PASSWORD)
return account.secret if account else None
@property
def private_key(self):
account = self.select_accounts.get(SecretType.SSH_KEY)
return account.secret if account else None
def private_key_path(self):
account = self.select_accounts.get(SecretType.SSH_KEY)
return account.private_key_path if account else None
@lazyproperty
def username(self):
accounts = self.select_accounts.values()
if len(accounts) == 0:
return None
accounts = sorted(
accounts, key=lambda x: x['privileged'], reverse=True
)
return accounts[0].username

View File

@ -39,21 +39,26 @@ class DomainSerializer(BulkOrgResourceModelSerializer):
class GatewaySerializer(HostSerializer): class GatewaySerializer(HostSerializer):
effective_accounts = serializers.SerializerMethodField()
class Meta(HostSerializer.Meta): class Meta(HostSerializer.Meta):
model = Gateway model = Gateway
fields = HostSerializer.Meta.fields + ['effective_accounts']
@staticmethod
class GatewayWithAuthSerializer(SecretReadableMixin, GatewaySerializer): def get_effective_accounts(obj):
class Meta(GatewaySerializer.Meta): accounts = obj.select_accounts.values()
extra_kwargs = { return [
'password': {'write_only': False}, {
'private_key': {"write_only": False}, 'id': account.id,
'public_key': {"write_only": False}, 'username': account.username,
} 'secret_type': account.secret_type,
} for account in accounts
]
class DomainWithGatewaySerializer(BulkOrgResourceModelSerializer): class DomainWithGatewaySerializer(BulkOrgResourceModelSerializer):
gateways = GatewayWithAuthSerializer(many=True, read_only=True) gateways = GatewaySerializer(many=True, read_only=True)
class Meta: class Meta:
model = Domain model = Domain

View File

@ -50,7 +50,7 @@ class JMSInventory:
0, "sshpass -p '{}'".format(gateway.password) 0, "sshpass -p '{}'".format(gateway.password)
) )
if gateway.private_key: if gateway.private_key:
proxy_command_list.append("-i {}".format(gateway.private_key_file)) proxy_command_list.append("-i {}".format(gateway.private_key_path))
proxy_command = "'-o ProxyCommand={}'".format( proxy_command = "'-o ProxyCommand={}'".format(
" ".join(proxy_command_list) " ".join(proxy_command_list)
@ -67,7 +67,7 @@ class JMSInventory:
if account.secret_type == 'password': if account.secret_type == 'password':
var['ansible_password'] = account.secret var['ansible_password'] = account.secret
elif account.secret_type == 'ssh_key': elif account.secret_type == 'ssh_key':
var['ansible_ssh_private_key_file'] = account.private_key_file var['ansible_ssh_private_key_file'] = account.private_key_path
return var return var
def make_ssh_account_vars(self, host, asset, account, automation, protocols, platform, gateway): def make_ssh_account_vars(self, host, asset, account, automation, protocols, platform, gateway):