diff --git a/apps/assets/api/__init__.py b/apps/assets/api/__init__.py index 14fd38e9a..5dba09522 100644 --- a/apps/assets/api/__init__.py +++ b/apps/assets/api/__init__.py @@ -2,11 +2,8 @@ from .mixin import * from .platform import * from .asset import * from .label import * -from .accounts import * +from .account import * from .node import * from .domain import * from .gathered_user import * from .favorite_asset import * -from .account_template import * -from .account_backup import * -from .account_history import * diff --git a/apps/assets/api/account/__init__.py b/apps/assets/api/account/__init__.py new file mode 100644 index 000000000..6e402a550 --- /dev/null +++ b/apps/assets/api/account/__init__.py @@ -0,0 +1,4 @@ +from .account import * +from .backup import * +from .history import * +from .template import * diff --git a/apps/assets/api/accounts.py b/apps/assets/api/account/account.py similarity index 96% rename from apps/assets/api/accounts.py rename to apps/assets/api/account/account.py index 00f569357..3e73fd353 100644 --- a/apps/assets/api/accounts.py +++ b/apps/assets/api/account/account.py @@ -9,9 +9,9 @@ from common.drf.filters import BaseFilterSet, UUIDInFilter from common.mixins import RecordViewLogMixin from common.permissions import UserConfirmation from authentication.const import ConfirmType -from ..tasks.account_connectivity import test_accounts_connectivity_manual -from ..models import Account, Node -from .. import serializers +from assets.tasks.account_connectivity import test_accounts_connectivity_manual +from assets.models import Account, Node +from assets import serializers __all__ = ['AccountFilterSet', 'AccountViewSet', 'AccountSecretsViewSet', 'AccountTaskCreateAPI'] diff --git a/apps/assets/api/account_backup.py b/apps/assets/api/account/backup.py similarity index 93% rename from apps/assets/api/account_backup.py rename to apps/assets/api/account/backup.py index bce4ce55b..79ae721f8 100644 --- a/apps/assets/api/account_backup.py +++ b/apps/assets/api/account/backup.py @@ -4,9 +4,9 @@ from rest_framework import status, viewsets from rest_framework.response import Response from orgs.mixins.api import OrgBulkModelViewSet -from .. import serializers -from ..tasks import execute_account_backup_plan -from ..models import ( +from assets import serializers +from assets.tasks import execute_account_backup_plan +from assets.models import ( AccountBackupPlan, AccountBackupPlanExecution ) diff --git a/apps/assets/api/account_history.py b/apps/assets/api/account/history.py similarity index 92% rename from apps/assets/api/account_history.py rename to apps/assets/api/account/history.py index 6ca4fd349..0db682177 100644 --- a/apps/assets/api/account_history.py +++ b/apps/assets/api/account/history.py @@ -1,9 +1,9 @@ -from assets.api.accounts import ( +from .account import ( AccountFilterSet, AccountViewSet, AccountSecretsViewSet ) from common.mixins import RecordViewLogMixin -from .. import serializers -from ..models import Account +from assets import serializers +from assets.models import Account __all__ = ['AccountHistoryViewSet', 'AccountHistorySecretsViewSet'] diff --git a/apps/assets/api/account_template.py b/apps/assets/api/account/template.py similarity index 80% rename from apps/assets/api/account_template.py rename to apps/assets/api/account/template.py index b88fcd0f6..c04fd8ab6 100644 --- a/apps/assets/api/account_template.py +++ b/apps/assets/api/account/template.py @@ -1,6 +1,6 @@ from orgs.mixins.api import OrgBulkModelViewSet -from ..models import AccountTemplate -from .. import serializers +from assets.models import AccountTemplate +from assets import serializers class AccountTemplateViewSet(OrgBulkModelViewSet): diff --git a/apps/assets/const.py b/apps/assets/const.py index 1fafcda80..ca33a93bb 100644 --- a/apps/assets/const.py +++ b/apps/assets/const.py @@ -38,18 +38,19 @@ class Category(PlatformMixin, ChoicesMixin, models.TextChoices): return { cls.HOST: { 'domain_enabled': True, - 'su_enabled': True, - 'ping_enabled': True, - 'gather_facts_enabled': True, - 'verify_account_enabled': True, - 'change_password_enabled': True, - 'create_account_enabled': True, - 'gather_accounts_enabled': True, - '_protocols': ['ssh', 'sftp'] + 'su_enabled': True, 'su_method': 'sudo', + 'ping_enabled': True, 'ping_method': 'ping', + 'gather_facts_enabled': True, 'gather_facts_method': 'gather_facts_posix', + 'verify_account_enabled': True, 'verify_account_method': 'verify_account_posix', + 'change_password_enabled': True, 'change_password_method': 'change_password_posix', + 'create_account_enabled': True, 'create_account_method': 'create_account_posix', + 'gather_accounts_enabled': True, 'gather_accounts_method': 'gather_accounts_posix', + '_protocols': ['ssh', 'telnet'], }, cls.NETWORKING: { 'domain_enabled': True, 'su_enabled': False, + 'ping_enabled': True, 'ping_method': 'ping', 'gather_facts_enabled': False, 'verify_account_enabled': False, 'change_password_enabled': False, @@ -65,15 +66,28 @@ class Category(PlatformMixin, ChoicesMixin, models.TextChoices): 'change_password_enabled': True, 'create_account_enabled': True, 'gather_accounts_enabled': True, + '_protocols': [] }, cls.WEB: { 'domain_enabled': False, 'su_enabled': False, + 'ping_enabled': False, + 'gather_facts_enabled': False, + 'verify_account_enabled': False, + 'change_password_enabled': False, + 'create_account_enabled': False, + 'gather_accounts_enabled': False, '_protocols': ['http', 'https'] }, cls.CLOUD: { 'domain_enabled': False, 'su_enabled': False, + 'ping_enabled': False, + 'gather_facts_enabled': False, + 'verify_account_enabled': False, + 'change_password_enabled': False, + 'create_account_enabled': False, + 'gather_accounts_enabled': False, '_protocols': [] } } @@ -83,9 +97,6 @@ class HostTypes(PlatformMixin, ChoicesMixin, models.TextChoices): LINUX = 'linux', 'Linux' WINDOWS = 'windows', 'Windows' UNIX = 'unix', 'Unix' - BSD = 'bsd', 'BSD' - MACOS = 'macos', 'MacOS' - MAINFRAME = 'mainframe', _("Mainframe") OTHER_HOST = 'other_host', _("Other host") @classmethod @@ -95,20 +106,26 @@ class HostTypes(PlatformMixin, ChoicesMixin, models.TextChoices): '_protocols': ['ssh', 'rdp', 'vnc', 'telnet'] }, cls.WINDOWS: { - '_protocols': ['ssh', 'rdp', 'vnc'], + 'gather_facts_method': 'gather_facts_windows', + 'verify_account_method': 'verify_account_windows', + 'change_password_method': 'change_password_windows', + 'create_account_method': 'create_account_windows', + 'gather_accounts_method': 'gather_accounts_windows', + '_protocols': ['rdp', 'ssh', 'vnc'], 'su_enabled': False }, - cls.MACOS: { + cls.UNIX: { '_protocols': ['ssh', 'vnc'] } } class NetworkingTypes(PlatformMixin, ChoicesMixin, models.TextChoices): + GENERAL = 'general', _("General device") SWITCH = 'switch', _("Switch") ROUTER = 'router', _("Router") FIREWALL = 'firewall', _("Firewall") - OTHER_NETWORK = 'other_network', _("Other device") + OTHER_NETWORK = 'other', _("Other device") class DatabaseTypes(PlatformMixin, ChoicesMixin, models.TextChoices): @@ -125,7 +142,12 @@ class DatabaseTypes(PlatformMixin, ChoicesMixin, models.TextChoices): meta = {} for name, label in cls.choices: meta[name] = { - '_protocols': [name] + '_protocols': [name], + 'gather_facts_method': f'gather_facts_{name}', + 'verify_account_method': f'verify_account_{name}', + 'change_password_method': f'change_password_{name}', + 'create_account_method': f'create_account_{name}', + 'gather_accounts_method': f'gather_accounts_{name}', } return meta diff --git a/apps/assets/migrations/0092_add_host.py b/apps/assets/migrations/0092_add_host.py index 3064d6b98..92e6aad69 100644 --- a/apps/assets/migrations/0092_add_host.py +++ b/apps/assets/migrations/0092_add_host.py @@ -12,38 +12,15 @@ class Migration(migrations.Migration): ] operations = [ - migrations.CreateModel( - name='DeviceInfo', - fields=[ - ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), - ('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')), - ('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')), - ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')), - ('vendor', models.CharField(blank=True, max_length=64, null=True, verbose_name='Vendor')), - ('model', models.CharField(blank=True, max_length=54, null=True, verbose_name='Model')), - ('sn', models.CharField(blank=True, max_length=128, null=True, verbose_name='Serial number')), - ('cpu_model', models.CharField(blank=True, max_length=64, null=True, verbose_name='CPU model')), - ('cpu_count', models.IntegerField(null=True, verbose_name='CPU count')), - ('cpu_cores', models.IntegerField(null=True, verbose_name='CPU cores')), - ('cpu_vcpus', models.IntegerField(null=True, verbose_name='CPU vcpus')), - ('memory', models.CharField(blank=True, max_length=64, null=True, verbose_name='Memory')), - ('disk_total', models.CharField(blank=True, max_length=1024, null=True, verbose_name='Disk total')), - ('disk_info', models.CharField(blank=True, max_length=1024, null=True, verbose_name='Disk info')), - ('os', models.CharField(blank=True, max_length=128, null=True, verbose_name='OS')), - ('os_version', models.CharField(blank=True, max_length=16, null=True, verbose_name='OS version')), - ('os_arch', models.CharField(blank=True, max_length=16, null=True, verbose_name='OS arch')), - ('hostname_raw', models.CharField(blank=True, max_length=128, null=True, verbose_name='Hostname raw')), - ('number', models.CharField(blank=True, max_length=128, null=True, verbose_name='Asset number')), - ], - options={ - 'verbose_name': 'DeviceInfo', - }, + migrations.AddField( + model_name='asset', + name='info', + field=models.JSONField(blank=True, default=dict, verbose_name='Info'), ), migrations.CreateModel( name='Host', fields=[ ('asset_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='assets.asset')), - ('device_info', models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.deviceinfo', verbose_name='Host')), ], ), ] diff --git a/apps/assets/migrations/0093_auto_20220403_1627.py b/apps/assets/migrations/0093_auto_20220403_1627.py index 3e2ff9b84..b013efdfe 100644 --- a/apps/assets/migrations/0093_auto_20220403_1627.py +++ b/apps/assets/migrations/0093_auto_20220403_1627.py @@ -4,46 +4,6 @@ from django.utils import timezone from django.db import migrations, models -def migrate_hardware(apps, *args): - host_model = apps.get_model('assets', 'Host') - asset_model = apps.get_model('assets', 'Asset') - hardware_model = apps.get_model('assets', 'DeviceInfo') - - created = 0 - batch_size = 1000 - - excludes = ['id', 'host', 'date_updated'] - fields = [f.name for f in hardware_model._meta.fields] - fields = [name for name in fields if name not in excludes] - - while True: - start = created - end = created + batch_size - hosts = host_model.objects.all()[start:end] - asset_ids = [h.asset_ptr_id for h in hosts] - assets = asset_model.objects.filter(id__in=asset_ids) - asset_mapper = {a.id: a for a in assets} - - if not hosts: - break - - hardware_infos = [] - hosts_updated = [] - for host in hosts: - hardware = hardware_model() - asset = asset_mapper[host.asset_ptr_id] - hardware.date_updated = timezone.now() - for name in fields: - setattr(hardware, name, getattr(asset, name)) - hardware_infos.append(hardware) - host.device_info_id = hardware.id - hosts_updated.append(host) - - hardware_model.objects.bulk_create(hardware_infos, ignore_conflicts=True) - host_model.objects.bulk_update(hosts_updated, ['device_info_id']) - created += len(hardware_infos) - - def migrate_to_host(apps, schema_editor): asset_model = apps.get_model("assets", "Asset") host_model = apps.get_model("assets", 'Host') @@ -64,6 +24,34 @@ def migrate_to_host(apps, schema_editor): created += len(hosts) +def migrate_hardware_info(apps, *args): + asset_model = apps.get_model("assets", "Asset") + + count = 0 + batch_size = 1000 + hardware_fields = [ + 'vendor', 'model', 'sn', 'cpu_model', 'cpu_count', 'cpu_cores', + 'cpu_vcpus', 'memory', 'disk_total', 'disk_info', 'os', 'os_arch', + 'os_version', 'hostname_raw', 'number' + ] + + while True: + start = count + end = count + batch_size + assets = asset_model.objects.all()[start:end] + if not assets: + break + + updated = [] + for asset in assets: + info = {getattr(asset, field) for field in hardware_fields if getattr(asset, field)} + if not info: + continue + asset.info = info + updated.append(asset) + asset_model.objects.bulk_update(updated, ['info']) + + class Migration(migrations.Migration): dependencies = [ @@ -71,6 +59,6 @@ class Migration(migrations.Migration): ] operations = [ + migrations.RunPython(migrate_hardware_info), migrations.RunPython(migrate_to_host), - migrations.RunPython(migrate_hardware), ] diff --git a/apps/assets/migrations/0096_auto_20220426_1550.py b/apps/assets/migrations/0096_auto_20220426_1550.py index 83b2aa141..bf7a93ac3 100644 --- a/apps/assets/migrations/0096_auto_20220426_1550.py +++ b/apps/assets/migrations/0096_auto_20220426_1550.py @@ -48,6 +48,10 @@ class Migration(migrations.Migration): fields=[ ('asset_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='assets.asset')), ('url', models.CharField(max_length=1024, verbose_name='url')), + ('autofill', models.CharField(default='basic', max_length=16)), + ('password_selector', models.CharField(blank=True, default='', max_length=128)), + ('submit_selector', models.CharField(blank=True, default='', max_length=128)), + ('username_selector', models.CharField(blank=True, default='', max_length=128)) ], options={ 'abstract': False, diff --git a/apps/assets/migrations/0098_auto_20220430_2126.py b/apps/assets/migrations/0098_auto_20220430_2126.py index fd3e27ba0..b52e5026f 100644 --- a/apps/assets/migrations/0098_auto_20220430_2126.py +++ b/apps/assets/migrations/0098_auto_20220430_2126.py @@ -1,6 +1,5 @@ # Generated by Django 3.1.14 on 2022-04-30 14:41 from django.db import migrations, models -import django.db.models.deletion class Migration(migrations.Migration): @@ -19,11 +18,6 @@ class Migration(migrations.Migration): ('setting', models.JSONField(default=dict, verbose_name='Setting')), ], ), - migrations.AddField( - model_name='platform', - name='domain_default', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.domain', verbose_name='Domain default'), - ), migrations.AddField( model_name='platform', name='domain_enabled', @@ -62,12 +56,12 @@ class Migration(migrations.Migration): migrations.AddField( model_name='platform', name='ping_enabled', - field=models.BooleanField(default=False), + field=models.BooleanField(default=False, verbose_name='Ping enabled'), ), migrations.AddField( model_name='platform', name='ping_method', - field=models.TextField(blank=True, max_length=32, null=True, verbose_name='Ping method'), + field=models.CharField(blank=True, max_length=32, null=True, verbose_name='Ping method'), ), migrations.AddField( model_name='platform', @@ -89,4 +83,24 @@ class Migration(migrations.Migration): name='verify_account_method', field=models.TextField(blank=True, max_length=32, null=True, verbose_name='Verify account method'), ), + migrations.AddField( + model_name='platform', + name='gather_accounts_enabled', + field=models.BooleanField(default=False, verbose_name='Gather facts enabled'), + ), + migrations.AddField( + model_name='platform', + name='gather_accounts_method', + field=models.TextField(blank=True, max_length=32, null=True, verbose_name='Gather facts method'), + ), + migrations.AddField( + model_name='platform', + name='gather_facts_enabled', + field=models.BooleanField(default=False, verbose_name='Gather facts enabled'), + ), + migrations.AddField( + model_name='platform', + name='gather_facts_method', + field=models.TextField(blank=True, max_length=32, null=True, verbose_name='Gather facts method'), + ), ] diff --git a/apps/assets/migrations/0099_auto_20220711_1409.py b/apps/assets/migrations/0099_auto_20220711_1409.py index 52c8750dc..af767c864 100644 --- a/apps/assets/migrations/0099_auto_20220711_1409.py +++ b/apps/assets/migrations/0099_auto_20220711_1409.py @@ -21,6 +21,7 @@ class Migration(migrations.Migration): fields=[ ('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')), ('id', models.UUIDField(db_index=True, default=uuid.uuid4)), + ('name', models.CharField(max_length=128, verbose_name='Name')), ('username', models.CharField(blank=True, db_index=True, max_length=128, verbose_name='Username')), ('password', common.db.fields.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password')), ('private_key', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH private key')), @@ -38,6 +39,7 @@ class Migration(migrations.Migration): ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), ('asset', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='assets.asset', verbose_name='Asset')), ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ('su_from', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='assets.account', verbose_name='Su from')), ], options={ 'verbose_name': 'historical Account', @@ -52,6 +54,7 @@ class Migration(migrations.Migration): fields=[ ('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')), ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=128, verbose_name='Name')), ('username', models.CharField(blank=True, db_index=True, max_length=128, verbose_name='Username')), ('password', common.db.fields.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password')), ('private_key', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH private key')), @@ -63,12 +66,13 @@ class Migration(migrations.Migration): ('created_by', models.CharField(max_length=128, null=True, verbose_name='Created by')), ('privileged', models.BooleanField(default=False, verbose_name='Privileged account')), ('version', models.IntegerField(default=0, verbose_name='Version')), - ('asset', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.asset', verbose_name='Asset')), + ('asset', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='accounts', to='assets.asset', verbose_name='Asset')), + ('su_from', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='su_to', to='assets.account', verbose_name='Su from')), ], options={ 'verbose_name': 'Account', 'permissions': [('view_accountsecret', 'Can view asset account secret'), ('change_accountsecret', 'Can change asset account secret'), ('view_historyaccount', 'Can view asset history account'), ('view_historyaccountsecret', 'Can view asset history account secret')], - 'unique_together': {('username', 'asset')}, + 'unique_together': {('username', 'asset'), ('name', 'asset')}, }, ), ] diff --git a/apps/assets/migrations/0100_auto_20220711_1413.py b/apps/assets/migrations/0100_auto_20220711_1413.py index 7aad60e2c..7f2e7eca7 100644 --- a/apps/assets/migrations/0100_auto_20220711_1413.py +++ b/apps/assets/migrations/0100_auto_20220711_1413.py @@ -40,7 +40,7 @@ def migrate_accounts(apps, schema_editor): values['version'] = 1 system_user = auth_book.systemuser - if auth_book.systemuser: + if system_user: values.update({attr: getattr(system_user, attr) for attr in auth_attrs}) values['created_by'] = str(system_user.id) values['privileged'] = system_user.type == 'admin' @@ -48,6 +48,7 @@ def migrate_accounts(apps, schema_editor): auth_book_auth = {attr: getattr(auth_book, attr) for attr in auth_attrs} auth_book_auth = {attr: value for attr, value in auth_book_auth.items() if value} values.update(auth_book_auth) + values['name'] = values['username'] account = account_model(**values) accounts.append(account) diff --git a/apps/assets/migrations/0103_auto_20220811_1511.py b/apps/assets/migrations/0103_auto_20220811_1511.py index 3a4504e46..f9dda2b56 100644 --- a/apps/assets/migrations/0103_auto_20220811_1511.py +++ b/apps/assets/migrations/0103_auto_20220811_1511.py @@ -41,6 +41,11 @@ class Migration(migrations.Migration): old_name='hostname', new_name='name', ), + migrations.AlterField( + model_name='asset', + name='name', + field=models.CharField(max_length=128, verbose_name='Name'), + ), migrations.AddField( model_name='asset', name='date_updated', diff --git a/apps/assets/migrations/0107_alter_accountbackupplan_types.py b/apps/assets/migrations/0107_alter_accountbackupplan_types.py index 2ce2d9302..d2d0eedb2 100644 --- a/apps/assets/migrations/0107_alter_accountbackupplan_types.py +++ b/apps/assets/migrations/0107_alter_accountbackupplan_types.py @@ -1,44 +1,10 @@ -# Generated by Django 3.2.13 on 2022-08-29 11:46 +# Generated by Django 3.2.14 on 2022-09-14 12:45 + from django.db import migrations, models -from assets.const import Category -from assets.models import Type - - -def update_account_backup_type(apps, schema_editor): - backup_model = apps.get_model('assets', 'AccountBackupPlan') - all_number = 4294967295 - asset_number = Type.choices_to_value([Category.HOST]) - app_number = Type.choices_to_value([ - Category.NETWORKING, Category.DATABASE, Category.CLOUD, Category.WEB] - ) - - backup_model.objects.filter(types=255).update(types=all_number) - backup_model.objects.filter(types=1).update(types=asset_number) - backup_model.objects.filter(types=2).update(types=app_number) - - backup_execution_model = apps.get_model('assets', 'AccountBackupPlanExecution') - choices_dict = { - 'all': Type.get_types(value=all_number), - 'asset': Type.get_types(value=asset_number), - 'app': Type.get_types(value=app_number) - } - qs_dict = { - 'all': backup_execution_model.objects.filter(plan__types=255), - 'asset': backup_execution_model.objects.filter(plan__types=1), - 'app': backup_execution_model.objects.filter(plan__types=2) - } - - backup_executions = [] - for k, qs in qs_dict.items(): - type_choices = choices_dict[k] - for i in qs: - i.plan_snapshot['types'] = type_choices - backup_executions.append(i) - backup_execution_model.objects.bulk_update(backup_executions, fields=['plan_snapshot']) - class Migration(migrations.Migration): + dependencies = [ ('assets', '0106_auto_20220819_1523'), ] @@ -47,13 +13,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='accountbackupplan', name='types', - field=models.BigIntegerField( - choices=[(4294967295, 'All'), (1, 'Linux'), (2, 'Windows'), (4, 'Unix'), (8, 'BSD'), (16, 'MacOS'), - (32, 'Mainframe'), (64, 'Other host'), (127, 'Host'), (128, 'Switch'), (256, 'Router'), - (512, 'Firewall'), (1024, 'Other device'), (1920, 'NetworkDevice'), (2048, 'MySQL'), - (4096, 'MariaDB'), (8192, 'PostgreSQL'), (16384, 'Oracle'), (32768, 'SQLServer'), - (65536, 'MongoDB'), (131072, 'Redis'), (260096, 'Database'), (262144, 'Clouding'), - (524288, 'Web')], default=4294967295, verbose_name='Type'), + field=models.BigIntegerField(), ), - migrations.RunPython(update_account_backup_type), ] diff --git a/apps/assets/migrations/0108_auto_20220901_1034.py b/apps/assets/migrations/0108_auto_20220901_1034.py deleted file mode 100644 index d19749cf7..000000000 --- a/apps/assets/migrations/0108_auto_20220901_1034.py +++ /dev/null @@ -1,37 +0,0 @@ -# Generated by Django 3.2.14 on 2022-09-01 02:34 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('assets', '0107_alter_accountbackupplan_types'), - ] - - operations = [ - migrations.RemoveField( - model_name='platform', - name='create_account_enabled', - ), - migrations.RemoveField( - model_name='platform', - name='create_account_method', - ), - migrations.RemoveField( - model_name='platform', - name='domain_default', - ), - migrations.RemoveField( - model_name='platform', - name='domain_enabled', - ), - migrations.RemoveField( - model_name='platform', - name='ping_enabled', - ), - migrations.RemoveField( - model_name='platform', - name='ping_method', - ), - ] diff --git a/apps/assets/migrations/0109_auto_20220901_1431.py b/apps/assets/migrations/0109_auto_20220901_1431.py deleted file mode 100644 index 07fad818e..000000000 --- a/apps/assets/migrations/0109_auto_20220901_1431.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 3.2.14 on 2022-09-01 06:31 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('assets', '0108_auto_20220901_1034'), - ] - - operations = [ - migrations.RemoveField( - model_name='host', - name='device_info', - ), - migrations.AddField( - model_name='asset', - name='info', - field=models.JSONField(blank=True, default=dict, verbose_name='Info'), - ), - migrations.AddField( - model_name='database', - name='version', - field=models.CharField(blank=True, max_length=16, verbose_name='Version'), - ), - migrations.DeleteModel( - name='DeviceInfo', - ), - ] diff --git a/apps/assets/migrations/0110_auto_20220901_1542.py b/apps/assets/migrations/0110_auto_20220901_1542.py deleted file mode 100644 index 1c37f19dd..000000000 --- a/apps/assets/migrations/0110_auto_20220901_1542.py +++ /dev/null @@ -1,43 +0,0 @@ -# Generated by Django 3.2.14 on 2022-09-01 07:42 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('assets', '0109_auto_20220901_1431'), - ] - - operations = [ - migrations.AddField( - model_name='platform', - name='create_account_enabled', - field=models.BooleanField(default=False, verbose_name='Create account enabled'), - ), - migrations.AddField( - model_name='platform', - name='create_account_method', - field=models.TextField(blank=True, max_length=32, null=True, verbose_name='Create account method'), - ), - migrations.AddField( - model_name='platform', - name='gather_accounts_enabled', - field=models.BooleanField(default=False, verbose_name='Gather facts enabled'), - ), - migrations.AddField( - model_name='platform', - name='gather_accounts_method', - field=models.TextField(blank=True, max_length=32, null=True, verbose_name='Gather facts method'), - ), - migrations.AddField( - model_name='platform', - name='gather_facts_enabled', - field=models.BooleanField(default=False, verbose_name='Gather facts enabled'), - ), - migrations.AddField( - model_name='platform', - name='gather_facts_method', - field=models.TextField(blank=True, max_length=32, null=True, verbose_name='Gather facts method'), - ), - ] diff --git a/apps/assets/migrations/0111_auto_20220908_1958.py b/apps/assets/migrations/0111_auto_20220908_1958.py deleted file mode 100644 index 7fb913fae..000000000 --- a/apps/assets/migrations/0111_auto_20220908_1958.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 3.2.14 on 2022-09-08 11:58 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('assets', '0110_auto_20220901_1542'), - ] - - operations = [ - migrations.AddField( - model_name='platform', - name='domain_enabled', - field=models.BooleanField(default=True, verbose_name='Domain enalbed'), - ), - migrations.AlterField( - model_name='account', - name='asset', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='accounts', to='assets.asset', verbose_name='Asset'), - ), - migrations.AlterField( - model_name='asset', - name='name', - field=models.CharField(max_length=128, verbose_name='Name'), - ), - ] diff --git a/apps/assets/migrations/0112_auto_20220909_1907.py b/apps/assets/migrations/0112_auto_20220909_1907.py deleted file mode 100644 index 6aaba7513..000000000 --- a/apps/assets/migrations/0112_auto_20220909_1907.py +++ /dev/null @@ -1,38 +0,0 @@ -# Generated by Django 3.2.14 on 2022-09-09 11:07 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('assets', '0111_auto_20220908_1958'), - ] - - operations = [ - migrations.AddField( - model_name='web', - name='autofill', - field=models.CharField(default='basic', max_length=16), - ), - migrations.AddField( - model_name='web', - name='password_selector', - field=models.CharField(blank=True, default='', max_length=128), - ), - migrations.AddField( - model_name='web', - name='submit_selector', - field=models.CharField(blank=True, default='', max_length=128), - ), - migrations.AddField( - model_name='web', - name='username_selector', - field=models.CharField(blank=True, default='', max_length=128), - ), - migrations.AlterField( - model_name='platform', - name='domain_enabled', - field=models.BooleanField(default=True, verbose_name='Domain enabled'), - ), - ] diff --git a/apps/assets/models/account.py b/apps/assets/models/account.py index 7b9e543a2..5e141fd28 100644 --- a/apps/assets/models/account.py +++ b/apps/assets/models/account.py @@ -3,20 +3,29 @@ from django.utils.translation import gettext_lazy as _ from simple_history.models import HistoricalRecords from common.utils import lazyproperty - from .base import BaseAccount __all__ = ['Account', 'AccountTemplate'] class Account(BaseAccount): - asset = models.ForeignKey('assets.Asset', related_name='accounts', on_delete=models.CASCADE, verbose_name=_('Asset')) + asset = models.ForeignKey( + 'assets.Asset', related_name='accounts', + on_delete=models.CASCADE, verbose_name=_('Asset') + ) + su_from = models.ForeignKey( + 'assets.Account', related_name='su_to', null=True, + on_delete=models.SET_NULL, verbose_name=_("Su from") + ) version = models.IntegerField(default=0, verbose_name=_('Version')) history = HistoricalRecords() class Meta: verbose_name = _('Account') - unique_together = [('username', 'asset')] + unique_together = [ + ('username', 'asset'), + ('name', 'asset'), + ] permissions = [ ('view_accountsecret', _('Can view asset account secret')), ('change_accountsecret', _('Can change asset account secret')), @@ -24,10 +33,6 @@ class Account(BaseAccount): ('view_historyaccountsecret', _('Can view asset history account secret')), ] - @property - def name(self): - return "{}({})_{}".format(self.asset_name, self.ip, self.username) - @lazyproperty def ip(self): return self.asset.ip diff --git a/apps/assets/models/asset/database.py b/apps/assets/models/asset/database.py index 9d5ee1325..cb97c95cd 100644 --- a/apps/assets/models/asset/database.py +++ b/apps/assets/models/asset/database.py @@ -6,7 +6,6 @@ from .common import Asset class Database(Asset): db_name = models.CharField(max_length=1024, verbose_name=_("Database"), blank=True) - version = models.CharField(max_length=16, verbose_name=_("Version"), blank=True) def __str__(self): return '{}({}://{}/{})'.format(self.name, self.type, self.ip, self.db_name) diff --git a/apps/assets/models/asset/networking.py b/apps/assets/models/asset/networking.py index d58995b42..48d73a4d9 100644 --- a/apps/assets/models/asset/networking.py +++ b/apps/assets/models/asset/networking.py @@ -3,4 +3,5 @@ from .common import Asset class Networking(Asset): + pass diff --git a/apps/assets/models/backup.py b/apps/assets/models/backup.py index debec255c..d7788a350 100644 --- a/apps/assets/models/backup.py +++ b/apps/assets/models/backup.py @@ -71,7 +71,7 @@ class Type(BitOperationChoice): class AccountBackupPlan(CommonModelMixin, PeriodTaskModelMixin, OrgModelMixin): id = models.UUIDField(default=uuid.uuid4, primary_key=True) - types = models.BigIntegerField(choices=Type.DB_CHOICES, default=Type.ALL, verbose_name=_('Type')) + types = models.BigIntegerField() recipients = models.ManyToManyField( 'users.User', related_name='recipient_escape_route_plans', blank=True, verbose_name=_("Recipient") diff --git a/apps/assets/models/platform.py b/apps/assets/models/platform.py index 38e6c6c90..8ca79254a 100644 --- a/apps/assets/models/platform.py +++ b/apps/assets/models/platform.py @@ -9,6 +9,12 @@ __all__ = ['Platform', 'PlatformProtocol'] class PlatformProtocol(models.Model): + SETTING_ATTRS = { + 'console': True, + 'security': 'any,tls,rdp', + 'sftp_enabled': True, + 'sftp_home': '/tmp' + } name = models.CharField(max_length=32, verbose_name=_('Name')) port = models.IntegerField(verbose_name=_('Port')) setting = models.JSONField(verbose_name=_('Setting'), default=dict) @@ -34,6 +40,8 @@ class Platform(models.Model): domain_enabled = models.BooleanField(default=True, verbose_name=_("Domain enabled")) protocols_enabled = models.BooleanField(default=True, verbose_name=_("Protocols enabled")) protocols = models.ManyToManyField(PlatformProtocol, blank=True, verbose_name=_("Protocols")) + 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")) gather_facts_method = models.TextField(max_length=32, blank=True, null=True, verbose_name=_("Gather facts method")) # 账号有关的 @@ -61,71 +69,7 @@ class Platform(models.Model): @staticmethod def set_default_platforms_ops(platform_model): - default_ok = { - 'su_enabled': True, - 'su_method': 'sudo', - 'domain_enabled': True, - 'change_password_enabled': True, - 'change_password_method': 'change_password_linux', - 'verify_account_enabled': True, - 'verify_account_method': 'ansible_posix_ping', - } - db_default = { - 'su_enabled': False, - 'domain_enabled': True, - 'change_password_enabled': True, - 'verify_account_enabled': True, - } - - platform_ops_map = { - ('host', 'linux'): { - **default_ok, - 'change_password_method': 'change_password_linux', - 'verify_account_method': 'ansible_posix_ping' - }, - ('host', 'windows'): { - **default_ok, - 'su_enabled': False, - 'change_password_method': 'change_password_windows', - 'verify_account_method': 'ansible_win_ping' - }, - ('host', 'unix'): { - **default_ok, - 'verify_account_method': 'ansible_posix_ping', - 'change_password_method': 'change_password_aix' - }, - ('database', 'mysql'): { - **db_default, - 'verify_account_method': 'mysql_ping', - 'change_password_method': 'change_password_mysql' - }, - ('database', 'postgresql'): { - **db_default, - 'verify_account_method': 'postgresql_ping', - 'change_password_method': 'change_password_postgresql' - }, - ('database', 'oracle'): { - **db_default, - 'verify_account_method': 'oracle_ping', - 'change_password_method': 'change_password_oracle' - }, - ('database', 'sqlserver'): { - **db_default, - 'verify_account_method': 'mysql_ping', - 'change_password_method': 'change_password_sqlserver' - }, - } - platforms = platform_model.objects.all() - - updated = [] - for p in platforms: - attrs = platform_ops_map.get((p.category, p.type), {}) - if not attrs: - continue - for k, v in attrs.items(): - setattr(p, k, v) - updated.append(p) - platform_model.objects.bulk_update(updated, list(default_ok.keys())) + pass def __str__(self): return self.name diff --git a/apps/assets/models/utils.py b/apps/assets/models/utils.py index bc6bdb21f..e2c89cd63 100644 --- a/apps/assets/models/utils.py +++ b/apps/assets/models/utils.py @@ -2,8 +2,6 @@ # -*- coding: utf-8 -*- # -from django.utils import timezone -from django.core.cache import cache from django.core.exceptions import ValidationError from django.utils.translation import ugettext_lazy as _ @@ -21,3 +19,53 @@ def private_key_validator(value): _('%(value)s is not an even number'), params={'value': value}, ) + + +def update_internal_platforms(platform_model): + from assets.const import AllTypes + + platforms = [ + {'name': 'Linux', 'category': 'host', 'type': 'linux'}, + {'name': 'BSD', 'category': 'host', 'type': 'unix'}, + {'name': 'Unix', 'category': 'host', 'type': 'unix'}, + {'name': 'MacOS', 'category': 'host', 'type': 'unix'}, + {'name': 'Windows', 'category': 'host', 'type': 'unix'}, + { + 'name': 'AIX', 'category': 'host', 'type': 'unix', + 'create_account_method': 'create_account_aix', + 'change_password_method': 'change_password_aix', + }, + {'name': 'Windows', 'category': 'host', 'type': 'windows'}, + {'name': 'Windows-TLS', 'category': 'host', 'type': 'windows'}, + {'name': 'Windows-RDP', 'category': 'host', 'type': 'windows'}, + + # 数据库 + {'name': 'MySQL', 'category': 'database', 'type': 'mysql'}, + {'name': 'PostgreSQL', 'category': 'database', 'type': 'postgresql'}, + {'name': 'Oracle', 'category': 'database', 'type': 'oracle'}, + {'name': 'SQLServer', 'category': 'database', 'type': 'sqlserver'}, + {'name': 'MongoDB', 'category': 'database', 'type': 'mongodb'}, + {'name': 'Redis', 'category': 'database', 'type': 'redis'}, + + # 网络设备 + {'name': 'Generic', 'category': 'networking', 'type': 'general'}, + {'name': 'Huawei', 'category': 'networking', 'type': 'general'}, + {'name': 'Cisco', 'category': 'networking', 'type': 'general'}, + {'name': 'H3C', 'category': 'networking', 'type': 'general'}, + + # Web + + # Cloud + ] + + platforms = platform_model.objects.all() + + updated = [] + for p in platforms: + attrs = platform_ops_map.get((p.category, p.type), {}) + if not attrs: + continue + for k, v in attrs.items(): + setattr(p, k, v) + updated.append(p) + platform_model.objects.bulk_update(updated, list(default_ok.keys())) diff --git a/apps/assets/playbooks/change_password/host/change_password_linux/main.yml b/apps/assets/playbooks/change_password/host/change_password_linux/main.yml index 6b5f0df66..a7d0f9417 100644 --- a/apps/assets/playbooks/change_password/host/change_password_linux/main.yml +++ b/apps/assets/playbooks/change_password/host/change_password_linux/main.yml @@ -1,4 +1,4 @@ -- hosts: {{ account.asset.name }} +- hosts: all vars: account: username: {{ account.username }} diff --git a/apps/assets/serializers/account/account.py b/apps/assets/serializers/account/account.py index 1c60e5d98..4ddcc1d49 100644 --- a/apps/assets/serializers/account/account.py +++ b/apps/assets/serializers/account/account.py @@ -4,7 +4,8 @@ from rest_framework import serializers from orgs.mixins.serializers import BulkOrgResourceModelSerializer from common.drf.serializers import SecretReadableMixin -from assets.models import Account, AccountTemplate +from common.drf.fields import ObjectRelatedField +from assets.models import Account, AccountTemplate, Asset from assets.serializers.base import AuthValidateMixin from .common import AccountFieldsSerializerMixin @@ -14,7 +15,9 @@ class AccountSerializerCreateMixin(serializers.ModelSerializer): required=False, allow_null=True, write_only=True, label=_('Account template') ) - push_to_asset = serializers.BooleanField(default=False, label=_("Push to asset"), write_only=True) + push_now = serializers.BooleanField( + default=False, label=_("Push now"), write_only=True + ) @staticmethod def validate_template(value): @@ -39,30 +42,34 @@ class AccountSerializerCreateMixin(serializers.ModelSerializer): account_template = attrs.pop('template', None) if account_template: self.replace_attrs(account_template, attrs) - push_to_asset = attrs.pop('push_to_asset', False) + self.push_now = attrs.pop('push_now', False) return super().validate(attrs) + def create(self, validated_data): + instance = super().create(validated_data) + if self.push_now: + print("Start push account to asset") + # Todo: push it + pass + return instance + class AccountSerializer(AuthValidateMixin, AccountSerializerCreateMixin, AccountFieldsSerializerMixin, BulkOrgResourceModelSerializer): - name = serializers.CharField(max_length=128, read_only=True, label=_("Name")) - ip = serializers.ReadOnlyField(label=_("IP")) - asset_name = serializers.ReadOnlyField(label=_("Asset")) + asset = ObjectRelatedField(required=False, queryset=Asset.objects, label=_('Asset'), attrs=('id', 'name', 'ip')) platform = serializers.ReadOnlyField(label=_("Platform")) class Meta(AccountFieldsSerializerMixin.Meta): model = Account fields = AccountFieldsSerializerMixin.Meta.fields \ - + ['template', 'push_to_asset'] + + ['template', 'push_now'] @classmethod def setup_eager_loading(cls, queryset): """ Perform necessary eager loading of data. """ - queryset = queryset.prefetch_related('asset') \ - .annotate(ip=F('asset__ip')) \ - .annotate(asset_name=F('asset__name')) + queryset = queryset.prefetch_related('asset') return queryset diff --git a/apps/assets/serializers/account/common.py b/apps/assets/serializers/account/common.py index 43c5c193b..6e8022aa7 100644 --- a/apps/assets/serializers/account/common.py +++ b/apps/assets/serializers/account/common.py @@ -8,8 +8,8 @@ __all__ = ['AccountFieldsSerializerMixin'] class AccountFieldsSerializerMixin(serializers.ModelSerializer): class Meta: fields_mini = [ - 'id', 'name', 'username', 'privileged', 'ip', - 'asset_name', 'platform', 'version' + 'id', 'name', 'username', 'privileged', + 'platform', 'version' ] fields_write_only = ['password', 'private_key', 'public_key', 'passphrase'] fields_other = ['date_created', 'date_updated', 'comment'] diff --git a/apps/assets/serializers/asset/common.py b/apps/assets/serializers/asset/common.py index b9f57725c..2717547cd 100644 --- a/apps/assets/serializers/asset/common.py +++ b/apps/assets/serializers/asset/common.py @@ -54,9 +54,12 @@ class AssetAccountSerializer(AccountSerializer): class Meta(AccountSerializer.Meta): fields_mini = [ - 'id', 'name', 'username', 'privileged', 'version' + 'id', 'name', 'username', 'privileged', 'version', + ] + fields_write_only = [ + 'password', 'private_key', 'public_key', + 'passphrase', 'token', 'push_now' ] - fields_write_only = ['password', 'private_key', 'public_key', 'passphrase', 'token'] fields = fields_mini + fields_write_only diff --git a/apps/ops/migrations/0023_automationstrategy_automationstrategyexecution_automationstrategytask_changeauthstrategy_collectstra.py b/apps/ops/migrations/0023_automation_strategy.py similarity index 99% rename from apps/ops/migrations/0023_automationstrategy_automationstrategyexecution_automationstrategytask_changeauthstrategy_collectstra.py rename to apps/ops/migrations/0023_automation_strategy.py index 361869794..5389c86e9 100644 --- a/apps/ops/migrations/0023_automationstrategy_automationstrategyexecution_automationstrategytask_changeauthstrategy_collectstra.py +++ b/apps/ops/migrations/0023_automation_strategy.py @@ -11,7 +11,7 @@ class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('assets', '0111_auto_20220908_1958'), + ('assets', '0106_auto_20220819_1523'), ('ops', '0022_auto_20220817_1346'), ]