diff --git a/.gitignore b/.gitignore
index 658ea27ac..959682445 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
.DS_Store
*.pyc
*.pyo
+*.swp
env
env*
dist
diff --git a/apps/assets/forms.py b/apps/assets/forms.py
index dec1b0876..101ad854b 100644
--- a/apps/assets/forms.py
+++ b/apps/assets/forms.py
@@ -1,7 +1,7 @@
# coding:utf-8
from django import forms
-from .models import IDC, Asset, AssetGroup
+from .models import IDC, Asset, AssetGroup, AdminUser, SystemUser
from django.utils.translation import gettext_lazy as _
@@ -11,7 +11,7 @@ class AssetForm(forms.ModelForm):
fields = [
"ip", "other_ip", "remote_card_ip", "hostname", "port", "groups", "username", "password",
- "idc", "mac_addr", "brand", "cpu", "memory", "disk", "os", "cabinet_no", "cabinet_pos",
+ "idc", "mac_address", "brand", "cpu", "memory", "disk", "os", "cabinet_no", "cabinet_pos",
"number", "status", "type", "env", "sn", "is_active", "comment"
]
@@ -21,6 +21,7 @@ class AssetForm(forms.ModelForm):
class AssetGroupForm(forms.ModelForm):
+ # See AdminUserForm comment same it
assets = forms.ModelMultipleChoiceField(queryset=Asset.objects.all(),
label=_('Asset'),
required=False,
@@ -51,6 +52,7 @@ class AssetGroupForm(forms.ModelForm):
class IDCForm(forms.ModelForm):
+ # See AdminUserForm comment same it
assets = forms.ModelMultipleChoiceField(queryset=Asset.objects.all(),
label=_('Asset'),
required=False,
@@ -78,3 +80,135 @@ class IDCForm(forms.ModelForm):
'network': forms.Textarea(
attrs={'placeholder': '192.168.1.0/24\n192.168.2.0/24'})
}
+
+
+class AdminUserForm(forms.ModelForm):
+ # Admin user assets define, let user select, save it in form not in view
+ assets = forms.ModelMultipleChoiceField(queryset=Asset.objects.all(),
+ label=_('Asset'),
+ required=False,
+ widget=forms.SelectMultiple(
+ attrs={'class': 'select2', 'data-placeholder': _('Select assets')})
+ )
+ auto_generate_key = forms.BooleanField(required=True, initial=True)
+ # Form field name can not start with `_`, so redefine it,
+ password = forms.CharField(widget=forms.PasswordInput, max_length=100, min_length=8, strip=True,
+ help_text=_('If also set private key, use that first'), required=False)
+ # Need use upload private key file except paste private key content
+ private_key_file = forms.FileField(required=False)
+
+ def __init__(self, *args, **kwargs):
+ # When update a admin user instance, initial it
+ if kwargs.get('instance'):
+ initial = kwargs.get('initial', {})
+ initial['assets'] = kwargs['instance'].assets.all()
+ super(AdminUserForm, self).__init__(*args, **kwargs)
+
+ def _save_m2m(self):
+ # Save assets relation with admin user
+ super(AdminUserForm, self)._save_m2m()
+ assets = self.cleaned_data['assets']
+ self.instance.assets.clear()
+ self.instance.assets.add(*tuple(assets))
+
+ def save(self, commit=True):
+ # Because we define custom field, so we need rewrite :method: `save`
+ admin_user = super(AdminUserForm, self).save(commit=commit)
+ password = self.cleaned_data['password']
+ private_key_file = self.cleaned_data['private_key_file']
+
+ if password:
+ admin_user.password = password
+ print(password)
+ # Todo: Validate private key file, and generate public key
+ # Todo: Auto generate private key and public key
+ if private_key_file:
+ admin_user.private_key = private_key_file.read()
+ admin_user.save()
+ return self.instance
+
+ class Meta:
+ model = AdminUser
+ fields = ['name', 'username', 'auto_generate_key', 'password', 'private_key_file', 'as_default', 'comment']
+ widgets = {
+ 'name': forms.TextInput(attrs={'placeholder': _('Name')}),
+ 'username': forms.TextInput(attrs={'placeholder': _('Username')}),
+ }
+ help_texts = {
+ 'name': '* required',
+ 'username': '* required',
+ }
+
+
+class SystemUserForm(forms.ModelForm):
+ # Admin user assets define, let user select, save it in form not in view
+ assets = forms.ModelMultipleChoiceField(queryset=Asset.objects.all(),
+ label=_('Asset'),
+ required=False,
+ widget=forms.SelectMultiple(
+ attrs={'class': 'select2', 'data-placeholder': _('Select assets')})
+ )
+ asset_groups = forms.ModelMultipleChoiceField(queryset=AssetGroup.objects.all(),
+ label=_('Asset group'),
+ required=False,
+ widget=forms.SelectMultiple(
+ attrs={'class': 'select2',
+ 'data-placeholder': _('Select asset groups')})
+ )
+ auto_generate_key = forms.BooleanField(initial=True)
+ # Form field name can not start with `_`, so redefine it,
+ password = forms.CharField(widget=forms.PasswordInput, max_length=100, min_length=8, strip=True,
+ help_text=_('If also set private key, use that first'), required=False)
+ # Need use upload private key file except paste private key content
+ private_key_file = forms.FileField(required=False)
+
+ def __init__(self, *args, **kwargs):
+ # When update a admin user instance, initial it
+ if kwargs.get('instance'):
+ initial = kwargs.get('initial', {})
+ initial['assets'] = kwargs['instance'].assets.all()
+ initial['asset_groups'] = kwargs['instance'].asset_groups.all()
+ super(SystemUserForm, self).__init__(*args, **kwargs)
+
+ def _save_m2m(self):
+ # Save assets relation with admin user
+ super(SystemUserForm, self)._save_m2m()
+ assets = self.cleaned_data['assets']
+ asset_groups = self.cleaned_data['asset_groups']
+ self.instance.assets.clear()
+ self.instance.assets.add(*tuple(assets))
+ self.instance.asset_groups.clear()
+ self.instance.asset_groups.add(*tuple(asset_groups))
+
+ def save(self, commit=True):
+ # Because we define custom field, so we need rewrite :method: `save`
+ system_user = super(SystemUserForm, self).save(commit=commit)
+ password = self.cleaned_data['password']
+ private_key_file = self.cleaned_data['private_key_file']
+
+ if password:
+ system_user.password = password
+ print(password)
+ # Todo: Validate private key file, and generate public key
+ # Todo: Auto generate private key and public key
+ if private_key_file:
+ system_user.private_key = private_key_file.read()
+ system_user.save()
+ return self.instance
+
+ class Meta:
+ model = SystemUser
+ fields = [
+ 'name', 'username', 'protocol', 'auto_generate_key', 'password', 'private_key_file', 'as_default',
+ 'auto_push', 'auto_update', 'sudo', 'comment', 'shell', 'home', 'uid',
+ ]
+ widgets = {
+ 'name': forms.TextInput(attrs={'placeholder': _('Name')}),
+ 'username': forms.TextInput(attrs={'placeholder': _('Username')}),
+ }
+ help_texts = {
+ 'name': '* required',
+ 'username': '* required',
+ 'auth_push': 'Auto push system user to asset',
+ 'auth_update': 'Auto update system user ssh key',
+ }
\ No newline at end of file
diff --git a/apps/assets/migrations/0001_initial.py b/apps/assets/migrations/0001_initial.py
index 94a2908dd..a63d3d984 100644
--- a/apps/assets/migrations/0001_initial.py
+++ b/apps/assets/migrations/0001_initial.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2016-09-07 15:11
+# Generated by Django 1.10 on 2016-09-08 03:02
from __future__ import unicode_literals
from django.db import migrations, models
@@ -18,15 +18,15 @@ class Migration(migrations.Migration):
name='AdminUser',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('name', models.CharField(blank=True, max_length=128, null=True, unique=True, verbose_name='Name')),
- ('username', models.CharField(blank=True, max_length=16, null=True, verbose_name='Username')),
- ('password', models.CharField(blank=True, max_length=256, null=True, verbose_name='Password')),
- ('private_key', models.CharField(blank=True, max_length=4096, null=True, verbose_name='SSH private key')),
- ('is_default', models.BooleanField(default=True, verbose_name='As default')),
- ('auto_update', models.BooleanField(default=True, verbose_name='Auto update pass/key')),
- ('date_created', models.DateTimeField(auto_now=True, null=True)),
- ('create_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
+ ('name', models.CharField(max_length=128, unique=True, verbose_name='Name')),
+ ('username', models.CharField(max_length=16, verbose_name='Username')),
+ ('_password', models.CharField(blank=True, max_length=256, verbose_name='Password')),
+ ('_private_key', models.CharField(blank=True, max_length=4096, verbose_name='SSH private key')),
+ ('_public_key', models.CharField(blank=True, max_length=4096, verbose_name='SSH public key')),
+ ('as_default', models.BooleanField(default=False, verbose_name='As default')),
('comment', models.TextField(blank=True, verbose_name='Comment')),
+ ('date_created', models.DateTimeField(auto_now=True, null=True)),
+ ('created_by', models.CharField(max_length=32, null=True, verbose_name='Created by')),
],
options={
'db_table': 'admin_user',
@@ -142,7 +142,7 @@ class Migration(migrations.Migration):
('home', models.CharField(blank=True, max_length=64, verbose_name='Home')),
('uid', models.IntegerField(blank=True, verbose_name='Uid')),
('date_created', models.DateTimeField(auto_now=True, null=True)),
- ('create_by', models.CharField(blank=True, max_length=32, verbose_name='Created by')),
+ ('created_by', models.CharField(blank=True, max_length=32, verbose_name='Created by')),
('comment', models.CharField(blank=True, max_length=128, verbose_name='Comment')),
],
options={
diff --git a/apps/assets/models.py b/apps/assets/models.py
index f968f05b0..71a076d02 100644
--- a/apps/assets/models.py
+++ b/apps/assets/models.py
@@ -61,22 +61,39 @@ class AssetExtend(models.Model):
comment = models.TextField(blank=True, verbose_name=_('Comment'))
def __unicode__(self):
- return self.name
+ return '%(key)s: %(value)s' % {'key': self.key, 'value': self.value}
+
+ @classmethod
+ def initial(cls):
+ for k, v in (
+ (_('status'), _('In use')),
+ (_('status'), _('Out of use')),
+ (_('type'), _('Server')),
+ (_('type'), _('VM')),
+ (_('type'), _('Switch')),
+ (_('type'), _('Router')),
+ (_('type'), _('Firewall')),
+ (_('type'), _('Storage')),
+ (_('env'), _('Production')),
+ (_('env'), _('Development')),
+ (_('env'), _('Testing')),
+ ):
+ cls.objects.create(key=k, value=v, created_by='System')
class Meta:
db_table = 'asset_extend'
class AdminUser(models.Model):
- name = models.CharField(max_length=128, unique=True, null=True, blank=True, verbose_name=_('Name'))
- username = models.CharField(max_length=16, null=True, blank=True, verbose_name=_('Username'))
- _password = models.CharField(max_length=256, null=True, blank=True, verbose_name=_('Password'))
- _private_key = models.CharField(max_length=4096, null=True, blank=True, verbose_name=_('SSH private key'))
- _public_key = models.CharField(max_length=4096, null=True, blank=True, verbose_name=_('SSH public key'))
- as_default = models.BooleanField(default=True, verbose_name=_('As default'))
+ name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
+ username = models.CharField(max_length=16, verbose_name=_('Username'))
+ _password = models.CharField(max_length=256, blank=True, verbose_name=_('Password'))
+ _private_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH private key'))
+ _public_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
+ as_default = models.BooleanField(default=False, verbose_name=_('As default'))
comment = models.TextField(blank=True, verbose_name=_('Comment'))
- date_created = models.DateTimeField(auto_now=True, null=True, blank=True)
- created_by = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Created by'))
+ date_created = models.DateTimeField(auto_now=True, null=True)
+ created_by = models.CharField(max_length=32, null=True, verbose_name=_('Created by'))
def __unicode__(self):
return self.name
@@ -110,7 +127,7 @@ class AdminUser(models.Model):
@classmethod
def generate_fake(cls, count=100):
- from random import seed, choice
+ from random import seed
import forgery_py
from django.db import IntegrityError
@@ -132,31 +149,74 @@ class AdminUser(models.Model):
class SystemUser(models.Model):
PROTOCOL_CHOICES = (
('ssh', 'ssh'),
- ('telnet', 'telnet'),
)
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
- username = models.CharField(max_length=16, blank=True, verbose_name=_('Username'))
- password = models.CharField(max_length=256, blank=True, verbose_name=_('Password'))
- protocol = models.CharField(max_length=16, default='ssh', verbose_name=_('Protocol'))
- private_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH private key'))
- public_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
- is_default = models.BooleanField(default=True, verbose_name=_('As default'))
+ username = models.CharField(max_length=16, verbose_name=_('Username'))
+ _password = models.CharField(max_length=256, blank=True, verbose_name=_('Password'))
+ protocol = models.CharField(max_length=16, choices=PROTOCOL_CHOICES, default='ssh', verbose_name=_('Protocol'))
+ _private_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH private key'))
+ _public_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
+ as_default = models.BooleanField(default=False, verbose_name=_('As default'))
auto_push = models.BooleanField(default=True, verbose_name=_('Auto push'))
auto_update = models.BooleanField(default=True, verbose_name=_('Auto update pass/key'))
- sudo = models.TextField(max_length=4096, blank=True, verbose_name=_('Sudo'))
- shell = models.CharField(max_length=64, blank=True, verbose_name=_('Shell'))
+ sudo = models.TextField(max_length=4096, default='/user/bin/whoami', verbose_name=_('Sudo'))
+ shell = models.CharField(max_length=64, default='/bin/bash', verbose_name=_('Shell'))
home = models.CharField(max_length=64, blank=True, verbose_name=_('Home'))
- uid = models.IntegerField(blank=True, verbose_name=_('Uid'))
- date_created = models.DateTimeField(auto_now=True, null=True)
+ uid = models.IntegerField(null=True, blank=True, verbose_name=_('Uid'))
+ date_created = models.DateTimeField(auto_now=True)
created_by = models.CharField(max_length=32, blank=True, verbose_name=_('Created by'))
- comment = models.CharField(max_length=128, blank=True, verbose_name=_('Comment'))
+ comment = models.TextField(max_length=128, blank=True, verbose_name=_('Comment'))
def __unicode__(self):
return self.name
+ @property
+ def password(self):
+ return decrypt(self._password)
+
+ @password.setter
+ def password(self, password_raw):
+ self._password = encrypt(password_raw)
+
+ @property
+ def private_key(self):
+ return decrypt(self._private_key)
+
+ @private_key.setter
+ def private_key(self, private_key_raw):
+ self._private_key = encrypt(private_key_raw)
+
+ @property
+ def public_key(self):
+ return decrypt(self._public_key)
+
+ @public_key.setter
+ def public_key(self, public_key_raw):
+ self._public_key = encrypt(public_key_raw)
+
class Meta:
db_table = 'system_user'
+ @classmethod
+ def generate_fake(cls, count=100):
+ from random import seed
+ import forgery_py
+ from django.db import IntegrityError
+
+ seed()
+ for i in range(count):
+ obj = cls(name=forgery_py.name.full_name(),
+ username=forgery_py.internet.user_name(),
+ password=forgery_py.lorem_ipsum.word(),
+ comment=forgery_py.lorem_ipsum.sentence(),
+ created_by='Fake')
+ try:
+ obj.save()
+ logger.debug('Generate fake asset group: %s' % obj.name)
+ except IntegrityError:
+ print('Error continue')
+ continue
+
class AssetGroup(models.Model):
name = models.CharField(max_length=64, unique=True, verbose_name=_('Name'))
@@ -204,10 +264,11 @@ class Asset(models.Model):
groups = models.ManyToManyField(AssetGroup, related_name='assets', verbose_name=_('Asset groups'))
username = models.CharField(max_length=16, null=True, blank=True, verbose_name=_('Admin user'))
password = models.CharField(max_length=256, null=True, blank=True, verbose_name=_("Admin password"))
- admin_user = models.ForeignKey(AdminUser, null=True, on_delete=models.SET_NULL, verbose_name=_("Admin user"))
- system_user = models.ManyToManyField(SystemUser, blank=True, verbose_name=_("System User"))
+ admin_user = models.ForeignKey(AdminUser, null=True, related_name='assets',
+ on_delete=models.SET_NULL, verbose_name=_("Admin user"))
+ system_user = models.ManyToManyField(SystemUser, blank=True, related_name='assets', verbose_name=_("System User"))
idc = models.ForeignKey(IDC, null=True, related_name='assets', on_delete=models.SET_NULL, verbose_name=_('IDC'))
- mac_addr = models.CharField(max_length=20, null=True, blank=True, verbose_name=_("Mac address"))
+ 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'))
@@ -226,7 +287,7 @@ class Asset(models.Model):
comment = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('Comment'))
def __unicode__(self):
- return '%(ip)s:%(port)d' % {'ip': self.ip, 'port': self.port}
+ return '%(ip)s:%(port)s' % {'ip': self.ip, 'port': self.port}
def initial(self):
pass
diff --git a/apps/assets/templates/assets/admin_user_create_update.html b/apps/assets/templates/assets/admin_user_create_update.html
new file mode 100644
index 000000000..7738adec0
--- /dev/null
+++ b/apps/assets/templates/assets/admin_user_create_update.html
@@ -0,0 +1,70 @@
+{% extends 'base.html' %}
+{% load i18n %}
+{% load static %}
+{% load bootstrap %}
+{% block custom_head_css_js %}
+
+
+{% endblock %}
+
+{% block content %}
+
+
+
+
+
+
{% trans 'Create admin user' %}
+
+
+
+
+
+
+
+{% endblock %}
+{% block custom_foot_js %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/apps/assets/templates/assets/admin_user_detail.html b/apps/assets/templates/assets/admin_user_detail.html
new file mode 100644
index 000000000..12a108f6c
--- /dev/null
+++ b/apps/assets/templates/assets/admin_user_detail.html
@@ -0,0 +1,220 @@
+{% extends 'base.html' %}
+{% load common_tags %}
+{% load users_tags %}
+{% load static %}
+{% load i18n %}
+
+{% block custom_head_css_js %}
+
+
+{% endblock %}
+{% block content %}
+
+
+
+
+
+
+
+
+
+
{{ admin_user.name }}
+
+
+
+
+
+
+ {% trans 'Name' %}: |
+ {{ admin_user.name }} |
+
+
+ {% trans 'Username' %}: |
+ {{ admin_user.username }} |
+
+
+ {% trans 'Date created' %}: |
+ {{ admin_user.date_created }} |
+
+
+ {% trans 'Created by' %}: |
+ {{ asset_group.created_by }} |
+
+
+ {% trans 'Comment' %}: |
+ {{ admin_user.comment }} |
+
+
+
+
+
+
+
+
+
{% trans 'Asset list of ' %} {{ admin_user.name }}
+
+
+
+
+
+
+ {% trans 'Hostname' %} |
+ {% trans 'IP' %} |
+ {% trans 'Port' %} |
+ {% trans 'Alive' %} |
+
+
+
+ {% for asset in page_obj %}
+
+ {{ asset.hostname }} |
+ {{ asset.ip }} |
+ {{ asset.port }} |
+ Alive |
+
+ {% endfor %}
+
+
+
+ {% include '_pagination.html' %}
+
+
+
+
+
+
+
+ {% trans 'Quick update' %}
+
+
+
+
+
+ {% trans 'Get install script' %}: |
+
+
+
+
+ |
+
+
+
+ {% trans 'Retest asset connectivity' %}: |
+
+
+
+
+ |
+
+
+
+ {% trans 'Reset private key' %}: |
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+ {% trans 'Replace asset admin user with this' %}
+
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
+{% block custom_foot_js %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/apps/assets/templates/assets/admin_user_list.html b/apps/assets/templates/assets/admin_user_list.html
index d7aae75aa..eeb4587bd 100644
--- a/apps/assets/templates/assets/admin_user_list.html
+++ b/apps/assets/templates/assets/admin_user_list.html
@@ -20,7 +20,7 @@
{{ admin_user.id }} |
-
+
{{ admin_user.name }}
|
diff --git a/apps/assets/templates/assets/asset_group_detail.html b/apps/assets/templates/assets/asset_group_detail.html
index 08272a04f..4c48867b9 100644
--- a/apps/assets/templates/assets/asset_group_detail.html
+++ b/apps/assets/templates/assets/asset_group_detail.html
@@ -69,7 +69,7 @@