models.py 7.3 KB
Newer Older
baltery's avatar
baltery 已提交
1 2 3
# ~*~ coding: utf-8 ~*~

import logging
baltery's avatar
baltery 已提交
4
import json
baltery's avatar
baltery 已提交
5
import uuid
baltery's avatar
baltery 已提交
6 7 8

from django.db import models
from django.utils.translation import ugettext_lazy as _
baltery's avatar
baltery 已提交
9

baltery's avatar
baltery 已提交
10
from common.utils import signer
baltery's avatar
baltery 已提交
11

baltery's avatar
baltery 已提交
12
__all__ = ["Task", "AdHoc", "AdHocRunHistory"]
baltery's avatar
baltery 已提交
13 14 15 16 17


logger = logging.getLogger(__name__)


baltery's avatar
baltery 已提交
18 19 20 21 22
class Task(models.Model):
    """
    This task is different ansible task, Task like 'push system user', 'get asset info' ..
    One task can have some versions of adhoc, run a task only run the latest version adhoc
    """
baltery's avatar
baltery 已提交
23
    id = models.UUIDField(default=uuid.uuid4, primary_key=True)
baltery's avatar
baltery 已提交
24
    name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
baltery's avatar
baltery 已提交
25
    is_deleted = models.BooleanField(default=False)
26
    created_by = models.CharField(max_length=128, blank=True, default='')
baltery's avatar
baltery 已提交
27
    date_created = models.DateTimeField(auto_now_add=True)
baltery's avatar
baltery 已提交
28
    __latest_adhoc = None
baltery's avatar
baltery 已提交
29 30 31 32

    @property
    def short_id(self):
        return str(self.id).split('-')[-1]
baltery's avatar
baltery 已提交
33

baltery's avatar
baltery 已提交
34 35 36 37 38
    @property
    def latest_adhoc(self):
        if not self.__latest_adhoc:
            self.__latest_adhoc = self.get_latest_adhoc()
        return self.__latest_adhoc
baltery's avatar
baltery 已提交
39

baltery's avatar
baltery 已提交
40 41 42
    @latest_adhoc.setter
    def latest_adhoc(self, item):
        self.__latest_adhoc = item
baltery's avatar
baltery 已提交
43

baltery's avatar
baltery 已提交
44 45 46 47 48 49
    @property
    def latest_history(self):
        try:
            return self.history.all().latest()
        except AdHocRunHistory.DoesNotExist:
            return None
baltery's avatar
baltery 已提交
50

baltery's avatar
baltery 已提交
51 52 53 54 55
    def get_latest_adhoc(self):
        try:
            return self.adhoc.all().latest()
        except AdHoc.DoesNotExist:
            return None
baltery's avatar
baltery 已提交
56

baltery's avatar
baltery 已提交
57 58 59 60 61 62
    @property
    def history_summary(self):
        history = self.get_run_history()
        total = len(history)
        success = len([history for history in history if history.is_success])
        failed = len([history for history in history if not history.is_success])
baltery's avatar
baltery 已提交
63
        return {'total': total, 'success': success, 'failed': failed}
baltery's avatar
baltery 已提交
64

baltery's avatar
baltery 已提交
65 66 67 68 69 70 71 72 73 74 75 76
    def get_run_history(self):
        return self.history.all()

    def run(self):
        if self.latest_adhoc:
            return self.latest_adhoc.run()
        else:
            return {'error': 'No adhoc'}

    def __str__(self):
        return self.name

baltery's avatar
baltery 已提交
77 78
    class Meta:
        db_table = 'ops_task'
baltery's avatar
baltery 已提交
79
        get_latest_by = 'date_created'
baltery's avatar
baltery 已提交
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97


class AdHoc(models.Model):
    """
    task: A task reference
    _tasks: [{'name': 'task_name', 'action': {'module': '', 'args': ''}, 'other..': ''}, ]
    _options: ansible options, more see ops.ansible.runner.Options
    _hosts: ["hostname1", "hostname2"], hostname must be unique key of cmdb
    run_as_admin: if true, then need get every host admin user run it, because every host may be have different admin user, so we choise host level
    run_as: if not run as admin, it run it as a system/common user from cmdb
    _become: May be using become [sudo, su] options. {method: "sudo", user: "user", pass: "pass"]
    pattern: Even if we set _hosts, We only use that to make inventory, We also can set `patter` to run task on match hosts
    """
    id = models.UUIDField(default=uuid.uuid4, primary_key=True)
    task = models.ForeignKey(Task, related_name='adhoc', on_delete=models.CASCADE)
    _tasks = models.TextField(verbose_name=_('Tasks'))
    pattern = models.CharField(max_length=64, default='', verbose_name=_('Pattern'))
    _options = models.CharField(max_length=1024, default='', verbose_name=_('Options'))
baltery's avatar
baltery 已提交
98 99
    _hosts = models.TextField(blank=True, verbose_name=_('Hosts'))  # ['hostname1', 'hostname2']
    run_as_admin = models.BooleanField(default=False, verbose_name=_('Run as admin'))
baltery's avatar
baltery 已提交
100 101 102
    run_as = models.CharField(max_length=128, default='', verbose_name=_("Run as"))
    _become = models.CharField(max_length=1024, default='', verbose_name=_("Become"))
    created_by = models.CharField(max_length=64, default='', verbose_name=_('Create by'))
