mirror of https://github.com/jumpserver/jumpserver
完成AdHoc JMSHost JMSInventory
parent
0524e8dddf
commit
01e50d592e
|
@ -87,6 +87,18 @@ class Asset(models.Model):
|
||||||
def to_json(self):
|
def to_json(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def _to_secret_json(self):
|
||||||
|
"""Ansible use it create inventory"""
|
||||||
|
return {
|
||||||
|
'hostname': self.hostname,
|
||||||
|
'ip': self.ip,
|
||||||
|
'port': self.port,
|
||||||
|
'groups': [group.name for group in self.groups.all()],
|
||||||
|
'username': self.admin_user.username,
|
||||||
|
'password': self.admin_user.password,
|
||||||
|
'private_key': self.admin_user.private_key,
|
||||||
|
}
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ('ip', 'port')
|
unique_together = ('ip', 'port')
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import logging
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
from common.utils import signer, validate_ssh_private_key
|
from common.utils import signer, validate_ssh_private_key, ssh_key_string_to_obj
|
||||||
|
|
||||||
__all__ = ['AdminUser', 'SystemUser', 'private_key_validator']
|
__all__ = ['AdminUser', 'SystemUser', 'private_key_validator']
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -24,12 +24,20 @@ def private_key_validator(value):
|
||||||
|
|
||||||
|
|
||||||
class AdminUser(models.Model):
|
class AdminUser(models.Model):
|
||||||
|
BECOME_METHOD_CHOICES = (
|
||||||
|
('sudo', 'sudo'),
|
||||||
|
('su', 'su'),
|
||||||
|
)
|
||||||
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
|
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
|
||||||
username = models.CharField(max_length=16, verbose_name=_('Username'))
|
username = models.CharField(max_length=16, verbose_name=_('Username'))
|
||||||
_password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
|
_password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
|
||||||
_private_key = models.CharField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'),
|
_private_key = models.CharField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'),
|
||||||
validators=[private_key_validator,])
|
validators=[private_key_validator,])
|
||||||
_public_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
|
_public_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
|
||||||
|
become = models.BooleanField(default=True)
|
||||||
|
become_method = models.CharField(choices=BECOME_METHOD_CHOICES, default='sudo', max_length=4)
|
||||||
|
become_user = models.CharField(default='root', max_length=64)
|
||||||
|
become_password = models.CharField(default='', max_length=128)
|
||||||
comment = models.TextField(blank=True, verbose_name=_('Comment'))
|
comment = models.TextField(blank=True, verbose_name=_('Comment'))
|
||||||
date_created = models.DateTimeField(auto_now_add=True, null=True)
|
date_created = models.DateTimeField(auto_now_add=True, null=True)
|
||||||
created_by = models.CharField(max_length=32, null=True, verbose_name=_('Created by'))
|
created_by = models.CharField(max_length=32, null=True, verbose_name=_('Created by'))
|
||||||
|
@ -41,7 +49,10 @@ class AdminUser(models.Model):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def password(self):
|
def password(self):
|
||||||
return signer.unsign(self._password)
|
if self._password:
|
||||||
|
return signer.unsign(self._password)
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
@password.setter
|
@password.setter
|
||||||
def password(self, password_raw):
|
def password(self, password_raw):
|
||||||
|
@ -49,7 +60,11 @@ class AdminUser(models.Model):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def private_key(self):
|
def private_key(self):
|
||||||
return signer.unsign(self._private_key)
|
if self._private_key:
|
||||||
|
key_str = signer.unsign(self._private_key)
|
||||||
|
return ssh_key_string_to_obj(key_str)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
@private_key.setter
|
@private_key.setter
|
||||||
def private_key(self, private_key_raw):
|
def private_key(self, private_key_raw):
|
||||||
|
|
|
@ -1,65 +1,4 @@
|
||||||
# ~*~ coding: utf-8 ~*~
|
# ~*~ coding: utf-8 ~*~
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from ops.models import *
|
|
||||||
from rest_framework import serializers
|
|
||||||
|
|
||||||
|
|
||||||
class HostAliaSerializer(serializers.ModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = HostAlia
|
|
||||||
|
|
||||||
|
|
||||||
class CmdAliaSerializer(serializers.ModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = CmdAlia
|
|
||||||
|
|
||||||
|
|
||||||
class UserAliaSerializer(serializers.ModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = UserAlia
|
|
||||||
|
|
||||||
|
|
||||||
class RunasAliaSerializer(serializers.ModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = RunasAlia
|
|
||||||
|
|
||||||
|
|
||||||
class ExtraconfSerializer(serializers.ModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Extra_conf
|
|
||||||
|
|
||||||
|
|
||||||
class PrivilegeSerializer(serializers.ModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Privilege
|
|
||||||
|
|
||||||
|
|
||||||
class SudoSerializer(serializers.ModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Sudo
|
|
||||||
|
|
||||||
|
|
||||||
class CronTableSerializer(serializers.ModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = CronTable
|
|
||||||
|
|
||||||
class TaskSerializer(serializers.ModelSerializer):
|
|
||||||
sub_tasks = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Task
|
|
||||||
read_only_fields = ('record',)
|
|
||||||
|
|
||||||
class SubTaskSerializer(serializers.ModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = SubTask
|
|
||||||
|
|
|
@ -5,75 +5,3 @@ from rest_framework import viewsets
|
||||||
from serializers import *
|
from serializers import *
|
||||||
from permissions import *
|
from permissions import *
|
||||||
|
|
||||||
__all__ = ["HostAliaViewSet",
|
|
||||||
"CmdAliaViewSet",
|
|
||||||
"UserAliaViewSet",
|
|
||||||
"RunasAliaViewSet",
|
|
||||||
"ExtraconfViewSet",
|
|
||||||
"PrivilegeViewSet",
|
|
||||||
"SudoViewSet",
|
|
||||||
"CronTableViewSet",
|
|
||||||
"TaskViewSet",
|
|
||||||
"SubTaskViewSet",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class HostAliaViewSet(viewsets.ModelViewSet):
|
|
||||||
queryset = HostAlia.objects.all()
|
|
||||||
serializer_class = HostAliaSerializer
|
|
||||||
permission_classes = (AdminUserRequired,)
|
|
||||||
|
|
||||||
|
|
||||||
class CmdAliaViewSet(viewsets.ModelViewSet):
|
|
||||||
queryset = CmdAlia.objects.all()
|
|
||||||
serializer_class = CmdAliaSerializer
|
|
||||||
permission_classes = (AdminUserRequired,)
|
|
||||||
|
|
||||||
|
|
||||||
class UserAliaViewSet(viewsets.ModelViewSet):
|
|
||||||
queryset = UserAlia.objects.all()
|
|
||||||
serializer_class = UserAliaSerializer
|
|
||||||
permission_classes = (AdminUserRequired,)
|
|
||||||
|
|
||||||
|
|
||||||
class RunasAliaViewSet(viewsets.ModelViewSet):
|
|
||||||
queryset = RunasAlia.objects.all()
|
|
||||||
serializer_class = RunasAliaSerializer
|
|
||||||
permission_classes = (AdminUserRequired,)
|
|
||||||
|
|
||||||
|
|
||||||
class ExtraconfViewSet(viewsets.ModelViewSet):
|
|
||||||
queryset = Extra_conf.objects.all()
|
|
||||||
serializer_class = ExtraconfSerializer
|
|
||||||
permission_classes = (AdminUserRequired,)
|
|
||||||
|
|
||||||
|
|
||||||
class PrivilegeViewSet(viewsets.ModelViewSet):
|
|
||||||
queryset = Privilege.objects.all()
|
|
||||||
serializer_class = PrivilegeSerializer
|
|
||||||
permission_classes = (AdminUserRequired,)
|
|
||||||
|
|
||||||
|
|
||||||
class SudoViewSet(viewsets.ModelViewSet):
|
|
||||||
queryset = Sudo.objects.all()
|
|
||||||
serializer_class = SudoSerializer
|
|
||||||
permission_classes = (AdminUserRequired,)
|
|
||||||
|
|
||||||
|
|
||||||
class CronTableViewSet(viewsets.ModelViewSet):
|
|
||||||
queryset = CronTable.objects.all()
|
|
||||||
serializer_class = CronTableSerializer
|
|
||||||
permission_classes = (AdminUserRequired,)
|
|
||||||
|
|
||||||
class TaskViewSet(viewsets.ModelViewSet):
|
|
||||||
queryset = Task.objects.all()
|
|
||||||
serializer_class = TaskSerializer
|
|
||||||
permission_classes = (AdminUserRequired,)
|
|
||||||
|
|
||||||
class SubTaskViewSet(viewsets.ModelViewSet):
|
|
||||||
queryset = SubTask.objects.all()
|
|
||||||
serializer_class = SubTaskSerializer
|
|
||||||
permission_classes = (AdminUserRequired,)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
from ansible import *
|
from ansible import *
|
||||||
from cron import *
|
|
||||||
from sudo import *
|
|
||||||
from utils import *
|
from utils import *
|
||||||
from task import *
|
from task import *
|
||||||
|
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
# ~*~ coding: utf-8 ~*~
|
|
||||||
from __future__ import unicode_literals, absolute_import
|
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from django.db import models
|
|
||||||
from assets.models import Asset
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
__all__ = ["CronTable"]
|
|
||||||
|
|
||||||
|
|
||||||
class CronTable(models.Model):
|
|
||||||
name = models.CharField(max_length=128, blank=True, null=True, unique=True, verbose_name=_('Name'),
|
|
||||||
help_text=_("Description of a crontab entry"))
|
|
||||||
month = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Month'),
|
|
||||||
help_text=_("Month of the year the job should run ( 1-12, *, */2, etc )"))
|
|
||||||
weekday = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('WeekDay'),
|
|
||||||
help_text=_("Day of the week that the job should run"
|
|
||||||
" ( 0-6 for Sunday-Saturday, *, etc )"))
|
|
||||||
day = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Day'),
|
|
||||||
help_text=_("Day of the month the job should run ( 1-31, *, */2, etc )"))
|
|
||||||
hour = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Hour'),
|
|
||||||
help_text=_("Hour when the job should run ( 0-23, *, */2, etc )"))
|
|
||||||
minute = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Minute'),
|
|
||||||
help_text=_("Minute when the job should run ( 0-59, *, */2, etc )"))
|
|
||||||
job = models.CharField(max_length=4096, blank=True, null=True, verbose_name=_('Job'),
|
|
||||||
help_text=_("The command to execute or, if env is set, the value of "
|
|
||||||
"environment variable. Required if state=present."))
|
|
||||||
user = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('User'),
|
|
||||||
help_text=_("The specific user whose crontab should be modified."))
|
|
||||||
asset = models.ForeignKey(Asset, null=True, blank=True, related_name='crontables')
|
|
||||||
|
|
||||||
@property
|
|
||||||
def describe(self):
|
|
||||||
return "http://docs.ansible.com/ansible/cron_module.html"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def generate_fake(cls, count=20):
|
|
||||||
from random import seed, choice
|
|
||||||
import forgery_py
|
|
||||||
|
|
||||||
seed()
|
|
||||||
for i in range(count):
|
|
||||||
cron = cls(name=forgery_py.name.full_name(),
|
|
||||||
month=str(choice(range(1,13))),
|
|
||||||
weekday=str(choice(range(0,7))),
|
|
||||||
day=str(choice(range(1,32))),
|
|
||||||
hour=str(choice(range(0,24))),
|
|
||||||
minute=str(choice(range(0,60))),
|
|
||||||
job=forgery_py.lorem_ipsum.sentence(),
|
|
||||||
user=forgery_py.name.first_name(),
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
cron.save()
|
|
||||||
logger.debug('Generate fake cron: %s' % cron.name)
|
|
||||||
except Exception as e:
|
|
||||||
print('Error: %s, continue...' % e.message)
|
|
||||||
continue
|
|
|
@ -1,321 +0,0 @@
|
||||||
# ~*~ coding: utf-8 ~*~
|
|
||||||
from __future__ import unicode_literals, absolute_import
|
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from jinja2 import Template
|
|
||||||
from django.db import models
|
|
||||||
from django.utils.timezone import now
|
|
||||||
from assets.models import Asset, AssetGroup
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
__all__ = ["HostAlia", "UserAlia", "CmdAlia", "RunasAlia", "Privilege", "Extra_conf", "Sudo"]
|
|
||||||
|
|
||||||
|
|
||||||
class HostAlia(models.Model):
|
|
||||||
name = models.CharField(max_length=128, blank=True, null=True, unique=True, verbose_name=_('Host_Alias'))
|
|
||||||
host_items = models.TextField(blank=True, null=True, verbose_name=_('Host_Items'))
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def generate_fake(cls, count=20):
|
|
||||||
from random import seed
|
|
||||||
import forgery_py
|
|
||||||
|
|
||||||
seed()
|
|
||||||
for i in range(count):
|
|
||||||
hostA = cls(name=forgery_py.name.full_name(),
|
|
||||||
host_items=forgery_py.lorem_ipsum.sentence(),
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
hostA.save()
|
|
||||||
logger.debug('Generate fake host alia: %s' % hostA.name)
|
|
||||||
except Exception as e:
|
|
||||||
print('Error: %s, continue...' % e.message)
|
|
||||||
continue
|
|
||||||
|
|
||||||
|
|
||||||
class UserAlia(models.Model):
|
|
||||||
name = models.CharField(max_length=128, blank=True, null=True, unique=True, verbose_name=_('User_Alias'))
|
|
||||||
user_items = models.TextField(blank=True, null=True, verbose_name=_('Host_Items'))
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def generate_fake(cls, count=20):
|
|
||||||
from random import seed
|
|
||||||
import forgery_py
|
|
||||||
|
|
||||||
seed()
|
|
||||||
for i in range(count):
|
|
||||||
userA = cls(name=forgery_py.name.full_name(),
|
|
||||||
user_items=forgery_py.lorem_ipsum.sentence(),
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
userA.save()
|
|
||||||
logger.debug('Generate fake host alia: %s' % userA.name)
|
|
||||||
except Exception as e:
|
|
||||||
print('Error: %s, continue...' % e.message)
|
|
||||||
continue
|
|
||||||
|
|
||||||
|
|
||||||
class CmdAlia(models.Model):
|
|
||||||
name = models.CharField(max_length=128, blank=True, null=True, unique=True, verbose_name=_('Command_Alias'))
|
|
||||||
cmd_items = models.TextField(blank=True, null=True, verbose_name=_('Host_Items'))
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def generate_fake(cls, count=20):
|
|
||||||
from random import seed
|
|
||||||
import forgery_py
|
|
||||||
|
|
||||||
seed()
|
|
||||||
for i in range(count):
|
|
||||||
cmdA = cls(name=forgery_py.name.full_name(),
|
|
||||||
cmd_items=forgery_py.lorem_ipsum.sentence(),
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
cmdA.save()
|
|
||||||
logger.debug('Generate fake command alia: %s' % cmdA.name)
|
|
||||||
except Exception as e:
|
|
||||||
print('Error: %s, continue...' % e.message)
|
|
||||||
continue
|
|
||||||
|
|
||||||
|
|
||||||
class RunasAlia(models.Model):
|
|
||||||
name = models.CharField(max_length=128, blank=True, null=True, unique=True, verbose_name=_('Runas_Alias'))
|
|
||||||
runas_items = models.TextField(blank=True, null=True, verbose_name=_('Host_Items'))
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def generate_fake(cls, count=20):
|
|
||||||
from random import seed
|
|
||||||
import forgery_py
|
|
||||||
|
|
||||||
seed()
|
|
||||||
for i in range(count):
|
|
||||||
runas = cls(name=forgery_py.name.full_name(),
|
|
||||||
runas_items=forgery_py.lorem_ipsum.sentence(),
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
runas.save()
|
|
||||||
logger.debug('Generate fake RunAs alia: %s' % runas.name)
|
|
||||||
except Exception as e:
|
|
||||||
print('Error: %s, continue...' % e.message)
|
|
||||||
continue
|
|
||||||
|
|
||||||
|
|
||||||
class Privilege(models.Model):
|
|
||||||
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
|
|
||||||
user = models.ForeignKey(UserAlia, blank=True, null=True, related_name='privileges')
|
|
||||||
host = models.ForeignKey(HostAlia, blank=True, null=True, related_name='privileges')
|
|
||||||
runas = models.ForeignKey(RunasAlia, blank=True, null=True, related_name='privileges')
|
|
||||||
command = models.ForeignKey(CmdAlia, blank=True, null=True, related_name='privileges')
|
|
||||||
nopassword = models.BooleanField(default=True, verbose_name=_('Is_NoPassword'))
|
|
||||||
comment = models.TextField(blank=True, null=True, verbose_name=_('Comment'))
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return "[%s %s %s %s %s]" % (self.user.name,
|
|
||||||
self.host.name,
|
|
||||||
self.runas.name,
|
|
||||||
self.command.name,
|
|
||||||
self.nopassword)
|
|
||||||
|
|
||||||
def to_tuple(self):
|
|
||||||
return self.user.name, self.host.name, self.runas.name, self.command.name, self.nopassword
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def generate_fake(cls, count=20):
|
|
||||||
from random import seed, choice
|
|
||||||
import forgery_py
|
|
||||||
|
|
||||||
seed()
|
|
||||||
for i in range(count):
|
|
||||||
pri = cls(name=forgery_py.name.full_name(),
|
|
||||||
comment=forgery_py.lorem_ipsum.sentence(),
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
pri.user = choice(UserAlia.objects.all())
|
|
||||||
pri.host = choice(HostAlia.objects.all())
|
|
||||||
pri.runas = choice(RunasAlia.objects.all())
|
|
||||||
pri.command = choice(CmdAlia.objects.all())
|
|
||||||
pri.save()
|
|
||||||
logger.debug('Generate fake privileges: %s' % pri.name)
|
|
||||||
except Exception as e:
|
|
||||||
print('Error: %s, continue...' % e.message)
|
|
||||||
continue
|
|
||||||
|
|
||||||
|
|
||||||
class Extra_conf(models.Model):
|
|
||||||
line = models.TextField(blank=True, null=True, verbose_name=_('Extra_Item'),
|
|
||||||
help_text=_('The extra sudo config line.'))
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return self.line
|
|
||||||
|
|
||||||
|
|
||||||
class Sudo(models.Model):
|
|
||||||
"""
|
|
||||||
Sudo配置文件对象, 用于配置sudo的配置文件
|
|
||||||
|
|
||||||
:param extra_lines: <list> [<line1>, <line2>,...]
|
|
||||||
:param privileges: <list> [(user, host, runas, command, nopassword),]
|
|
||||||
"""
|
|
||||||
|
|
||||||
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'),
|
|
||||||
help_text=_('Name for this sudo'))
|
|
||||||
created_time = models.DateTimeField(verbose_name=_('Created Time'), auto_created=True,
|
|
||||||
help_text=_('The create time of this sudo'))
|
|
||||||
modify_time = models.DateTimeField(auto_now=True, verbose_name=_('Modify Time'),
|
|
||||||
help_text=_('The recent modify time of this sudo'))
|
|
||||||
assets = models.ManyToManyField(Asset, blank=True, related_name='sudos')
|
|
||||||
asset_groups = models.ManyToManyField(AssetGroup, blank=True, related_name='sudos')
|
|
||||||
extra_lines = models.ManyToManyField(Extra_conf, related_name='sudos', blank=True)
|
|
||||||
privilege_items = models.ManyToManyField(Privilege, related_name='sudos', blank=True)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def all_assets(self):
|
|
||||||
assets = list(self.assets.all())
|
|
||||||
for group in self.asset_groups.all():
|
|
||||||
for asset in group.assets.all():
|
|
||||||
if asset not in assets:
|
|
||||||
assets.append(asset)
|
|
||||||
return assets
|
|
||||||
|
|
||||||
@property
|
|
||||||
def users(self):
|
|
||||||
return {privilege.user.name: privilege.user.user_items.split(',') for privilege in self.privilege_items.all()}
|
|
||||||
|
|
||||||
@property
|
|
||||||
def commands(self):
|
|
||||||
return {privilege.command.name: privilege.command.cmd_items.split(',') for privilege in self.privilege_items.all()}
|
|
||||||
|
|
||||||
@property
|
|
||||||
def hosts(self):
|
|
||||||
return {privilege.host.name: privilege.host.host_items.split(',') for privilege in self.privilege_items.all()}
|
|
||||||
|
|
||||||
@property
|
|
||||||
def runas(self):
|
|
||||||
return {privilege.runas.name: privilege.runas.runas_items.split(',') for privilege in self.privilege_items.all()}
|
|
||||||
|
|
||||||
@property
|
|
||||||
def extras(self):
|
|
||||||
return [extra.line for extra in self.extra_lines.all()]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def privileges(self):
|
|
||||||
return [privilege.to_tuple() for privilege in self.privilege_items.all()]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def content(self):
|
|
||||||
template = Template(self.__sudoers_jinja2_tmp__)
|
|
||||||
context = {"User_Alias": self.users,
|
|
||||||
"Cmnd_Alias": self.commands,
|
|
||||||
"Host_Alias": self.hosts,
|
|
||||||
"Runas_Alias": self.runas,
|
|
||||||
"Extra_Lines": self.extras,
|
|
||||||
"Privileges": self.privileges}
|
|
||||||
return template.render(context)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def __sudoers_jinja2_tmp__(self):
|
|
||||||
return """# management by JumpServer
|
|
||||||
# This file MUST be edited with the 'visudo' command as root.
|
|
||||||
#
|
|
||||||
# Please consider adding local content in /etc/sudoers.d/ instead of
|
|
||||||
# directly modifying this file.
|
|
||||||
#
|
|
||||||
# See the man page for details on how to write a sudoers file.
|
|
||||||
#
|
|
||||||
Defaults env_reset
|
|
||||||
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
|
||||||
|
|
||||||
# JumpServer Generate Other Configure is here
|
|
||||||
{% if Extra_Lines -%}
|
|
||||||
{% for line in Extra_Lines -%}
|
|
||||||
{{ line }}
|
|
||||||
{% endfor %}
|
|
||||||
{%- endif %}
|
|
||||||
|
|
||||||
# Host alias specification
|
|
||||||
{% if Host_Alias -%}
|
|
||||||
{% for flag, items in Host_Alias.iteritems() -%}
|
|
||||||
Host_Alias {{ flag }} = {{ items|join(', ') }}
|
|
||||||
{% endfor %}
|
|
||||||
{%- endif %}
|
|
||||||
|
|
||||||
# User alias specification
|
|
||||||
{% if User_Alias -%}
|
|
||||||
{% for flag, items in User_Alias.iteritems() -%}
|
|
||||||
User_Alias {{ flag }} = {{ items|join(', ') }}
|
|
||||||
{% endfor %}
|
|
||||||
{%- endif %}
|
|
||||||
|
|
||||||
|
|
||||||
# Cmnd alias specification
|
|
||||||
{% if Cmnd_Alias -%}
|
|
||||||
{% for flag, items in Cmnd_Alias.iteritems() -%}
|
|
||||||
Cmnd_Alias {{ flag }} = {{ items|join(', ') }}
|
|
||||||
{% endfor %}
|
|
||||||
{%- endif %}
|
|
||||||
|
|
||||||
# Run as alias specification
|
|
||||||
{% if Runas_Alias -%}
|
|
||||||
{% for flag, items in Runas_Alias.iteritems() -%}
|
|
||||||
Runas_Alias {{ flag }} = {{ items|join(', ') }}
|
|
||||||
{% endfor %}
|
|
||||||
{%- endif %}
|
|
||||||
|
|
||||||
# User privilege specification
|
|
||||||
root ALL=(ALL:ALL) ALL
|
|
||||||
|
|
||||||
# JumpServer Generate User privilege is here.
|
|
||||||
# Note privileges is a tuple list like [(user, host, runas, command, nopassword),]
|
|
||||||
{% if Privileges -%}
|
|
||||||
{% for User_Flag, Host_Flag, Runas_Flag, Command_Flag, NopassWord in Privileges -%}
|
|
||||||
{% if NopassWord -%}
|
|
||||||
{{ User_Flag }} {{ Host_Flag }}=({{ Runas_Flag }}) NOPASSWD: {{ Command_Flag }}
|
|
||||||
{%- else -%}
|
|
||||||
{{ User_Flag }} {{ Host_Flag }}=({{ Runas_Flag }}) {{ Command_Flag }}
|
|
||||||
{%- endif %}
|
|
||||||
{% endfor %}
|
|
||||||
{%- endif %}
|
|
||||||
|
|
||||||
# Members of the admin group may gain root privileges
|
|
||||||
%admin ALL=(ALL) ALL
|
|
||||||
|
|
||||||
# Allow members of group sudo to execute any command
|
|
||||||
%sudo ALL=(ALL:ALL) ALL
|
|
||||||
|
|
||||||
# See sudoers(5) for more information on "#include" directives:
|
|
||||||
|
|
||||||
#includedir /etc/sudoers.d
|
|
||||||
"""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def generate_fake(cls, count=20):
|
|
||||||
from random import seed, choice
|
|
||||||
import forgery_py
|
|
||||||
|
|
||||||
seed()
|
|
||||||
for i in range(count):
|
|
||||||
sudo = cls(name=forgery_py.name.full_name(),
|
|
||||||
created_time=now()
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
sudo.save()
|
|
||||||
sudo.privilege_items = [choice(Privilege.objects.all())]
|
|
||||||
sudo.save()
|
|
||||||
logger.debug('Generate fake cron: %s' % sudo.name)
|
|
||||||
except Exception as e:
|
|
||||||
print('Error: %s, continue...' % e.message)
|
|
||||||
continue
|
|
|
@ -2,13 +2,10 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from ansible import *
|
from ansible import *
|
||||||
from cron import *
|
|
||||||
from sudo import *
|
|
||||||
|
|
||||||
__all__ = ["generate_fake"]
|
__all__ = ["generate_fake"]
|
||||||
|
|
||||||
|
|
||||||
def generate_fake():
|
def generate_fake():
|
||||||
for cls in (TaskRecord, AnsiblePlay, AnsibleTask, AnsibleHostResult, CronTable,
|
for cls in (TaskRecord, AnsiblePlay, AnsibleTask, AnsibleHostResult):
|
||||||
HostAlia, UserAlia, CmdAlia, RunasAlia, Privilege, Sudo):
|
|
||||||
cls.generate_fake()
|
cls.generate_fake()
|
|
@ -2,20 +2,6 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from rest_framework.routers import DefaultRouter
|
from rest_framework.routers import DefaultRouter
|
||||||
from ops import api as v1_api
|
|
||||||
|
|
||||||
__all__ = ["urlpatterns"]
|
|
||||||
|
|
||||||
api_router = DefaultRouter()
|
urlpatterns = []
|
||||||
api_router.register(r'v1/host_alia', v1_api.HostAliaViewSet)
|
|
||||||
api_router.register(r'v1/user_alia', v1_api.UserAliaViewSet)
|
|
||||||
api_router.register(r'v1/cmd_alia', v1_api.CmdAliaViewSet)
|
|
||||||
api_router.register(r'v1/runas_alia', v1_api.RunasAliaViewSet)
|
|
||||||
api_router.register(r'v1/extra_conf', v1_api.ExtraconfViewSet)
|
|
||||||
api_router.register(r'v1/privilege', v1_api.PrivilegeViewSet)
|
|
||||||
api_router.register(r'v1/sudo', v1_api.SudoViewSet)
|
|
||||||
api_router.register(r'v1/cron', v1_api.CronTableViewSet)
|
|
||||||
api_router.register(r'v1/task', v1_api.TaskViewSet)
|
|
||||||
api_router.register(r'v1/subtask', v1_api.SubTaskViewSet)
|
|
||||||
|
|
||||||
urlpatterns = api_router.urls
|
|
|
@ -8,18 +8,6 @@ from ops import views as page_view
|
||||||
__all__ = ["urlpatterns"]
|
__all__ = ["urlpatterns"]
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# Resource Sudo url
|
|
||||||
url(r'^sudo/list$', page_view.SudoListView.as_view(), name='page-sudo-list'),
|
|
||||||
url(r'^sudo/create$', page_view.SudoCreateView.as_view(), name='page-sudo-create'),
|
|
||||||
url(r'^sudo/(?P<pk>[0-9]+)/detail$', page_view.SudoDetailView.as_view(), name='page-sudo-detail'),
|
|
||||||
url(r'^sudo/(?P<pk>[0-9]+)/update$', page_view.SudoUpdateView.as_view(), name='page-sudo-update'),
|
|
||||||
|
|
||||||
# Resource Cron url
|
|
||||||
url(r'^cron/list$', page_view.CronListView.as_view(), name='page-cron-list'),
|
|
||||||
url(r'^cron/create$', page_view.CronCreateView.as_view(), name='page-cron-create'),
|
|
||||||
url(r'^cron/(?P<pk>[0-9]+)/detail$', page_view.CronDetailView.as_view(), name='page-cron-detail'),
|
|
||||||
url(r'^cron/(?P<pk>[0-9]+)/update$', page_view.CronUpdateView.as_view(), name='page-cron-update'),
|
|
||||||
|
|
||||||
# TResource Task url
|
# TResource Task url
|
||||||
url(r'^task/list$', page_view.TaskListView.as_view(), name='page-task-list'),
|
url(r'^task/list$', page_view.TaskListView.as_view(), name='page-task-list'),
|
||||||
url(r'^task/create$', page_view.TaskCreateView.as_view(), name='page-task-create'),
|
url(r'^task/create$', page_view.TaskCreateView.as_view(), name='page-task-create'),
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
# ~*~ coding: utf-8 ~*~
|
# ~*~ coding: utf-8 ~*~
|
||||||
from __future__ import unicode_literals, print_function
|
# from __future__ import unicode_literals, print_function
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
import ansible.constants as default_config
|
import ansible.constants as default_config
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
@ -17,11 +18,15 @@ from ansible.executor import playbook_executor
|
||||||
from ansible.utils.display import Display
|
from ansible.utils.display import Display
|
||||||
from ansible.playbook.play import Play
|
from ansible.playbook.play import Play
|
||||||
from ansible.plugins.callback import CallbackBase
|
from ansible.plugins.callback import CallbackBase
|
||||||
|
import ansible.constants as C
|
||||||
|
from ansible.utils.vars import load_extra_vars
|
||||||
|
from ansible.utils.vars import load_options_vars
|
||||||
|
|
||||||
from ops.models import TaskRecord, AnsiblePlay, AnsibleTask, AnsibleHostResult
|
from ..models import TaskRecord, AnsiblePlay, AnsibleTask, AnsibleHostResult
|
||||||
|
|
||||||
__all__ = ["ADHocRunner", "Options"]
|
__all__ = ["ADHocRunner", "Options"]
|
||||||
|
|
||||||
|
C.HOST_KEY_CHECKING = False
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -30,149 +35,187 @@ class AnsibleError(StandardError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Options(object):
|
# class Options(object):
|
||||||
"""Ansible运行时配置类, 用于初始化Ansible的一些默认配置.
|
# """Ansible运行时配置类, 用于初始化Ansible的一些默认配置.
|
||||||
"""
|
# """
|
||||||
def __init__(self, verbosity=None, inventory=None, listhosts=None, subset=None, module_paths=None, extra_vars=None,
|
# def __init__(self, verbosity=None, inventory=None, listhosts=None, subset=None, module_paths=None, extra_vars=None,
|
||||||
forks=10, ask_vault_pass=False, vault_password_files=None, new_vault_password_file=None,
|
# forks=10, ask_vault_pass=False, vault_password_files=None, new_vault_password_file=None,
|
||||||
output_file=None, tags=None, skip_tags=None, one_line=None, tree=None, ask_sudo_pass=False, ask_su_pass=False,
|
# output_file=None, tags=None, skip_tags=None, one_line=None, tree=None, ask_sudo_pass=False, ask_su_pass=False,
|
||||||
sudo=None, sudo_user=None, become=None, become_method=None, become_user=None, become_ask_pass=False,
|
# sudo=None, sudo_user=None, become=None, become_method=None, become_user=None, become_ask_pass=False,
|
||||||
ask_pass=False, private_key_file=None, remote_user=None, connection="smart", timeout=10, ssh_common_args=None,
|
# ask_pass=False, private_key_file=None, remote_user=None, connection="smart", timeout=10, ssh_common_args=None,
|
||||||
sftp_extra_args=None, scp_extra_args=None, ssh_extra_args=None, poll_interval=None, seconds=None, check=False,
|
# sftp_extra_args=None, scp_extra_args=None, ssh_extra_args=None, poll_interval=None, seconds=None, check=False,
|
||||||
syntax=None, diff=None, force_handlers=None, flush_cache=None, listtasks=None, listtags=None, module_path=None):
|
# syntax=None, diff=None, force_handlers=None, flush_cache=None, listtasks=None, listtags=None, module_path=None):
|
||||||
self.verbosity = verbosity
|
# self.verbosity = verbosity
|
||||||
self.inventory = inventory
|
# self.inventory = inventory
|
||||||
self.listhosts = listhosts
|
# self.listhosts = listhosts
|
||||||
self.subset = subset
|
# self.subset = subset
|
||||||
self.module_paths = module_paths
|
# self.module_paths = module_paths
|
||||||
self.extra_vars = extra_vars
|
# self.extra_vars = extra_vars
|
||||||
self.forks = forks
|
# self.forks = forks
|
||||||
self.ask_vault_pass = ask_vault_pass
|
# self.ask_vault_pass = ask_vault_pass
|
||||||
self.vault_password_files = vault_password_files
|
# self.vault_password_files = vault_password_files
|
||||||
self.new_vault_password_file = new_vault_password_file
|
# self.new_vault_password_file = new_vault_password_file
|
||||||
self.output_file = output_file
|
# self.output_file = output_file
|
||||||
self.tags = tags
|
# self.tags = tags
|
||||||
self.skip_tags = skip_tags
|
# self.skip_tags = skip_tags
|
||||||
self.one_line = one_line
|
# self.one_line = one_line
|
||||||
self.tree = tree
|
# self.tree = tree
|
||||||
self.ask_sudo_pass = ask_sudo_pass
|
# self.ask_sudo_pass = ask_sudo_pass
|
||||||
self.ask_su_pass = ask_su_pass
|
# self.ask_su_pass = ask_su_pass
|
||||||
self.sudo = sudo
|
# self.sudo = sudo
|
||||||
self.sudo_user = sudo_user
|
# self.sudo_user = sudo_user
|
||||||
self.become = become
|
# self.become = become
|
||||||
self.become_method = become_method
|
# self.become_method = become_method
|
||||||
self.become_user = become_user
|
# self.become_user = become_user
|
||||||
self.become_ask_pass = become_ask_pass
|
# self.become_ask_pass = become_ask_pass
|
||||||
self.ask_pass = ask_pass
|
# self.ask_pass = ask_pass
|
||||||
self.private_key_file = private_key_file
|
# self.private_key_file = private_key_file
|
||||||
self.remote_user = remote_user
|
# self.remote_user = remote_user
|
||||||
self.connection = connection
|
# self.connection = connection
|
||||||
self.timeout = timeout
|
# self.timeout = timeout
|
||||||
self.ssh_common_args = ssh_common_args
|
# self.ssh_common_args = ssh_common_args
|
||||||
self.sftp_extra_args = sftp_extra_args
|
# self.sftp_extra_args = sftp_extra_args
|
||||||
self.scp_extra_args = scp_extra_args
|
# self.scp_extra_args = scp_extra_args
|
||||||
self.ssh_extra_args = ssh_extra_args
|
# self.ssh_extra_args = ssh_extra_args
|
||||||
self.poll_interval = poll_interval
|
# self.poll_interval = poll_interval
|
||||||
self.seconds = seconds
|
# self.seconds = seconds
|
||||||
self.check = check
|
# self.check = check
|
||||||
self.syntax = syntax
|
# self.syntax = syntax
|
||||||
self.diff = diff
|
# self.diff = diff
|
||||||
self.force_handlers = force_handlers
|
# self.force_handlers = force_handlers
|
||||||
self.flush_cache = flush_cache
|
# self.flush_cache = flush_cache
|
||||||
self.listtasks = listtasks
|
# self.listtasks = listtasks
|
||||||
self.listtags = listtags
|
# self.listtags = listtags
|
||||||
self.module_path = module_path
|
# self.module_path = module_path
|
||||||
self.__overwrite_default()
|
# self.__overwrite_default()
|
||||||
|
#
|
||||||
def __overwrite_default(self):
|
# def __overwrite_default(self):
|
||||||
"""上面并不能包含Ansible所有的配置, 如果有其他的配置,
|
# """上面并不能包含Ansible所有的配置, 如果有其他的配置,
|
||||||
可以通过替换default_config模块里面的变量进行重载,
|
# 可以通过替换default_config模块里面的变量进行重载,
|
||||||
比如 default_config.DEFAULT_ASK_PASS = False.
|
# 比如 default_config.DEFAULT_ASK_PASS = False.
|
||||||
"""
|
# """
|
||||||
default_config.HOST_KEY_CHECKING = False
|
# default_config.HOST_KEY_CHECKING = False
|
||||||
|
Options = namedtuple("Options", [
|
||||||
|
'connection', 'module_path', 'private_key_file', "remote_user", "timeout",
|
||||||
|
'forks', 'become', 'become_method', 'become_user', 'check', "extra_vars",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class InventoryMixin(object):
|
class JMSHost(Host):
|
||||||
|
def __init__(self, asset):
|
||||||
|
self.asset = asset
|
||||||
|
self.name = name = asset.get('hostname') or asset.get('ip')
|
||||||
|
self.port = port = asset.get('port') or 22
|
||||||
|
super(JMSHost, self).__init__(name, port)
|
||||||
|
self.set_all_variable()
|
||||||
|
|
||||||
|
def set_all_variable(self):
|
||||||
|
asset = self.asset
|
||||||
|
self.set_variable('ansible_host', asset['ip'])
|
||||||
|
self.set_variable('ansible_port', asset['port'])
|
||||||
|
self.set_variable('ansible_user', asset['username'])
|
||||||
|
|
||||||
|
# 添加密码和秘钥
|
||||||
|
if asset.get('password'):
|
||||||
|
self.set_variable('ansible_ssh_pass', asset['password'])
|
||||||
|
if asset.get('key'):
|
||||||
|
self.set_variable('ansible_ssh_private_key_file', asset['private_key'])
|
||||||
|
|
||||||
|
# 添加become支持
|
||||||
|
become = asset.get("become", None)
|
||||||
|
if become is not None:
|
||||||
|
self.set_variable("ansible_become", True)
|
||||||
|
self.set_variable("ansible_become_method", become.get('method'))
|
||||||
|
self.set_variable("ansible_become_user", become.get('user'))
|
||||||
|
self.set_variable("ansible_become_pass", become.get('pass'))
|
||||||
|
else:
|
||||||
|
self.set_variable("ansible_become", False)
|
||||||
|
|
||||||
|
|
||||||
|
class JMSInventory(Inventory):
|
||||||
"""
|
"""
|
||||||
提供生成Ansible inventory对象的方法
|
提供生成Ansible inventory对象的方法
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def gen_inventory(self):
|
def __init__(self, host_list=None):
|
||||||
|
if host_list is None:
|
||||||
|
host_list = []
|
||||||
|
assert isinstance(host_list, list)
|
||||||
|
self.host_list = host_list
|
||||||
|
self.loader = DataLoader()
|
||||||
|
self.variable_manager = VariableManager()
|
||||||
|
super(JMSInventory, self).__init__(self.loader, self.variable_manager,
|
||||||
|
host_list=host_list)
|
||||||
|
|
||||||
|
def parse_inventory(self, host_list):
|
||||||
"""用于生成动态构建Ansible Inventory.
|
"""用于生成动态构建Ansible Inventory.
|
||||||
self.hosts: [
|
self.host_list: [
|
||||||
{"host": <ip>,
|
{"name": "asset_name",
|
||||||
"port": <port>,
|
"ip": <ip>,
|
||||||
"user": <user>,
|
"port": <port>,
|
||||||
"pass": <pass>,
|
"user": <user>,
|
||||||
"key": <sshKey>,
|
"pass": <pass>,
|
||||||
"group": <default>
|
"key": <sshKey>,
|
||||||
"other_host_var": <other>},
|
"groups": ['group1', 'group2'],
|
||||||
{...},
|
"other_host_var": <other>},
|
||||||
]
|
{...},
|
||||||
self.group_vars: {
|
]
|
||||||
"groupName1": {"var1": <value>, "var2": <value>, ...},
|
|
||||||
"groupName2": {"var1": <value>, "var2": <value>, ...},
|
|
||||||
}
|
|
||||||
|
|
||||||
:return: 返回一个Ansible的inventory对象
|
:return: 返回一个Ansible的inventory对象
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# TODO: 验证输入
|
# TODO: 验证输入
|
||||||
|
|
||||||
# 创建Ansible Group,如果没有则创建default组
|
# 创建Ansible Group,如果没有则创建default组
|
||||||
for asset in self.hosts:
|
ungrouped = Group('ungrouped')
|
||||||
g_name = asset.get('group', 'default')
|
all = Group('all')
|
||||||
if g_name not in [g.name for g in self.groups]:
|
all.add_child_group(ungrouped)
|
||||||
group = Group(name=g_name)
|
self.groups = dict(all=all, ungrouped=ungrouped)
|
||||||
self.groups.append(group)
|
|
||||||
|
|
||||||
# 添加组变量到相应的组上
|
for asset in host_list:
|
||||||
for group_name, variables in self.group_vars.iteritems():
|
host = JMSHost(asset=asset)
|
||||||
for g in self.groups:
|
asset_groups = asset.get('groups')
|
||||||
if g.name == group_name:
|
if asset_groups:
|
||||||
for v_name, v_value in variables.iteritems():
|
for group_name in asset_groups:
|
||||||
g.set_variable(v_name, v_value)
|
if group_name not in self.groups:
|
||||||
|
group = Group(group_name)
|
||||||
# 往组里面添加Host
|
self.groups[group_name] = group
|
||||||
for asset in self.hosts:
|
else:
|
||||||
# 添加Host链接的常用变量(host,port,user,pass,key)
|
group = self.groups[group_name]
|
||||||
host = Host(name=asset['name'], port=asset['port'])
|
group.add_host(host)
|
||||||
host.set_variable('ansible_host', asset['ip'])
|
|
||||||
host.set_variable('ansible_port', asset['port'])
|
|
||||||
host.set_variable('ansible_user', asset['username'])
|
|
||||||
|
|
||||||
# 添加密码和秘钥
|
|
||||||
if asset.get('password'):
|
|
||||||
host.set_variable('ansible_ssh_pass', asset['password'])
|
|
||||||
if asset.get('key'):
|
|
||||||
host.set_variable('ansible_ssh_private_key_file', asset['key'])
|
|
||||||
|
|
||||||
# 添加become支持
|
|
||||||
become = asset.get("become", None)
|
|
||||||
if become is not None:
|
|
||||||
host.set_variable("ansible_become", True)
|
|
||||||
host.set_variable("ansible_become_method", become.get('method'))
|
|
||||||
host.set_variable("ansible_become_user", become.get('user'))
|
|
||||||
host.set_variable("ansible_become_pass", become.get('pass'))
|
|
||||||
else:
|
else:
|
||||||
host.set_variable("ansible_become", False)
|
ungrouped.add_host(host)
|
||||||
|
all.add_host(host)
|
||||||
|
|
||||||
# 添加其他Host的额外变量
|
|
||||||
for key, value in asset.iteritems():
|
|
||||||
if key not in ["name", "port", "ip", "username", "password", "key"]:
|
|
||||||
host.set_variable(key, value)
|
|
||||||
|
|
||||||
# 将host添加到组里面
|
class BasicResultCallback(CallbackBase):
|
||||||
for g in self.groups:
|
"""
|
||||||
if g.name == asset.get('group', 'default'):
|
Custom Callback
|
||||||
g.add_host(host)
|
"""
|
||||||
|
def __init__(self, display=None):
|
||||||
|
self.result_q = dict(contacted={}, dark={})
|
||||||
|
super(BasicResultCallback, self).__init__(display)
|
||||||
|
|
||||||
# 将组添加到Inventory里面,生成真正的inventory对象
|
def gather_result(self, n, res):
|
||||||
inventory = Inventory(loader=self.loader, variable_manager=self.variable_manager, host_list=[])
|
self.result_q[n].update({res._host.name: res._result})
|
||||||
for g in self.groups:
|
|
||||||
inventory.add_group(g)
|
def v2_runner_on_ok(self, result):
|
||||||
self.variable_manager.set_inventory(inventory)
|
self.gather_result("contacted", result)
|
||||||
return inventory
|
|
||||||
|
def v2_runner_on_failed(self, result, ignore_errors=False):
|
||||||
|
self.gather_result("dark", result)
|
||||||
|
|
||||||
|
def v2_runner_on_unreachable(self, result):
|
||||||
|
self.gather_result("dark", result)
|
||||||
|
|
||||||
|
def v2_runner_on_skipped(self, result):
|
||||||
|
self.gather_result("dark", result)
|
||||||
|
|
||||||
|
def v2_playbook_on_task_start(self, task, is_conditional):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def v2_playbook_on_play_start(self, play):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CallbackModule(CallbackBase):
|
class CallbackModule(CallbackBase):
|
||||||
|
@ -310,7 +353,7 @@ class CallbackModule(CallbackBase):
|
||||||
print("summary: %s", summary)
|
print("summary: %s", summary)
|
||||||
|
|
||||||
|
|
||||||
class PlayBookRunner(InventoryMixin):
|
class PlayBookRunner(object):
|
||||||
"""用于执行AnsiblePlaybook的接口.简化Playbook对象的使用.
|
"""用于执行AnsiblePlaybook的接口.简化Playbook对象的使用.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -393,133 +436,121 @@ class PlayBookRunner(InventoryMixin):
|
||||||
return stats
|
return stats
|
||||||
|
|
||||||
|
|
||||||
class ADHocRunner(InventoryMixin):
|
class ADHocRunner(object):
|
||||||
"""
|
"""
|
||||||
ADHoc接口
|
ADHoc接口
|
||||||
"""
|
"""
|
||||||
def __init__(self, play_data, config=None, *hosts, **group_vars):
|
def __init__(self,
|
||||||
"""
|
hosts=C.DEFAULT_HOST_LIST,
|
||||||
:param hosts: 见PlaybookRunner参数
|
module_name=C.DEFAULT_MODULE_NAME, # * command
|
||||||
:param group_vars: 见PlaybookRunner参数
|
module_args=C.DEFAULT_MODULE_ARGS, # * 'cmd args'
|
||||||
:param config: Config实例
|
forks=C.DEFAULT_FORKS, # 5
|
||||||
|
timeout=C.DEFAULT_TIMEOUT, # SSH timeout = 10s
|
||||||
|
pattern="all", # all
|
||||||
|
remote_user=C.DEFAULT_REMOTE_USER, # root
|
||||||
|
module_path=None, # dirs of custome modules
|
||||||
|
connection_type="smart",
|
||||||
|
become=None,
|
||||||
|
become_method=None,
|
||||||
|
become_user=None,
|
||||||
|
check=False,
|
||||||
|
passwords=None,
|
||||||
|
extra_vars=None,
|
||||||
|
private_key_file=None,
|
||||||
|
gather_facts='no'):
|
||||||
|
|
||||||
:param play_data:
|
self.pattern = pattern
|
||||||
play_data = dict(
|
|
||||||
name="Ansible Ad-Hoc",
|
|
||||||
hosts=pattern,
|
|
||||||
gather_facts=True,
|
|
||||||
tasks=[dict(action=dict(module='service', args={'name': 'vsftpd', 'state': 'restarted'}), async=async, poll=poll)]
|
|
||||||
)
|
|
||||||
"""
|
|
||||||
self.options = config if config != None else Options()
|
|
||||||
|
|
||||||
# 设置verbosity级别, 及命令行的--verbose选项
|
|
||||||
self.display = Display()
|
|
||||||
self.display.verbosity = self.options.verbosity
|
|
||||||
|
|
||||||
# sudo的配置移到了Host级别去了,因此这里不再需要处理
|
|
||||||
self.passwords = None
|
|
||||||
|
|
||||||
# 生成Ansible inventory, 这些变量Mixin都会用到
|
|
||||||
self.hosts = hosts
|
|
||||||
self.group_vars = group_vars
|
|
||||||
self.loader = DataLoader()
|
|
||||||
self.variable_manager = VariableManager()
|
self.variable_manager = VariableManager()
|
||||||
self.groups = []
|
self.loader = DataLoader()
|
||||||
self.inventory = self.gen_inventory()
|
self.module_name = module_name
|
||||||
|
self.module_args = module_args
|
||||||
|
self.check_module_args()
|
||||||
|
self.gather_facts = gather_facts
|
||||||
|
self.results_callback = BasicResultCallback()
|
||||||
|
self.options = Options(
|
||||||
|
connection=connection_type,
|
||||||
|
timeout=timeout,
|
||||||
|
module_path=module_path,
|
||||||
|
forks=forks,
|
||||||
|
become=become,
|
||||||
|
become_method=become_method,
|
||||||
|
become_user=become_user,
|
||||||
|
check=check,
|
||||||
|
remote_user=remote_user,
|
||||||
|
extra_vars=extra_vars or [],
|
||||||
|
private_key_file=private_key_file,
|
||||||
|
)
|
||||||
|
|
||||||
self.play = Play().load(play_data, variable_manager=self.variable_manager, loader=self.loader)
|
self.variable_manager.extra_vars = load_extra_vars(self.loader, options=self.options)
|
||||||
|
self.variable_manager.options_vars = load_options_vars(self.options)
|
||||||
|
self.passwords = passwords or {}
|
||||||
|
self.inventory = JMSInventory(hosts)
|
||||||
|
self.variable_manager.set_inventory(self.inventory)
|
||||||
|
|
||||||
|
self.play_source = dict(
|
||||||
|
name='Ansible Ad-hoc',
|
||||||
|
hosts=self.pattern,
|
||||||
|
gather_facts=self.gather_facts,
|
||||||
|
tasks=[dict(action=dict(
|
||||||
|
module=self.module_name,
|
||||||
|
args=self.module_args
|
||||||
|
))]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.play = Play().load(
|
||||||
|
self.play_source,
|
||||||
|
variable_manager=self.variable_manager,
|
||||||
|
loader=self.loader,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.runner = TaskQueueManager(
|
||||||
|
inventory=self.inventory,
|
||||||
|
variable_manager=self.variable_manager,
|
||||||
|
loader=self.loader,
|
||||||
|
options=self.options,
|
||||||
|
passwords=self.passwords,
|
||||||
|
stdout_callback=self.results_callback,
|
||||||
|
)
|
||||||
|
|
||||||
|
def check_module_args(self):
|
||||||
|
if self.module_name in C.MODULE_REQUIRE_ARGS and not self.module_args:
|
||||||
|
err = "No argument passed to '%s' module." % self.module_name
|
||||||
|
raise AnsibleError(err)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
if not self.inventory.list_hosts("all"):
|
||||||
|
raise AnsibleError("Inventory is empty.")
|
||||||
|
|
||||||
|
if not self.inventory.list_hosts(self.pattern):
|
||||||
|
raise AnsibleError(
|
||||||
|
"pattern: %s dose not match any hosts." % self.pattern)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def update_db_tasker(tasker_id, ext_code):
|
|
||||||
try:
|
try:
|
||||||
tasker = TaskRecord.objects.get(uuid=tasker_id)
|
self.runner.run(self.play)
|
||||||
tasker.end = timezone.now()
|
|
||||||
tasker.completed = True
|
|
||||||
tasker.exit_code = ext_code
|
|
||||||
tasker.save()
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Update Tasker Status into database error!, %s" % e.message)
|
pass
|
||||||
|
else:
|
||||||
def create_db_tasker(self, name, uuid):
|
return self.results_callback.result_q
|
||||||
try:
|
|
||||||
hosts = [host.get('name') for host in self.hosts]
|
|
||||||
tasker = TaskRecord(name=name, uuid=uuid, hosts=','.join(hosts), start=timezone.now())
|
|
||||||
tasker.save()
|
|
||||||
except Exception as e:
|
|
||||||
logger.error("Save Tasker to database error!, %s" % e.message)
|
|
||||||
|
|
||||||
def run(self, tasker_name, tasker_uuid):
|
|
||||||
"""执行ADHoc, 执行完后, 修改AnsiblePlay的状态为完成状态.
|
|
||||||
|
|
||||||
:param tasker_uuid <str> 用于标示此次task
|
|
||||||
"""
|
|
||||||
# 初始化callback插件,以及Tasker
|
|
||||||
|
|
||||||
self.create_db_tasker(tasker_name, tasker_uuid)
|
|
||||||
self.results_callback = CallbackModule(tasker_uuid)
|
|
||||||
|
|
||||||
tqm = None
|
|
||||||
# TODO:日志和结果分析
|
|
||||||
try:
|
|
||||||
tqm = TaskQueueManager(
|
|
||||||
inventory=self.inventory,
|
|
||||||
variable_manager=self.variable_manager,
|
|
||||||
loader=self.loader,
|
|
||||||
stdout_callback=self.results_callback,
|
|
||||||
options=self.options,
|
|
||||||
passwords=self.passwords
|
|
||||||
)
|
|
||||||
ext_code = tqm.run(self.play)
|
|
||||||
result = self.results_callback.results
|
|
||||||
|
|
||||||
# 任务运行结束, 标示任务完成
|
|
||||||
self.update_db_tasker(tasker_uuid, ext_code)
|
|
||||||
|
|
||||||
ret = json.dumps(result)
|
|
||||||
return ext_code, ret
|
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
if tqm:
|
if self.runner:
|
||||||
tqm.cleanup()
|
self.runner.cleanup()
|
||||||
|
if self.loader:
|
||||||
|
self.loader.cleanup_all_tmp_files()
|
||||||
|
|
||||||
|
|
||||||
def test_run():
|
def test_run():
|
||||||
conf = Options()
|
|
||||||
assets = [
|
assets = [
|
||||||
{
|
{
|
||||||
"name": "192.168.1.119",
|
"hostname": "192.168.152.129",
|
||||||
"ip": "192.168.1.119",
|
"ip": "192.168.152.129",
|
||||||
"port": "22",
|
"port": 22,
|
||||||
"username": "root",
|
"username": "root",
|
||||||
"password": "tongfang_test",
|
"password": "redhat",
|
||||||
"key": "asset_private_key",
|
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "192.168.232.135",
|
|
||||||
"ip": "192.168.232.135",
|
|
||||||
"port": "22",
|
|
||||||
"username": "yumaojun",
|
|
||||||
"password": "xxx",
|
|
||||||
"key": "asset_private_key",
|
|
||||||
"become": {"method": "sudo", "user": "root", "pass": "xxx"}
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
# 初始化Play
|
hoc = ADHocRunner(module_name='shell', module_args='ls', hosts=assets)
|
||||||
play_source = {
|
ret = hoc.run()
|
||||||
"name": "Ansible Play",
|
print(ret)
|
||||||
"hosts": "default",
|
|
||||||
"gather_facts": "no",
|
|
||||||
"tasks": [
|
|
||||||
dict(action=dict(module='ping')),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
hoc = ADHocRunner(conf, play_source, *assets)
|
|
||||||
uuid = "tasker-" + uuid4().hex
|
|
||||||
ext_code, result = hoc.run("test_task", uuid)
|
|
||||||
print(ext_code)
|
|
||||||
print(result)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
test_run()
|
test_run()
|
||||||
|
|
|
@ -8,54 +8,9 @@ from django.views.generic.detail import DetailView, SingleObjectMixin
|
||||||
|
|
||||||
from users.utils import AdminUserRequiredMixin
|
from users.utils import AdminUserRequiredMixin
|
||||||
from ops.utils.mixins import CreateSudoPrivilegesMixin, ListSudoPrivilegesMixin
|
from ops.utils.mixins import CreateSudoPrivilegesMixin, ListSudoPrivilegesMixin
|
||||||
from ops.models import *
|
from .models import Task
|
||||||
|
|
||||||
|
|
||||||
class SudoListView(AdminUserRequiredMixin, ListSudoPrivilegesMixin, ListView):
|
|
||||||
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
|
|
||||||
model = Sudo
|
|
||||||
context_object_name = 'sudos'
|
|
||||||
template_name = 'sudo/list.html'
|
|
||||||
|
|
||||||
|
|
||||||
class SudoCreateView(AdminUserRequiredMixin, CreateSudoPrivilegesMixin, CreateView):
|
|
||||||
model = Sudo
|
|
||||||
template_name = 'sudo/create.html'
|
|
||||||
|
|
||||||
|
|
||||||
class SudoUpdateView(AdminUserRequiredMixin, UpdateView):
|
|
||||||
model = Sudo
|
|
||||||
template_name = 'sudo/update.html'
|
|
||||||
|
|
||||||
|
|
||||||
class SudoDetailView(DetailView):
|
|
||||||
model = Sudo
|
|
||||||
context_object_name = 'sudo'
|
|
||||||
template_name = 'sudo/detail.html'
|
|
||||||
|
|
||||||
|
|
||||||
class CronListView(AdminUserRequiredMixin, ListView):
|
|
||||||
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
|
|
||||||
model = CronTable
|
|
||||||
context_object_name = 'crons'
|
|
||||||
template_name = 'cron/list.html'
|
|
||||||
|
|
||||||
|
|
||||||
class CronCreateView(AdminUserRequiredMixin, CreateView):
|
|
||||||
model = CronTable
|
|
||||||
template_name = 'cron/create.html'
|
|
||||||
|
|
||||||
|
|
||||||
class CronUpdateView(AdminUserRequiredMixin, UpdateView):
|
|
||||||
model = CronTable
|
|
||||||
template_name = 'cron/update.html'
|
|
||||||
|
|
||||||
|
|
||||||
class CronDetailView(DetailView):
|
|
||||||
model = CronTable
|
|
||||||
context_object_name = 'cron'
|
|
||||||
template_name = 'cron/detail.html'
|
|
||||||
|
|
||||||
class TaskListView(AdminUserRequiredMixin, ListView):
|
class TaskListView(AdminUserRequiredMixin, ListView):
|
||||||
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
|
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
|
||||||
model = Task
|
model = Task
|
||||||
|
|
Loading…
Reference in New Issue