X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Fdird%2Fpythondir.c;h=20a78ab29f9c9dc57b7bf819543fc77684233ae3;hb=369727090d4c5ca6f9b258c037ae6d6df5d49d5a;hp=4ab14fb7856269d51a4454967fef7a4c6ac05da0;hpb=263ef0e6a5375847aba1af7d4c9dda62190c6884;p=bacula%2Fbacula diff --git a/bacula/src/dird/pythondir.c b/bacula/src/dird/pythondir.c index 4ab14fb785..20a78ab29f 100644 --- a/bacula/src/dird/pythondir.c +++ b/bacula/src/dird/pythondir.c @@ -1,31 +1,38 @@ /* - * - * Bacula interface to Python for the Director - * - * Kern Sibbald, November MMIV - * - * Version $Id$ - * - */ + Bacula® - The Network Backup Solution -/* - Copyright (C) 2004-2005 Kern Sibbald + Copyright (C) 2004-2008 Free Software Foundation Europe e.V. - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. + The main author of Bacula is Kern Sibbald, with contributions from + many others, a complete list can be found in the file AUTHORS. + This program is Free Software; you can redistribute it and/or + modify it under the terms of version three of the GNU Affero General Public + License as published by the Free Software Foundation and included + in the file LICENSE. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public - License along with this program; if not, write to the Free - Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - MA 02111-1307, USA. + You should have received a copy of the GNU Affero General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + Bacula® is a registered trademark of Kern Sibbald. + The licensor of Bacula is the Free Software Foundation Europe + (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, + Switzerland, email:ftf@fsfeurope.org. +*/ +/* + * + * Bacula interface to Python for the Director + * + * Kern Sibbald, November MMIV + * + * Version $Id$ + * */ #include "bacula.h" @@ -35,60 +42,63 @@ #undef _POSIX_C_SOURCE #include -extern JCR *get_jcr_from_PyObject(PyObject *self); -extern PyObject *find_method(PyObject *eventsObject, PyObject *method, - const char *name); +#include +extern struct s_jl joblevels[]; -static int set_job_events(PyObject *self, PyObject *arg); -static int job_run(PyObject *self, PyObject *arg); +static PyObject *set_job_events(PyObject *self, PyObject *arg); +static PyObject *job_run(PyObject *self, PyObject *arg); +static PyObject *job_write(PyObject *self, PyObject *arg); +static PyObject *job_cancel(PyObject *self, PyObject *arg); +static PyObject *job_does_vol_exist(PyObject *self, PyObject *arg); -#ifdef needed -static PyObject *set_bacula_job_events(PyObject *self, PyObject *arg) -{ - Dmsg2(000, "In set_bacula_job_events self=%p arg=%p\n", - self, arg); - Py_INCREF(Py_None); - return Py_None; -} PyMethodDef JobMethods[] = { - {"set_events", set_bacula_job_events, METH_VARARGS, "Define Bacula events."}, + {"set_events", set_job_events, METH_VARARGS, "Set Job events"}, + {"run", job_run, METH_VARARGS, "Run a Job"}, + {"write", job_write, METH_VARARGS, "Write to output"}, + {"cancel", job_cancel, METH_VARARGS, "Cancel a Job"}, + {"DoesVolumeExist", job_does_vol_exist, METH_VARARGS, "Does Volume Exist"}, {NULL, NULL, 0, NULL} /* last item */ }; -#endif - struct s_vars { const char *name; - char *fmt; + const char *fmt; }; /* Read-only variables */ static struct s_vars getvars[] = { - { N_("Job"), "s"}, - { N_("DirName"), "s"}, - { N_("Level"), "s"}, - { N_("Type"), "s"}, - { N_("JobId"), "i"}, - { N_("Client"), "s"}, - { N_("NumVols"), "i"}, - { N_("Pool"), "s"}, - { N_("Storage"), "s"}, - { N_("Catalog"), "s"}, - { N_("MediaType"), "s"}, - { N_("JobName"), "s"}, - { N_("JobStatus"), "s"}, + { "Job", "s"}, + { "Level", "s"}, + { "Type", "s"}, + { "JobId", "i"}, + { "Client", "s"}, + { "NumVols", "i"}, + { "Pool", "s"}, + { "Storage", "s"}, + { "Catalog", "s"}, + { "MediaType", "s"}, + { "JobName", "s"}, + { "JobStatus", "s"}, + { "Priority", "i"}, + { "VolumeName", "s"}, + { "CatalogRes", "(sssssis)"}, + { "JobErrors", "i"}, + { "JobFiles", "i"}, + { "SDJobFiles", "i"}, + { "SDErrors", "i"}, + { "FDJobStatus","s"}, + { "SDJobStatus","s"}, { NULL, NULL} }; /* Writable variables */ static struct s_vars setvars[] = { - { N_("set_events"), NULL}, - { N_("run"), NULL}, - { N_("JobReport"), "s"}, - { N_("write"), "s"}, - { N_("VolumeName") , "s"}, + { "JobReport", "s"}, + { "VolumeName", "s"}, + { "Priority", "i"}, + { "JobLevel", "s"}, { NULL, NULL} }; @@ -109,7 +119,7 @@ PyObject *job_getattr(PyObject *self, char *attrname) Dmsg0(100, "In job_getattr.\n"); jcr = get_jcr_from_PyObject(self); if (!jcr) { - bstrncpy(errmsg, "Job pointer not found.", sizeof(errmsg)); + bstrncpy(errmsg, _("Job pointer not found."), sizeof(errmsg)); goto bail_out; } for (i=0; getvars[i].name; i++) { @@ -119,40 +129,85 @@ PyObject *job_getattr(PyObject *self, char *attrname) } } if (!found) { - goto not_found; + /* Try our methods */ + return Py_FindMethod(JobMethods, self, attrname); } switch (i) { case 0: /* Job */ - return Py_BuildValue(getvars[i].fmt, jcr->job->hdr.name); - case 1: /* Director's name */ - return Py_BuildValue(getvars[i].fmt, my_name); - case 2: /* level */ - return Py_BuildValue(getvars[i].fmt, job_level_to_str(jcr->JobLevel)); - case 3: /* type */ - return Py_BuildValue(getvars[i].fmt, job_type_to_str(jcr->JobType)); - case 4: /* JobId */ - return Py_BuildValue(getvars[i].fmt, jcr->JobId); - case 5: /* Client */ - return Py_BuildValue(getvars[i].fmt, jcr->client->hdr.name); - case 6: /* NumVols */ - return Py_BuildValue(getvars[i].fmt, jcr->NumVols); - case 7: /* Pool */ - return Py_BuildValue(getvars[i].fmt, jcr->pool->hdr.name); - case 8: /* Storage */ - return Py_BuildValue(getvars[i].fmt, jcr->store->hdr.name); - case 9: - return Py_BuildValue(getvars[i].fmt, jcr->catalog->hdr.name); - case 10: /* MediaType */ - return Py_BuildValue(getvars[i].fmt, jcr->store->media_type); - case 11: /* JobName */ - return Py_BuildValue(getvars[i].fmt, jcr->Job); - case 12: /* JobStatus */ + return Py_BuildValue((char *)getvars[i].fmt, jcr->job->hdr.name); + case 1: /* level */ + return Py_BuildValue((char *)getvars[i].fmt, job_level_to_str(jcr->getJobLevel())); + case 2: /* type */ + return Py_BuildValue((char *)getvars[i].fmt, job_type_to_str(jcr->getJobType())); + case 3: /* JobId */ + return Py_BuildValue((char *)getvars[i].fmt, jcr->JobId); + case 4: /* Client */ + return Py_BuildValue((char *)getvars[i].fmt, jcr->client->hdr.name); + case 5: /* NumVols */ + POOL_DBR pr; + memset(&pr, 0, sizeof(pr)); + bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name)); + if (db_get_pool_record(jcr, jcr->db, &pr)) { + jcr->NumVols = pr.NumVols; + return Py_BuildValue((char *)getvars[i].fmt, jcr->NumVols); + } else { + bsnprintf(errmsg, sizeof(errmsg), _("Pool record not found.")); + goto bail_out; + } + case 6: /* Pool */ + return Py_BuildValue((char *)getvars[i].fmt, jcr->pool->name()); + case 7: /* Storage */ + if (jcr->wstore) { + return Py_BuildValue((char *)getvars[i].fmt, jcr->wstore->name()); + } else if (jcr->rstore) { + return Py_BuildValue((char *)getvars[i].fmt, jcr->rstore->name()); + } else { + goto bail_out; + } + case 8: + return Py_BuildValue((char *)getvars[i].fmt, jcr->catalog->name()); + case 9: /* MediaType */ + if (jcr->wstore) { + return Py_BuildValue((char *)getvars[i].fmt, jcr->wstore->media_type); + } else if (jcr->rstore) { + return Py_BuildValue((char *)getvars[i].fmt, jcr->rstore->media_type); + } else { + goto bail_out; + } + case 10: /* JobName */ + return Py_BuildValue((char *)getvars[i].fmt, jcr->Job); + case 11: /* JobStatus */ buf[1] = 0; buf[0] = jcr->JobStatus; - return Py_BuildValue(getvars[i].fmt, buf); + return Py_BuildValue((char *)getvars[i].fmt, buf); + case 12: /* Priority */ + return Py_BuildValue((char *)getvars[i].fmt, jcr->JobPriority); + case 13: + return Py_BuildValue((char *)getvars[i].fmt, jcr->VolumeName); + case 14: /* CatalogRes */ + return Py_BuildValue((char *)getvars[i].fmt, + jcr->catalog->db_name, jcr->catalog->db_address, + jcr->catalog->db_user, jcr->catalog->db_password, + jcr->catalog->db_socket, jcr->catalog->db_port, + db_get_type(jcr->db)); + case 15: /* JobErrors */ + return Py_BuildValue((char *)getvars[i].fmt, jcr->JobErrors); + case 16: /* JobFiles */ + return Py_BuildValue((char *)getvars[i].fmt, jcr->JobFiles); + case 17: /* SDJobFiles */ + return Py_BuildValue((char *)getvars[i].fmt, jcr->SDJobFiles); + case 18: /* SDErrors */ + return Py_BuildValue((char *)getvars[i].fmt, jcr->SDErrors); + case 19: /* FDJobStatus */ + buf[1] = 0; + buf[0] = jcr->FDJobStatus; + return Py_BuildValue((char *)getvars[i].fmt, buf); + case 20: /* SDJobStatus */ + buf[1] = 0; + buf[0] = jcr->SDJobStatus; + return Py_BuildValue((char *)getvars[i].fmt, buf); } -not_found: - bsnprintf(errmsg, sizeof(errmsg), "Attribute %s not found.", attrname); + bsnprintf(errmsg, sizeof(errmsg), _("Attribute %s not found."), attrname); bail_out: PyErr_SetString(PyExc_AttributeError, errmsg); return NULL; @@ -168,6 +223,7 @@ int job_setattr(PyObject *self, char *attrname, PyObject *value) JCR *jcr; bool found = false; char *strval = NULL; + int intval = 0; int i; Dmsg2(100, "In job_setattr=%s val=%p.\n", attrname, value); @@ -189,23 +245,28 @@ int job_setattr(PyObject *self, char *attrname, PyObject *value) if (!found) { goto bail_out; } - /* Get argument value ***FIXME*** handle other formats */ + /* Get argument value */ if (setvars[i].fmt != NULL) { - if (!PyArg_Parse(value, setvars[i].fmt, &strval)) { - PyErr_SetString(PyExc_TypeError, "Read-only attribute"); - return -1; + switch (setvars[i].fmt[0]) { + case 's': + if (!PyArg_Parse(value, (char *)setvars[i].fmt, &strval)) { + PyErr_SetString(PyExc_TypeError, _("Read-only attribute")); + return -1; + } + break; + case 'i': + if (!PyArg_Parse(value, (char *)setvars[i].fmt, &intval)) { + PyErr_SetString(PyExc_TypeError, _("Read-only attribute")); + return -1; + } + break; } } switch (i) { - case 0: /* set_events */ - return set_job_events(self, value); - case 1: /* run */ - return job_run(self, value); - case 2: /* JobReport */ - case 3: /* write */ + case 0: /* JobReport */ Jmsg(jcr, M_INFO, 0, "%s", strval); return 0; - case 4: /* VolumeName */ + case 1: /* VolumeName */ /* Make sure VolumeName is valid and we are in VolumeName event */ if (strcmp("NewVolume", jcr->event) == 0 && is_volume_name_legal(NULL, strval)) { @@ -215,49 +276,73 @@ int job_setattr(PyObject *self, char *attrname, PyObject *value) } else { jcr->VolumeName[0] = 0; } + break; + case 2: /* Priority */ + Dmsg1(000, "Set priority=%d\n", intval); + if (intval >= 1 && intval <= 100) { + jcr->JobPriority = intval; + } else { + PyErr_SetString(PyExc_ValueError, _("Priority must be 1-100")); + return -1; + } + case 3: /* Job Level */ + if (strcmp("JobInit", jcr->event) != 0) { + PyErr_SetString(PyExc_RuntimeError, _("Job Level can be set only during JobInit")); + return -1; + } + if (strval != NULL) { + for (i=0; joblevels[i].level_name; i++) { + if (strcmp(strval, joblevels[i].level_name) == 0) { + if (joblevels[i].job_type == jcr->getJobType()) { + jcr->setJobLevel(joblevels[i].level); + jcr->jr.JobLevel = jcr->getJobLevel(); + return 0; + } + } + } + } + PyErr_SetString(PyExc_ValueError, _("Bad JobLevel string")); + return -1; } bail_out: PyErr_SetString(PyExc_AttributeError, attrname); return -1; } -#ifdef needed -static PyObject *set_bacula_job_events(PyObject *self, PyObject *arg) -{ - Dmsg2(000, "In set_bacula_job_events self=%p arg=%p\n", - self, arg); - Py_INCREF(Py_None); - return Py_None; -} -#endif - - -static int set_job_events(PyObject *self, PyObject *arg) +/* + * Set pointer to instantiated events class + */ +static PyObject *set_job_events(PyObject *self, PyObject *arg) { PyObject *eObject; JCR *jcr; Dmsg0(100, "In set_job_events.\n"); - if (!PyArg_Parse(arg, "O", &eObject)) { - return -1; + if (!PyArg_ParseTuple(arg, "O:set_events", &eObject)) { + Dmsg0(000, "Error in ParseTuple\n"); + return NULL; } jcr = get_jcr_from_PyObject(self); Py_XDECREF((PyObject *)jcr->Python_events); Py_INCREF(eObject); jcr->Python_events = (void *)eObject; - return 0; /* good return */ + Py_INCREF(Py_None); + return Py_None; } -/* Run a Bacula command */ -static int job_run(PyObject *self, PyObject *arg) +/* Run a Bacula job */ +static PyObject *job_run(PyObject *self, PyObject *arg) { JCR *jcr; char *item; int stat; - if (!PyArg_Parse(arg, "s", &item)) { - return -1; + if (!PyArg_ParseTuple(arg, "s:run", &item)) { + Dmsg0(000, "Error in ParseTuple\n"); + return NULL; } + /* Release lock due to recursion */ +// PyEval_ReleaseLock(); jcr = get_jcr_from_PyObject(self); UAContext *ua = new_ua_context(jcr); ua->batch = true; @@ -265,32 +350,114 @@ static int job_run(PyObject *self, PyObject *arg) parse_ua_args(ua); /* parse command */ stat = run_cmd(ua, ua->cmd); free_ua_context(ua); - /* ***FIXME*** check stat */ - return 0; +// PyEval_AcquireLock(); + return PyInt_FromLong((long)stat); } +static PyObject *job_write(PyObject *self, PyObject *args) +{ + char *text = NULL; + + if (!PyArg_ParseTuple(args, "s:write", &text)) { + Dmsg0(000, "Parse tuple error in job_write\n"); + return NULL; + } + if (text) { + JCR *jcr = get_jcr_from_PyObject(self); + Jmsg(jcr, M_INFO, 0, "%s", text); + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *job_does_vol_exist(PyObject *self, PyObject *args) +{ + char *VolName = NULL; + + if (!PyArg_ParseTuple(args, "s:does_volume_exist", &VolName)) { + Dmsg0(000, "Parse tuple error in job_does_vol_exist\n"); + return NULL; + } + if (VolName) { + MEDIA_DBR mr; + int ok; + JCR *jcr = get_jcr_from_PyObject(self); + memset(&mr, 0, sizeof(mr)); + bstrncpy(mr.VolumeName, VolName, sizeof(mr.VolumeName)); + ok = db_get_media_record(jcr, jcr->db, &mr); + return Py_BuildValue("i", ok); + } + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject *job_cancel(PyObject *self, PyObject *args) +{ + JobId_t JobId = 0; + JCR *jcr; + bool found = false; + + if (!PyArg_ParseTuple(args, "i:cancel", &JobId)) { + Dmsg0(000, "Parse tuple error in job_write\n"); + return NULL; + } + foreach_jcr(jcr) { + if (jcr->JobId == 0) { + continue; + } + if (jcr->JobId == JobId) { + found = true; + break; + } + } + /* endeach_jcr(jcr) not needed because freed below */ + if (!found) { + /* ***FIXME*** raise exception */ + return NULL; + } +// PyEval_ReleaseLock(); + UAContext *ua = new_ua_context(jcr); + ua->batch = true; + if (!cancel_job(ua, jcr)) { + /* ***FIXME*** raise exception */ + return NULL; + } + free_ua_context(ua); + free_jcr(jcr); +// PyEval_AcquireLock(); + Py_INCREF(Py_None); + return Py_None; +} + +/* + * Generate a Job event, which means look up the event + * method defined by the user, and if it exists, + * call it. + */ int generate_job_event(JCR *jcr, const char *event) { PyObject *method = NULL; PyObject *Job = (PyObject *)jcr->Python_job; + PyObject *events = (PyObject *)jcr->Python_events; PyObject *result = NULL; int stat = 0; - if (!Job) { + if (!Job || !events) { return 0; } - PyEval_AcquireLock(); + lock_python(); +// PyEval_AcquireLock(); - PyObject *events = (PyObject *)jcr->Python_events; method = find_method(events, method, event); if (!method) { goto bail_out; } bstrncpy(jcr->event, event, sizeof(jcr->event)); - result = PyObject_CallFunction(method, "O", Job); + result = PyObject_CallFunction(method, (char *)"O", Job); jcr->event[0] = 0; /* no event in progress */ if (result == NULL) { if (PyErr_Occurred()) { @@ -303,7 +470,8 @@ int generate_job_event(JCR *jcr, const char *event) Py_XDECREF(result); bail_out: - PyEval_ReleaseLock(); + unlock_python(); +// PyEval_ReleaseLock(); return stat; }