]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/pythondir.c
kes Ensure that job report is always printed even if job is failed
[bacula/bacula] / bacula / src / dird / pythondir.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2004-2008 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
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    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    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  * Bacula interface to Python for the Director
31  *
32  * Kern Sibbald, November MMIV
33  *
34  *   Version $Id$
35  *
36  */
37
38 #include "bacula.h"
39 #include "dird.h"
40
41 #ifdef HAVE_PYTHON
42 #undef _POSIX_C_SOURCE
43 #include <Python.h>
44
45 #include <lib/pythonlib.h>
46
47 extern struct s_jl joblevels[];
48
49 static PyObject *set_job_events(PyObject *self, PyObject *arg);
50 static PyObject *job_run(PyObject *self, PyObject *arg);
51 static PyObject *job_write(PyObject *self, PyObject *arg);
52 static PyObject *job_cancel(PyObject *self, PyObject *arg);
53 static PyObject *job_does_vol_exist(PyObject *self, PyObject *arg);
54
55 PyMethodDef JobMethods[] = {
56     {"set_events", set_job_events, METH_VARARGS, "Set Job events"},
57     {"run", job_run, METH_VARARGS, "Run a Job"},
58     {"write", job_write, METH_VARARGS, "Write to output"},
59     {"cancel", job_cancel, METH_VARARGS, "Cancel a Job"},
60     {"DoesVolumeExist", job_does_vol_exist, METH_VARARGS, "Does Volume Exist"},
61     {NULL, NULL, 0, NULL}             /* last item */
62 };
63  
64 struct s_vars {
65    const char *name;
66    const char *fmt;
67 };
68
69 /* Read-only variables */
70 static struct s_vars getvars[] = {
71    { "Job",        "s"},
72    { "Level",      "s"},
73    { "Type",       "s"},
74    { "JobId",      "i"},
75    { "Client",     "s"},
76    { "NumVols",    "i"},
77    { "Pool",       "s"},
78    { "Storage",    "s"},
79    { "Catalog",    "s"},
80    { "MediaType",  "s"},
81    { "JobName",    "s"},
82    { "JobStatus",  "s"},
83    { "Priority",   "i"},
84    { "VolumeName", "s"},
85    { "CatalogRes", "(sssssis)"},
86    { "JobErrors",  "i"},
87    { "JobFiles",   "i"},
88    { "SDJobFiles", "i"},
89    { "SDErrors",   "i"},
90    { "FDJobStatus","s"},
91    { "SDJobStatus","s"},
92
93    { NULL,             NULL}
94 };
95
96 /* Writable variables */
97 static struct s_vars setvars[] = {
98    { "JobReport",   "s"},
99    { "VolumeName",  "s"},
100    { "Priority",    "i"},
101    { "JobLevel",    "s"},
102
103    { NULL,             NULL}
104 };
105
106
107 /* Return Job variables */
108 /* Returns:  NULL if error
109  *           PyObject * return value if OK
110  */
111 PyObject *job_getattr(PyObject *self, char *attrname)
112 {
113    JCR *jcr;
114    bool found = false;
115    int i;
116    char buf[10];
117    char errmsg[200];
118
119    Dmsg0(100, "In job_getattr.\n");
120    jcr = get_jcr_from_PyObject(self);
121    if (!jcr) {
122       bstrncpy(errmsg, _("Job pointer not found."), sizeof(errmsg));
123       goto bail_out;
124    }
125    for (i=0; getvars[i].name; i++) {
126       if (strcmp(getvars[i].name, attrname) == 0) {
127          found = true;
128          break;
129       }
130    }
131    if (!found) {
132       /* Try our methods */
133       return Py_FindMethod(JobMethods, self, attrname);
134    }
135    switch (i) {
136    case 0:                            /* Job */
137       return Py_BuildValue((char *)getvars[i].fmt, jcr->job->hdr.name);
138    case 1:                            /* level */
139       return Py_BuildValue((char *)getvars[i].fmt, job_level_to_str(jcr->get_JobLevel()));
140    case 2:                            /* type */
141       return Py_BuildValue((char *)getvars[i].fmt, job_type_to_str(jcr->get_JobType()));
142    case 3:                            /* JobId */
143       return Py_BuildValue((char *)getvars[i].fmt, jcr->JobId);
144    case 4:                            /* Client */
145       return Py_BuildValue((char *)getvars[i].fmt, jcr->client->hdr.name);
146    case 5:                            /* NumVols */
147       POOL_DBR pr;
148       memset(&pr, 0, sizeof(pr));
149       bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
150       if (db_get_pool_record(jcr, jcr->db, &pr)) {
151          jcr->NumVols = pr.NumVols;
152          return Py_BuildValue((char *)getvars[i].fmt, jcr->NumVols);
153       } else {
154          bsnprintf(errmsg, sizeof(errmsg), _("Pool record not found."));
155          goto bail_out;
156       }
157    case 6:                            /* Pool */
158       return Py_BuildValue((char *)getvars[i].fmt, jcr->pool->name());
159    case 7:                            /* Storage */
160       if (jcr->wstore) {
161          return Py_BuildValue((char *)getvars[i].fmt, jcr->wstore->name());
162       } else if (jcr->rstore) {
163          return Py_BuildValue((char *)getvars[i].fmt, jcr->rstore->name());
164       } else {
165          goto bail_out;
166       }
167    case 8:
168       return Py_BuildValue((char *)getvars[i].fmt, jcr->catalog->name());
169    case  9:                           /* MediaType */
170       if (jcr->wstore) {
171          return Py_BuildValue((char *)getvars[i].fmt, jcr->wstore->media_type);
172       } else if (jcr->rstore) {
173          return Py_BuildValue((char *)getvars[i].fmt, jcr->rstore->media_type);
174       } else {
175          goto bail_out;
176       }
177    case 10:                           /* JobName */
178       return Py_BuildValue((char *)getvars[i].fmt, jcr->Job);
179    case 11:                           /* JobStatus */
180       buf[1] = 0;
181       buf[0] = jcr->JobStatus;
182       return Py_BuildValue((char *)getvars[i].fmt, buf);
183    case 12:                           /* Priority */
184       return Py_BuildValue((char *)getvars[i].fmt, jcr->JobPriority);
185    case 13:
186       return Py_BuildValue((char *)getvars[i].fmt, jcr->VolumeName);
187    case 14:                           /* CatalogRes */
188       return Py_BuildValue((char *)getvars[i].fmt,
189          jcr->catalog->db_name, jcr->catalog->db_address, 
190          jcr->catalog->db_user, jcr->catalog->db_password,
191          jcr->catalog->db_socket, jcr->catalog->db_port,
192          db_get_type());
193    case 15:                           /* JobErrors */
194       return Py_BuildValue((char *)getvars[i].fmt, jcr->JobErrors);
195    case 16:                           /* JobFiles */
196       return Py_BuildValue((char *)getvars[i].fmt, jcr->JobFiles);
197    case 17:                           /* SDJobFiles */
198       return Py_BuildValue((char *)getvars[i].fmt, jcr->SDJobFiles);
199    case 18:                           /* SDErrors */
200       return Py_BuildValue((char *)getvars[i].fmt, jcr->SDErrors);
201    case 19:                           /* FDJobStatus */
202       buf[1] = 0;
203       buf[0] = jcr->FDJobStatus;
204       return Py_BuildValue((char *)getvars[i].fmt, buf);
205    case 29:                           /* SDJobStatus */
206       buf[1] = 0;
207       buf[0] = jcr->SDJobStatus;
208       return Py_BuildValue((char *)getvars[i].fmt, buf);
209    }
210    bsnprintf(errmsg, sizeof(errmsg), _("Attribute %s not found."), attrname);
211 bail_out:
212    PyErr_SetString(PyExc_AttributeError, errmsg);
213    return NULL;
214 }
215
216
217 /* Set Job variables */
218 /*  Returns:   0 for OK
219  *            -1 for error
220  */
221 int job_setattr(PyObject *self, char *attrname, PyObject *value)
222 {
223    JCR *jcr;
224    bool found = false;
225    char *strval = NULL;
226    int intval = 0;
227    int i;
228
229    Dmsg2(100, "In job_setattr=%s val=%p.\n", attrname, value);
230    if (value == NULL) {                /* Cannot delete variables */
231        goto bail_out;
232    }
233    jcr = get_jcr_from_PyObject(self);
234    if (!jcr) {
235       goto bail_out;
236    }
237
238    /* Find attribute name in list */
239    for (i=0; setvars[i].name; i++) {
240       if (strcmp(setvars[i].name, attrname) == 0) {
241          found = true;
242          break;
243       }
244    }
245    if (!found) {
246       goto bail_out;
247    }
248    /* Get argument value */
249    if (setvars[i].fmt != NULL) {
250       switch (setvars[i].fmt[0]) {
251       case 's':
252          if (!PyArg_Parse(value, (char *)setvars[i].fmt, &strval)) {
253             PyErr_SetString(PyExc_TypeError, _("Read-only attribute"));
254             return -1;
255          }
256          break;
257       case 'i':
258          if (!PyArg_Parse(value, (char *)setvars[i].fmt, &intval)) {
259             PyErr_SetString(PyExc_TypeError, _("Read-only attribute"));
260             return -1;
261          }
262          break;
263       }
264    }   
265    switch (i) {
266    case 0:                            /* JobReport */
267       Jmsg(jcr, M_INFO, 0, "%s", strval);
268       return 0;
269    case 1:                            /* VolumeName */
270       /* Make sure VolumeName is valid and we are in VolumeName event */
271       if (strcmp("NewVolume", jcr->event) == 0 &&
272           is_volume_name_legal(NULL, strval)) {
273          pm_strcpy(jcr->VolumeName, strval);
274          Dmsg1(100, "Set Vol=%s\n", strval);
275          return 0;
276       } else {
277          jcr->VolumeName[0] = 0;
278       }
279       break;
280    case 2:                            /* Priority */
281       Dmsg1(000, "Set priority=%d\n", intval);
282       if (intval >= 1 && intval <= 100) {
283          jcr->JobPriority = intval;
284       } else {
285          PyErr_SetString(PyExc_ValueError, _("Priority must be 1-100"));
286          return -1;
287       }
288    case 3:                            /* Job Level */
289       if (strcmp("JobInit", jcr->event) != 0) {
290          PyErr_SetString(PyExc_RuntimeError, _("Job Level can be set only during JobInit"));
291          return -1;
292       }
293       for (i=0; joblevels[i].level_name; i++) {
294          if (strcmp(strval, joblevels[i].level_name) == 0) {
295             if (joblevels[i].job_type == jcr->get_JobType()) {
296                jcr->set_JobLevel(joblevels[i].level);
297                jcr->jr.JobLevel = jcr->get_JobLevel();
298                return 0;
299             }
300          }
301       }
302       PyErr_SetString(PyExc_ValueError, _("Bad JobLevel string"));
303       return -1;
304    }
305 bail_out:
306    PyErr_SetString(PyExc_AttributeError, attrname);
307    return -1;
308 }
309
310 /*
311  * Set pointer to instantiated events class
312  */
313 static PyObject *set_job_events(PyObject *self, PyObject *arg)
314 {
315    PyObject *eObject;
316    JCR *jcr;
317
318    Dmsg0(100, "In set_job_events.\n");
319    if (!PyArg_ParseTuple(arg, "O:set_events", &eObject)) {
320       Dmsg0(000, "Error in ParseTuple\n");
321       return NULL;
322    }
323    jcr = get_jcr_from_PyObject(self);
324    Py_XDECREF((PyObject *)jcr->Python_events);
325    Py_INCREF(eObject);
326    jcr->Python_events = (void *)eObject;
327    Py_INCREF(Py_None);
328    return Py_None;
329 }
330
331 /* Run a Bacula job */
332 static PyObject *job_run(PyObject *self, PyObject *arg)
333 {
334    JCR *jcr;
335    char *item;
336    int stat;
337
338    if (!PyArg_ParseTuple(arg, "s:run", &item)) {
339       Dmsg0(000, "Error in ParseTuple\n");
340       return NULL;
341    }
342    /* Release lock due to recursion */
343 // PyEval_ReleaseLock();
344    jcr = get_jcr_from_PyObject(self);
345    UAContext *ua = new_ua_context(jcr);
346    ua->batch = true;
347    pm_strcpy(ua->cmd, item);          /* copy command */
348    parse_ua_args(ua);                 /* parse command */
349    stat = run_cmd(ua, ua->cmd);
350    free_ua_context(ua);
351 // PyEval_AcquireLock();
352    return PyInt_FromLong((long)stat);
353 }
354
355 static PyObject *job_write(PyObject *self, PyObject *args)
356 {
357    char *text = NULL;
358
359    if (!PyArg_ParseTuple(args, "s:write", &text)) {
360       Dmsg0(000, "Parse tuple error in job_write\n");
361       return NULL;
362    }
363    if (text) {
364       JCR *jcr = get_jcr_from_PyObject(self);
365       Jmsg(jcr, M_INFO, 0, "%s", text);
366    }
367    Py_INCREF(Py_None);
368    return Py_None;
369 }
370
371 static PyObject *job_does_vol_exist(PyObject *self, PyObject *args)
372 {
373    char *VolName = NULL;
374
375    if (!PyArg_ParseTuple(args, "s:does_volume_exist", &VolName)) {
376       Dmsg0(000, "Parse tuple error in job_does_vol_exist\n");
377       return NULL;
378    }
379    if (VolName) {
380       MEDIA_DBR mr;
381       int ok;
382       JCR *jcr = get_jcr_from_PyObject(self);
383       memset(&mr, 0, sizeof(mr));
384       bstrncpy(mr.VolumeName, VolName, sizeof(mr.VolumeName));
385       ok = db_get_media_record(jcr, jcr->db, &mr);
386       return Py_BuildValue("i", ok);
387    }
388    Py_INCREF(Py_None);
389    return Py_None;
390 }
391
392
393 static PyObject *job_cancel(PyObject *self, PyObject *args)
394 {
395    JobId_t JobId = 0;
396    JCR *jcr;
397    bool found = false;
398
399    if (!PyArg_ParseTuple(args, "i:cancel", &JobId)) {
400       Dmsg0(000, "Parse tuple error in job_write\n");
401       return NULL;
402    }
403    foreach_jcr(jcr) {
404       if (jcr->JobId == 0) {
405          continue;
406       }
407       if (jcr->JobId == JobId) {
408          found = true;
409          break;
410       }
411    }
412    /* endeach_jcr(jcr) not needed because freed below */
413
414    if (!found) {
415       /* ***FIXME*** raise exception */
416       return NULL;
417    }
418 // PyEval_ReleaseLock();
419    UAContext *ua = new_ua_context(jcr);
420    ua->batch = true;
421    if (!cancel_job(ua, jcr)) {
422       /* ***FIXME*** raise exception */
423       return NULL;
424    }
425    free_ua_context(ua);
426    free_jcr(jcr);
427 // PyEval_AcquireLock();   
428    Py_INCREF(Py_None);
429    return Py_None;
430 }
431
432 /*
433  * Generate a Job event, which means look up the event
434  *  method defined by the user, and if it exists, 
435  *  call it.
436  */
437 int generate_job_event(JCR *jcr, const char *event)
438 {
439    PyObject *method = NULL;
440    PyObject *Job = (PyObject *)jcr->Python_job;
441    PyObject *events = (PyObject *)jcr->Python_events;
442    PyObject *result = NULL;
443    int stat = 0;
444
445    if (!Job || !events) {
446       return 0;
447    }
448
449    lock_python();
450 // PyEval_AcquireLock();
451
452    method = find_method(events, method, event);
453    if (!method) {
454       goto bail_out;
455    }
456
457    bstrncpy(jcr->event, event, sizeof(jcr->event));
458    result = PyObject_CallFunction(method, (char *)"O", Job);
459    jcr->event[0] = 0;             /* no event in progress */
460    if (result == NULL) {
461       if (PyErr_Occurred()) {
462          PyErr_Print();
463          Dmsg1(000, "Error in Python method %s\n", event);
464       }
465    } else {
466       stat = 1;
467    }
468    Py_XDECREF(result);
469
470 bail_out:
471    unlock_python();
472 // PyEval_ReleaseLock();
473    return stat;
474 }
475
476 bool python_set_prog(JCR*, char const*) { return false; }
477
478 #else
479
480 /* Dummy if Python not configured */
481 int generate_job_event(JCR *jcr, const char *event) { return 1; }
482    
483
484 #endif /* HAVE_PYTHON */