#                Perforce Defect Tracking Integration Project
#                 <http://www.ravenbrook.com/project/p4dti/>
#
#             INIT.PY -- INITIALIZE REPLICATOR AND DEFECT TRACKER
#
#              Richard Brooksby, Ravenbrook Limited, 2000-12-08
#
#
# 1. INTRODUCTION
#
# This module must:
#
#  1. Supply default values for configuration parameters added since
# release 1.0.6.
#
#  2. Check the configuration parameters, construct configuration and generate
# a jobspec by calling the method configure_DT.configuration().
#
#  3. Construct the defect tracker object 'dt'.
#
#  4. Construct the replicator object 'r' and call its init() method.
#
#  5. Update the Perforce jobspec, unless it's the first time the replicator
# has run and there are existing jobs (see job000219 and job000240).
#
# The design for this module is given in [GDR 2000-09-13, 5].
#
# The intended readership of this document is project developers.
#
# This document is not confidential.

import imp
import os
import sys

# If the environment variable P4DTI_CONFIG is specified, then load the
# configuration from that file (this is so we can support multiple P4DTI
# instances using multiple configuration files, either for testing, or
# when there are multiple Perforce servers -- requirement 96).
#
# Don't load the P4DTI configuration if the config module is already
# loaded, regardless of the P4DTI_CONFIG environment variable.  This is
# so that a test harness can substitute its own preferred configuration.
# See [GDR 2001-03-14, 2.2].

if (not sys.modules.has_key('config')
    and os.environ.has_key('P4DTI_CONFIG')):
    file = open(os.environ['P4DTI_CONFIG'])
    try:
        imp.load_source('config', os.environ['P4DTI_CONFIG'], file)
    finally:
        file.close()

import catalog
import check_config
import config
import p4
import re
import replicator
import socket
import string

error = "P4DTI Initialization error"


# 2. CHECK PARAMETERS


# 2.1. Supply defaults for new parameters
#
# We supply default values for configuration parameters that have been
# added since the first supported release (1.0.7) so that if people use
# an old configuration file with a new P4DTI release, at least things
# won't break.  See job000347.

default_parameters = { 'teamtrack_version': '5.0',
                       'job_url': None,
                       'use_windows_event_log': 0,
                       }
for k, v in default_parameters.items():
    if not config.__dict__.has_key(k):
        config.__dict__[k] = v


# 2.2. Check generic parameters
#
# Defect tracker specific configuration parameters are checked by the
# configuration generator for that defect tracker.

check_config.check_identifier(config.rid, 'rid')
check_config.check_identifier(config.sid, 'sid')
if config.administrator_address != None:
    check_config.check_email(config.administrator_address,
                             'administrator_address')
check_config.check_string_or_none(config.log_file, 'log_file')
check_config.check_int(config.log_level, 'log_level')
check_config.check_string(config.p4_client_executable, 'p4_client_executable')
check_config.check_string(config.p4_port, 'p4_port')
check_config.check_string(config.p4_user, 'p4_user')
check_config.check_string(config.p4_password, 'p4_password')
check_config.check_string(config.p4_server_description,
                          'p4_server_description')
check_config.check_int(config.poll_period, 'poll_period')
check_config.check_function(config.replicate_p, 'replicate_p')
check_config.check_email(config.replicator_address, 'replicator_address')
if config.smtp_server != None:
    check_config.check_host(config.smtp_server, 'smtp_server')
check_config.check_date(config.start_date, 'start_date')
check_config.check_string_or_none(config.closed_state, 'closed_state')
check_config.check_changelist_url(config.changelist_url, 'changelist_url')
check_config.check_job_url(config.job_url, 'job_url')


# 3. CALL THE CONFIGURATION GENERATOR; MAKE A DEFECT TRACKER INTERFACE
#
# Import conifguration generator and defect tracker module based on names in
# the configuration.  We don't want people to have to edit this file (or any
# P4DTI files) just to add a new defect tracker or make a new configuration.
#
# If the configure_name parameter exists, we use that to make the name for the
# configuration module.  If not, we revert to dt_name.  This allows people to
# prepare advanced configurations by writing their own configuration generator
# and specifying it in the configure_name parameter.  See [IG, 8.6].

dt_name = string.lower(config.dt_name)
if config.__dict__.has_key('configure_name'):
    configure_name = config.configure_name
else:
    configure_name = dt_name
configure_module = __import__('configure_' + configure_name)
dt_module = __import__('dt_' + dt_name)

