1 6
import logging
2

3 6
from django.db import models
4 6
from django.db.models.query import QuerySet
5 6
from django.utils.translation import ugettext_lazy as _
6

7 6
import tzcron
8 6
import pytz
9 6
from dateutil.tz import datetime_exists
10

11 6
from cyborgbackup.api.versioning import reverse
12 6
from cyborgbackup.main.utils.common import copy_model_by_class
13 6
from cyborgbackup.main.models.base import PrimordialModel
14 6
from cyborgbackup.main.models.settings import Setting
15 6
from cyborgbackup.main.consumers import emit_channel_notification
16

17 6
from cyborgbackup.celery import app
18

19 6
logger = logging.getLogger('cyborgbackup.models.policy')
20

21 6
__all__ = ['Policy']
22

23

24 6
class PolicyFilterMethods(object):
25

26 6
    def enabled(self, enabled=True):
27 0
        return self.filter(enabled=enabled)
28

29 6
    def before(self, dt):
30 0
        return self.filter(next_run__lt=dt)
31

32 6
    def after(self, dt):
33 0
        return self.filter(next_run__gt=dt)
34

35 6
    def between(self, begin, end):
36 0
        return self.after(begin).before(end)
37

38

39 6
class PolicyQuerySet(PolicyFilterMethods, QuerySet):
40 6
    pass
41

42

43 6
class PolicyManager(PolicyFilterMethods, models.Manager):
44

45 6
    use_for_related_objects = True
46

47 6
    def get_queryset(self):
48 6
        return PolicyQuerySet(self.model, using=self._db)
49

50

51 6
class Policy(PrimordialModel):
52 6
    POLICY_TYPE_CHOICES = [
53
        ('rootfs', 'Root FileSystem'),      # Backup all / filesystem
54
        ('vm', 'Virtual Machine'),          # Backup Virtual Machine disk using snapshot
55
        ('mysql', 'MySQL'),                 # Backup MySQL Database
56
        ('postgresql', 'PostgreSQL'),       # Backup PostgreSQL
57
        ('piped', 'Piped Backup'),          # Backup using pipe program
58
        ('config', 'Only /etc'),            # Backup only /etc
59
        ('mail', 'Only mail directory'),    # Backup only mail directory
60
        ('folders', 'Specified folders'),   # Backup only specified folders
61
        ('proxmox', 'Proxmox')              # Backup only specified folders
62
    ]
63

64 6
    objects = PolicyManager()
65

66 6
    name = models.CharField(
67
        max_length=1024,
68
    )
69

70 6
    schedule = models.ForeignKey(
71
        'Schedule',
72
        related_name='policies',
73
        on_delete=models.CASCADE,
74
        null=False,
75
        editable=True,
76
    )
77

78 6
    repository = models.ForeignKey(
79
        'Repository',
80
        related_name='policies',
81
        on_delete=models.CASCADE,
82
        null=False,
83
        editable=True,
84
    )
85

86 6
    clients = models.ManyToManyField("client", blank=True)
87

88 6
    policy_type = models.CharField(
89
        max_length=20,
90
        choices=POLICY_TYPE_CHOICES,
91
        default='rootfs',
92
    )
93

94 6
    enabled = models.BooleanField(
95
        default=True
96
    )
97

98 6
    extra_vars = models.TextField(
99
        blank=True,
100
        default='',
101
    )
102

103 6
    mode_pull = models.BooleanField(
104
        default=False
105
    )
106

107 6
    keep_hourly = models.IntegerField(
108
        null=True,
109
        default=None,
110
        blank=True
111
    )
112

113 6
    keep_daily = models.IntegerField(
114
        null=True,
115
        default=None,
116
        blank=True
117
    )
118

119 6
    keep_weekly = models.IntegerField(
120
        null=True,
121
        default=None,
122
        blank=True
123
    )
124

