mirror of https://github.com/jumpserver/jumpserver
[Feature] 修改ansible和ops
parent
e57121a780
commit
2b3551f1fc
|
@ -44,28 +44,18 @@ class Asset(models.Model):
|
||||||
ip = models.GenericIPAddressField(max_length=32, verbose_name=_('IP'), db_index=True)
|
ip = models.GenericIPAddressField(max_length=32, verbose_name=_('IP'), db_index=True)
|
||||||
hostname = models.CharField(max_length=128, unique=True, verbose_name=_('Hostname'))
|
hostname = models.CharField(max_length=128, unique=True, verbose_name=_('Hostname'))
|
||||||
port = models.IntegerField(default=22, verbose_name=_('Port'))
|
port = models.IntegerField(default=22, verbose_name=_('Port'))
|
||||||
groups = models.ManyToManyField(AssetGroup, blank=True, related_name='assets',
|
groups = models.ManyToManyField(AssetGroup, blank=True, related_name='assets', verbose_name=_('Asset groups'))
|
||||||
verbose_name=_('Asset groups'))
|
admin_user = models.ForeignKey(AdminUser, null=True, blank=True, related_name='assets', on_delete=models.SET_NULL, verbose_name=_("Admin user"))
|
||||||
admin_user = models.ForeignKey(AdminUser, null=True, blank=True, related_name='assets',
|
system_users = models.ManyToManyField(SystemUser, blank=True, related_name='assets', verbose_name=_("System User"))
|
||||||
on_delete=models.SET_NULL, verbose_name=_("Admin user"))
|
idc = models.ForeignKey(IDC, blank=True, null=True, related_name='assets', on_delete=models.SET_NULL, verbose_name=_('IDC'),)
|
||||||
system_users = models.ManyToManyField(SystemUser, blank=True,
|
|
||||||
related_name='assets',
|
|
||||||
verbose_name=_("System User"))
|
|
||||||
idc = models.ForeignKey(IDC, blank=True, null=True, related_name='assets',
|
|
||||||
on_delete=models.SET_NULL, verbose_name=_('IDC'),)
|
|
||||||
is_active = models.BooleanField(default=True, verbose_name=_('Is active'))
|
is_active = models.BooleanField(default=True, verbose_name=_('Is active'))
|
||||||
type = models.CharField(choices=TYPE_CHOICES, max_length=16, blank=True, null=True,
|
type = models.CharField(choices=TYPE_CHOICES, max_length=16, blank=True, null=True, default='Server', verbose_name=_('Asset type'),)
|
||||||
default='Server', verbose_name=_('Asset type'),)
|
env = models.CharField(choices=ENV_CHOICES, max_length=8, blank=True, null=True, default='Prod', verbose_name=_('Asset environment'),)
|
||||||
env = models.CharField(choices=ENV_CHOICES, max_length=8, blank=True, null=True,
|
status = models.CharField(choices=STATUS_CHOICES, max_length=12, null=True, blank=True, default='In use', verbose_name=_('Asset status'))
|
||||||
default='Prod', verbose_name=_('Asset environment'),)
|
|
||||||
status = models.CharField(choices=STATUS_CHOICES, max_length=12, null=True, blank=True,
|
|
||||||
default='In use', verbose_name=_('Asset status'))
|
|
||||||
|
|
||||||
# Some information
|
# Some information
|
||||||
public_ip = models.GenericIPAddressField(max_length=32, blank=True,
|
public_ip = models.GenericIPAddressField(max_length=32, blank=True, null=True, verbose_name=_('Public IP'))
|
||||||
null=True, verbose_name=_('Public IP'))
|
remote_card_ip = models.CharField(max_length=16, null=True, blank=True, verbose_name=_('Remote control card IP'))
|
||||||
remote_card_ip = models.CharField(max_length=16, null=True, blank=True,
|
|
||||||
verbose_name=_('Remote control card IP'))
|
|
||||||
cabinet_no = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Cabinet number'))
|
cabinet_no = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Cabinet number'))
|
||||||
cabinet_pos = models.IntegerField(null=True, blank=True, verbose_name=_('Cabinet position'))
|
cabinet_pos = models.IntegerField(null=True, blank=True, verbose_name=_('Cabinet position'))
|
||||||
number = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Asset number'))
|
number = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Asset number'))
|
||||||
|
@ -114,22 +104,27 @@ class Asset(models.Model):
|
||||||
|
|
||||||
def _to_secret_json(self):
|
def _to_secret_json(self):
|
||||||
"""Ansible use it create inventory"""
|
"""Ansible use it create inventory"""
|
||||||
return {
|
data = {
|
||||||
'id': self.id,
|
'id': self.id,
|
||||||
'hostname': self.hostname,
|
'hostname': self.hostname,
|
||||||
'ip': self.ip,
|
'ip': self.ip,
|
||||||
'port': self.port,
|
'port': self.port,
|
||||||
'groups': [group.name for group in self.groups.all()],
|
'groups': [group.name for group in self.groups.all()],
|
||||||
'username': self.admin_user.username if self.admin_user else '',
|
|
||||||
'password': self.admin_user.password if self.admin_user else '',
|
|
||||||
'private_key': self.admin_user.private_key_file if self.admin_user else None,
|
|
||||||
'become': {
|
|
||||||
'method': self.admin_user.become_method,
|
|
||||||
'user': self.admin_user.become_user,
|
|
||||||
'pass': self.admin_user.become_pass,
|
|
||||||
} if self.admin_user and self.admin_user.become else {},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.admin_user:
|
||||||
|
data.update({
|
||||||
|
'username': self.admin_user.username,
|
||||||
|
'password': self.admin_user.password,
|
||||||
|
'private_key': self.admin_user.private_key_file,
|
||||||
|
'become': {
|
||||||
|
'method': self.admin_user.become_method,
|
||||||
|
'user': self.admin_user.become_user,
|
||||||
|
'pass': self.admin_user.become_pass,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return data
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ('ip', 'port')
|
unique_together = ('ip', 'port')
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,9 @@ def private_key_validator(value):
|
||||||
|
|
||||||
|
|
||||||
class AdminUser(models.Model):
|
class AdminUser(models.Model):
|
||||||
|
"""
|
||||||
|
Ansible use admin user as devops user to run adHoc and Playbook
|
||||||
|
"""
|
||||||
BECOME_METHOD_CHOICES = (
|
BECOME_METHOD_CHOICES = (
|
||||||
('sudo', 'sudo'),
|
('sudo', 'sudo'),
|
||||||
('su', 'su'),
|
('su', 'su'),
|
||||||
|
@ -35,24 +38,19 @@ class AdminUser(models.Model):
|
||||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
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(
|
_password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
|
||||||
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,])
|
||||||
_private_key = models.TextField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'),
|
|
||||||
validators=[private_key_validator,])
|
|
||||||
become = models.BooleanField(default=True)
|
become = models.BooleanField(default=True)
|
||||||
become_method = models.CharField(choices=BECOME_METHOD_CHOICES, default='sudo', max_length=4)
|
become_method = models.CharField(choices=BECOME_METHOD_CHOICES, default='sudo', max_length=4)
|
||||||
become_user = models.CharField(default='root', max_length=64)
|
become_user = models.CharField(default='root', max_length=64)
|
||||||
become_pass = models.CharField(default='', max_length=128)
|
become_pass = models.CharField(default='', max_length=128)
|
||||||
_public_key = models.TextField(
|
_public_key = models.TextField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
|
||||||
max_length=4096, blank=True, verbose_name=_('SSH public key'))
|
|
||||||
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(
|
created_by = models.CharField(max_length=32, null=True, verbose_name=_('Created by'))
|
||||||
max_length=32, null=True, verbose_name=_('Created by'))
|
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
__str__ = __unicode__
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def password(self):
|
def password(self):
|
||||||
|
@ -134,33 +132,22 @@ class SystemUser(models.Model):
|
||||||
('K', 'Public key'),
|
('K', 'Public key'),
|
||||||
)
|
)
|
||||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
name = models.CharField(max_length=128, unique=True,
|
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
|
||||||
verbose_name=_('Name'))
|
|
||||||
username = models.CharField(max_length=16, verbose_name=_('Username'))
|
username = models.CharField(max_length=16, verbose_name=_('Username'))
|
||||||
_password = models.CharField(
|
_password = models.CharField(max_length=256, blank=True, verbose_name=_('Password'))
|
||||||
max_length=256, blank=True, verbose_name=_('Password'))
|
protocol = models.CharField(max_length=16, choices=PROTOCOL_CHOICES, default='ssh', verbose_name=_('Protocol'))
|
||||||
protocol = models.CharField(
|
_private_key = models.TextField(max_length=8192, blank=True, verbose_name=_('SSH private key'))
|
||||||
max_length=16, choices=PROTOCOL_CHOICES, default='ssh', verbose_name=_('Protocol'))
|
_public_key = models.TextField(max_length=8192, blank=True, verbose_name=_('SSH public key'))
|
||||||
_private_key = models.TextField(
|
auth_method = models.CharField(choices=AUTH_METHOD_CHOICES, default='K', max_length=1, verbose_name=_('Auth method'))
|
||||||
max_length=8192, blank=True, verbose_name=_('SSH private key'))
|
|
||||||
_public_key = models.TextField(
|
|
||||||
max_length=8192, blank=True, verbose_name=_('SSH public key'))
|
|
||||||
auth_method = models.CharField(choices=AUTH_METHOD_CHOICES, default='K',
|
|
||||||
max_length=1, verbose_name=_('Auth method'))
|
|
||||||
auto_push = models.BooleanField(default=True, verbose_name=_('Auto push'))
|
auto_push = models.BooleanField(default=True, verbose_name=_('Auto push'))
|
||||||
sudo = models.TextField(
|
sudo = models.TextField(default='/sbin/ifconfig', verbose_name=_('Sudo'))
|
||||||
max_length=4096, default='/sbin/ifconfig', verbose_name=_('Sudo'))
|
shell = models.CharField(max_length=64, default='/bin/bash', verbose_name=_('Shell'))
|
||||||
shell = models.CharField(
|
|
||||||
max_length=64, default='/bin/bash', verbose_name=_('Shell'))
|
|
||||||
date_created = models.DateTimeField(auto_now_add=True)
|
date_created = models.DateTimeField(auto_now_add=True)
|
||||||
created_by = models.CharField(
|
created_by = models.CharField(max_length=32, blank=True, verbose_name=_('Created by'))
|
||||||
max_length=32, blank=True, verbose_name=_('Created by'))
|
comment = models.TextField(max_length=128, blank=True, verbose_name=_('Comment'))
|
||||||
comment = models.TextField(
|
|
||||||
max_length=128, blank=True, verbose_name=_('Comment'))
|
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
__str__ = __unicode__
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def password(self):
|
def password(self):
|
||||||
|
@ -182,9 +169,24 @@ class SystemUser(models.Model):
|
||||||
def private_key(self, private_key_raw):
|
def private_key(self, private_key_raw):
|
||||||
self._private_key = signer.sign(private_key_raw)
|
self._private_key = signer.sign(private_key_raw)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def private_key_file(self):
|
||||||
|
if not self.private_key:
|
||||||
|
return None
|
||||||
|
project_dir = settings.PROJECT_DIR
|
||||||
|
tmp_dir = os.path.join(project_dir, 'tmp')
|
||||||
|
key_name = md5(self._private_key.encode()).hexdigest()
|
||||||
|
key_path = os.path.join(tmp_dir, key_name)
|
||||||
|
if not os.path.exists(key_path):
|
||||||
|
self.private_key.write_private_key_file(key_path)
|
||||||
|
return key_path
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def public_key(self):
|
def public_key(self):
|
||||||
return signer.unsign(self._public_key)
|
if self._public_key:
|
||||||
|
return signer.unsign(self._public_key)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
@public_key.setter
|
@public_key.setter
|
||||||
def public_key(self, public_key_raw):
|
def public_key(self, public_key_raw):
|
||||||
|
@ -213,7 +215,8 @@ class SystemUser(models.Model):
|
||||||
'shell': self.shell,
|
'shell': self.shell,
|
||||||
'sudo': self.sudo,
|
'sudo': self.sudo,
|
||||||
'password': self.password,
|
'password': self.password,
|
||||||
'public_key': self.public_key
|
'public_key': self.public_key,
|
||||||
|
'private_key_file': self.private_key_file,
|
||||||
}
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -19,3 +19,32 @@ def test_admin_user_connective_manual(asset):
|
||||||
|
|
||||||
def get_assets_by_id_list(id_list):
|
def get_assets_by_id_list(id_list):
|
||||||
return Asset.objects.filter(id__in=id_list)
|
return Asset.objects.filter(id__in=id_list)
|
||||||
|
|
||||||
|
|
||||||
|
def get_assets_by_hostname_list(hostname_list):
|
||||||
|
return Asset.objects.filter(hostname__in=hostname_list)
|
||||||
|
|
||||||
|
|
||||||
|
def get_asset_admin_user(user, asset):
|
||||||
|
if user.is_superuser:
|
||||||
|
return asset.admin_user
|
||||||
|
else:
|
||||||
|
msg = "{} have no permission for admin user".format(user.username)
|
||||||
|
raise PermissionError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
def get_asset_system_user(user, asset, system_user_name):
|
||||||
|
from perms.utils import get_user_granted_assets
|
||||||
|
assets = get_user_granted_assets(user)
|
||||||
|
system_users = {system_user.name: system_user for system_user in assets.get(asset)}
|
||||||
|
|
||||||
|
if system_user_name in system_users:
|
||||||
|
return system_users[system_user_name]
|
||||||
|
else:
|
||||||
|
msg = "{} have no permission for {}".format(user.name, system_user_name)
|
||||||
|
raise PermissionError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
def get_assets_with_admin_by_hostname_list(hostname_list):
|
||||||
|
assets = Asset.objects.filter(hostname__in=hostname_list)
|
||||||
|
return [(asset, asset.admin_user) for asset in assets]
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
|
@ -9,65 +9,85 @@ class AdHocResultCallback(CallbackBase):
|
||||||
"""
|
"""
|
||||||
def __init__(self, display=None):
|
def __init__(self, display=None):
|
||||||
# result_raw example: {
|
# result_raw example: {
|
||||||
# "ok": {"hostname": []},
|
# "ok": {"hostname": [{"task_name": {},...],..},
|
||||||
# "failed": {"hostname": []},
|
# "failed": {"hostname": ["task_name": {}..], ..},
|
||||||
# "unreachable: {"hostname": []},
|
# "unreachable: {"hostname": ["task_name": {}, ..]},
|
||||||
# "skipped": {"hostname": []},
|
# "skipped": {"hostname": ["task_name": {}, ..], ..},
|
||||||
# }
|
# }
|
||||||
# results_summary example: {
|
# results_summary example: {
|
||||||
# "contacted": {"hostname",...},
|
# "contacted": {"hostname",...},
|
||||||
# "dark": {"hostname": ["error",...],},
|
# "dark": {"hostname": [{"task_name": "error"},...],},
|
||||||
# }
|
# }
|
||||||
self.results_raw = dict(ok={}, failed={}, unreachable={}, skipped={})
|
self.results_raw = dict(ok={}, failed={}, unreachable={}, skipped={})
|
||||||
self.results_summary = dict(contacted=set(), dark={})
|
self.results_summary = dict(contacted=set(), dark={})
|
||||||
super().__init__(display)
|
super().__init__(display)
|
||||||
|
|
||||||
def gather_result(self, t, host, res):
|
def gather_result(self, t, res):
|
||||||
if self.results_raw[t].get(host):
|
host = res._host.get_name()
|
||||||
self.results_raw[t][host].append(res)
|
task_name = res.task_name
|
||||||
else:
|
task_result = res._result
|
||||||
self.results_raw[t][host] = [res]
|
|
||||||
self.clean_result(t, host, res)
|
|
||||||
|
|
||||||
def clean_result(self, t, host, res):
|
if self.results_raw[t].get(host):
|
||||||
|
self.results_raw[t][host].append({task_name: task_result})
|
||||||
|
else:
|
||||||
|
self.results_raw[t][host] = [{task_name: task_result}]
|
||||||
|
self.clean_result(t, host, task_name, task_result)
|
||||||
|
|
||||||
|
def clean_result(self, t, host, task_name, task_result):
|
||||||
contacted = self.results_summary["contacted"]
|
contacted = self.results_summary["contacted"]
|
||||||
dark = self.results_summary["dark"]
|
dark = self.results_summary["dark"]
|
||||||
if t in ("ok", "skipped") and host not in dark:
|
if t in ("ok", "skipped") and host not in dark:
|
||||||
contacted.add(host)
|
contacted.add(host)
|
||||||
else:
|
else:
|
||||||
dark[host].append(res)
|
if dark.get(host):
|
||||||
|
dark[host].append({task_name: task_result})
|
||||||
|
else:
|
||||||
|
dark[host] = [{task_name: task_result}]
|
||||||
if host in contacted:
|
if host in contacted:
|
||||||
contacted.remove(dark)
|
contacted.remove(dark)
|
||||||
|
|
||||||
def runner_on_ok(self, host, res):
|
def v2_runner_on_failed(self, result, ignore_errors=False):
|
||||||
self.gather_result("ok", host, res)
|
self.gather_result("failed", result)
|
||||||
|
|
||||||
def runner_on_failed(self, host, res, ignore_errors=False):
|
def v2_runner_on_ok(self, result):
|
||||||
self.gather_result("failed", host, res)
|
self.gather_result("ok", result)
|
||||||
|
|
||||||
def runner_on_unreachable(self, host, res):
|
def v2_runner_on_skipped(self, result):
|
||||||
self.gather_result("unreachable", host, res)
|
self.gather_result("skipped", result)
|
||||||
|
|
||||||
def runner_on_skipped(self, host, item=None):
|
def v2_runner_on_unreachable(self, result):
|
||||||
self.gather_result("skipped", host, item)
|
self.gather_result("unreachable", result)
|
||||||
|
|
||||||
|
|
||||||
class CommandResultCallback(AdHocResultCallback):
|
class CommandResultCallback(AdHocResultCallback):
|
||||||
|
"""
|
||||||
|
Command result callback
|
||||||
|
"""
|
||||||
def __init__(self, display=None):
|
def __init__(self, display=None):
|
||||||
|
# results_command: {
|
||||||
|
# "cmd": "",
|
||||||
|
# "stderr": "",
|
||||||
|
# "stdout": "",
|
||||||
|
# "rc": 0,
|
||||||
|
# "delta": 0:0:0.123
|
||||||
|
# }
|
||||||
|
#
|
||||||
self.results_command = dict()
|
self.results_command = dict()
|
||||||
super().__init__(display)
|
super().__init__(display)
|
||||||
|
|
||||||
def gather_result(self, t, host, res):
|
def gather_result(self, t, res):
|
||||||
super().gather_result(t, host, res)
|
super().gather_result(t, res)
|
||||||
self.gather_cmd(t, host, res)
|
self.gather_cmd(t, res)
|
||||||
|
|
||||||
def gather_cmd(self, t, host, res):
|
def gather_cmd(self, t, res):
|
||||||
|
host = res._host.get_name()
|
||||||
cmd = {}
|
cmd = {}
|
||||||
if t == "ok":
|
if t == "ok":
|
||||||
cmd['cmd'] = res.get('cmd')
|
cmd['cmd'] = res._result.get('cmd')
|
||||||
cmd['stderr'] = res.get('stderr')
|
cmd['stderr'] = res._result.get('stderr')
|
||||||
cmd['stdout'] = res.get('stdout')
|
cmd['stdout'] = res._result.get('stdout')
|
||||||
cmd['rc'] = res.get('rc')
|
cmd['rc'] = res._result.get('rc')
|
||||||
|
cmd['delta'] = res._result.get('delta')
|
||||||
else:
|
else:
|
||||||
cmd['err'] = "Error: {}".format(res)
|
cmd['err'] = "Error: {}".format(res)
|
||||||
self.results_command[host] = cmd
|
self.results_command[host] = cmd
|
||||||
|
|
|
@ -170,6 +170,10 @@ class AdHocRunner:
|
||||||
"pattern: %s dose not match any hosts." % pattern
|
"pattern: %s dose not match any hosts." % pattern
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def set_option(self, k, v):
|
||||||
|
kwargs = {k: v}
|
||||||
|
self.options = self.options._replace(**kwargs)
|
||||||
|
|
||||||
def run(self, tasks, pattern, play_name='Ansible Ad-hoc', gather_facts='no'):
|
def run(self, tasks, pattern, play_name='Ansible Ad-hoc', gather_facts='no'):
|
||||||
"""
|
"""
|
||||||
:param tasks: [{'action': {'module': 'shell', 'args': 'ls'}, ...}, ]
|
:param tasks: [{'action': {'module': 'shell', 'args': 'ls'}, ...}, ]
|
||||||
|
|
|
@ -24,8 +24,8 @@ class TestAdHocRunner(unittest.TestCase):
|
||||||
|
|
||||||
def test_run(self):
|
def test_run(self):
|
||||||
tasks = [
|
tasks = [
|
||||||
{"action": {"module": "shell", "args": "ls"}},
|
{"action": {"module": "shell", "args": "ls"}, "name": "run_cmd"},
|
||||||
{"action": {"module": "shell", "args": "whoami"}},
|
{"action": {"module": "shell", "args": "whoami"}, "name": "run_whoami"},
|
||||||
]
|
]
|
||||||
ret = self.runner.run(tasks, "all")
|
ret = self.runner.run(tasks, "all")
|
||||||
print(ret.results_summary)
|
print(ret.results_summary)
|
||||||
|
@ -48,6 +48,7 @@ class TestCommandRunner(unittest.TestCase):
|
||||||
def test_execute(self):
|
def test_execute(self):
|
||||||
res = self.runner.execute('ls', 'all')
|
res = self.runner.execute('ls', 'all')
|
||||||
print(res.results_command)
|
print(res.results_command)
|
||||||
|
print(res.results_raw)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
|
|
||||||
from .hands import IsSuperUser
|
from .hands import IsSuperUser
|
||||||
from .models import Playbook
|
from .models import AdHoc
|
||||||
from .serializers import TaskSerializer
|
from .serializers import TaskSerializer
|
||||||
|
|
||||||
|
|
||||||
class TaskViewSet(viewsets.ModelViewSet):
|
class TaskViewSet(viewsets.ModelViewSet):
|
||||||
queryset = Playbook.objects.all()
|
queryset = AdHoc.objects.all()
|
||||||
serializer_class = TaskSerializer
|
serializer_class = TaskSerializer
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsSuperUser,)
|
||||||
|
|
||||||
|
|
|
@ -1,56 +1,76 @@
|
||||||
# ~*~ coding: utf-8 ~*~
|
# ~*~ coding: utf-8 ~*~
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from collections import OrderedDict
|
|
||||||
import json
|
import json
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
__all__ = ["Playbook"]
|
__all__ = ["AdHoc", "History"]
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class AdHoc(models.Model):
|
class AdHoc(models.Model):
|
||||||
uuid = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
name = models.CharField(max_length=128, blank=True, verbose_name=_('Name'))
|
name = models.CharField(max_length=128, blank=True, verbose_name=_('Name'))
|
||||||
tasks = models.TextField(verbose_name=_('Tasks')) # [{'name': 'task_name', 'module': '', 'args': ''}, ]
|
|
||||||
hosts = models.TextField(blank=True, null=True, verbose_name=_('Hosts')) # Asset inventory may be change
|
@property
|
||||||
pattern = models.CharField(max_length=64, default='all', verbose_name=_('Playbook run pattern'))
|
def short_id(self):
|
||||||
|
return str(self.id).split('-')[-1]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s" % self.name
|
return self.name
|
||||||
|
|
||||||
def get_hosts_mapped_assets(self):
|
|
||||||
from assets.utils import get_assets_by_id_list
|
class AdHocData(models.Model):
|
||||||
assets_id = [i for i in self.hosts.split(',')]
|
BECOME_METHOD_CHOICES = (
|
||||||
assets = get_assets_by_id_list(assets_id)
|
('sudo', 'sudo'),
|
||||||
return assets
|
('su', 'su'),
|
||||||
|
)
|
||||||
|
version = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
|
subject = models.ForeignKey(AdHoc, on_delete=models.CASCADE)
|
||||||
|
_tasks = models.TextField(verbose_name=_('Tasks')) # [{'name': 'task_name', 'action': {'module': '', 'args': ''}, 'other..': ''}, ]
|
||||||
|
_hosts = models.TextField(blank=True, verbose_name=_('Hosts')) # ['hostname1', 'hostname2']
|
||||||
|
run_as_admin = models.BooleanField(default=False, verbose_name=_('Run as admin'))
|
||||||
|
run_as = models.CharField(max_length=128, verbose_name=_("Run as"))
|
||||||
|
become = models.BooleanField(default=False, verbose_name=_("Become"))
|
||||||
|
become_method = models.CharField(choices=BECOME_METHOD_CHOICES, default='sudo', max_length=4)
|
||||||
|
become_user = models.CharField(default='root', max_length=64)
|
||||||
|
become_pass = models.CharField(default='', max_length=128)
|
||||||
|
pattern = models.CharField(max_length=64, default='', verbose_name=_('Pattern'))
|
||||||
|
created_by = models.CharField(max_length=64, verbose_name=_('Create by'))
|
||||||
|
date_created = models.DateTimeField(auto_created=True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def inventory(self):
|
def tasks(self):
|
||||||
return [asset._to_secret_json() for asset in self.get_hosts_mapped_assets()]
|
return json.loads(self._tasks)
|
||||||
|
|
||||||
|
@tasks.setter
|
||||||
|
def tasks(self, item):
|
||||||
|
self._tasks = json.dumps(item)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def module_args(self):
|
def hosts(self):
|
||||||
task_tuple = []
|
return json.loads(self._hosts)
|
||||||
for module, args in json.loads(self._modules_args, object_pairs_hook=OrderedDict).items():
|
|
||||||
task_tuple.append((module, args))
|
|
||||||
return task_tuple
|
|
||||||
|
|
||||||
@module_args.setter
|
@hosts.setter
|
||||||
def module_args(self, task_tuple):
|
def hosts(self, item):
|
||||||
module_args_ = OrderedDict({})
|
self._hosts = json.dumps(item)
|
||||||
for module, args in task_tuple:
|
|
||||||
module_args_[module] = args
|
@property
|
||||||
self._modules_args = json.dumps(module_args_)
|
def short_version(self):
|
||||||
|
return str(self.version).split('-')[-1]
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "{} of {}".format(self.subject.name, self.short_version)
|
||||||
|
|
||||||
|
|
||||||
class History(models.Model):
|
class AdHocHistory(models.Model):
|
||||||
uuid = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
uuid = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
|
adhoc = models.ForeignKey(AdHocData, on_delete=models.CASCADE)
|
||||||
date_start = models.DateTimeField(auto_now_add=True, verbose_name=_('Start time'))
|
date_start = models.DateTimeField(auto_now_add=True, verbose_name=_('Start time'))
|
||||||
date_finished = models.DateTimeField(blank=True, null=True, verbose_name=_('End time'))
|
date_finished = models.DateTimeField(blank=True, null=True, verbose_name=_('End time'))
|
||||||
timedelta = models.FloatField(default=0.0, verbose_name=_('Time'), null=True)
|
timedelta = models.FloatField(default=0.0, verbose_name=_('Time'), null=True)
|
||||||
|
@ -58,3 +78,10 @@ class History(models.Model):
|
||||||
is_success = models.BooleanField(default=False, verbose_name=_('Is success'))
|
is_success = models.BooleanField(default=False, verbose_name=_('Is success'))
|
||||||
result = models.TextField(blank=True, null=True, verbose_name=_('Playbook raw result'))
|
result = models.TextField(blank=True, null=True, verbose_name=_('Playbook raw result'))
|
||||||
summary = models.TextField(blank=True, null=True, verbose_name=_('Playbook summary'))
|
summary = models.TextField(blank=True, null=True, verbose_name=_('Playbook summary'))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def short_id(self):
|
||||||
|
return str(self.id).split('-')[-1]
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.short_id
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from .models import Playbook
|
from .models import AdHoc
|
||||||
|
|
||||||
|
|
||||||
class TaskSerializer(serializers.ModelSerializer):
|
class TaskSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Playbook
|
model = AdHoc
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -88,3 +88,14 @@ def is_uuid(s):
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def asset_to_dict(asset):
|
||||||
|
return asset.to_json()
|
||||||
|
|
||||||
|
|
||||||
|
def asset_to_dict_with_credential(asset):
|
||||||
|
return asset._to_secret_json()
|
||||||
|
|
||||||
|
|
||||||
|
def system_user_to_dict_with_credential(system_user):
|
||||||
|
return system_user._to_secret_json()
|
||||||
|
|
|
@ -9,13 +9,13 @@ from django.views.generic import ListView, DetailView, View
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.shortcuts import redirect, reverse
|
from django.shortcuts import redirect, reverse
|
||||||
|
|
||||||
from .models import Playbook
|
from .models import AdHoc, AdHocData, AdHocHistory
|
||||||
from ops.tasks import rerun_task
|
from ops.tasks import rerun_task
|
||||||
|
|
||||||
|
|
||||||
class TaskListView(ListView):
|
class TaskListView(ListView):
|
||||||
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
|
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
|
||||||
model = Playbook
|
model = AdHoc
|
||||||
ordering = ('-date_start',)
|
ordering = ('-date_start',)
|
||||||
context_object_name = 'task_list'
|
context_object_name = 'task_list'
|
||||||
template_name = 'ops/task_list.html'
|
template_name = 'ops/task_list.html'
|
||||||
|
@ -63,7 +63,7 @@ class TaskListView(ListView):
|
||||||
|
|
||||||
|
|
||||||
class TaskDetailView(DetailView):
|
class TaskDetailView(DetailView):
|
||||||
model = Playbook
|
model = AdHocHistory
|
||||||
template_name = 'ops/task_detail.html'
|
template_name = 'ops/task_detail.html'
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
|
|
|
@ -53,79 +53,6 @@ def get_user_group_granted_assets(user_group):
|
||||||
return assets
|
return assets
|
||||||
|
|
||||||
|
|
||||||
# def get_user_granted_asset_groups_direct(user):
|
|
||||||
# """Return asset groups granted of the user direct nor inherit from user group
|
|
||||||
#
|
|
||||||
# :param user: Instance of :class: ``User``
|
|
||||||
# :return: {asset_group: {system_user1, },
|
|
||||||
# asset_group2: {system_user1, system_user2]}
|
|
||||||
# """
|
|
||||||
# asset_groups = {}
|
|
||||||
# asset_permissions_direct = user.asset_permissions.all()
|
|
||||||
#
|
|
||||||
# for asset_permission in asset_permissions_direct:
|
|
||||||
# if not asset_permission.is_valid:
|
|
||||||
# continue
|
|
||||||
# for asset_group in asset_permission.asset_groups.all():
|
|
||||||
# if asset_group in asset_groups:
|
|
||||||
# asset_groups[asset_group] |= set(asset_permission.system_users.all())
|
|
||||||
# else:
|
|
||||||
# setattr(asset_group, 'inherited', False)
|
|
||||||
# asset_groups[asset_group] = set(asset_permission.system_users.all())
|
|
||||||
#
|
|
||||||
# return asset_groups
|
|
||||||
|
|
||||||
|
|
||||||
# def get_user_granted_asset_groups_inherit_from_user_groups(user):
|
|
||||||
# """Return asset groups granted of the user and inherit from user group
|
|
||||||
#
|
|
||||||
# :param user: Instance of :class: ``User``
|
|
||||||
# :return: {asset_group: {system_user1, },
|
|
||||||
# asset_group2: {system_user1, system_user2]}
|
|
||||||
# """
|
|
||||||
# asset_groups = {}
|
|
||||||
# user_groups = user.groups.all()
|
|
||||||
# asset_permissions = set()
|
|
||||||
#
|
|
||||||
# # Get asset permission list of user groups for this user
|
|
||||||
# for user_group in user_groups:
|
|
||||||
# asset_permissions |= set(user_group.asset_permissions.all())
|
|
||||||
#
|
|
||||||
# # Get asset groups granted from user groups
|
|
||||||
# for asset_permission in asset_permissions:
|
|
||||||
# if not asset_permission.is_valid:
|
|
||||||
# continue
|
|
||||||
# for asset_group in asset_permission.asset_groups.all():
|
|
||||||
# if asset_group in asset_groups:
|
|
||||||
# asset_groups[asset_group] |= set(asset_permission.system_users.all())
|
|
||||||
# else:
|
|
||||||
# setattr(asset_group, 'inherited', True)
|
|
||||||
# asset_groups[asset_group] = set(asset_permission.system_users.all())
|
|
||||||
#
|
|
||||||
# return asset_groups
|
|
||||||
|
|
||||||
|
|
||||||
# def get_user_granted_asset_groups(user):
|
|
||||||
# """Get user granted asset groups all, include direct and inherit from user group
|
|
||||||
#
|
|
||||||
# :param user: Instance of :class: ``User``
|
|
||||||
# :return: {asset_group1: {system_user1, system_user2}, asset_group2: {...}}
|
|
||||||
# """
|
|
||||||
#
|
|
||||||
# asset_groups_inherit_from_user_groups = \
|
|
||||||
# get_user_granted_asset_groups_inherit_from_user_groups(user)
|
|
||||||
# asset_groups_direct = get_user_granted_asset_groups_direct(user)
|
|
||||||
# asset_groups = asset_groups_inherit_from_user_groups
|
|
||||||
#
|
|
||||||
# # Merge direct granted and inherit from user group
|
|
||||||
# for asset_group, system_users in asset_groups_direct.items():
|
|
||||||
# if asset_group in asset_groups:
|
|
||||||
# asset_groups[asset_group] |= asset_groups_direct[asset_group]
|
|
||||||
# else:
|
|
||||||
# asset_groups[asset_group] = asset_groups_direct[asset_group]
|
|
||||||
# return asset_groups
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_granted_assets_direct(user):
|
def get_user_granted_assets_direct(user):
|
||||||
"""Return assets granted of the user directly
|
"""Return assets granted of the user directly
|
||||||
|
|
||||||
|
@ -225,6 +152,22 @@ def get_user_asset_permissions(user):
|
||||||
return direct_permissions | user_group_permissions
|
return direct_permissions | user_group_permissions
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_granted_system_users(user):
|
||||||
|
"""
|
||||||
|
:param user: the user
|
||||||
|
:return: {"system_user": ["asset", "asset1"], "system_user": []}
|
||||||
|
"""
|
||||||
|
assets = get_user_granted_assets(user)
|
||||||
|
system_users_dict = {}
|
||||||
|
for asset, system_users in assets.items():
|
||||||
|
for system_user in system_users:
|
||||||
|
if system_user in system_users_dict:
|
||||||
|
system_users_dict[system_user].append(asset)
|
||||||
|
else:
|
||||||
|
system_users_dict[system_user] = [asset]
|
||||||
|
return system_users_dict
|
||||||
|
|
||||||
|
|
||||||
def get_user_groups_granted_in_asset(asset):
|
def get_user_groups_granted_in_asset(asset):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue