diff --git a/apps/assets/forms.py b/apps/assets/forms.py index 0001eeea0..8fc51bc87 100644 --- a/apps/assets/forms.py +++ b/apps/assets/forms.py @@ -32,9 +32,33 @@ class AssetCreateForm(forms.ModelForm): model = Asset tags = forms.ModelMultipleChoiceField(queryset=Tag.objects.all()) fields = [ - 'hostname', 'ip', 'port', 'type', 'comment', 'admin_user', 'idc', 'groups', - 'other_ip', 'remote_card_ip', 'mac_address', 'brand', 'cpu', 'memory', 'disk', 'os', 'cabinet_no', - 'cabinet_pos', 'number', 'status', 'env', 'sn', 'tags', + 'hostname', 'ip', 'public_ip', 'port', 'type', 'comment', 'admin_user', + 'idc', 'groups', 'status', 'env', 'tags', 'is_active' + ] + widgets = { + 'groups': forms.SelectMultiple(attrs={'class': 'select2', + 'data-placeholder': _('Select asset groups')}), + 'tags': forms.SelectMultiple(attrs={'class': 'select2', + 'data-placeholder': _('Select asset tags')}), + 'admin_user': forms.Select(attrs={'class': 'select2', 'data-placeholder': _('Select asset admin user')}), + } + help_texts = { + 'hostname': '* required', + 'ip': '* required', + 'system_users': _('System user will be granted for user to login assets (using ansible create automatic)'), + 'admin_user': _('Admin user should be exist on asset already, And have sudo ALL permission'), + 'tags': '最多5个标签,单个标签最长8个汉字,按回车确认' + } + + +class AssetUpdateForm(AssetCreateForm): + class Meta: + model = Asset + tags = forms.ModelMultipleChoiceField(queryset=Tag.objects.all()) + fields = [ + 'hostname', 'ip', 'port', 'groups', 'admin_user', 'idc', 'is_active', + 'type', 'env', 'status', 'public_ip', 'remote_card_ip', 'cabinet_no', + 'cabinet_pos', 'number', 'comment', 'tags' ] widgets = { 'groups': forms.SelectMultiple(attrs={'class': 'select2', diff --git a/apps/assets/models/asset.py b/apps/assets/models/asset.py index 7386b23bc..068f41aa2 100644 --- a/apps/assets/models/asset.py +++ b/apps/assets/models/asset.py @@ -37,9 +37,8 @@ class Asset(models.Model): ('Test', 'Testing'), ) + # Important ip = models.GenericIPAddressField(max_length=32, verbose_name=_('IP'), db_index=True) - other_ip = models.CharField(max_length=255, null=True, blank=True, verbose_name=_('Other IP')) - remote_card_ip = models.CharField(max_length=16, null=True, blank=True, verbose_name=_('Remote card IP')) hostname = models.CharField(max_length=128, unique=True, verbose_name=_('Hostname')) port = models.IntegerField(default=22, verbose_name=_('Port')) groups = models.ManyToManyField(AssetGroup, blank=True, related_name='assets', verbose_name=_('Asset groups')) @@ -48,24 +47,40 @@ class Asset(models.Model): system_users = models.ManyToManyField(SystemUser, blank=True, related_name='assets', verbose_name=_("System User")) idc = models.ForeignKey(IDC, blank=True, null=True, related_name='assets', on_delete=models.SET_NULL, verbose_name=_('IDC'),) - mac_address = models.CharField(max_length=20, null=True, blank=True, verbose_name=_("Mac address")) - brand = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('Brand')) - cpu = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('CPU')) - memory = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('Memory')) - disk = models.CharField(max_length=1024, null=True, blank=True, verbose_name=_('Disk')) - os = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('OS')) - cabinet_no = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Cabinet number')) - cabinet_pos = models.IntegerField(null=True, blank=True, verbose_name=_('Cabinet position')) - number = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Asset number')) - status = models.CharField(choices=STATUS_CHOICES, max_length=8, null=True, blank=True, - default='In use', verbose_name=_('Asset status')) + is_active = models.BooleanField(default=True, verbose_name=_('Is active')) type = models.CharField(choices=TYPE_CHOICES, max_length=16, blank=True, null=True, default='Server', verbose_name=_('Asset type'),) env = models.CharField(choices=ENV_CHOICES, max_length=8, blank=True, null=True, default='Prod', verbose_name=_('Asset environment'),) + status = models.CharField(choices=STATUS_CHOICES, max_length=8, null=True, blank=True, + default='In use', verbose_name=_('Asset status')) + + # Some information + public_ip = models.GenericIPAddressField(max_length=32, blank=True, null=True, verbose_name=_('Public IP')) + remote_card_ip = models.CharField(max_length=16, null=True, blank=True, verbose_name=_('Remote control card IP')) + cabinet_no = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Cabinet number')) + cabinet_pos = models.IntegerField(null=True, blank=True, verbose_name=_('Cabinet position')) + number = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Asset number')) + + # 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')) sn = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('Serial number')) + + cpu_model = models.CharField(max_length=64, null=True, blank=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')) + memory = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('Memory')) + disk_total = models.CharField(max_length=1024, null=True, blank=True, verbose_name=_('Disk total')) + disk_info = models.CharField(max_length=1024, null=True, blank=True, verbose_name=_('Disk info')) + + platform = models.CharField(max_length=128, null=True, blank=True, verbose_name='Platform') + os = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('OS')) + os_version = models.FloatField(null=True, blank=True, verbose_name=_('OS Version')) + os_arch = models.CharField(max_length=16, blank=True, null=True, verbose_name=_('OS Arch')) + hostname_raw = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Hostname raw')) + created_by = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Created by')) - is_active = models.BooleanField(default=True, verbose_name=_('Is active')) date_created = models.DateTimeField(auto_now_add=True, null=True, blank=True, verbose_name=_('Date added')) comment = models.TextField(max_length=128, default='', blank=True, verbose_name=_('Comment')) tags = models.ManyToManyField('Tag', related_name='assets', blank=True, verbose_name=_('Tags')) @@ -90,6 +105,7 @@ class Asset(models.Model): def _to_secret_json(self): """Ansible use it create inventory""" return { + 'id': self.id, 'hostname': self.hostname, 'ip': self.ip, 'port': self.port, diff --git a/apps/assets/serializers.py b/apps/assets/serializers.py index a9c9c41a8..aae1c4b13 100644 --- a/apps/assets/serializers.py +++ b/apps/assets/serializers.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- from django.utils.translation import ugettext_lazy as _ -from rest_framework import viewsets, serializers,generics +from rest_framework import viewsets, serializers, generics from .models import AssetGroup, Asset, IDC, AdminUser, SystemUser, Tag from common.mixins import IDInFilterMixin from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin @@ -139,8 +139,8 @@ class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer): @staticmethod def get_hardware(obj): - if obj.cpu: - return '%s %s %s' % (obj.cpu, obj.memory, obj.disk) + if obj.cpu_count: + return '{} Core {} {}'.format(obj.cpu_count*obj.cpu_cores, obj.memory, obj.disk_total) else: return '' diff --git a/apps/assets/tasks.py b/apps/assets/tasks.py new file mode 100644 index 000000000..8610deb2e --- /dev/null +++ b/apps/assets/tasks.py @@ -0,0 +1,72 @@ +# ~*~ coding: utf-8 ~*~ +from celery import shared_task +import json + +from ops.tasks import run_AdHoc +from common.utils import get_object_or_none, capacity_convert, sum_capacity +from .models import Asset + + +@shared_task +def update_assets_hardware_info(assets): + task_tuple = ( + ('setup', ''), + ) + task = run_AdHoc.delay(task_tuple, assets, record=False) + summary, result = task.get(timeout=60*10) + for hostname, info in result['contacted'].items(): + if info: + info = info[0]['ansible_facts'] + else: + continue + asset = get_object_or_none(Asset, hostname=hostname) + if not asset: + continue + + ___vendor = info['ansible_system_vendor'] + ___model = info['ansible_product_version'] + ___sn = info['ansible_product_serial'] + + for ___cpu_model in info['ansible_processor']: + if ___cpu_model.endswith('GHz'): + break + else: + ___cpu_model = 'Unknown' + ___cpu_count = info['ansible_processor_count'] + ___cpu_cores = info['ansible_processor_cores'] + ___memory = '%s %s' % capacity_convert('{} MB'.format(info['ansible_memtotal_mb'])) + disk_info = {} + for dev, dev_info in info['ansible_devices'].items(): + if dev_info['removable'] == '0': + disk_info[dev] = dev_info['size'] + ___disk_total = '%s %s' % sum_capacity(disk_info.values()) + ___disk_info = json.dumps(disk_info) + + ___platform = info['ansible_system'] + ___os = info['ansible_distribution'] + ___os_version = float(info['ansible_distribution_version']) + ___os_arch = info['ansible_architecture'] + ___hostname_raw = info['ansible_hostname'] + + for k, v in locals().items(): + if k.startswith('___'): + setattr(asset, k.strip('_'), v) + asset.save() + + +@shared_task(name="asset_test_ping_check") +def asset_test_ping_check(assets): + task_tuple = ( + ('ping', ''), + ) + hoc = AdHocRunner(assets) + result = hoc.run(task_tuple) + return result['contacted'].keys(), result['dark'].keys() + + +def get_assets_hardware_info(assets): + task_tuple = ( + ('setup', ''), + ) + task = run_AdHoc.delay(task_tuple, assets, record=False) + return task \ No newline at end of file diff --git a/apps/assets/templates/assets/asset_create.html b/apps/assets/templates/assets/asset_create.html index 5fbe1b772..a1fa33705 100644 --- a/apps/assets/templates/assets/asset_create.html +++ b/apps/assets/templates/assets/asset_create.html @@ -5,10 +5,16 @@ {% block form %}