diff --git a/apps/assets/const/base.py b/apps/assets/const/base.py index 71e68cd83..e39f8c09c 100644 --- a/apps/assets/const/base.py +++ b/apps/assets/const/base.py @@ -52,7 +52,3 @@ class BaseType(TextChoices): @classmethod def internal_platforms(cls): raise NotImplementedError - - @classmethod - def create_or_update_internal_platforms(cls): - data = cls._internal_platforms() diff --git a/apps/assets/const/cloud.py b/apps/assets/const/cloud.py index e344d08fb..fb9214c65 100644 --- a/apps/assets/const/cloud.py +++ b/apps/assets/const/cloud.py @@ -20,6 +20,8 @@ class CloudTypes(BaseType): def _get_automation_constrains(cls) -> dict: constrains = { '*': { + 'ansible_enabled': False, + 'ansible_config': {}, 'gather_facts_enabled': False, 'verify_account_enabled': False, 'change_password_enabled': False, diff --git a/apps/assets/const/database.py b/apps/assets/const/database.py index 372a941d5..0a6bf0853 100644 --- a/apps/assets/const/database.py +++ b/apps/assets/const/database.py @@ -25,6 +25,10 @@ class DatabaseTypes(BaseType): def _get_automation_constrains(cls) -> dict: constrains = { '*': { + 'ansible_enabled': True, + 'ansible_config': { + 'ansible_connection': 'local', + }, 'gather_facts_enabled': True, 'gather_accounts_enabled': True, 'verify_account_enabled': True, diff --git a/apps/assets/const/device.py b/apps/assets/const/device.py index 1cb0f47d8..3524c3c7a 100644 --- a/apps/assets/const/device.py +++ b/apps/assets/const/device.py @@ -31,6 +31,10 @@ class DeviceTypes(BaseType): def _get_automation_constrains(cls) -> dict: return { '*': { + 'ansible_enabled': True, + 'ansible_config': { + 'ansible_connection': 'local', + }, 'ping_enabled': True, 'gather_facts_enabled': False, 'gather_accounts_enabled': False, diff --git a/apps/assets/const/host.py b/apps/assets/const/host.py index 387a2e7e7..160d6754b 100644 --- a/apps/assets/const/host.py +++ b/apps/assets/const/host.py @@ -40,13 +40,23 @@ class HostTypes(BaseType): def _get_automation_constrains(cls) -> dict: return { '*': { + 'ansible_enabled': True, + 'ansible_config': { + 'ansible_connection': 'smart', + }, 'ping_enabled': True, 'gather_facts_enabled': True, 'gather_accounts_enabled': True, 'verify_account_enabled': True, 'change_password_enabled': True, 'create_account_enabled': True, - } + }, + cls.WINDOWS: { + 'ansible_config': { + 'ansible_shell_type': 'powershell', + 'ansible_connection': 'ssh', + }, + }, } @classmethod diff --git a/apps/assets/const/types.py b/apps/assets/const/types.py index 043779790..454a5d358 100644 --- a/apps/assets/const/types.py +++ b/apps/assets/const/types.py @@ -1,6 +1,6 @@ from copy import deepcopy -from common.db.models import IncludesTextChoicesMeta, ChoicesMixin +from common.db.models import ChoicesMixin from common.tree import TreeNode from .category import Category @@ -11,7 +11,7 @@ from .web import WebTypes from .cloud import CloudTypes -class AllTypes(ChoicesMixin, metaclass=IncludesTextChoicesMeta): +class AllTypes(ChoicesMixin): choices: list includes = [ HostTypes, DeviceTypes, DatabaseTypes, @@ -19,6 +19,13 @@ class AllTypes(ChoicesMixin, metaclass=IncludesTextChoicesMeta): ] _category_constrains = {} + @classmethod + def choices(cls): + choices = [] + for tp in cls.includes: + choices.extend(tp.choices) + return choices + @classmethod def get_constraints(cls, category, tp): types_cls = dict(cls.category_types()).get(category) diff --git a/apps/assets/migrations/0096_auto_20220426_1550.py b/apps/assets/migrations/0096_auto_20220426_1550.py index 486a3883f..acfa93e7d 100644 --- a/apps/assets/migrations/0096_auto_20220426_1550.py +++ b/apps/assets/migrations/0096_auto_20220426_1550.py @@ -25,6 +25,8 @@ class Migration(migrations.Migration): name='PlatformAutomation', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('ansible_enabled', models.BooleanField(default=False, verbose_name='Enabled')), + ('ansible_config', models.JSONField(default=dict, verbose_name='Ansible config')), ('ping_enabled', models.BooleanField(default=False, verbose_name='Ping enabled')), ('ping_method', models.CharField(blank=True, max_length=32, null=True, verbose_name='Ping method')), ('gather_facts_enabled', models.BooleanField(default=False, verbose_name='Gather facts enabled')), diff --git a/apps/assets/models/platform.py b/apps/assets/models/platform.py index 30f377b00..3b2ea2ae5 100644 --- a/apps/assets/models/platform.py +++ b/apps/assets/models/platform.py @@ -31,6 +31,8 @@ class PlatformProtocol(models.Model): class PlatformAutomation(models.Model): + ansible_enabled = models.BooleanField(default=False, verbose_name=_("Enabled")) + ansible_config = models.JSONField(default=dict, verbose_name=_("Ansible config")) ping_enabled = models.BooleanField(default=False, verbose_name=_("Ping enabled")) ping_method = models.CharField(max_length=32, blank=True, null=True, verbose_name=_("Ping method")) gather_facts_enabled = models.BooleanField(default=False, verbose_name=_("Gather facts enabled")) diff --git a/apps/assets/serializers/asset/common.py b/apps/assets/serializers/asset/common.py index b2746493c..419beba2b 100644 --- a/apps/assets/serializers/asset/common.py +++ b/apps/assets/serializers/asset/common.py @@ -59,11 +59,9 @@ class AssetAccountSerializer(AccountSerializer): fields = fields_mini + fields_write_only - - class AssetSerializer(JMSWritableNestedModelSerializer): category = LabeledChoiceField(choices=Category.choices, read_only=True, label=_('Category')) - type = LabeledChoiceField(choices=AllTypes.choices, read_only=True, label=_('Type')) + type = LabeledChoiceField(choices=AllTypes.choices(), read_only=True, label=_('Type')) domain = ObjectRelatedField(required=False, queryset=Domain.objects, label=_('Domain')) platform = ObjectRelatedField(required=False, queryset=Platform.objects, label=_('Platform')) nodes = ObjectRelatedField(many=True, required=False, queryset=Node.objects, label=_('Nodes')) diff --git a/apps/assets/serializers/platform.py b/apps/assets/serializers/platform.py index c592f73e5..b1e6820cd 100644 --- a/apps/assets/serializers/platform.py +++ b/apps/assets/serializers/platform.py @@ -4,7 +4,7 @@ from django.utils.translation import gettext_lazy as _ from common.drf.fields import LabeledChoiceField from common.drf.serializers import JMSWritableNestedModelSerializer from ..models import Platform, PlatformProtocol, PlatformAutomation -from ..const import Category, AllTypes, Protocol +from ..const import Category, AllTypes __all__ = ['PlatformSerializer', 'PlatformOpsMethodSerializer'] @@ -36,7 +36,8 @@ class PlatformAutomationSerializer(serializers.ModelSerializer): class Meta: model = PlatformAutomation fields = [ - 'id', 'ping_enabled', 'ping_method', + 'id', 'ansible_enabled', 'ansible_config', + 'ping_enabled', 'ping_method', 'gather_facts_enabled', 'gather_facts_method', 'create_account_enabled', 'create_account_method', 'change_password_enabled', 'change_password_method', @@ -68,7 +69,7 @@ class PlatformProtocolsSerializer(serializers.ModelSerializer): class PlatformSerializer(JMSWritableNestedModelSerializer): - type = LabeledChoiceField(choices=AllTypes.choices, label=_("Type")) + type = LabeledChoiceField(choices=AllTypes.choices(), label=_("Type")) category = LabeledChoiceField(choices=Category.choices, label=_("Category")) protocols = PlatformProtocolsSerializer(label=_('Protocols'), many=True, required=False) automation = PlatformAutomationSerializer(label=_('Automation'), required=False) diff --git a/apps/common/db/models.py b/apps/common/db/models.py index 4969bd1a7..bac1f1b52 100644 --- a/apps/common/db/models.py +++ b/apps/common/db/models.py @@ -15,36 +15,12 @@ import inspect from django.db import models from django.db.models import F, Value, ExpressionWrapper -from enum import _EnumDict from django.db import transaction from django.db.models import QuerySet from django.db.models.functions import Concat from django.utils.translation import ugettext_lazy as _ -class IncludesTextChoicesMeta(type): - def __new__(metacls, classname, bases, classdict): - includes = classdict.pop('includes', None) - assert includes - - attrs = _EnumDict() - attrs._cls_name = classname - for k, v in classdict.items(): - attrs[k] = v - - for cls in includes: - _member_names_ = cls._member_names_ - _member_map_ = cls._member_map_ - _value2label_map_ = cls._value2label_map_ - - for name in _member_names_: - value = str(_member_map_[name]) - label = _value2label_map_[value] - attrs[name] = value, label - bases = (models.TextChoices,) - return type(classname, bases, attrs) - - class BitOperationChoice: NONE = 0 NAME_MAP: dict diff --git a/apps/ops/ansible/inventory.py b/apps/ops/ansible/inventory.py index c94290592..e024bc45b 100644 --- a/apps/ops/ansible/inventory.py +++ b/apps/ops/ansible/inventory.py @@ -83,7 +83,7 @@ class BaseInventory(InventoryManager): 用于生成动态构建Ansible Inventory. super().__init__ 会自动调用 host_list: [{ "name": "", - "ip": "", + "address": "", "port": "", "username": "", "password": "", @@ -154,3 +154,14 @@ class BaseInventory(InventoryManager): return self.get_hosts(pattern) +class JMSInventory: + def __init__(self, assets, account=None, ansible_connection='ssh', + account_policy='smart', host_var_callback=None): + """ + :param assets: + :param account: account username name if not set use account_policy + :param ansible_connection: ssh, local, + :param account_policy: + :param host_var_callback: + """ + pass diff --git a/apps/ops/ansible/new_runner.py b/apps/ops/ansible/new_runner.py new file mode 100644 index 000000000..7802c1a82 --- /dev/null +++ b/apps/ops/ansible/new_runner.py @@ -0,0 +1,15 @@ +import ansible_runner + + +class AnsibleInventory: + def __init__(self, assets, account=None, ansible_connection='ssh'): + self.assets = assets + self.account = account + + +class AdHocRunner: + pass + + +class PlaybookRunner: + pass