diff --git a/apps/assets/migrations/0092_add_host.py b/apps/assets/migrations/0092_add_host.py index 185842c42..3064d6b98 100644 --- a/apps/assets/migrations/0092_add_host.py +++ b/apps/assets/migrations/0092_add_host.py @@ -12,12 +12,6 @@ class Migration(migrations.Migration): ] operations = [ - 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')), - ], - ), migrations.CreateModel( name='DeviceInfo', fields=[ @@ -39,11 +33,17 @@ class Migration(migrations.Migration): ('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')), - ('host', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.host', verbose_name='Host')), ('number', models.CharField(blank=True, max_length=128, null=True, verbose_name='Asset number')), ], options={ 'verbose_name': 'DeviceInfo', }, ), + 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 64e79ccda..3e2ff9b84 100644 --- a/apps/assets/migrations/0093_auto_20220403_1627.py +++ b/apps/assets/migrations/0093_auto_20220403_1627.py @@ -28,16 +28,19 @@ def migrate_hardware(apps, *args): break hardware_infos = [] + hosts_updated = [] for host in hosts: hardware = hardware_model() asset = asset_mapper[host.asset_ptr_id] - hardware.host = host 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) diff --git a/apps/assets/models/asset/host.py b/apps/assets/models/asset/host.py index 12f35ea5a..79df6eb58 100644 --- a/apps/assets/models/asset/host.py +++ b/apps/assets/models/asset/host.py @@ -7,13 +7,14 @@ from .common import Asset class Host(Asset): + device_info = models.OneToOneField('DeviceInfo', null=True, on_delete=models.SET_NULL, verbose_name=_("Host")) + def save(self, *args, **kwargs): self.category = Category.HOST return super().save(*args, **kwargs) class DeviceInfo(CommonModelMixin): - host = models.ForeignKey(Host, on_delete=models.CASCADE, verbose_name=_("Host")) # Collect vendor = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('Vendor')) model = models.CharField(max_length=54, null=True, blank=True, verbose_name=_('Model')) diff --git a/apps/assets/resources/__init__.py b/apps/assets/resources/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/apps/assets/resources/platform/__init__.py b/apps/assets/resources/platform/__init__.py new file mode 100644 index 000000000..1290c8930 --- /dev/null +++ b/apps/assets/resources/platform/__init__.py @@ -0,0 +1,44 @@ +import os +import yaml + +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) + +platform_ops_methods = [] + + +def get_platform_methods(): + methods = [] + for root, dirs, files in os.walk(BASE_DIR, topdown=False): + for name in dirs: + path = os.path.join(root, name) + rel_path = path.replace(BASE_DIR, '.') + if len(rel_path.split('/')) != 4: + continue + manifest_path = os.path.join(path, 'manifest.yml') + if not os.path.exists(manifest_path): + print("Path not exists: {}".format(manifest_path)) + continue + f = open(manifest_path, 'r') + try: + manifest = yaml.safe_load(f) + except yaml.YAMLError as e: + print(e) + continue + current, category, tp, name = rel_path.split('/') + manifest.update({ + 'id': name, + 'category': category, + 'type': tp, + }) + methods.append(manifest) + return methods + + +def get_platform_method(platform, method): + methods = get_platform_methods() + + def key(m): + return m.get('method') == method \ + and m['category'] == platform.category \ + and m['type'] == platform.type + return list(filter(key, methods)) diff --git a/apps/assets/resources/platform/host/linux/change_password_ansible/main.yml b/apps/assets/resources/platform/host/linux/change_password_ansible/main.yml new file mode 100644 index 000000000..402c7fa8d --- /dev/null +++ b/apps/assets/resources/platform/host/linux/change_password_ansible/main.yml @@ -0,0 +1,10 @@ +{% for account in accounts %} +- hosts: {{ account.asset.name }} + vars: + account: + username: {{ account.username }} + password: {{ account.password }} + public_key: {{ account.public_key }} + roles: + - change_password +{% endfor %} diff --git a/apps/assets/resources/platform/host/linux/change_password_ansible/manifest.yml b/apps/assets/resources/platform/host/linux/change_password_ansible/manifest.yml new file mode 100644 index 000000000..b0c52754d --- /dev/null +++ b/apps/assets/resources/platform/host/linux/change_password_ansible/manifest.yml @@ -0,0 +1,10 @@ +name: Change password using ansible +version: 1 +description: 使用特权账号更改账号的密码 +author: ibuler +method: change_password +vars: + account: + username: test + password: teset123 + public_key: test diff --git a/apps/assets/resources/platform/host/linux/change_password_ansible/roles/change_password/tasks/main.yml b/apps/assets/resources/platform/host/linux/change_password_ansible/roles/change_password/tasks/main.yml new file mode 100644 index 000000000..78cc1776e --- /dev/null +++ b/apps/assets/resources/platform/host/linux/change_password_ansible/roles/change_password/tasks/main.yml @@ -0,0 +1,27 @@ +- name: ping + ping: + +#- name: print variables +# debug: +# msg: "Username: {{ account.username }}, Password: {{ account.password }}" + +- name: Change password + user: + name: "{{ account.username }}" + password: "{{ account.password | password_hash('sha512') }}" + update_password: always + when: account.password + +- name: Change public key + authorized_key: + user: "{{ account.username }}" + key: "{{ account.public_key }}" + state: present + when: account.public_key + +- name: Verify password + ping: + vars: + ansible_user: "{{ account.username }}" + ansible_pass: "{{ account.password }}" + ansible_ssh_connection: paramiko diff --git a/apps/assets/resources/platform/host/linux/create_account_ansible/main.yml b/apps/assets/resources/platform/host/linux/create_account_ansible/main.yml new file mode 100644 index 000000000..c5ec26def --- /dev/null +++ b/apps/assets/resources/platform/host/linux/create_account_ansible/main.yml @@ -0,0 +1,15 @@ +- hosts: centos + gather_facts: no + vars: + account: + username: web + password: test123 + + tasks: + - name: Verify password + ping: + vars: + ansible_ssh_user: "{{ account.username }}" + ansible_ssh_pass: "{{ account.password }}" + + diff --git a/apps/assets/resources/platform/host/linux/create_account_ansible/manifest.yml b/apps/assets/resources/platform/host/linux/create_account_ansible/manifest.yml new file mode 100644 index 000000000..4768a4423 --- /dev/null +++ b/apps/assets/resources/platform/host/linux/create_account_ansible/manifest.yml @@ -0,0 +1,5 @@ +name: Create account by ansible +version: 1 +description: 使用特权账号更改账号的密码 +author: ibuler +method: create_account diff --git a/apps/assets/resources/platform/host/linux/verifiy_account_ansible/main.yml b/apps/assets/resources/platform/host/linux/verifiy_account_ansible/main.yml new file mode 100644 index 000000000..d681b54cc --- /dev/null +++ b/apps/assets/resources/platform/host/linux/verifiy_account_ansible/main.yml @@ -0,0 +1,15 @@ +- hosts: centos + gather_facts: no + vars: + account: + username: web + password: test123 + + tasks: + - name: Verify password + ping: + vars: + ansible_user: "{{ account.username }}" + ansible_pass: "{{ account.password }}" + + diff --git a/apps/assets/resources/platform/host/linux/verifiy_account_ansible/manifest.yml b/apps/assets/resources/platform/host/linux/verifiy_account_ansible/manifest.yml new file mode 100644 index 000000000..b7baca4e7 --- /dev/null +++ b/apps/assets/resources/platform/host/linux/verifiy_account_ansible/manifest.yml @@ -0,0 +1,4 @@ +name: Change password using ansible +version: 1 +description: 使用特权账号更改账号的密码 +author: ibuler diff --git a/apps/assets/serializers/platform.py b/apps/assets/serializers/platform.py index 6cbd5b898..5f44dc520 100644 --- a/apps/assets/serializers/platform.py +++ b/apps/assets/serializers/platform.py @@ -6,6 +6,7 @@ from common.drf.serializers import JMSWritableNestedModelSerializer from ..models import Platform, PlatformProtocol from ..const import Category, AllTypes + __all__ = ['PlatformSerializer'] @@ -20,6 +21,10 @@ class PlatformSerializer(JMSWritableNestedModelSerializer): category = ChoiceDisplayField(choices=Category.choices, label=_("Category")) protocols = PlatformProtocolsSerializer(label=_('Protocols'), many=True, required=False) type_constraints = serializers.ReadOnlyField(required=False, read_only=True) + su_method = ChoiceDisplayField( + choices=[('sudo', 'sudo su -'), ('su', 'su - ')], + label='切换方式', required=False, default='sudo' + ) class Meta: model = Platform @@ -41,5 +46,14 @@ class PlatformSerializer(JMSWritableNestedModelSerializer): read_only_fields = [ 'category_display', 'type_display', ] + extra_kwargs = { + 'su_enabled': {'label': '启用切换账号'}, + 'verify_account_enabled': {'label': '启用校验账号'}, + 'verify_account_method': {'label': '校验账号方式'}, + 'create_account_enabled': {'label': '启用创建账号'}, + 'create_account_method': {'label': '创建账号方式'}, + 'change_password_enabled': {'label': '启用账号改密'}, + 'change_password_method': {'label': '账号改密方式'}, + }