125 6
    keep_monthly = models.IntegerField(
126
        null=True,
127
        default=None,
128
        blank=True
129
    )
130

131 6
    keep_yearly = models.IntegerField(
132
        null=True,
133
        default=None,
134
        blank=True
135
    )
136

137 6
    vmprovider = models.CharField(
138
        max_length=128,
139
        default='',
140
        blank=True
141
    )
142

143 6
    next_run = models.DateTimeField(
144
        null=True,
145
        default=None,
146
        editable=False,
147
        help_text=_("The next time that the scheduled action will run.")
148
    )
149

150 6
    posthook = models.CharField(
151
        max_length=128,
152
        default='',
153
        blank=True
154
    )
155

156 6
    prehook = models.CharField(
157
        max_length=128,
158
        default='',
159
        blank=True
160
    )
161

162 6
    def get_absolute_url(self, request=None):
163 6
        return reverse('api:policy_detail', kwargs={'pk': self.pk}, request=request)
164

165 6
    def get_ui_url(self):
166 0
        return "/#/policies/{}".format(self.pk)
167

168 6
    def update_computed_fields(self):
169 6
        future_rs = tzcron.Schedule(self.schedule.crontab, pytz.utc)
170 6
        next_run_actual = next(future_rs)
171

172 6
        if next_run_actual is not None:
173 6
            if not datetime_exists(next_run_actual):
174
                # skip imaginary dates, like 2:30 on DST boundaries
175 0
                next_run_actual = next(future_rs)
176 6
            next_run_actual = next_run_actual.astimezone(pytz.utc)
177

178 6
        self.next_run = next_run_actual
179 6
        emit_channel_notification('schedules-changed', dict(id=self.id, group_name='schedules'))
180

181 6
    def save(self, *args, **kwargs):
182 6
        self.update_computed_fields()
183
        # If update_fields has been specified, add our field names to it,
184
        # if it hasn't been specified, then we're just doing a normal save.
185 6
        super(Policy, self).save(*args, **kwargs)
186

187 6
    @classmethod
188
    def get_cache_key(self, key):
189 0
        return key
190

191 6
    @classmethod
192
    def get_cache_id_key(self, key):
193 0
        return '{}_ID'.format(key)
194

195 6
    @classmethod
196
    def _get_job_class(cls):
197 0
        from cyborgbackup.main.models.jobs import Job
198 0
        return Job
199

200 6
    def create_job(self, **kwargs):
201
        '''
202
        Create a new job based on this policy.
203
        '''
204

205 0
        job_class = self._get_job_class()
206 0
        fields = ('extra_vars', 'job_type')
207 0
        unallowed_fields = set(kwargs.keys()) - set(fields)
208 0
        if unallowed_fields:
209 0
            logger.warn('Fields {} are not allowed as overrides.'.format(unallowed_fields))
210 0
            map(kwargs.pop, unallowed_fields)
211

212 0
        try:
213 0
            setting = Setting.objects.get(key='cyborgbackup_catalog_enabled')
214 0
            if setting.value == 'True':
215 0
                catalog_enabled = True
216
            else:
217 0
                catalog_enabled = False
218 0
        except Exception:
219 0
            catalog_enabled = True
220

221 0
        try:
222 0
            setting = Setting.objects.get(key='cyborgbackup_auto_prune')
223 0
            if setting.value == 'True':
224 0
                auto_prune_enabled = True
225
            else:
226 0
                auto_prune_enabled = False
227 0
        except Exception:
228 0
            auto_prune_enabled = True
229

230 0
        app.send_task('cyborgbackup.main.tasks.cyborgbackup_notifier', args=('summary', self.pk))
231

232 0
        have_prune_info = (self.keep_hourly or self.keep_daily
233
                           or self.keep_weekly or self.keep_monthly or self.keep_yearly)
234

235 0
        jobs = []
236 0
        previous_job = None
237 0
        for client in self.clients.filter(enabled=True):
