diff --git a/apps/accounts/api/account/account.py b/apps/accounts/api/account/account.py
index 631669acc..17938358c 100644
--- a/apps/accounts/api/account/account.py
+++ b/apps/accounts/api/account/account.py
@@ -45,6 +45,7 @@ class AccountViewSet(OrgBulkModelViewSet):
             accounts = asset.accounts.all()
         else:
             accounts = []
+        accounts = self.filter_queryset(accounts)
         serializer = serializers.AccountSerializer(accounts, many=True)
         return Response(data=serializer.data)
 
diff --git a/apps/accounts/api/account/task.py b/apps/accounts/api/account/task.py
index 050e69052..e5d4b36bb 100644
--- a/apps/accounts/api/account/task.py
+++ b/apps/accounts/api/account/task.py
@@ -3,6 +3,7 @@ from rest_framework.response import Response
 
 from accounts import serializers
 from accounts.tasks import verify_accounts_connectivity_task, push_accounts_to_assets_task
+from assets.exceptions import NotSupportedTemporarilyError
 
 __all__ = [
     'AccountsTaskCreateAPI',
@@ -28,6 +29,11 @@ class AccountsTaskCreateAPI(CreateAPIView):
         if data['action'] == 'push':
             task = push_accounts_to_assets_task.delay(account_ids)
         else:
+            account = accounts[0]
+            asset = account.asset
+            if not asset.auto_info['ansible_enabled'] or \
+                not asset.auto_info['ping_enabled']:
+                raise NotSupportedTemporarilyError()
             task = verify_accounts_connectivity_task.delay(account_ids)
 
         data = getattr(serializer, '_data', {})
diff --git a/apps/accounts/automations/change_secret/manager.py b/apps/accounts/automations/change_secret/manager.py
index c076ab56b..a6c14fa23 100644
--- a/apps/accounts/automations/change_secret/manager.py
+++ b/apps/accounts/automations/change_secret/manager.py
@@ -86,6 +86,10 @@ class ChangeSecretManager(AccountBasePlaybookManager):
             accounts = accounts.filter(username__in=self.snapshot_account_usernames)
 
         accounts = accounts.filter(secret_type=self.secret_type)
+        if not accounts:
+            print('没有发现待改密账号: %s 用户名: %s 类型: %s' % (asset.name, account.username, self.secret_type))
+            return []
+
         method_attr = getattr(automation, self.method_type() + '_method')
         method_hosts = self.method_hosts_mapper[method_attr]
         method_hosts = [h for h in method_hosts if h != host['name']]
@@ -137,8 +141,10 @@ class ChangeSecretManager(AccountBasePlaybookManager):
         recorder.status = 'success'
         recorder.date_finished = timezone.now()
         recorder.save()
-        print('recorder.new_secret', recorder.new_secret)
         account = recorder.account
+        if not account:
+            print("Account not found, deleted ?", recorder)
+            return
         account.secret = recorder.new_secret
         account.save(update_fields=['secret'])
 
diff --git a/apps/accounts/automations/push_account/manager.py b/apps/accounts/automations/push_account/manager.py
index f2f21c51a..42a53fcb1 100644
--- a/apps/accounts/automations/push_account/manager.py
+++ b/apps/accounts/automations/push_account/manager.py
@@ -36,7 +36,7 @@ class PushAccountManager(ChangeSecretManager, AccountBasePlaybookManager):
 
     def get_accounts(self, privilege_account, accounts: QuerySet):
         if not privilege_account:
-            logger.debug(f'not privilege account')
+            print(f'not privilege account')
             return []
         snapshot_account_usernames = self.execution.snapshot['accounts']
         if '*' in snapshot_account_usernames:
diff --git a/apps/accounts/automations/verify_account/database/mongodb/main.yml b/apps/accounts/automations/verify_account/database/mongodb/main.yml
index 261fe63ca..483bfc127 100644
--- a/apps/accounts/automations/verify_account/database/mongodb/main.yml
+++ b/apps/accounts/automations/verify_account/database/mongodb/main.yml
@@ -6,8 +6,8 @@
   tasks:
     - name: Verify account
       mongodb_ping:
-        login_user: "{{ jms_account.username }}"
-        login_password: "{{ jms_account.secret }}"
+        login_user: "{{ account.username }}"
+        login_password: "{{ account.secret }}"
         login_host: "{{ jms_asset.address }}"
         login_port: "{{ jms_asset.port }}"
         login_database: "{{ jms_asset.spec_info.db_name }}"
diff --git a/apps/accounts/automations/verify_account/database/oracle/main.yml b/apps/accounts/automations/verify_account/database/oracle/main.yml
index 12896f09a..3da515e4f 100644
--- a/apps/accounts/automations/verify_account/database/oracle/main.yml
+++ b/apps/accounts/automations/verify_account/database/oracle/main.yml
@@ -6,9 +6,9 @@
   tasks:
     - name: Verify account
       oracle_ping:
-        login_user: "{{ jms_account.username }}"
-        login_password: "{{ jms_account.secret }}"
+        login_user: "{{ account.username }}"
+        login_password: "{{ account.secret }}"
         login_host: "{{ jms_asset.address }}"
         login_port: "{{ jms_asset.port }}"
         login_database: "{{ jms_asset.spec_info.db_name }}"
-        mode: "{{ jms_account.mode }}"
+        mode: "{{ account.mode }}"
diff --git a/apps/accounts/automations/verify_account/database/sqlserver/main.yml b/apps/accounts/automations/verify_account/database/sqlserver/main.yml
index bb079fa59..fa6c78ed7 100644
--- a/apps/accounts/automations/verify_account/database/sqlserver/main.yml
+++ b/apps/accounts/automations/verify_account/database/sqlserver/main.yml
@@ -6,8 +6,8 @@
   tasks:
     - name: Verify account
       community.general.mssql_script:
-        login_user: "{{ jms_account.username }}"
-        login_password: "{{ jms_account.secret }}"
+        login_user: "{{ account.username }}"
+        login_password: "{{ account.secret }}"
         login_host: "{{ jms_asset.address }}"
         login_port: "{{ jms_asset.port }}"
         name: '{{ jms_asset.spec_info.db_name }}'
diff --git a/apps/accounts/migrations/0008_alter_account_options.py b/apps/accounts/migrations/0008_alter_account_options.py
deleted file mode 100644
index 949840740..000000000
--- a/apps/accounts/migrations/0008_alter_account_options.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 3.2.14 on 2023-02-21 05:13
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('accounts', '0007_alter_account_options'),
-    ]
-
-    operations = [
-        migrations.AlterModelOptions(
-            name='account',
-            options={'permissions': [('view_accountsecret', 'Can view asset account secret'), ('view_historyaccount', 'Can view asset history account'), ('view_historyaccountsecret', 'Can view asset history account secret'), ('verify_account', 'Can verify account'), ('push_account', 'Can push account')], 'verbose_name': 'Account'},
-        ),
-    ]
diff --git a/apps/accounts/models/account.py b/apps/accounts/models/account.py
index 7367c53de..00934b759 100644
--- a/apps/accounts/models/account.py
+++ b/apps/accounts/models/account.py
@@ -68,6 +68,9 @@ class Account(AbsConnectivity, BaseAccount):
             ('push_account', _('Can push account')),
         ]
 