103
    date_created = models.DateTimeField(auto_now_add=True)
baltery's avatar
baltery 已提交
104

baltery's avatar
baltery 已提交
105
    @property
baltery's avatar
baltery 已提交
106 107 108 109 110
    def tasks(self):
        return json.loads(self._tasks)

    @tasks.setter
    def tasks(self, item):
baltery's avatar
baltery 已提交
111 112 113 114
        if item and isinstance(item, list):
            self._tasks = json.dumps(item)
        else:
            raise SyntaxError('Tasks should be a list')
baltery's avatar
baltery 已提交
115 116

    @property
baltery's avatar
baltery 已提交
117 118
    def hosts(self):
        return json.loads(self._hosts)
baltery's avatar
baltery 已提交
119

baltery's avatar
baltery 已提交
120 121 122
    @hosts.setter
    def hosts(self, item):
        self._hosts = json.dumps(item)
baltery's avatar
baltery 已提交
123

baltery's avatar
baltery 已提交
124
    @property
baltery's avatar
baltery 已提交
125 126 127 128 129 130
    def become(self):
        if self._become:
            return json.loads(signer.unsign(self._become))
        else:
            return {}

baltery's avatar
baltery 已提交
131 132 133 134
    def run(self):
        from .utils import run_adhoc_object
        return run_adhoc_object(self, **self.options)

baltery's avatar
baltery 已提交
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
    @become.setter
    def become(self, item):
        """
        :param item:  {
            method: "sudo",
            user: "user",
            pass: "pass",
        }
        :return:
        """
        self._become = signer.sign(json.dumps(item))

    @property
    def options(self):
        if self._options:
            return json.loads(self._options)
        else:
            return {}
baltery's avatar
baltery 已提交
153

baltery's avatar
baltery 已提交
154 155 156
    @options.setter
    def options(self, item):
        self._options = json.dumps(item)
baltery's avatar
baltery 已提交
157

baltery's avatar
baltery 已提交
158
    @property
baltery's avatar
baltery 已提交
159 160
    def short_id(self):
        return str(self.id).split('-')[-1]
baltery's avatar
baltery 已提交
161

baltery's avatar
baltery 已提交
162 163 164 165 166 167
    @property
    def latest_history(self):
        try:
            return self.history.all().latest()
        except AdHocRunHistory.DoesNotExist:
            return None
baltery's avatar
baltery 已提交
168

baltery's avatar
baltery 已提交
169
    def __str__(self):
baltery's avatar
baltery 已提交
170
        return "{} of {}".format(self.task.name, self.short_id)
baltery's avatar
baltery 已提交
171

baltery's avatar
baltery 已提交
172 173 174 175 176 177 178 179 180 181 182 183
    def __eq__(self, other):
        if not isinstance(other, self.__class__):
            return False
        fields_check = []
        for field in self.__class__._meta.fields:
            if field.name not in ['id', 'date_created']:
                fields_check.append(field)
        for field in fields_check:
            if getattr(self, field.name) != getattr(other, field.name):
                return False
        return True

baltery's avatar
baltery 已提交
184
    class Meta:
baltery's avatar
baltery 已提交
185
        db_table = "ops_adhoc"
baltery's avatar
baltery 已提交
186
        get_latest_by = 'date_created'
baltery's avatar
baltery 已提交
187

baltery's avatar
baltery 已提交
188

baltery's avatar
baltery 已提交
189
class AdHocRunHistory(models.Model):
baltery's avatar
baltery 已提交
190 191 192 193
    """
    AdHoc running history.
    """
    id = models.UUIDField(default=uuid.uuid4, primary_key=True)
baltery's avatar
baltery 已提交
194 195
    task = models.ForeignKey(Task, related_name='history', on_delete=models.SET_NULL, null=True)
    adhoc = models.ForeignKey(AdHoc, related_name='history', on_delete=models.SET_NULL, null=True)
baltery's avatar
baltery 已提交
196 197 198 199 200
    date_start = models.DateTimeField(auto_now_add=True, verbose_name=_('Start time'))
    date_finished = models.DateTimeField(blank=True, null=True, verbose_name=_('End time'))
    timedelta = models.FloatField(default=0.0, verbose_name=_('Time'), null=True)
    is_finished = models.BooleanField(default=False, verbose_name=_('Is finished'))
    is_success = models.BooleanField(default=False, verbose_name=_('Is success'))
baltery's avatar
baltery 已提交
201 202
    _result = models.TextField(blank=True, null=True, verbose_name=_('Adhoc raw result'))
    _summary = models.TextField(blank=True, null=True, verbose_name=_('Adhoc result summary'))
baltery's avatar
baltery 已提交
203 204 205 206 207

    @property
    def short_id(self):
        return str(self.id).split('-')[-1]

baltery's avatar
baltery 已提交
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
    @property
    def result(self):
        return json.loads(self._result)

    @result.setter
    def result(self, item):
        self._result = json.dumps(item)

    @property
    def summary(self):
        return json.loads(self._summary)

    @summary.setter
    def summary(self, item):
        self._summary = json.dumps(item)

baltery's avatar
baltery 已提交
224 225
    def __str__(self):
        return self.short_id
baltery's avatar
baltery 已提交
226 227 228

    class Meta:
        db_table = "ops_adhoc_history"
baltery's avatar
baltery 已提交
229
        get_latest_by = 'date_start'