]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/pythondir.c
kes Add JobErrors, JobFiles, SDJobFiles, SDJobErrors, SDJobStatus,
[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                return 0;
289             }
290          }
291       }
292       PyErr_SetString(PyExc_ValueError, _("Bad JobLevel string"));
293       return -1;
294    }
295 bail_out:
296    PyErr_SetString(PyExc_AttributeError, attrname);
297    return -1;
298 }
299
300 /*
301  * Set pointer to instantiated events class
302  */
303 static PyObject *set_job_events(PyObject *self, PyObject *arg)
304 {
305    PyObject *eObject;
306    JCR *jcr;
307
308    Dmsg0(100, "In set_job_events.\n");
309    if (!PyArg_ParseTuple(arg, "O:set_events", &eObject)) {
310       Dmsg0(000, "Error in ParseTuple\n");
311       return NULL;
312    }
313    jcr = get_jcr_from_PyObject(self);
314    Py_XDECREF((PyObject *)jcr->Python_events);
315    Py_INCREF(eObject);
316    jcr->Python_events = (void *)eObject;
317    Py_INCREF(Py_None);
318    return Py_None;
319 }
320
321 /* Run a Bacula job */
322 static PyObject *job_run(PyObject *self, PyObject *arg)
323 {
324    JCR *jcr;
325    char *item;
326    int stat;
327
328    if (!PyArg_ParseTuple(arg, "s:run", &item)) {
329       Dmsg0(000, "Error in ParseTuple\n");
330       return NULL;
331    }
332    /* Release lock due to recursion */
333 // PyEval_ReleaseLock();
334    jcr = get_jcr_from_PyObject(self);
335    UAContext *ua = new_ua_context(jcr);
336    ua->batch = true;
337    pm_strcpy(ua->cmd, item);          /* copy command */
338    parse_ua_args(ua);                 /* parse command */
339    stat = run_cmd(ua, ua->cmd);
340    free_ua_context(ua);
341 // PyEval_AcquireLock();
342    return PyInt_FromLong((long)stat);
343 }
344
345 static PyObject *job_write(PyObject *self, PyObject *args)
346 {
347    char *text = NULL;
348
349    if (!PyArg_ParseTuple(args, "s:write", &text)) {
350       Dmsg0(000, "Parse tuple error in job_write\n");
351       return NULL;
352    }
353    if (text) {
354       JCR *jcr = get_jcr_from_PyObject(self);
355       Jmsg(jcr, M_INFO, 0, "%s", text);
356    }
357    Py_INCREF(Py_None);
358    return Py_None;
359 }
360
361 static PyObject *job_does_vol_exist(PyObject *self, PyObject *args)
362 {
363    char *VolName = NULL;
364
365    if (!PyArg_ParseTuple(args, "s:does_volume_exist", &VolName)) {
366       Dmsg0(000, "Parse tuple error in job_does_vol_exist\n");
367       return NULL;
368    }
369    if (VolName) {
370       MEDIA_DBR mr;
371       int ok;
372       JCR *jcr = get_jcr_from_PyObject(self);
373       memset(&mr, 0, sizeof(mr));
374       bstrncpy(mr.VolumeName, VolName, sizeof(mr.VolumeName));
375       ok = db_get_media_record(jcr, jcr->db, &mr);
376       return Py_BuildValue("i", ok);
377    }
378    Py_INCREF(Py_None);
379    return Py_None;
380 }
381
382
383 static PyObject *job_cancel(PyObject *self, PyObject *args)
384 {
385    JobId_t JobId = 0;
386    JCR *jcr;
387    bool found = false;
388
389    if (!PyArg_ParseTuple(args, "i:cancel", &JobId)) {
390       Dmsg0(000, "Parse tuple error in job_write\n");
391       return NULL;
392    }
393    foreach_jcr(jcr) {
394       if (jcr->JobId == 0) {
395          continue;
396       }
397       if (jcr->JobId == JobId) {
398          found = true;
399          break;
400       }
401    }
402    /* endeach_jcr(jcr) not needed because freed below */
403
404    if (!found) {
405       /* ***FIXME*** raise exception */
406       return NULL;
407    }
408 // PyEval_ReleaseLock();
409    UAContext *ua = new_ua_context(jcr);
410    ua->batch = true;
411    if (!cancel_job(ua, jcr)) {
412       /* ***FIXME*** raise exception */
413       return NULL;
414    }
415    free_ua_context(ua);
416    free_jcr(jcr);
417 // PyEval_AcquireLock();   
418    Py_INCREF(Py_None);
419    return Py_None;
420 }
421
422 /*
423  * Generate a Job event, which means look up the event
424  *  method defined by the user, and if it exists, 
425  *  call it.
426  */
427 int generate_job_event(JCR *jcr, const char *event)
428 {
429    PyObject *method = NULL;
430    PyObject *Job = (PyObject *)jcr->Python_job;
431    PyObject *events = (PyObject *)jcr->Python_events;
432    PyObject *result = NULL;
433    int stat = 0;
434
435    if (!Job || !events) {
436       return 0;
437    }
438
439    lock_python();
440 // PyEval_AcquireLock();
441
442    method = find_method(events, method, event);
443    if (!method) {
444       goto bail_out;
445    }
446
447    bstrncpy(jcr->event, event, sizeof(jcr->event));
448    result = PyObject_CallFunction(method, "O", Job);
449    jcr->event[0] = 0;             /* no event in progress */
450    if (result == NULL) {
451       if (PyErr_Occurred()) {
452          PyErr_Print();
453          Dmsg1(000, "Error in Python method %s\n", event);
454       }
455    } else {
456       stat = 1;
457    }
458    Py_XDECREF(result);
459
460 bail_out:
461    unlock_python();
462 // PyEval_ReleaseLock();
463    return stat;
464 }
465
466 bool python_set_prog(JCR*, char const*) { return false; }
467
468 #else
469
470 /* Dummy if Python not configured */
471 int generate_job_event(JCR *jcr, const char *event) { return 1; }
472    
473
474 #endif /* HAVE_PYTHON */