+    def __str__(self):
+        return '{}'.format(self.username)
+
     @lazyproperty
     def platform(self):
         return self.asset.platform
@@ -78,9 +81,6 @@ class Account(AbsConnectivity, BaseAccount):
             return self.username
         return self.name
 
-    def __str__(self):
-        return '{}'.format(self.username)
-
     @lazyproperty
     def has_secret(self):
         return bool(self.secret)
@@ -99,14 +99,6 @@ class Account(AbsConnectivity, BaseAccount):
         """ 排除自己和以自己为 su-from 的账号 """
         return self.asset.accounts.exclude(id=self.id).exclude(su_from=self)
 
-    def secret_changed(self):
-        history = self.history.first()
-        if not history:
-            return True
-        if history.secret != self.secret or history.secret_type != self.secret_type:
-            return True
-        return False
-
 
 class AccountTemplate(BaseAccount):
     class Meta:
diff --git a/apps/accounts/models/base.py b/apps/accounts/models/base.py
index cd1fef5e8..7233a12a2 100644
--- a/apps/accounts/models/base.py
+++ b/apps/accounts/models/base.py
@@ -109,7 +109,7 @@ class BaseAccount(JMSOrgBaseModel):
 
     @property
     def private_key_path(self):
-        if not self.secret_type != SecretType.SSH_KEY \
+        if self.secret_type != SecretType.SSH_KEY \
                 or not self.secret \
                 or not self.private_key:
             return None
diff --git a/apps/accounts/serializers/account/account.py b/apps/accounts/serializers/account/account.py
index 1f9c143bd..8cf92671e 100644
--- a/apps/accounts/serializers/account/account.py
+++ b/apps/accounts/serializers/account/account.py
@@ -43,7 +43,7 @@ class AccountSerializerCreateValidateMixin:
     def push_account(instance, push_now):
         if not push_now:
             return
-        push_accounts_to_assets_task.delay([instance.id], [instance.asset_id])
+        push_accounts_to_assets_task.delay([instance.id])
 
     def create(self, validated_data):
         push_now = validated_data.pop('push_now', None)
@@ -102,7 +102,7 @@ class AccountSerializer(AccountSerializerCreateMixin, BaseAccountSerializer):
     class Meta(BaseAccountSerializer.Meta):
         model = Account
         fields = BaseAccountSerializer.Meta.fields \
-                 + ['su_from', 'version', 'asset'] \
+                 + ['su_from', 'asset'] \
                  + ['template', 'push_now', 'source']
         extra_kwargs = {
             **BaseAccountSerializer.Meta.extra_kwargs,
diff --git a/apps/accounts/serializers/automations/base.py b/apps/accounts/serializers/automations/base.py
index b8b5339d7..6b3792559 100644
--- a/apps/accounts/serializers/automations/base.py
+++ b/apps/accounts/serializers/automations/base.py
@@ -38,6 +38,8 @@ class BaseAutomationSerializer(PeriodTaskSerializerMixin, BulkOrgResourceModelSe
         }
 
     def validate_name(self, name):
+        if self.instance:
+            return name
         if BaseAutomation.objects.filter(name=name, type=self.model_type).exists():
             raise serializers.ValidationError(_('Name already exists'))
         return name
diff --git a/apps/accounts/signal_handlers.py b/apps/accounts/signal_handlers.py
index df2b0e5b7..bb4eaedb8 100644
--- a/apps/accounts/signal_handlers.py
+++ b/apps/accounts/signal_handlers.py
@@ -1,17 +1,3 @@
-from django.db.models.signals import pre_save
-from django.dispatch import receiver
-
 from common.utils import get_logger
-from .models import Account
 
 logger = get_logger(__name__)
-
-
-@receiver(pre_save, sender=Account)
-def on_account_pre_create(sender, instance, update_fields=(), **kwargs):
-    # 这是创建时
-    if instance.version == 0 or instance.secret_changed():
-        instance.version += 1
-
-    # 即使在 root 组织也不怕
-    instance.org_id = instance.asset.org_id
diff --git a/apps/assets/api/asset/asset.py b/apps/assets/api/asset/asset.py
index 5b6b9084b..5b67e9114 100644
--- a/apps/assets/api/asset/asset.py
+++ b/apps/assets/api/asset/asset.py
@@ -8,12 +8,10 @@ from rest_framework.response import Response
 
 from accounts.tasks import push_accounts_to_assets_task, verify_accounts_connectivity_task
 from assets import serializers
+from assets.exceptions import NotSupportedTemporarilyError
 from assets.filters import IpInFilterBackend, LabelFilterBackend, NodeFilterBackend
 from assets.models import Asset, Gateway
-from assets.tasks import (
-    test_assets_connectivity_manual,
-    update_assets_hardware_info_manual
-)
+from assets.tasks import test_assets_connectivity_manual, update_assets_hardware_info_manual
 from common.api import SuggestionMixin
 from common.drf.filters import BaseFilterSet
 from common.utils import get_logger, is_uuid
@@ -154,6 +152,10 @@ class AssetsTaskMixin:
         if data["action"] == "refresh":
             task = update_assets_hardware_info_manual(assets)
         else:
+            asset = assets[0]
+            if not asset.auto_info['ansible_enabled'] or \
+                not asset.auto_info['ping_enabled']:
+                raise NotSupportedTemporarilyError()
             task = test_assets_connectivity_manual(assets)
         return task
 
@@ -205,9 +207,9 @@ class AssetTaskCreateApi(AssetsTaskMixin, generics.CreateAPIView):
         asset_ids = [asset.id]
         account_ids = accounts.values_list("id", flat=True)
         if action == "push_account":
-            task = push_accounts_to_assets_task.delay(account_ids, asset_ids)
+            task = push_accounts_to_assets_task.delay(account_ids)
         elif action == "test_account":
-            task = verify_accounts_connectivity_task.delay(account_ids, asset_ids)
+            task = verify_accounts_connectivity_task.delay(account_ids)
         else:
             task = None
         return task
diff --git a/apps/assets/automations/base/manager.py b/apps/assets/automations/base/manager.py
index 7ef2e528c..6c8dc189a 100644
--- a/apps/assets/automations/base/manager.py
+++ b/apps/assets/automations/base/manager.py
@@ -67,7 +67,7 @@ class BasePlaybookManager:
         if not os.path.exists(path):
             os.makedirs(path, exist_ok=True, mode=0o755)
         if settings.DEBUG_DEV:
-            logger.debug('Ansible runtime dir: {}'.format(path))
+            print(f'Ansible runtime dir: {path}')
         return path
 
     @staticmethod
@@ -156,10 +156,9 @@ class BasePlaybookManager:
         return sub_playbook_path
 
     def get_runners(self):
-        # TODO 临时打印一下 找一下打印不出日志的原因
-        print('ansible runner: 任务开始执行')
         assets_group_by_platform = self.get_assets_group_by_platform()
-        print('ansible runner: 获取资产分组', assets_group_by_platform)
+        if settings.DEBUG_DEV:
+            print("assets_group_by_platform: {}".format(assets_group_by_platform))
         runners = []
         for platform, assets in assets_group_by_platform.items():
             assets_bulked = [assets[i:i + self.bulk_size] for i in range(0, len(assets), self.bulk_size)]
@@ -213,6 +212,7 @@ class BasePlaybookManager:
     def file_to_json(path):
         with open(path, 'r') as f:
             d = json.load(f)
+
         return d
 
     @staticmethod
diff --git a/apps/assets/exceptions.py b/apps/assets/exceptions.py
index 099e68f11..ad22b6339 100644
--- a/apps/assets/exceptions.py
+++ b/apps/assets/exceptions.py
@@ -1,6 +1,12 @@
+from django.utils.translation import ugettext_lazy as _
 from rest_framework import status
+
 from common.exceptions import JMSException
 
 
 class NodeIsBeingUpdatedByOthers(JMSException):
     status_code = status.HTTP_409_CONFLICT
+
+
+class NotSupportedTemporarilyError(JMSException):
+    default_detail = _("This function is not supported temporarily")
diff --git a/apps/assets/migrations/0109_alter_asset_options.py b/apps/assets/migrations/0109_alter_asset_options.py
index 859b1ca0c..4a1c93a15 100644
--- a/apps/assets/migrations/0109_alter_asset_options.py
+++ b/apps/assets/migrations/0109_alter_asset_options.py
@@ -12,6 +12,6 @@ class Migration(migrations.Migration):
     operations = [
         migrations.AlterModelOptions(
             name='asset',
-            options={'ordering': ['name'], 'permissions': [('refresh_assethardwareinfo', 'Can refresh asset hardware info'), ('test_assetconnectivity', 'Can test asset connectivity'), ('push_assetaccount', 'Can push account to asset'), ('test_account', 'Can verify account'), ('match_asset', 'Can match asset'), ('change_assettonode', 'Can change asset nodes')], 'verbose_name': 'Asset'},
+            options={'ordering': ['name'], 'permissions': [('refresh_assethardwareinfo', 'Can refresh asset hardware info'), ('test_assetconnectivity', 'Can test asset connectivity'), ('match_asset', 'Can match asset'), ('change_assetnodes', 'Can change asset nodes')], 'verbose_name': 'Asset'},
         ),
     ]
diff --git a/apps/assets/migrations/0110_alter_asset_options.py b/apps/assets/migrations/0110_alter_asset_options.py
deleted file mode 100644
index fa5495e33..000000000
--- a/apps/assets/migrations/0110_alter_asset_options.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 3.2.14 on 2023-02-21 05:11
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('assets', '0109_alter_asset_options'),
-    ]
-
-    operations = [
-        migrations.AlterModelOptions(
-            name='asset',
-            options={'ordering': ['name'], 'permissions': [('refresh_assethardwareinfo', 'Can refresh asset hardware info'), ('test_assetconnectivity', 'Can test asset connectivity'), ('match_asset', 'Can match asset'), ('change_assetnodes', 'Can change asset nodes')], 'verbose_name': 'Asset'},
-        ),
-    ]
diff --git a/apps/assets/serializers/asset/common.py b/apps/assets/serializers/asset/common.py
index 9c31ee695..c5a9ed02d 100644
--- a/apps/assets/serializers/asset/common.py
+++ b/apps/assets/serializers/asset/common.py
@@ -277,6 +277,8 @@ class AssetSerializer(BulkOrgResourceModelSerializer, WritableNestedModelSeriali
 
     @atomic
     def update(self, instance, validated_data):
+        if not validated_data.get('accounts'):
+            validated_data.pop('accounts', None)
         nodes_display = validated_data.pop('nodes_display', '')
         instance = super().update(instance, validated_data)
         self.perform_nodes_display_create(instance, nodes_display)
diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py
index f7c555504..b192cd993 100644
--- a/apps/jumpserver/conf.py
+++ b/apps/jumpserver/conf.py
@@ -530,7 +530,7 @@ class Config(dict):
         'PERIOD_TASK_ENABLED': True,
 
         # 导航栏 帮助
-        'HELP_DOCUMENT_URL': 'http://docs.jumpserver.org',
+        'HELP_DOCUMENT_URL': 'https://docs.jumpserver.org/zh/v3/',
         'HELP_SUPPORT_URL': 'http://www.jumpserver.org/support/',
 
         'FORGOT_PASSWORD_URL': '',
diff --git a/apps/locale/ja/LC_MESSAGES/django.mo b/apps/locale/ja/LC_MESSAGES/django.mo
index df0751934..781aa1a90 100644
--- a/apps/locale/ja/LC_MESSAGES/django.mo
+++ b/apps/locale/ja/LC_MESSAGES/django.mo
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:331188bb5169bb463da018a635589e12a2136d476db264ac7e5d6e5d63ca474a
-size 135916
+oid sha256:af57d16430705feb02ebbb99fc3a2f5fc3bab69209f558aa4d69b1e8055a6f5f
+size 136036
diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po
index 4e86c7f0c..6e38486bd 100644
--- a/apps/locale/ja/LC_MESSAGES/django.po
+++ b/apps/locale/ja/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-02-21 18:29+0800\n"
+"POT-Creation-Date: 2023-02-21 22:44+0800\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -908,7 +908,7 @@ msgstr "アプリケーション"
 msgid "Can match application"
 msgstr "アプリケーションを一致させることができます"
 
-#: assets/api/asset/asset.py:144
+#: assets/api/asset/asset.py:142
 msgid "Cannot create asset directly, you should create a host or other"
 msgstr ""
 "資産を直接作成することはできません。ホストまたはその他を作成する必要がありま"
@@ -1051,6 +1051,10 @@ msgstr "基本"
 msgid "Script"
 msgstr "脚本"
 
+#: assets/exceptions.py:12
+msgid "This function is not supported temporarily"
+msgstr "この機能は一時的にサポートされていません"
+
 #: assets/models/_user.py:25
 msgid "SSH private key"
 msgstr "SSH秘密鍵"
diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo
index cd68677c8..02f116670 100644
--- a/apps/locale/zh/LC_MESSAGES/django.mo
+++ b/apps/locale/zh/LC_MESSAGES/django.mo
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:2cdc2b875c98f41bd698833a989195d8cc4245f39f52b7eab41ad4d95075cb17
-size 111666
+oid sha256:3b6ee4a378810f2515be5020e3fa0b1297e1c207260ca60bb14dc5407ca19c43
+size 111750
diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po
index dc0f1de28..c3f661344 100644
--- a/apps/locale/zh/LC_MESSAGES/django.po
+++ b/apps/locale/zh/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: JumpServer 0.3.3\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-02-21 18:29+0800\n"
+"POT-Creation-Date: 2023-02-21 22:44+0800\n"
 "PO-Revision-Date: 2021-05-20 10:54+0800\n"
 "Last-Translator: ibuler <ibuler@qq.com>\n"
 "Language-Team: JumpServer team<ibuler@qq.com>\n"
@@ -902,7 +902,7 @@ msgstr "应用程序"
 msgid "Can match application"
 msgstr "匹配应用"
 
-#: assets/api/asset/asset.py:144
+#: assets/api/asset/asset.py:142
 msgid "Cannot create asset directly, you should create a host or other"
 msgstr "不能直接创建资产, 你应该创建主机或其他资产"
 
@@ -1043,6 +1043,10 @@ msgstr "基本"
 msgid "Script"
 msgstr "脚本"
 
+#: assets/exceptions.py:12
+msgid "This function is not supported temporarily"
+msgstr "暂时不支持此功能"
+
 #: assets/models/_user.py:25
 msgid "SSH private key"
 msgstr "SSH密钥"
diff --git a/apps/ops/api/playbook.py b/apps/ops/api/playbook.py
index c26643fe5..b3c1fe564 100644
--- a/apps/ops/api/playbook.py
+++ b/apps/ops/api/playbook.py
@@ -6,6 +6,7 @@ from django.conf import settings
 from django.shortcuts import get_object_or_404
 from rest_framework import status
 
+from common.exceptions import JMSException
 from orgs.mixins.api import OrgBulkModelViewSet
 from ..exception import PlaybookNoValidEntry
 from ..models import Playbook
@@ -39,7 +40,11 @@ class PlaybookViewSet(OrgBulkModelViewSet):
         if 'multipart/form-data' in self.request.headers['Content-Type']:
             src_path = os.path.join(settings.MEDIA_ROOT, instance.path.name)
             dest_path = os.path.join(settings.DATA_DIR, "ops", "playbook", instance.id.__str__())
-            unzip_playbook(src_path, dest_path)
+            try:
+                unzip_playbook(src_path, dest_path)
+            except RuntimeError as e:
+                raise JMSException(code='invalid_playbook_file', detail={"msg": "Unzip failed"})
+
             if 'main.yml' not in os.listdir(dest_path):
                 raise PlaybookNoValidEntry
 
diff --git a/apps/terminal/models/applet/applet.py b/apps/terminal/models/applet/applet.py
index c71fe95c2..ee83c8e52 100644
--- a/apps/terminal/models/applet/applet.py
+++ b/apps/terminal/models/applet/applet.py
@@ -111,7 +111,8 @@ class Applet(JMSBaseModel):
         return instance
 
     def select_host_account(self):
-        hosts = list(self.hosts.all())
+        # 选择激活的发布机
+        hosts = list(self.hosts.filter(is_active=True).all())
         if not hosts:
             return None