jobspec, config = configure_module.configuration(config)
dt = dt_module.__dict__['dt_' + dt_name](config)


# 4. MAKE A PERFORCE INTERFACE AND A "DEFECT TRACKER" FOR PERFORCE

p4_interface = p4.p4(client = ('p4dti-%s' % socket.gethostname()),
                     client_executable = config.p4_client_executable,
                     password = config.p4_password,
                     port = config.p4_port,
                     user = config.p4_user,
                     logger = config.logger)


# 5. UPDATE THE PERFORCE JOBSPEC
#
# Only update the Perforce jobspec if the configuration generator returned one.
# This allows people to make advanced configurations that use an existing
# Perforce jobspec [IG, 8.6].
#
# Take a look at the old jobspec.  If it has no P4DTI-rid field, then we assume
# that this is the first time the P4DTI has been run.  If so, check for the
# existence of jobs; if there are any, don't go ahead with the change to the
# jobspec but instead warn the administrator.  See job000219.

if jobspec != None:
    field_re = re.compile('^Fields[0-9]+$')
    p4dti_rid_re = re.compile('^[0-9]+ P4DTI-rid ')
    first_time = 1
    for k, v in p4_interface.run('jobspec -o')[0].items():
        if field_re.match(k) and p4dti_rid_re.match(v):
            first_time = 0
            break
    if first_time and p4_interface.run('jobs'):
        # "You must delete your Perforce jobs before running the P4DTI for the
        # first time.  See section 3.2.2 of the Administrator's Guide."
        raise error, catalog.msg(1001)
    p4_interface.run('jobspec -i', jobspec)


# 6. MAKE THE REPLICATOR AND INITIALIZE IT

r = replicator.replicator(dt, p4_interface, config)
r.init()


# A. REFERENCES
#
# [GDR 2000-09-13] "Replicator design"; Gareth Rees; Ravenbrook Limited;
# 2000-09-13.
#
# [GDR 2001-03-14] "test_p4dti.py -- Test the P4DTI"; Gareth Rees;
# Ravenbrook Limited; 2001-03-14.
#
# [IG] "Perforce Defect Tracking Integration Integrator's Guide" (living
# document); Gareth Rees; Ravenbrook Limited; 2000-10-16.
#
#
# B. Document History
#
# 2000-11-30 GDR Added changelist_url configuration parameter.
#
# 2000-12-04 GDR Alphabetized parameters.  Added replicator_address.  Made sure
# to pass all parameters to configure_teamtrack.
#
# 2000-12-08 RB Moved configuration parameters to config.py.  Merged separate
# TeamTrack and Bugzilla activation scripts.
#
# 2001-01-11 NB Bugzilla startup has changed because configure_bugzilla now
# opens the MySQL connection.
#
# 2001-01-18 NB bugzilla_user has gone.
#
# 2001-01-25 NB Added bugzilla_directory.
#
# 2001-02-04 GDR Added start_date parameter.
#
# 2001-02-16 NB Added replicate_p configuration parameter.
#
# 2001-03-02 RB Transferred copyright to Perforce under their license.
#
# 2001-03-13 GDR Removed verbose parameter; added log_level.
#
# 2001-03-14 GDR Pass rid and sid parameters to configure_teamtrack.
#
# 2001-03-15 GDR Formatted as a document.  Use messages when raising errors.
# Store configuration in config module.
#
# 2001-03-22 GDR Use the configure_name parameter, if specified, to make the
# name of the configuration generator.  Don't update the jobspec if the
# configuration generator returned None.
#
# 2001-07-09 NB Added job_url config parameter.
#
# 2001-07-14 GDR Supply default values for new configuration parameters
# to fix job000347.  Removed try/except around imports; this was
# obscuring other import errors; see job000315 and job000336.
#
# 2001-08-07 GDR Added support for the P4DTI_CONFIG environment
# variable.
#
# 2001-09-12 GDR Added `use_windows_event_log' configuration parameter.
#
# 2001-09-24 GDR Don't reload the P4DTI configuration if already loaded.
#
#
# C. COPYRIGHT AND LICENCE
#
# This file is copyright (c) 2001 Perforce Software, Inc.  All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1.  Redistributions of source code must retain the above copyright notice,
#     this list of conditions and the following disclaimer.
#
# 2.  Redistributions in binary form must reproduce the above copyright notice,
#     this list of conditions and the following disclaimer in the documentation
#     and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
#
# $Id: //info.ravenbrook.com/project/p4dti/version/1.2/code/replicator/init.py#1 $
