1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
5 Copyright (C) 2011-2016 Planets Communications B.V.
6 Copyright (C) 2013-2019 Bareos GmbH & Co. KG
7
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
11 in the file LICENSE.
12
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Affero General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 02110-1301, USA.
22 */
23 /*
24 * Matthew Ife Matthew.Ife@ukfast.co.uk
25 */
26 /**
27 * @file
28 * Quota processing routines.
29 */
30
31 #include "include/bareos.h"
32 #include "dird.h"
33 #include "dird/jcr_private.h"
34
35 namespace directordaemon {
36
37 #define debuglevel 100
38 /**
39 * This function returns the total number of bytes difference remaining before
40 * going over quota. Returns: unsigned long long containing remaining bytes
41 * before going over quota. 0 if over quota
42 */
FetchRemainingQuotas(JobControlRecord * jcr)43 uint64_t FetchRemainingQuotas(JobControlRecord* jcr)
44 {
45 uint64_t remaining = 0;
46 uint64_t now = (uint64_t)time(NULL);
47
48 /*
49 * Quotas not being used ?
50 */
51 if (!jcr->impl->HasQuota) { return 0; }
52
53 Dmsg2(debuglevel, "JobSumTotalBytes for JobId %d is %llu\n", jcr->JobId,
54 jcr->impl->jr.JobSumTotalBytes);
55 Dmsg1(debuglevel, "Fetching remaining quotas for JobId %d\n", jcr->JobId);
56
57 /*
58 * If strict quotas on and grace exceeded, enforce the softquota
59 */
60 if (jcr->impl->res.client->StrictQuotas &&
61 jcr->impl->res.client->SoftQuota &&
62 jcr->impl->res.client->GraceTime > 0 &&
63 (now - (uint64_t)jcr->impl->res.client->GraceTime) >
64 (uint64_t)jcr->impl->res.client->SoftQuotaGracePeriod &&
65 jcr->impl->res.client->SoftQuotaGracePeriod > 0) {
66 remaining =
67 jcr->impl->res.client->SoftQuota - jcr->impl->jr.JobSumTotalBytes;
68 } else if (!jcr->impl->res.client->StrictQuotas &&
69 jcr->impl->res.client->SoftQuota &&
70 jcr->impl->res.client->GraceTime > 0 &&
71 jcr->impl->res.client->SoftQuotaGracePeriod > 0 &&
72 (now - (uint64_t)jcr->impl->res.client->GraceTime) >
73 (uint64_t)jcr->impl->res.client->SoftQuotaGracePeriod) {
74 /*
75 * If strict quotas turned off and grace exceeded use the last known limit
76 */
77 if (jcr->impl->res.client->QuotaLimit >
78 jcr->impl->res.client->SoftQuota) {
79 remaining =
80 jcr->impl->res.client->QuotaLimit - jcr->impl->jr.JobSumTotalBytes;
81 } else {
82 remaining =
83 jcr->impl->res.client->SoftQuota - jcr->impl->jr.JobSumTotalBytes;
84 }
85 } else if (jcr->impl->jr.JobSumTotalBytes <
86 jcr->impl->res.client->HardQuota) {
87 /*
88 * If within the hardquota.
89 */
90 remaining =
91 jcr->impl->res.client->HardQuota - jcr->impl->jr.JobSumTotalBytes;
92 } else {
93 /*
94 * If just over quota return 0. This shouldnt happen because quotas
95 * are checked properly prior to this code.
96 */
97 remaining = 0;
98 }
99
100 Dmsg4(debuglevel,
101 "Quota for %s is %llu. Remainder is %llu, QuotaLimit: %llu\n",
102 jcr->impl->jr.Name, jcr->impl->jr.JobSumTotalBytes, remaining,
103 jcr->impl->res.client->QuotaLimit);
104
105 return remaining;
106 }
107
108 /**
109 * This function returns a truth value depending on the state of hard quotas.
110 * The function compares the total jobbytes against the hard quota.
111 * If the value is true, the quota is reached and termination of the job should
112 * occur.
113 *
114 * Returns: true on reaching quota
115 * false on not reaching quota.
116 */
CheckHardquotas(JobControlRecord * jcr)117 bool CheckHardquotas(JobControlRecord* jcr)
118 {
119 bool retval = false;
120
121 /*
122 * Do not check if hardquota is not set
123 */
124 if (jcr->impl->res.client->HardQuota == 0) { goto bail_out; }
125
126 Dmsg1(debuglevel, "Checking hard quotas for JobId %d\n", jcr->JobId);
127 if (!jcr->impl->HasQuota) {
128 if (jcr->impl->res.client->QuotaIncludeFailedJobs) {
129 if (!jcr->db->get_quota_jobbytes(jcr, &jcr->impl->jr,
130 jcr->impl->res.client->JobRetention)) {
131 Jmsg(jcr, M_WARNING, 0, _("Error getting Quota value: ERR=%s"),
132 jcr->db->strerror());
133 goto bail_out;
134 }
135 } else {
136 if (!jcr->db->get_quota_jobbytes_nofailed(
137 jcr, &jcr->impl->jr, jcr->impl->res.client->JobRetention)) {
138 Jmsg(jcr, M_WARNING, 0, _("Error getting Quota value: ERR=%s"),
139 jcr->db->strerror());
140 goto bail_out;
141 }
142 }
143 jcr->impl->HasQuota = true;
144 }
145
146 if (jcr->impl->jr.JobSumTotalBytes > jcr->impl->res.client->HardQuota) {
147 retval = true;
148 goto bail_out;
149 }
150
151 Dmsg2(debuglevel, "Quota for JobID: %d is %llu\n", jcr->impl->jr.JobId,
152 jcr->impl->jr.JobSumTotalBytes);
153
154 bail_out:
155 return retval;
156 }
157
158 /**
159 * This function returns a truth value depending on the state of soft quotas.
160 * The function compares the total jobbytes against the soft quota.
161 *
162 * If the quotas are not strict (the default) it checks the jobbytes value
163 * against the quota limit previously when running in burst mode during the
164 * grace period.
165 *
166 * It checks if we have exceeded our grace time.
167 *
168 * If the value is true, the quota is reached and termination of the job should
169 * occur.
170 *
171 * Returns: true on reaching soft quota
172 * false on not reaching soft quota.
173 */
CheckSoftquotas(JobControlRecord * jcr)174 bool CheckSoftquotas(JobControlRecord* jcr)
175 {
176 bool retval = false;
177 uint64_t now = (uint64_t)time(NULL);
178
179 /*
180 * Do not check if the softquota is not set
181 */
182 if (jcr->impl->res.client->SoftQuota == 0) { goto bail_out; }
183
184 Dmsg1(debuglevel, "Checking soft quotas for JobId %d\n", jcr->JobId);
185 if (!jcr->impl->HasQuota) {
186 if (jcr->impl->res.client->QuotaIncludeFailedJobs) {
187 if (!jcr->db->get_quota_jobbytes(jcr, &jcr->impl->jr,
188 jcr->impl->res.client->JobRetention)) {
189 Jmsg(jcr, M_WARNING, 0, _("Error getting Quota value: ERR=%s"),
190 jcr->db->strerror());
191 goto bail_out;
192 }
193 Dmsg0(debuglevel, "Quota Includes Failed Jobs\n");
194 } else {
195 if (!jcr->db->get_quota_jobbytes_nofailed(
196 jcr, &jcr->impl->jr, jcr->impl->res.client->JobRetention)) {
197 Jmsg(jcr, M_WARNING, 0, _("Error getting Quota value: ERR=%s"),
198 jcr->db->strerror());
199 goto bail_out;
200 }
201 Jmsg(jcr, M_INFO, 0, _("Quota does NOT include Failed Jobs\n"));
202 }
203 jcr->impl->HasQuota = true;
204 }
205
206 Dmsg2(debuglevel, "Quota for %s is %llu\n", jcr->impl->jr.Name,
207 jcr->impl->jr.JobSumTotalBytes);
208 Dmsg2(debuglevel, "QuotaLimit for %s is %llu\n", jcr->impl->jr.Name,
209 jcr->impl->res.client->QuotaLimit);
210 Dmsg2(debuglevel, "HardQuota for %s is %llu\n", jcr->impl->jr.Name,
211 jcr->impl->res.client->HardQuota);
212 Dmsg2(debuglevel, "SoftQuota for %s is %llu\n", jcr->impl->jr.Name,
213 jcr->impl->res.client->SoftQuota);
214 Dmsg2(debuglevel, "SoftQuota Grace Period for %s is %d\n",
215 jcr->impl->jr.Name, jcr->impl->res.client->SoftQuotaGracePeriod);
216 Dmsg2(debuglevel, "SoftQuota Grace Time for %s is %d\n", jcr->impl->jr.Name,
217 jcr->impl->res.client->GraceTime);
218
219 if ((jcr->impl->jr.JobSumTotalBytes + jcr->impl->SDJobBytes) >
220 jcr->impl->res.client->SoftQuota) {
221 /*
222 * Only warn once about softquotas in the job
223 * Check if gracetime has been set
224 */
225 if (jcr->impl->res.client->GraceTime == 0 &&
226 jcr->impl->res.client->SoftQuotaGracePeriod) {
227 Dmsg1(debuglevel, "UpdateQuotaGracetime: %d\n", now);
228 if (!jcr->db->UpdateQuotaGracetime(jcr, &jcr->impl->jr)) {
229 Jmsg(jcr, M_WARNING, 0, _("Error setting Quota gracetime: ERR=%s"),
230 jcr->db->strerror());
231 } else {
232 Jmsg(jcr, M_ERROR, 0,
233 _("Softquota Exceeded, Grace Period starts now.\n"));
234 }
235 jcr->impl->res.client->GraceTime = now;
236 goto bail_out;
237 } else if (jcr->impl->res.client->SoftQuotaGracePeriod &&
238 (now - (uint64_t)jcr->impl->res.client->GraceTime) <
239 (uint64_t)jcr->impl->res.client->SoftQuotaGracePeriod) {
240 Jmsg(jcr, M_ERROR, 0,
241 _("Softquota Exceeded, will be enforced after Grace Period "
242 "expires.\n"));
243 } else if (jcr->impl->res.client->SoftQuotaGracePeriod &&
244 (now - (uint64_t)jcr->impl->res.client->GraceTime) >
245 (uint64_t)jcr->impl->res.client->SoftQuotaGracePeriod) {
246 /*
247 * If gracetime has expired update else check more if not set softlimit
248 * yet then set and bail out.
249 */
250 if (jcr->impl->res.client->QuotaLimit < 1) {
251 if (!jcr->db->UpdateQuotaSoftlimit(jcr, &jcr->impl->jr)) {
252 Jmsg(jcr, M_WARNING, 0, _("Error setting Quota Softlimit: ERR=%s"),
253 jcr->db->strerror());
254 }
255 Jmsg(jcr, M_WARNING, 0,
256 _("Softquota Exceeded and Grace Period expired.\n"));
257 Jmsg(jcr, M_INFO, 0, _("Setting Burst Quota to %d Bytes.\n"),
258 jcr->impl->jr.JobSumTotalBytes);
259 jcr->impl->res.client->QuotaLimit = jcr->impl->jr.JobSumTotalBytes;
260 retval = true;
261 goto bail_out;
262 } else {
263 /*
264 * If gracetime has expired update else check more if not set softlimit
265 * yet then set and bail out.
266 */
267 if (jcr->impl->res.client->QuotaLimit < 1) {
268 if (!jcr->db->UpdateQuotaSoftlimit(jcr, &jcr->impl->jr)) {
269 Jmsg(jcr, M_WARNING, 0, _("Error setting Quota Softlimit: ERR=%s"),
270 jcr->db->strerror());
271 }
272 Jmsg(jcr, M_WARNING, 0,
273 _("Soft Quota exceeded and Grace Period expired.\n"));
274 Jmsg(jcr, M_INFO, 0, _("Setting Burst Quota to %d Bytes.\n"),
275 jcr->impl->jr.JobSumTotalBytes);
276 jcr->impl->res.client->QuotaLimit = jcr->impl->jr.JobSumTotalBytes;
277 retval = true;
278 goto bail_out;
279 } else {
280 /*
281 * If we use strict quotas enforce the pure soft quota limit.
282 */
283 if (jcr->impl->res.client->StrictQuotas) {
284 if (jcr->impl->jr.JobSumTotalBytes >
285 jcr->impl->res.client->SoftQuota) {
286 Dmsg0(debuglevel,
287 "Soft Quota exceeded, enforcing Strict Quota Limit.\n");
288 retval = true;
289 goto bail_out;
290 }
291 } else {
292 if (jcr->impl->jr.JobSumTotalBytes >=
293 jcr->impl->res.client->QuotaLimit) {
294 /*
295 * If strict quotas turned off use the last known limit
296 */
297 Jmsg(jcr, M_WARNING, 0,
298 _("Soft Quota exceeded, enforcing Burst Quota Limit.\n"));
299 retval = true;
300 goto bail_out;
301 }
302 }
303 }
304 }
305 }
306 } else if (jcr->impl->res.client->GraceTime != 0) {
307 /*
308 * Reset softquota
309 */
310 ClientDbRecord cr;
311 cr.ClientId = jcr->impl->jr.ClientId;
312 if (!jcr->db->ResetQuotaRecord(jcr, &cr)) {
313 Jmsg(jcr, M_WARNING, 0, _("Error setting Quota gracetime: ERR=%s\n"),
314 jcr->db->strerror());
315 } else {
316 jcr->impl->res.client->GraceTime = 0;
317 Jmsg(jcr, M_INFO, 0, _("Soft Quota reset, Grace Period ends now.\n"));
318 }
319 }
320
321 bail_out:
322 return retval;
323 }
324
325 } /* namespace directordaemon */
326