mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-04-27 11:12:54 +00:00
perf: applet account select
This commit is contained in:
parent
fa70fb2921
commit
1ee70af93d
@ -138,9 +138,15 @@ class Account(AbsConnectivity, LabeledMixin, BaseAccount, JSONFilterMixin):
|
||||
return self.username
|
||||
return str(self.id)
|
||||
|
||||
def is_virtual(self):
|
||||
"""
|
||||
不要用 username 去判断,因为可能是构造的 account 对象,设置了同名账号的用户名,
|
||||
"""
|
||||
return self.alias.startswith('@')
|
||||
|
||||
def is_ds_account(self):
|
||||
if self.username.startswith('@'):
|
||||
return False
|
||||
if self.is_virtual():
|
||||
return ''
|
||||
if not self.asset.is_directory_service:
|
||||
return False
|
||||
return True
|
||||
@ -154,6 +160,8 @@ class Account(AbsConnectivity, LabeledMixin, BaseAccount, JSONFilterMixin):
|
||||
@lazyproperty
|
||||
def ds_domain(self):
|
||||
"""这个不能去掉,perm_account 会动态设置这个值,以更改 full_username"""
|
||||
if self.is_virtual():
|
||||
return ''
|
||||
if self.ds and self.ds.domain_name:
|
||||
return self.ds.domain_name
|
||||
return ''
|
||||
|
@ -92,8 +92,9 @@ class VirtualAccount(JMSOrgBaseModel):
|
||||
from .account import Account
|
||||
username = user.username
|
||||
|
||||
alias = AliasAccount.USER.value
|
||||
with tmp_to_org(asset.org):
|
||||
same_account = cls.objects.filter(alias='@USER').first()
|
||||
same_account = cls.objects.filter(alias=alias).first()
|
||||
|
||||
secret = ''
|
||||
if same_account and same_account.secret_from_login:
|
||||
@ -101,4 +102,6 @@ class VirtualAccount(JMSOrgBaseModel):
|
||||
|
||||
if not secret and not from_permed:
|
||||
secret = input_secret
|
||||
return Account(name=AliasAccount.USER.label, username=username, secret=secret)
|
||||
account = Account(name=AliasAccount.USER.label, username=username, secret=secret)
|
||||
account.alias = alias
|
||||
return account
|
||||
|
@ -146,7 +146,7 @@ class BaseAssetViewSet(OrgBulkModelViewSet):
|
||||
|
||||
def paginate_queryset(self, queryset):
|
||||
page = super().paginate_queryset(queryset)
|
||||
page = Asset.compute_accounts_amount(page)
|
||||
page = Asset.compute_all_accounts_amount(page)
|
||||
return page
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
|
@ -255,10 +255,15 @@ class Asset(NodesRelationMixin, LabeledMixin, AbsConnectivity, JSONFilterMixin,
|
||||
|
||||
@property
|
||||
def all_accounts(self):
|
||||
if not self.joined_dir_svc_ids:
|
||||
if not self.joined_dir_svcs:
|
||||
queryset = self.accounts.all()
|
||||
else:
|
||||
queryset = self.accounts.model.objects.filter(asset__in=[self.id, *self.joined_dir_svc_ids])
|
||||
queryset = self.accounts.model.objects.filter(asset__in=[self.id, *self.joined_dir_svcs])
|
||||
return queryset
|
||||
|
||||
@property
|
||||
def dc_accounts(self):
|
||||
queryset = self.accounts.model.objects.filter(asset__in=[*self.joined_dir_svcs])
|
||||
return queryset
|
||||
|
||||
@lazyproperty
|
||||
@ -285,27 +290,22 @@ class Asset(NodesRelationMixin, LabeledMixin, AbsConnectivity, JSONFilterMixin,
|
||||
return self.category == const.Category.DS
|
||||
|
||||
@property
|
||||
def joined_dir_svc_ids(self):
|
||||
def joined_dir_svcs(self):
|
||||
return self.directory_services.all()
|
||||
|
||||
def is_joined_ad(self):
|
||||
if self.joined_dir_svc_ids:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def compute_accounts_amount(cls, assets):
|
||||
def compute_all_accounts_amount(cls, assets):
|
||||
from .ds import DirectoryService
|
||||
asset_ids = [asset.id for asset in assets]
|
||||
asset_id_dc_ids_mapper = defaultdict(list)
|
||||
dc_ids = set()
|
||||
relations = (
|
||||
|
||||
asset_dc_relations = (
|
||||
Asset.directory_services.through.objects
|
||||
.filter(asset_id__in=asset_ids)
|
||||
.values_list('asset_id', 'directoryservice_id')
|
||||
)
|
||||
for asset_id, ds_id in relations:
|
||||
for asset_id, ds_id in asset_dc_relations:
|
||||
dc_ids.add(ds_id)
|
||||
asset_id_dc_ids_mapper[asset_id].append(ds_id)
|
||||
|
||||
@ -313,11 +313,11 @@ class Asset(NodesRelationMixin, LabeledMixin, AbsConnectivity, JSONFilterMixin,
|
||||
DirectoryService.objects.filter(id__in=dc_ids)
|
||||
.annotate(accounts_amount=Count('accounts'))
|
||||
)
|
||||
ds_accounts_mapper = {ds.id: ds.accounts_amount for ds in directory_services}
|
||||
ds_accounts_amount_mapper = {ds.id: ds.accounts_amount for ds in directory_services}
|
||||
for asset in assets:
|
||||
asset_dc_ids = asset_id_dc_ids_mapper.get(asset.id, [])
|
||||
for dc_id in asset_dc_ids:
|
||||
ds_accounts = ds_accounts_mapper.get(dc_id, 0)
|
||||
ds_accounts = ds_accounts_amount_mapper.get(dc_id, 0)
|
||||
asset.accounts_amount += ds_accounts
|
||||
return assets
|
||||
|
||||
|
@ -166,9 +166,42 @@ class Applet(JMSBaseModel):
|
||||
hosts = list(sorted(hosts, key=lambda h: counts[str(h.id)]))
|
||||
return hosts[0] if hosts else None
|
||||
|
||||
def select_host(self, user, asset):
|
||||
def _filter_published_hosts(self, hosts):
|
||||
if settings.DEBUG_DEV:
|
||||
return True
|
||||
exclude_status = [PublishStatus.pending, PublishStatus.failed]
|
||||
publications = (
|
||||
AppletPublication.objects
|
||||
.filter(applet=self, host__in=hosts)
|
||||
.exclude(status__in=exclude_status)
|
||||
)
|
||||
if not publications:
|
||||
return None
|
||||
return [p.host for p in publications]
|
||||
|
||||
def filter_available_hosts(self):
|
||||
hosts = self.hosts.filter(is_active=True)
|
||||
|
||||
if not hosts:
|
||||
logger.info("No active host for applet: {}".format(self.name))
|
||||
return None
|
||||
|
||||
if settings.DEBUG_DEV:
|
||||
return hosts
|
||||
|
||||
hosts = [host for host in hosts if host.load != 'offline']
|
||||
if not hosts:
|
||||
logger.info("No online host for applet: {}".format(self.name))
|
||||
return None
|
||||
|
||||
hosts = self._filter_published_hosts(hosts)
|
||||
if not hosts:
|
||||
logger.info("No published host for applet: {}".format(self.name))
|
||||
return None
|
||||
return hosts
|
||||
|
||||
def select_host(self, user, asset):
|
||||
hosts = self.filter_available_hosts()
|
||||
if not hosts:
|
||||
return None
|
||||
|
||||
@ -230,15 +263,50 @@ class Applet(JMSBaseModel):
|
||||
account = self.random_select_prefer_account(user, host, public_accounts)
|
||||
return account
|
||||
|
||||
@staticmethod
|
||||
def _try_virtual_private_account(user, host):
|
||||
from accounts.models import VirtualAccount
|
||||
|
||||
if not host.using_same_account:
|
||||
return
|
||||
account = VirtualAccount.get_same_account(user, host)
|
||||
if not account.secret:
|
||||
return
|
||||
return account
|
||||
|
||||
@staticmethod
|
||||
def _try_local_private_account(user, valid_accounts):
|
||||
private_account = valid_accounts.filter(username='js_{}'.format(user.username)).first()
|
||||
return private_account
|
||||
|
||||
@staticmethod
|
||||
def _try_dc_private_account(user, host):
|
||||
if not host.joined_dir_svcs:
|
||||
return None
|
||||
account = host.dc_accounts.filter(username=user.username).first()
|
||||
return account
|
||||
|
||||
def _select_a_private_account(self, user, host, valid_accounts):
|
||||
private_account = self._try_virtual_private_account(user, host)
|
||||
if not private_account:
|
||||
logger.debug('Virtual private account not found ...')
|
||||
private_account = self._try_dc_private_account(user, host)
|
||||
|
||||
if not private_account:
|
||||
logger.debug('DC private account not found ...')
|
||||
private_account = self._try_local_private_account(user, valid_accounts)
|
||||
|
||||
if not private_account:
|
||||
logger.debug('Private account not found ...')
|
||||
return None
|
||||
return private_account
|
||||
|
||||
def try_to_use_private_account(self, user, host, valid_accounts):
|
||||
host_can_concurrent = str(host.deploy_options.get('RDS_fSingleSessionPerUser', 0)) == '0'
|
||||
app_can_concurrent = self.can_concurrent or self.type == 'web'
|
||||
all_can_concurrent = host_can_concurrent and app_can_concurrent
|
||||
|
||||
private_account = valid_accounts.filter(username='js_{}'.format(user.username)).first()
|
||||
if not private_account:
|
||||
logger.debug('Private account not found ...')
|
||||
return None
|
||||
private_account = self._select_a_private_account(user, host, valid_accounts)
|
||||
# 优先使用 private account,支持并发或者不支持并发时,如果私有没有被占用,则使用私有
|
||||
account = None
|
||||
# 如果都支持,不管私有是否被占用,都使用私有
|
||||
@ -261,32 +329,14 @@ class Applet(JMSBaseModel):
|
||||
account = private_account
|
||||
return account
|
||||
|
||||
@staticmethod
|
||||
def try_to_use_same_account(user, host):
|
||||
from accounts.models import VirtualAccount
|
||||
|
||||
if not host.using_same_account:
|
||||
return
|
||||
account = VirtualAccount.get_same_account(user, host)
|
||||
if not account.secret:
|
||||
return
|
||||
return account
|
||||
|
||||
def select_host_account(self, user, asset):
|
||||
# 选择激活的发布机
|
||||
host = self.select_host(user, asset)
|
||||
if not host:
|
||||
return None
|
||||
logger.info('Select applet host: {}'.format(host.name))
|
||||
if not self.is_available_on_host(host):
|
||||
logger.debug('No available applet {} for applet host: {}'.format(self.name, host.name))
|
||||
return None
|
||||
valid_accounts = host.all_valid_accounts.all().filter(privileged=False)
|
||||
account = self.try_to_use_same_account(user, host)
|
||||
if not account:
|
||||
logger.debug('No same account, try to use private account')
|
||||
account = self.try_to_use_private_account(user, host, valid_accounts)
|
||||
|
||||
valid_accounts = host.accounts.all().filter(privileged=False)
|
||||
account = self.try_to_use_private_account(user, host, valid_accounts)
|
||||
if not account:
|
||||
logger.debug('No private account, try to use public account')
|
||||
account = self.select_a_public_account(user, host, valid_accounts)
|
||||
@ -313,14 +363,6 @@ class Applet(JMSBaseModel):
|
||||
platform.delete()
|
||||
return super().delete(using, keep_parents)
|
||||
|
||||
def is_available_on_host(self, host):
|
||||
publication = AppletPublication.objects.filter(applet=self, host=host).first()
|
||||
if not publication:
|
||||
return False
|
||||
if publication.status in [PublishStatus.pending, PublishStatus.failed]:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class AppletPublication(JMSBaseModel):
|
||||
applet = models.ForeignKey('Applet', on_delete=models.CASCADE, related_name='publications',
|
||||
|
Loading…
Reference in New Issue
Block a user