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