238 0
            job = copy_model_by_class(self, job_class, fields, kwargs)
239 0
            job.policy_id = self.pk
240 0
            job.client_id = client.pk
241 0
            job.status = 'waiting'
242 0
            job.name = "Backup Job {} {}".format(self.name, client.hostname)
243 0
            job.description = "Backup Job for Policy {} of client {}".format(self.name, client.hostname)
244 0
            job.save()
245 0
            if catalog_enabled:
246 0
                catalog_job = copy_model_by_class(self, job_class, fields, kwargs)
247 0
                catalog_job.policy_id = self.pk
248 0
                catalog_job.client_id = client.pk
249 0
                catalog_job.status = 'waiting'
250 0
                catalog_job.job_type = 'catalog'
251 0
                catalog_job.name = "Catalog Job {} {}".format(self.name, client.hostname)
252 0
                catalog_job.description = "Catalog Job for Policy {} of client {}".format(self.name, client.hostname)
253 0
                catalog_job.save()
254 0
                job.dependent_jobs = catalog_job
255 0
                job.save()
256 0
            if auto_prune_enabled:
257 0
                if have_prune_info:
258 0
                    prune_job = copy_model_by_class(self, job_class, fields, kwargs)
259 0
                    prune_job.policy_id = self.pk
260 0
                    prune_job.client_id = client.pk
261 0
                    prune_job.status = 'waiting'
262 0
                    prune_job.job_type = 'prune'
263 0
                    prune_job.name = "Prune Job {} {}".format(self.name, client.hostname)
264 0
                    prune_job.description = "Prune Job for Policy {} of client {}".format(self.name, client.hostname)
265 0
                    prune_job.save()
266 0
                    if catalog_enabled:
267 0
                        catalog_job.dependent_jobs = prune_job
268 0
                        catalog_job.save()
269
                    else:
270 0
                        job.dependent_jobs = prune_job
271 0
                        job.save()
272

273 0
            if auto_prune_enabled:
274 0
                if have_prune_info:
275 0
                    if previous_job:
276 0
                        previous_job.dependent_jobs = prune_job
277 0
                        previous_job.save()
278 0
                    previous_job = prune_job
279 0
            elif catalog_enabled:
280 0
                if previous_job:
281 0
                    previous_job.dependent_jobs = catalog_job
282 0
                    previous_job.save()
283 0
                previous_job = catalog_job
284
            else:
285 0
                if previous_job:
286 0
                    previous_job.dependent_jobs = job
287 0
                    previous_job.save()
288 0
                previous_job = job
289

290 0
            jobs.append(job)
291 0
        if len(jobs) > 0:
292 0
            jobs[0].status = 'new'
293 0
            jobs[0].save()
294 0
            return jobs[0]
295
        else:
296 0
            return None
297

298 6
    def create_restore_job(self, source_job, **kwargs):
299 0
        job_class = self._get_job_class()
300 0
        fields = ('extra_vars', )
301 0
        unallowed_fields = set(kwargs.keys()) - set(fields)
302 0
        if unallowed_fields:
303 0
            logger.warn('Fields {} are not allowed as overrides.'.format(unallowed_fields))
304 0
            map(kwargs.pop, unallowed_fields)
305

306 0
        job = copy_model_by_class(self, job_class, fields, kwargs)
307 0
        job.launch_type = 'manual'
308 0
        job.job_type = 'restore'
309 0
        job.policy_id = self.pk
310 0
        job.client_id = source_job.client.pk
311 0
        job.archive_name = source_job.archive_name
312 0
        job.status = 'waiting'
313 0
        job.name = "Restore Job {} {}".format(self.name, source_job.client.hostname)
314 0
        job.description = "Restore Job for Policy {} of client {}".format(self.name, source_job.client.hostname)
315 0
        job.save()
316 0
        return job

Read our documentation on viewing source code .

Loading