Commit 7f4f67aa authored by ibuler's avatar ibuler

[Update] 支持网域

parent fe186212
......@@ -3,3 +3,4 @@ from .asset import *
from .label import *
from .system_user import *
from .node import *
from .domain import *
# ~*~ coding: utf-8 ~*~
from rest_framework_bulk import BulkModelViewSet
from rest_framework.views import APIView
from rest_framework.views import Response
from django.views.generic.detail import SingleObjectMixin
from common.utils import get_logger
from ..hands import IsSuperUser
from ..models import Domain, Gateway
from ..utils import test_gateway_connectability
from .. import serializers
logger = get_logger(__file__)
__all__ = ['DomainViewSet', 'GatewayViewSet', "GatewayTestConnectionApi"]
class DomainViewSet(BulkModelViewSet):
queryset = Domain.objects.all()
permission_classes = (IsSuperUser,)
serializer_class = serializers.DomainSerializer
class GatewayViewSet(BulkModelViewSet):
filter_fields = ("domain",)
search_fields = filter_fields
queryset = Gateway.objects.all()
permission_classes = (IsSuperUser,)
serializer_class = serializers.GatewaySerializer
class GatewayTestConnectionApi(SingleObjectMixin, APIView):
permission_classes = (IsSuperUser,)
model = Gateway
object = None
def get(self, request, *args, **kwargs):
self.object = self.get_object(Gateway.objects.all())
ok, e = test_gateway_connectability(self.object)
if ok:
return Response("ok")
else:
return Response({"failed": e}, status=404)
......@@ -3,3 +3,4 @@
from .asset import *
from .label import *
from .user import *
from .domain import *
......@@ -16,6 +16,7 @@ class AssetCreateForm(forms.ModelForm):
fields = [
'hostname', 'ip', 'public_ip', 'port', 'comment',
'nodes', 'is_active', 'admin_user', 'labels', 'platform',
'domain',
]
widgets = {
......@@ -29,6 +30,9 @@ class AssetCreateForm(forms.ModelForm):
'class': 'select2', 'data-placeholder': _('Labels')
}),
'port': forms.TextInput(),
'domain': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Domain')
}),
}
help_texts = {
'hostname': '* required',
......@@ -38,7 +42,8 @@ class AssetCreateForm(forms.ModelForm):
'root or other NOPASSWD sudo privilege user existed in asset,'
'If asset is windows or other set any one, more see admin user left menu'
),
'platform': _("* required Must set exact system platform, Windows, Linux ...")
'platform': _("* required Must set exact system platform, Windows, Linux ..."),
'domain': _("If your have some network not connect with each other, you can set domain")
}
......@@ -48,6 +53,7 @@ class AssetUpdateForm(forms.ModelForm):
fields = [
'hostname', 'ip', 'port', 'nodes', 'is_active', 'platform',
'public_ip', 'number', 'comment', 'admin_user', 'labels',
'domain',
]
widgets = {
'nodes': forms.SelectMultiple(attrs={
......@@ -60,6 +66,9 @@ class AssetUpdateForm(forms.ModelForm):
'class': 'select2', 'data-placeholder': _('Labels')
}),
'port': forms.TextInput(),
'domain': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Domain')
}),
}
help_texts = {
'hostname': '* required',
......@@ -70,7 +79,8 @@ class AssetUpdateForm(forms.ModelForm):
'root or other NOPASSWD sudo privilege user existed in asset,'
'If asset is windows or other set any one, more see admin user left menu'
),
'platform': _("* required Must set exact system platform, Windows, Linux ...")
'platform': _("* required Must set exact system platform, Windows, Linux ..."),
'domain': _("If your have some network not connect with each other, you can set domain")
}
......
# -*- coding: utf-8 -*-
#
from django import forms
from django.utils.translation import gettext_lazy as _
from ..models import Domain, Asset, Gateway
from .user import PasswordAndKeyAuthForm
__all__ = ['DomainForm', 'GatewayForm']
class DomainForm(forms.ModelForm):
assets = forms.ModelMultipleChoiceField(
queryset=Asset.objects.all(), label=_('Asset'), required=False,
widget=forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select assets')}
)
)
class Meta:
model = Domain
fields = ['name', 'comment', 'assets']
def __init__(self, *args, **kwargs):
if kwargs.get('instance', None):
initial = kwargs.get('initial', {})
initial['assets'] = kwargs['instance'].assets.all()
super().__init__(*args, **kwargs)
def save(self, commit=True):
instance = super().save(commit=commit)
assets = self.cleaned_data['assets']
instance.assets.set(assets)
return instance
class GatewayForm(PasswordAndKeyAuthForm):
def save(self, commit=True):
# Because we define custom field, so we need rewrite :method: `save`
instance = super().save()
password = self.cleaned_data.get('password')
private_key, public_key = super().gen_keys()
instance.set_auth(password=password, private_key=private_key)
return instance
class Meta:
model = Gateway
fields = [
'name', 'ip', 'port', 'username', 'protocol', 'domain', 'password',
'private_key_file', 'is_active', 'comment',
]
widgets = {
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
'username': forms.TextInput(attrs={'placeholder': _('Username')}),
}
help_texts = {
'name': '* required',
'username': '* required',
}
......@@ -8,7 +8,7 @@ from common.utils import validate_ssh_private_key, ssh_pubkey_gen, get_logger
logger = get_logger(__file__)
__all__ = [
'FileForm', 'SystemUserForm', 'AdminUserForm',
'FileForm', 'SystemUserForm', 'AdminUserForm', 'PasswordAndKeyAuthForm',
]
......
......@@ -5,6 +5,7 @@ from .user import AdminUser, SystemUser
from .label import Label
from .cluster import *
from .group import *
from .domain import *
from .node import *
from .asset import *
from .utils import *
......@@ -4,13 +4,13 @@
import uuid
import logging
import random
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.core.cache import cache
from ..const import ASSET_ADMIN_CONN_CACHE_KEY
from .group import AssetGroup
from .user import AdminUser, SystemUser
__all__ = ['Asset']
......@@ -49,6 +49,7 @@ class Asset(models.Model):
ip = models.GenericIPAddressField(max_length=32, verbose_name=_('IP'), db_index=True)
hostname = models.CharField(max_length=128, unique=True, verbose_name=_('Hostname'))
port = models.IntegerField(default=22, verbose_name=_('Port'))
domain = models.ForeignKey("assets.Domain", null=True, blank=True, related_name='assets', verbose_name=_("Domain"))
nodes = models.ManyToManyField('assets.Node', default=default_node, related_name='assets', verbose_name=_("Nodes"))
is_active = models.BooleanField(default=True, verbose_name=_('Is active'))
......@@ -121,12 +122,15 @@ class Asset(models.Model):
return False
def to_json(self):
return {
info = {
'id': self.id,
'hostname': self.hostname,
'ip': self.ip,
'port': self.port,
}
if self.domain and self.domain.gateway_set.all():
info["gateways"] = [d.id for d in self.domain.gateway_set.all()]
return info
def _to_secret_json(self):
"""
......
# -*- coding: utf-8 -*-
#
import os
import uuid
from hashlib import md5
import sshpubkeys
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
from common.utils import get_signer, ssh_key_string_to_obj, ssh_key_gen
from .utils import private_key_validator
signer = get_signer()
class AssetUser(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
username = models.CharField(max_length=128, verbose_name=_('Username'))
_password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
_private_key = models.TextField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'), validators=[private_key_validator, ])
_public_key = models.TextField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
comment = models.TextField(blank=True, verbose_name=_('Comment'))
date_created = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True)
created_by = models.CharField(max_length=128, null=True, verbose_name=_('Created by'))
@property
def password(self):
if self._password:
return signer.unsign(self._password)
else:
return None
@password.setter
def password(self, password_raw):
raise AttributeError("Using set_auth do that")
# self._password = signer.sign(password_raw)
@property
def private_key(self):
if self._private_key:
return signer.unsign(self._private_key)
@private_key.setter
def private_key(self, private_key_raw):
raise AttributeError("Using set_auth do that")
# self._private_key = signer.sign(private_key_raw)
@property
def private_key_obj(self):
if self._private_key:
key_str = signer.unsign(self._private_key)
return ssh_key_string_to_obj(key_str, password=self.password)
else:
return None
@property
def private_key_file(self):
if not self.private_key_obj:
return None
project_dir = settings.PROJECT_DIR
tmp_dir = os.path.join(project_dir, 'tmp')
key_str = signer.unsign(self._private_key)
key_name = '.' + md5(key_str.encode('utf-8')).hexdigest()
key_path = os.path.join(tmp_dir, key_name)
if not os.path.exists(key_path):
self.private_key_obj.write_private_key_file(key_path)
os.chmod(key_path, 0o400)
return key_path
@property
def public_key(self):
key = signer.unsign(self._public_key)
if key:
return key
else:
return None
@property
def public_key_obj(self):
if self.public_key:
try:
return sshpubkeys.SSHKey(self.public_key)
except TabError:
pass
return None
def set_auth(self, password=None, private_key=None, public_key=None):
update_fields = []
if password:
self._password = signer.sign(password)
update_fields.append('_password')
if private_key:
self._private_key = signer.sign(private_key)
update_fields.append('_private_key')
if public_key:
self._public_key = signer.sign(public_key)
update_fields.append('_public_key')
if update_fields:
self.save(update_fields=update_fields)
def auto_gen_auth(self):
password = str(uuid.uuid4())
private_key, public_key = ssh_key_gen(
username=self.name, password=password
)
self.set_auth(password=password,
private_key=private_key,
public_key=public_key)
def _to_secret_json(self):
"""Push system user use it"""
return {
'name': self.name,
'username': self.username,
'password': self.password,
'public_key': self.public_key,
'private_key': self.private_key_file,
}
class Meta:
abstract = True
# -*- coding: utf-8 -*-
#
import uuid
from django.db import models
from django.utils.translation import ugettext_lazy as _
from .base import AssetUser
__all__ = ['Domain', 'Gateway']
class Domain(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
comment = models.TextField(blank=True, verbose_name=_('Comment'))
date_created = models.DateTimeField(auto_now_add=True, null=True,
verbose_name=_('Date created'))
def __str__(self):
return self.name
class Gateway(AssetUser):
SSH_PROTOCOL = 'ssh'
RDP_PROTOCOL = 'rdp'
PROTOCOL_CHOICES = (
(SSH_PROTOCOL, 'ssh'),
(RDP_PROTOCOL, 'rdp'),
)
ip = models.GenericIPAddressField(max_length=32, verbose_name=_('IP'), db_index=True)
port = models.IntegerField(default=22, verbose_name=_('Port'))
protocol = models.CharField(choices=PROTOCOL_CHOICES, max_length=16, default=SSH_PROTOCOL, verbose_name=_("Protocol"))
domain = models.ForeignKey(Domain, verbose_name=_("Domain"))
comment = models.CharField(max_length=128, blank=True, null=True, verbose_name=_("Comment"))
is_active = models.BooleanField(default=True, verbose_name=_("Is active"))
def __str__(self):
return self.name
......@@ -2,20 +2,15 @@
# -*- coding: utf-8 -*-
#
import os
import logging
import uuid
from hashlib import md5
import sshpubkeys
from django.core.cache import cache
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
from common.utils import get_signer, ssh_key_string_to_obj, ssh_key_gen
from .utils import private_key_validator
from common.utils import get_signer
from ..const import SYSTEM_USER_CONN_CACHE_KEY
from .base import AssetUser
__all__ = ['AdminUser', 'SystemUser',]
......@@ -23,117 +18,6 @@ logger = logging.getLogger(__name__)
signer = get_signer()
class AssetUser(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
username = models.CharField(max_length=128, verbose_name=_('Username'))
_password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
_private_key = models.TextField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'), validators=[private_key_validator, ])
_public_key = models.TextField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
comment = models.TextField(blank=True, verbose_name=_('Comment'))
date_created = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True)
created_by = models.CharField(max_length=128, null=True, verbose_name=_('Created by'))
@property
def password(self):
if self._password:
return signer.unsign(self._password)
else:
return None
@password.setter
def password(self, password_raw):
raise AttributeError("Using set_auth do that")
# self._password = signer.sign(password_raw)
@property
def private_key(self):
if self._private_key:
return signer.unsign(self._private_key)
@private_key.setter
def private_key(self, private_key_raw):
raise AttributeError("Using set_auth do that")
# self._private_key = signer.sign(private_key_raw)
@property
def private_key_obj(self):
if self._private_key:
key_str = signer.unsign(self._private_key)
return ssh_key_string_to_obj(key_str, password=self.password)
else:
return None
@property
def private_key_file(self):
if not self.private_key_obj:
return None
project_dir = settings.PROJECT_DIR
tmp_dir = os.path.join(project_dir, 'tmp')
key_str = signer.unsign(self._private_key)
key_name = '.' + md5(key_str.encode('utf-8')).hexdigest()
key_path = os.path.join(tmp_dir, key_name)
if not os.path.exists(key_path):
self.private_key_obj.write_private_key_file(key_path)
os.chmod(key_path, 0o400)
return key_path
@property
def public_key(self):
key = signer.unsign(self._public_key)
if key:
return key
else:
return None
@property
def public_key_obj(self):
if self.public_key:
try:
return sshpubkeys.SSHKey(self.public_key)
except TabError:
pass
return None
def set_auth(self, password=None, private_key=None, public_key=None):
update_fields = []
if password:
self._password = signer.sign(password)
update_fields.append('_password')
if private_key:
self._private_key = signer.sign(private_key)
update_fields.append('_private_key')
if public_key:
self._public_key = signer.sign(public_key)
update_fields.append('_public_key')
if update_fields:
self.save(update_fields=update_fields)
def auto_gen_auth(self):
password = str(uuid.uuid4())
private_key, public_key = ssh_key_gen(
username=self.name, password=password
)
self.set_auth(password=password,
private_key=private_key,
public_key=public_key)
def _to_secret_json(self):
"""Push system user use it"""
return {
'name': self.name,
'username': self.username,
'password': self.password,
'public_key': self.public_key,
'private_key': self.private_key_file,
}
class Meta:
abstract = True
class AdminUser(AssetUser):
"""
A privileged user that ansible can use it to push system user and so on
......
......@@ -6,3 +6,4 @@ from .admin_user import *
from .label import *
from .system_user import *
from .node import *
from .domain import *
# -*- coding: utf-8 -*-
#
from rest_framework import serializers
from ..models import Domain, Gateway
class DomainSerializer(serializers.ModelSerializer):
asset_count = serializers.SerializerMethodField()
gateway_count = serializers.SerializerMethodField()
class Meta:
model = Domain
fields = '__all__'
@staticmethod
def get_asset_count(obj):
return obj.assets.count()
@staticmethod
def get_gateway_count(obj):
return obj.gateway_set.all().count()
class GatewaySerializer(serializers.ModelSerializer):
class Meta:
model = Gateway
fields = '__all__'
......@@ -3,6 +3,7 @@ import json
import re
import os
import paramiko
from celery import shared_task
from django.core.cache import cache
from django.utils.translation import ugettext as _
......@@ -438,3 +439,7 @@ def push_node_system_users_to_asset(node, assets):
# def push_system_user_period():
# for system_user in SystemUser.objects.all():
# push_system_user_related_nodes(system_user)
......@@ -19,6 +19,7 @@
{% bootstrap_field form.port layout="horizontal" %}
{% bootstrap_field form.platform layout="horizontal" %}
{% bootstrap_field form.public_ip layout="horizontal" %}
{% bootstrap_field form.domain layout="horizontal" %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Auth' %}</h3>
......
......@@ -24,6 +24,7 @@
{% bootstrap_field form.port layout="horizontal" %}
{% bootstrap_field form.platform layout="horizontal" %}
{% bootstrap_field form.public_ip layout="horizontal" %}
{% bootstrap_field form.domain layout="horizontal" %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Auth' %}</h3>
......
{% extends '_base_create_update.html' %}
{% load static %}
{% load bootstrap3 %}
{% load i18n %}
{% block form %}
<form id="groupForm" method="post" class="form-horizontal">
{% csrf_token %}
{% bootstrap_field form.name layout="horizontal" %}
{% bootstrap_field form.assets layout="horizontal" %}
{% bootstrap_field form.comment layout="horizontal" %}
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-default" type="reset"> {% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</form>
{% endblock %}
{% block custom_foot_js %}
<script type="text/javascript">
$(document).ready(function () {
$('.select2').select2({
closeOnSelect: false
});
});
</script>
{% endblock %}
\ No newline at end of file
{% extends 'base.html' %}
{% load static %}
{% load i18n %}
{% block custom_head_css_js %}
<link href='{% static "css/plugins/select2/select2.min.css" %}' rel="stylesheet">
<script src='{% static "js/plugins/select2/select2.full.min.js" %}'></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="panel-options">
<ul class="nav nav-tabs">
<li class="active">
<a href="{% url 'assets:domain-detail' pk=object.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
</li>
<li>
<a href="{% url 'assets:domain-gateway-list' pk=object.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Gateway' %} </a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'assets:domain-update' pk=object.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-danger btn-del">
<i class="fa fa-trash-o"></i>{% trans 'Delete' %}
</a>
</li>
</ul>
</div>
<div class="tab-content">
<div class="col-sm-9" style="padding-left: 0;">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label"><b>{{ object.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table">
<tbody>
<tr class="no-borders-tr">
<td>{% trans 'Name' %}:</td>
<td><b>{{ object.name }}</b></td>
</tr>
<tr>
<td>{% trans 'Asset' %}:</td>
<td><b>{{ object.assets.count }}</b></td>
</tr>
<tr>
<td>{% trans 'Gateway' %}:</td>
<td><b>{{ object.gateway_set.count }}</b></td>
</tr>
<tr>
<td>{% trans 'Date created' %}:</td>
<td><b>{{ object.date_created }}</b></td>
</tr>
<tr>
<td>{% trans 'Created by' %}:</td>
<td><b>{{ object.created_by }}</b></td>
</tr>
<tr>
<td>{% trans 'Comment' %}:</td>
<td><b>{{ object.comment }}</b></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %}
<script>
function initTable() {
var options = {
ele: $('#domain_list_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "assets:domain-detail" pk=DEFAULT_PK %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}},
{targets: 5, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "assets:domain-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn-delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
$(td).html(update_btn + del_btn)
}}
],
ajax_url: '{% url "api-assets:domain-list" %}',
columns: [
{data: "id"}, {data: "name" }, {data: "asset_count" },
{data: "gateway_count" }, {data: "comment" }, {data: "id"}
],
op_html: $('#actions').html()
};
jumpserver.initDataTable(options);
}
$(document).ready(function(){
initTable();
})
.on('click', '.btn-delete', function () {
var $this = $(this);
var $data_table = $('#domain_list_table').DataTable();
var name = $(this).closest("tr").find(":nth-child(2)").children('a').html();
var uid = $this.data('uid');
var the_url = '{% url "api-assets:domain-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid);
objectDelete($this, name, the_url);
setTimeout( function () {
$data_table.ajax.reload();
}, 3000);
});
</script>
{% endblock %}
\ No newline at end of file
{% extends 'base.html' %}
{% load static %}
{% load i18n %}
{% block custom_head_css_js %}
<link href='{% static "css/plugins/select2/select2.min.css" %}' rel="stylesheet">
<script src='{% static "js/plugins/select2/select2.full.min.js" %}'></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="panel-options">
<ul class="nav nav-tabs">
<li>
<a href="{% url 'assets:domain-detail' pk=object.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
</li>
<li class="active">
<a href="{% url 'assets:domain-detail' pk=object.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Gateway' %} </a>
</li>
</ul>
</div>
<div class="tab-content">
<div class="col-sm-12" style="padding-left: 0;">
<div class="" id="content_start">
</div>
<div class="ibox float-e-margins">
<div class="ibox-title">
<span style="float: left"><b>{% trans 'Gateway list' %}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<div class="uc pull-left m-r-5">
<a href="{% url 'assets:domain-gateway-create' pk=object.id %}" class="btn btn-sm btn-primary"> {% trans "Create gateway" %} </a>
</div>
<table class="table table-striped table-bordered table-hover " id="domain_list_table" >
<thead>
<tr>
<th class="text-center">
<input type="checkbox" id="check_all" class="ipt_check_all" >
</th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'IP' %}</th>
<th class="text-center">{% trans 'Port' %}</th>
<th class="text-center">{% trans 'Protocol' %}</th>
<th class="text-center">{% trans 'Username' %}</th>
<th class="text-center">{% trans 'Comment' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %}
<script>
function initTable() {
var options = {
ele: $('#domain_list_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "assets:domain-detail" pk=DEFAULT_PK %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}},
{targets: 7, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "assets:domain-gateway-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn-delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
var test_btn = '<a class="btn btn-xs btn-warning m-l-xs btn-test" data-uid="{{ DEFAULT_PK }}">{% trans "Test connection" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
$(td).html(update_btn + test_btn + del_btn)
}}
],
ajax_url: '{% url "api-assets:gateway-list" %}?domain={{ object.id }}',
columns: [
{data: "id"}, {data: "name" }, {data: 'ip'}, {data: 'port'}, {data: "username" },
{data: "protocol"}, {data: "comment" }, {data: "id"}
],
op_html: $('#actions').html()
};
jumpserver.initDataTable(options);
}
$(document).ready(function(){
initTable();
})
.on('click', '.btn-delete', function () {
var $this = $(this);
var $data_table = $('#domain_list_table').DataTable();
var name = $(this).closest("tr").find(":nth-child(2)").children('a').html();
var uid = $this.data('uid');
var the_url = '{% url "api-assets:gateway-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid);
objectDelete($this, name, the_url);
setTimeout( function () {
$data_table.ajax.reload();
}, 3000);
}).on('click', '.btn-test', function () {
var $this = $(this);
var uid = $this.data('uid');
var the_url = '{% url "api-assets:test-gateway-connective" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid);
APIUpdateAttr({
url: the_url,
method: "GET",
success_message: "可连接",
fail_message: "连接失败"
})
})
</script>
{% endblock %}
{% extends '_base_list.html' %}
{% load i18n static %}
{% block table_search %}{% endblock %}
{% block table_container %}
<div class="uc pull-left m-r-5">
<a href="{% url 'assets:domain-create' %}" class="btn btn-sm btn-primary"> {% trans "Create domain" %} </a>
</div>
<table class="table table-striped table-bordered table-hover " id="domain_list_table" >
<thead>
<tr>
<th class="text-center">
<input type="checkbox" id="check_all" class="ipt_check_all" >
</th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'Asset' %}</th>
<th class="text-center">{% trans 'Gateway' %}</th>
<th class="text-center">{% trans 'Comment' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
{% endblock %}
{% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %}
<script>
function initTable() {
var options = {
ele: $('#domain_list_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "assets:domain-detail" pk=DEFAULT_PK %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}},
{targets: 5, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "assets:domain-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn-delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
$(td).html(update_btn + del_btn)
}}
],
ajax_url: '{% url "api-assets:domain-list" %}',
columns: [
{data: "id"}, {data: "name" }, {data: "asset_count" },
{data: "gateway_count" }, {data: "comment" }, {data: "id"}
],
op_html: $('#actions').html()
};
jumpserver.initDataTable(options);
}
$(document).ready(function(){
initTable();
})
.on('click', '.btn-delete', function () {
var $this = $(this);
var $data_table = $('#domain_list_table').DataTable();
var name = $(this).closest("tr").find(":nth-child(2)").children('a').html();
var uid = $this.data('uid');
var the_url = '{% url "api-assets:domain-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid);
objectDelete($this, name, the_url);
setTimeout( function () {
$data_table.ajax.reload();
}, 3000);
});
</script>
{% endblock %}
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% load bootstrap3 %}
{% block custom_head_css_js %}
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>{{ action }}</h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<form enctype="multipart/form-data" method="post" class="form-horizontal" action="" >
{% csrf_token %}
{% if form.non_field_errors %}
<div class="alert alert-danger">
{{ form.non_field_errors }}
</div>
{% endif %}
<h3>{% trans 'Basic' %}</h3>
{% bootstrap_field form.name layout="horizontal" %}
{% bootstrap_field form.ip layout="horizontal" %}
{% bootstrap_field form.port layout="horizontal" %}
{% bootstrap_field form.protocol layout="horizontal" %}
{% bootstrap_field form.domain layout="horizontal" %}
{% block auth %}
<h3>{% trans 'Auth' %}</h3>
<div class="auth-fields">
{% bootstrap_field form.username layout="horizontal" %}
{% bootstrap_field form.password layout="horizontal" %}
{% bootstrap_field form.private_key_file layout="horizontal" %}
</div>
{% endblock %}
<h3>{% trans 'Other' %}</h3>
{% bootstrap_field form.is_active layout="horizontal" %}
{% bootstrap_field form.comment layout="horizontal" %}
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" type="reset">{% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
......@@ -25,6 +25,7 @@
</th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'Username' %}</th>
<th class="text-center">{% trans 'Protocol' %}</th>
<th class="text-center">{% trans 'Asset' %}</th>
<th class="text-center">{% trans 'Reachable' %}</th>
<th class="text-center">{% trans 'Unreachable' %}</th>
......@@ -47,7 +48,7 @@ function initTable() {
var detail_btn = '<a href="{% url "assets:system-user-detail" pk=DEFAULT_PK %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}},
{targets: 4, createdCell: function (td, cellData) {
{targets: 5, createdCell: function (td, cellData) {
var innerHtml = "";
if (cellData !== 0) {
innerHtml = "<span class='text-navy'>" + cellData + "</span>";
......@@ -56,7 +57,7 @@ function initTable() {
}
$(td).html('<span href="javascript:void(0);" data-toggle="tooltip" title="' + cellData +'">' + innerHtml + '</span>');
}},
{targets: 5, createdCell: function (td, cellData) {
{targets: 6, createdCell: function (td, cellData) {
var innerHtml = "";
if (cellData !== 0) {
innerHtml = "<span class='text-danger'>" + cellData + "</span>";
......@@ -65,7 +66,7 @@ function initTable() {
}
$(td).html('<span href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</span>');
}},
{targets: 6, createdCell: function (td, cellData, rowData) {
{targets: 7, createdCell: function (td, cellData, rowData) {
var val = 0;
var innerHtml = "";
var total = rowData.assets_amount;
......@@ -83,14 +84,14 @@ function initTable() {
$(td).html('<span href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</span>');
}},
{targets: 8, createdCell: function (td, cellData, rowData) {
{targets: 9, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "assets:system-user-update" pk=DEFAULT_PK %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_admin_user_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
$(td).html(update_btn + del_btn)
}}],
ajax_url: '{% url "api-assets:system-user-list" %}',
columns: [
{data: "id" }, {data: "name" }, {data: "username" }, {data: "assets_amount" },
{data: "id" }, {data: "name" }, {data: "username" }, {data: "protocol"}, {data: "assets_amount" },
{data: "reachable_amount"}, {data: "unreachable_amount"}, {data: "id"}, {data: "comment" }, {data: "id" }
],
op_html: $('#actions').html()
......
......@@ -14,6 +14,8 @@ router.register(r'v1/admin-user', api.AdminUserViewSet, 'admin-user')
router.register(r'v1/system-user', api.SystemUserViewSet, 'system-user')
router.register(r'v1/labels', api.LabelViewSet, 'label')
router.register(r'v1/nodes', api.NodeViewSet, 'node')
router.register(r'v1/domain', api.DomainViewSet, 'domain')
router.register(r'v1/gateway', api.GatewayViewSet, 'gateway')
urlpatterns = [
url(r'^v1/assets-bulk/$', api.AssetListUpdateApi.as_view(), name='asset-bulk-update'),
......@@ -49,6 +51,8 @@ urlpatterns = [
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/remove/$', api.NodeRemoveAssetsApi.as_view(), name='node-remove-assets'),
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/refresh-hardware-info/$', api.RefreshNodeHardwareInfoApi.as_view(), name='node-refresh-hardware-info'),
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/test-connective/$', api.TestNodeConnectiveApi.as_view(), name='node-test-connective'),
url(r'^v1/gateway/(?P<pk>[0-9a-zA-Z\-]{36})/test-connective/$', api.GatewayTestConnectionApi.as_view(), name='test-gateway-connective'),
]
urlpatterns += router.urls
......
......@@ -39,5 +39,15 @@ urlpatterns = [
url(r'^label/create/$', views.LabelCreateView.as_view(), name='label-create'),
url(r'^label/(?P<pk>[0-9a-zA-Z\-]{36})/update/$', views.LabelUpdateView.as_view(), name='label-update'),
url(r'^label/(?P<pk>[0-9a-zA-Z\-]{36})/delete/$', views.LabelDeleteView.as_view(), name='label-delete'),
url(r'^domain/$', views.DomainListView.as_view(), name='domain-list'),
url(r'^domain/create/$', views.DomainCreateView.as_view(), name='domain-create'),
url(r'^domain/(?P<pk>[0-9a-zA-Z\-]{36})/$', views.DomainDetailView.as_view(), name='domain-detail'),
url(r'^domain/(?P<pk>[0-9a-zA-Z\-]{36})/update/$', views.DomainUpdateView.as_view(), name='domain-update'),
url(r'^domain/(?P<pk>[0-9a-zA-Z\-]{36})/delete/$', views.DomainDeleteView.as_view(), name='domain-delete'),
url(r'^domain/(?P<pk>[0-9a-zA-Z\-]{36})/gateway/$', views.DomainGatewayListView.as_view(), name='domain-gateway-list'),
url(r'^domain/(?P<pk>[0-9a-zA-Z\-]{36})/gateway/create/$', views.DomainGatewayCreateView.as_view(), name='domain-gateway-create'),
url(r'^domain/gateway/(?P<pk>[0-9a-zA-Z\-]{36})/update/$', views.DomainGatewayUpdateView.as_view(), name='domain-gateway-update'),
]
# ~*~ coding: utf-8 ~*~
#
from collections import defaultdict
from functools import reduce
import operator
from django.db.models import Q
import paramiko
from common.utils import get_object_or_none
from .models import Asset, SystemUser, Label
......@@ -44,5 +41,41 @@ class LabelFilter:
return queryset
def test_gateway_connectability(gateway):
"""
Test system cant connect his assets or not.
:param gateway:
:return:
"""
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
proxy_command = [
"ssh", "{}@{}".format(gateway.username, gateway.ip),
"-p", str(gateway.port), "-W", "127.0.0.1:{}".format(gateway.port),
]
if gateway.password:
proxy_command.insert(0, "sshpass -p '{}'".format(gateway.password))
if gateway.private_key:
proxy_command.append("-i {}".format(gateway.private_key_file))
try:
sock = paramiko.ProxyCommand(" ".join(proxy_command))
except paramiko.ProxyCommandFailure as e:
return False, str(e)
try:
client.connect("127.0.0.1", port=gateway.port,
username=gateway.username,
password=gateway.password,
key_filename=gateway.private_key_file,
sock=sock,
timeout=5
)
except (paramiko.SSHException, paramiko.ssh_exception.SSHException,
paramiko.AuthenticationException, TimeoutError) as e:
return False, str(e)
finally:
client.close()
return True, None
......@@ -3,3 +3,4 @@ from .asset import *
from .system_user import *
from .admin_user import *
from .label import *
from .domain import *
# -*- coding: utf-8 -*-
#
from django.views.generic import TemplateView, CreateView, \
UpdateView, DeleteView, DetailView
from django.views.generic.detail import SingleObjectMixin
from django.utils.translation import ugettext_lazy as _
from django.urls import reverse_lazy, reverse
from common.mixins import AdminUserRequiredMixin
from common.const import create_success_msg, update_success_msg
from common.utils import get_object_or_none
from ..models import Domain, Gateway
from ..forms import DomainForm, GatewayForm
__all__ = (
"DomainListView", "DomainCreateView", "DomainUpdateView",
"DomainDetailView", "DomainDeleteView", "DomainGatewayListView",
"DomainGatewayCreateView", 'DomainGatewayUpdateView',
)
class DomainListView(AdminUserRequiredMixin, TemplateView):
template_name = 'assets/domain_list.html'
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('Domain list'),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
class DomainCreateView(AdminUserRequiredMixin, CreateView):
model = Domain
template_name = 'assets/domain_create_update.html'
form_class = DomainForm
success_url = reverse_lazy('assets:domain-list')
success_message = create_success_msg
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('Create domain'),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
class DomainUpdateView(AdminUserRequiredMixin, UpdateView):
model = Domain
template_name = 'assets/domain_create_update.html'
form_class = DomainForm
success_url = reverse_lazy('assets:domain-list')
success_message = update_success_msg
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('Update domain'),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
class DomainDetailView(AdminUserRequiredMixin, DetailView):
model = Domain
template_name = 'assets/domain_detail.html'
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('Domain detail'),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
class DomainDeleteView(AdminUserRequiredMixin, DeleteView):
model = Domain
template_name = 'delete_confirm.html'
success_url = reverse_lazy('assets:domain-list')
class DomainGatewayListView(AdminUserRequiredMixin, SingleObjectMixin, TemplateView):
template_name = 'assets/domain_gateway_list.html'
model = Domain
object = None
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=self.model.objects.all())
return super().get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('Domain gateway list'),
'object': self.get_object()
}
kwargs.update(context)
return super().get_context_data(**kwargs)
class DomainGatewayCreateView(AdminUserRequiredMixin, CreateView):
model = Gateway
template_name = 'assets/gateway_create_update.html'
form_class = GatewayForm
success_message = create_success_msg
def get_success_url(self):
domain = self.object.domain
return reverse('assets:domain-gateway-list', kwargs={"pk": domain.id})
def get_form(self, form_class=None):
form = super().get_form(form_class=form_class)
domain_id = self.kwargs.get("pk")
domain = get_object_or_none(Domain, id=domain_id)
if domain:
form['domain'].initial = domain
return form
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('Create gateway'),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
class DomainGatewayUpdateView(AdminUserRequiredMixin, UpdateView):
model = Gateway
template_name = 'assets/gateway_create_update.html'
form_class = GatewayForm
success_message = update_success_msg
def get_success_url(self):
domain = self.object.domain
return reverse('assets:domain-gateway-list', kwargs={"pk": domain.id})
def form_valid(self, form):
response = super().form_valid(form)
print(form.cleaned_data)
return response
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('Update gateway'),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
......@@ -359,3 +359,4 @@ def is_uuid(s):
def get_signer():
signer = Signer(settings.SECRET_KEY)
return signer
This diff is collapsed.
......@@ -29,6 +29,7 @@ class BaseHost(Host):
}
"groups": [],
"vars": {},
"other_ansbile_vars":
}
"""
self.host_data = host_data
......
# -*- coding: utf-8 -*-
#
import random
from .ansible.inventory import BaseInventory
from assets.utils import get_assets_by_hostname_list, get_system_user_by_name
......@@ -9,6 +10,23 @@ __all__ = [
]
def make_proxy_command(asset):
gateway = random.choice(asset.domain.gateway_set.filter(is_active=True))
proxy_command = [
"ssh", "-p", str(gateway.port),
"{}@{}".format(gateway.username, gateway.ip),
"-W", "%h:%p", "-q",
]
if gateway.password:
proxy_command.insert(0, "sshpass -p {}".format(gateway.password))
if gateway.private_key:
proxy_command.append("-i {}".format(gateway.private_key_file))
return {"ansible_ssh_common_args": "'-o ProxyCommand={}'".format(" ".join(proxy_command))}
class JMSInventory(BaseInventory):
"""
JMS Inventory is the manager with jumpserver assets, so you can
......@@ -21,17 +39,29 @@ class JMSInventory(BaseInventory):
self.become_info = become_info
assets = self.get_jms_assets()
if run_as_admin:
host_list = [asset._to_secret_json() for asset in assets]
else:
host_list = [asset.to_json() for asset in assets]
if run_as:
run_user_info = self.get_run_user_info()
for host in host_list:
host.update(run_user_info)
if become_info:
for host in host_list:
host.update(become_info)
host_list = []
for asset in assets:
vars = {}
if run_as_admin:
info = asset._to_secret_json()
else:
info = asset.to_json()
info["vars"] = vars
if asset.domain and asset.domain.gateway_set.count():
vars.update(make_proxy_command(asset))
info.update(vars)
host_list.append(info)
if run_as:
run_user_info = self.get_run_user_info()
for host in host_list:
host.update(run_user_info)
if become_info:
for host in host_list:
host.update(become_info)
super().__init__(host_list=host_list)
def get_jms_assets(self):
......
......@@ -205,6 +205,7 @@ function objectDelete(obj, name, url, redirectTo) {
url: url,
body: JSON.stringify(body),
method: 'DELETE',
success_message: "删除成功",
success: success,
error: fail
});
......
......@@ -21,6 +21,7 @@
</a>
<ul class="nav nav-second-level">
<li id="asset"><a href="{% url 'assets:asset-list' %}">{% trans 'Asset list' %}</a></li>
<li id="domain"><a href="{% url 'assets:domain-list' %}">{% trans 'Domain list' %}</a></li>
<li id="admin-user"><a href="{% url 'assets:admin-user-list' %}">{% trans 'Admin user' %}</a></li>
<li id="system-user"><a href="{% url 'assets:system-user-list' %}">{% trans 'System user' %}</a></li>
<li id="label"><a href="{% url 'assets:label-list' %}">{% trans 'Labels' %}</a></li>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment