%%
%%

\section*{Python Scripting}
\label{_ChapterStart60}
\index[general]{Python Scripting}
\index[general]{Scripting!Python}
\addcontentsline{toc}{section}{Python Scripting}

You may be asking what Python is and why a scripting language is
needed in Bacula. The answer to the first question is that Python
is an Object Oriented scripting language with features similar
to those found in Perl, but the syntax of the language is much
cleaner and simpler.  The answer to why have scripting in Bacula is to
give the user more control over the whole backup process. Probably 
the simplest example is when Bacula needs a new Volume name, with
a scripting language such as Python, you can generate any name 
you want, based on the current state of Bacula.

\subsection*{Python Configuration}
\index[general]{Python Configuration}
\index[general]{Configuration!Python}
\addcontentsline{toc}{subsection}{Python Configuration}

Python must be enabled during the configuration process by adding
a \verb:--:with-python, and possibly specifying an alternate
directory if your Python is not installed in a standard system
location. If you are using RPMs you will need the python-devel package
installed.

When Python is configured, it becomes an integral part of Bacula and
runs in Bacula's address space, so even though it is an interpreted 
language, it is very efficient.

When the Director starts, it looks to see if you have a {\bf
Scripts Directory} Directive defined, if so, it looks in that directory for
a file named {\bf DirStartUp.py}. If it is found, Bacula will pass this
file to Python for execution. The {\bf Scripts Directory} is a new
directive that you add to the Director resource of your bacula-dir.conf
file.

\subsection*{Bacula Events}
\index[general]{Bacula Events}
\index[general]{Events}
\addcontentsline{toc}{subsection}{Bacula Events}
A Bacula event is a point in the Bacula code where Bacula
will call a subroutine (actually a method) that you have 
defined in the Python StartUp script. Events correspond 
to some significant event such as a Job Start, a Job End,
Bacula needs a new Volume Name, ... When your script is
called, it will have access to all the Bacula variables
specific to the Job (attributes of the Job Object), and
it can even call some of the Job methods (subroutines)
or set new values in the Job attributes, such as the 
Priority. You will see below how the events are used.

\subsection*{Python Objects}
\index[general]{Python Objects}
\index[general]{Objects!Python}
\addcontentsline{toc}{subsection}{Python Objects}

There are four Python objects that you will need to work with:
\begin{description}
\item [The Bacula Object]
   The Bacula object is created by the Bacula daemon (the Director
   in the present case) when the daemon starts. It is available to
   the Python startup script, {\bf DirStartup.py}, by importing the
   Bacula definitions with {\bf import bacula}. The methods
   available with this object are described below. 

\item [The Bacula Events Class]
   You create this class in the startup script, and you pass
   it to the Bacula Object's {\bf set\_events} method. The 
   purpose of the Bacula Events Class is to define what global
   or daemon events you want to monitor. When one of those events
   occurs, your Bacula Events Class will be called at the method
   corresponding to the event. There are currently three events,
   JobStart, JobEnd, and Exit, which are described in detail below.
   
\item [The Job Object]
   When a Job starts, and assuming you have defined a JobStart method
   in your Bacula Events Class, Bacula will create a Job Object. This
   object will be passed to the JobStart event. The Job Object has a
   has good number of read-only members or attributes providing many
   details of the Job, and it also has a number of writable attributes
   that allow you to pass information into the Job.  These attributes
   are described below.
   
\item [The Job Events Class]
   You create this class in the JobStart method of your Bacula Events
   class, and it allows you to define which of the possible Job Object
   events you want to see. You must pass an instance of your Job Events
   class to the Job Object set\_events() method.
   Normally, you will probably only have one
   Job Events Class, which will be instantiated for each Job. However,
   if you wish to see different events in different Jobs, you may have
   as many Job Events classes as you wish.
\end{description}


The first thing the startup script must do is to define what global Bacula
events (daemon events), it wants to see. This is done by creating a 
Bacula Events class, instantiating it, then passing it to the 
{\bf set\_events} method. There are three possible
events.

\begin{description}
\item [JobStart]
   \index[dir]{JobStart}
   This Python method, if defined, will be called each time a Job is started.
   The method is passed the class instantiation object as the first argument,
   and the Bacula Job object as the second argument.  The Bacula Job object
   has several built-in methods, and you can define which ones you
   want called. If you do not define this method, you will not be able
   to interact with Bacula jobs.

\item [JobEnd]
   This Python method, if defined, will be called each time a Job terminates.
   The method is passed the class instantiation object as the first argument,
   and the Bacula Job object as the second argument.  

\item [Exit]
   This Python method, if defined, will be called when the Director terminates.
   The method is passed the class instantiation object as the first argument.
\end{description}

Access to the Bacula variables and methods is done with:

     import bacula

The following are the read-only attributes provided by the bacula object.
\begin{description}
\item [Name]
\item [ConfigFile]
\item [WorkingDir]
\item [Version] string consisting of "Version  Build-date"
\end{description}


A simple definition of the Bacula Events Class might be the following:

\footnotesize
\begin{verbatim}
import sys, bacula
class BaculaEvents:
  def JobStart(self, job):
     ...
\end{verbatim}
\normalsize

Then to instantiate the class and pass it to Bacula, you
would do:

\footnotesize
\begin{verbatim}
bacula.set_events(BaculaEvents()) # register Bacula Events wanted
\end{verbatim}
\normalsize

And at that point, each time a Job is started, your BaculaEvents JobStart
method will be called.

Now to actually do anything with a Job, you must define which Job events
you want to see, and this is done by defining a JobEvents class containing
the methods you want called.  Each method name corresponds to one of the
Job Events that Bacula will generate.

A simple Job Events class might look like the following:

\footnotesize
\begin{verbatim}
class JobEvents:
  def NewVolume(self, job):
     ...
\end{verbatim}
\normalsize

Here, your JobEvents class method NewVolume will be called each time
the Job needs a new Volume name.  To actually register the events defined
in your class with the Job, you must instantiate the JobEvents class and
set it in the Job {\bf set\_events} variable. Note, this is a bit different 
from how you registered the Bacula events. The registration process must
be done in the Bacula JobStart event (your method).  So, you would modify 
Bacula Events (not the Job events) as follows:

\footnotesize
\begin{verbatim}
import sys, bacula
class BaculaEvents:
  def JobStart(self, job):
     events = JobEvents()         # create instance of Job class
     job.set_events(events)       # register Job events desired
     ...
\end{verbatim}
\normalsize

When a job event is triggered, the appropriate event definition is
called in the JobEvents class. This is the means by which your Python
script or code gets control. Once it has control, it may read job
attributes, or set them. See below for a list of read-only attributes,
and those that are writable.  

In addition, the Bacula {\bf job} obbject in the Director has
a number of methods (subroutines) that can be called. They
are:
\begin{description}
\item [set\_events] The set\_events takes a single
   argument, which is the instantation of the Job Events class
   that contains the methods that you want called. The method
   names that will be called must correspond to the Bacula
   defined events. You may define additional methods but Bacula
   will not use them.
\item [run] The run method takes a single string
   argument, which is the run command (same as in the Console)
   that you want to submit to start a new Job. The value
   returned by the run method is the JobId of the job that
   started, or -1 if there was an error.
\item [write] The write method is used to be able to send
   print output to the Job Report. This will be described later.
\item [DoesVolumeExist] The DoesVolumeExist takes a single
   string argument, which is the Volume name, and returns 
   1 if the volume exists in the Catalog and 0 if the volume
   does not exist.
\end{description}

The following attributes are read/write within the Director 
for the {\bf job} object.

\begin{description}
\item [Priority] Read or set the Job priority.
Note, that setting a Job Priority is effective only before
the Job actually starts.  (not functional yet)
\end{description}

The following read-only attributes are available within the Director
for the {\bf job} object.

\begin{description}

\item [Level] This attribute contains a string representing the Job 
        level, e.g. Full, Differential, Incremental, ...
\item [Type]  This attribute contains a string representing the Job
        type, e.g. Backup, Restore, Verify, ...
\item [JobId] This attribute contains an integer representing the
        JobId.
\item [Client] This attribute contains a string with the name of the
          Client for this job.
\item [NumVols]  This attribute contains an integer with the number of
          Volumes in the Pool being used by the Job.
\item [Pool] This attribute contains a string with the name of the Pool
          being used by the Job.
\item [Storage] This attribute contains a string with the name of the
          Storage resource being used by the Job.
\item [Catalog]  This attribute contains a string with the name of the
          Catalog resource being used by the Job.
\item [MediaType] This attribute contains a string with the name of the
          Media Type associated with the Storage resource being used by the Job.
\item [Job] This attribute contains a string containing the name of the
           Job resource used by this job (not unique).
\item [JobName] This attribute contains a string representing the full
            unique Job name.
\item [JobStatus] This attribute contains a single character string
            representing the current Job status. The status may change
            during execution of the job.
\item [Priority]  This attribute contains an integer with the priority
          assigned to the job.
\item [CatalogRes] tuple consisting of (DBName, Address, User,
  Password, Socket, Port, Database Vendor) taken from the Catalog resource 
   for the Job with the exception of Database Vendor, which is
   one of the following: MySQL, PostgreSQL, SQLite, Internal,
   depending on what database you configured.
\item [VolumeName]
   After a Volume has been purged, this attribute will contain the
   name of that Volume. At other times, this value may have no meaning.
\end{description}

The following write-only attributes are available within the
Director:

\begin{description}
\item [JobReport] Send line to the Job Report.
\item [VolumeName] Set a new Volume name. Valid only during the
   NewVolume event.
\end{description}

\subsection*{Python Console Command}
\index[general]{Python Console Command}
\index[general]{Console Command!Python}
\addcontentsline{toc}{subsection}{Python Console Command}

There is a new Console command named {\bf python}. It takes
a single argument {\bf restart}. Example:
\begin{verbatim}
  python restart
\end{verbatim}

This command restarts the Python interpreter in the Director.
This can be useful when you are modifying the DirStartUp script,
because normally Python will cache it, and thus the
script will be read one time.


\subsection*{Python Example}
\index[general]{Python Example}
\index[general]{Example!Python}
\addcontentsline{toc}{subsection}{Python Example}

An example script for the Director startup file is provided in
{\bf examples/python/DirStartup.py} as follows:

\footnotesize
\begin{verbatim}
#
# Bacula Python interface script for the Director
#

# You must import both sys and bacula
import sys, bacula

# This is the list of Bacula daemon events that you
#  can receive.
class BaculaEvents:
  def __init__(self):
     # Called here when a new Bacula Events class is
     #  is created. Normally not used 
     noop = 1

  def JobStart(self, job):
     """
       Called here when a new job is started. If you want
       to do anything with the Job, you must register
       events you want to receive.
     """
     events = JobEvents()         # create instance of Job class
     events.job = job             # save Bacula's job pointer
     job.set_events(events)       # register events desired
     sys.stderr = events          # send error output to Bacula
     sys.stdout = events          # send stdout to Bacula
     jobid = job.JobId; client = job.Client
     numvols = job.NumVols 
     job.JobReport="Python Dir JobStart: JobId=%d Client=%s NumVols=%d\n" % (jobid,client,numvols) 

  # Bacula Job is going to terminate
  def JobEnd(self, job):    
     jobid = job.JobId
     client = job.Client 
     job.JobReport="Python Dir JobEnd output: JobId=%d Client=%s.\n" % (jobid, client) 

  # Called here when the Bacula daemon is going to exit
  def Exit(self, job):
      print "Daemon exiting."
     
bacula.set_events(BaculaEvents()) # register daemon events desired

"""
  These are the Job events that you can receive.
"""
class JobEvents:
  def __init__(self):
     # Called here when you instantiate the Job. Not
     # normally used
     noop = 1
     
  def JobInit:
     # Called when the job is first scheduled
     noop = 1
     
  def JobRun:
     # Called just before running the job after initializing
     #  This is the point to change most Job parameters.
     #  It is equivalent to the JobRunBefore point.
     noop = 1

  def NewVolume(self, job):
     # Called when Bacula wants a new Volume name. The Volume
     #  name returned, if any, must be stored in job.VolumeName
     jobid = job.JobId
     client = job.Client 
     numvol = job.NumVols
     volname = "TestA-001"
     job.JobReport = "JobId=%d Client=%s NumVols=%d VolumeName=%s" % (jobid, client, numvol,volname)
     job.JobReport="Python before New Volume set for Job.\n"
     job.VolumeName=volname

  def VolumePurged(self, job):
     # Called when a Volume is purged. The Volume name can be referenced
     #  with job.VolumeName
     noop = 1



\end{verbatim}
\normalsize
