@python -x "%~f0" %* & goto :EOF

import sys
import os
import string
import re 
import time
import shutil
import socket
import UserList
import win32con
import win32api
import win32pipe
import win32net
import win32service
import win32file
import winerror
import pywintypes
import types

from win32con import NULL
from com.creo.win32 import pypsapi
from com.creo.win32 import win32io
from com.creo.win32 import win32x
from pywintypes import FALSE, TRUE

UTIL_VERSION = '1.1'
BANNER = 'Orbacus configuration utility version %s. ' % UTIL_VERSION

# Service Globals
MAX_WAIT = 300
CHK_INTERVAL = 1

# Timeout values for process
PROCESS_LOOP_COUNT = 50
PROCESS_SLEEP = 5

# IMR WAIT
# Wait in seconds for IMR to startup and Stop
IMR_SLEEP = 5

# Sets up global variabLes
_araxiHomeDirectory = None
_machineName = None
_creoHomeDirectory = None
_pathAddCounter = 0
_pathRemoveCounter = 0
_logFile = '.\\OrbacusConfig.log'


# path to backup folder which will contain previous Orbacus configuration files
_pathOrbacusLastBackupFolder = None

# Command line options
_corbaServerName = None
_initializeOrbacus = FALSE
_createCorbaServer = FALSE
_listCorbaServers = FALSE
_showCorbaServer = FALSE
_startCorbaServer = FALSE
_stopCorbaServer = FALSE
_startIMR = FALSE
_stopIMR = FALSE
_addPath = FALSE
_addRegistry = FALSE
_verbose = FALSE
_debug = FALSE


# Max try to create NameService in corba
MAX_NAMESERVICE_TRY = 10

# Orbacus info string
ORBACUS_INFO_ID = 'ID:'
ORBACUS_INFO_STATUS = 'Status:'
ORBACUS_INFO_HOST = 'Host:'
ORBACUS_INFO_PATH = 'Path:'
ORBACUS_INFO_RUNDIR = 'RunDir:'
ORBACUS_INFO_ARGUMENTS = 'Arguments:'
ORBACUS_INFO_ACTIVATION_MODE = 'Activation Mode:'
ORBACUS_INFO_POA_ACTIVATION = 'POA Activation:'
ORBACUS_INFO_UPDATE_TIMEOUT = 'Update timeout (ms):'
ORBACUS_INFO_FAILURE_TIMEOUT = 'Failure timeout (secs):'
ORBACUS_INFO_MAX_SPAWN = 'Maximum spawn count:'
ORBACUS_INFO_START_MANUALLY = 'Started manually:'
ORBACUS_INFO_NUMBER_SPAWNED = 'Number of times spawned:'

ORBACUS_SERVER_NAMESERVICE = 'NameService'
ORBACUS_INFO_RUNNING = 'running'
ORBACUS_INFO_STOPPED = 'stopped'

# IMR Admin command string
IMRADMIN_ADD   = '--add-server'
IMRADMIN_GET   = '--get-server-info'
IMRADMIN_SET   = '--set-server'
IMRADMIN_STOP  = '--stop-server'
IMRADMIN_START = '--start-server'
IMRADMIN_LIST  = '--list-servers'

# Known product constants (values are case sensitive)
PRODUCT_PRINERGY = 'Prinergy'
PRODUCT_INSITE   = 'InSite'

# Log Message Levels
VERBOSE = 0
ERROR = 1
WARNING = 2
INFO = 3
ALWAYS = 4

# Help screen
USAGE_TEXT = \
'''\
Usage: OrbacusConfig [-options]

Options:

    -init                       \tInitialize Orbacus configuration
    -list                       \tList Corba servers
    -show <Corba server>        \tDisplay Corba server configuration
    -start <Corba server>       \tStart a Corba server
    -stop <Corba server>        \tStop a Corba server
    -startIMR                   \tStart the IMR
    -stopIMR                    \tStop the IMR
    -addPath                    \tAdd Orbacus to PATH environment variable
    -addRegistry                \tCreate or update OOC registry key
    -verbose                    \tverbose mode.
    -?, -h, -help               \tshow help
'''

HELP_TEXT = \
'''\
Configures Orbacus and displays configuration of Orbacus servers.
'''

# Misc constants
DEFAULT_VERBOSITY = 1

EXIT_SUCCESS = 0
EXIT_FAILURE = 1

#TRUE = (0 == 0)
#FALSE = (0 == 1)

RESERVED = 0

# Environment path registry location
REG_PATH_VALUE = 'Path'
REG_ENVIRONMENT_KEY = r'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment'

def main( ):
    LogMessage( BANNER, ALWAYS )
    # get command line options
    getopts()

    CheckRequirements( )

    if _initializeOrbacus == TRUE:
        install()
#    elif _createCorbaServer == TRUE:
#        print _corbaServerName
    elif _listCorbaServers == TRUE:
        ListCorbaServers( )
    elif _showCorbaServer == TRUE:
        DisplayCorbaServer( _corbaServerName )
    elif _startCorbaServer == TRUE:
        StartCorbaServer( _corbaServerName )
    elif _stopCorbaServer == TRUE:
        StopCorbaServer( _corbaServerName )
    elif _startIMR == TRUE:
        StartIMR()
    elif _stopIMR == TRUE:
        StopAllCorbaServers()
        StopIMR()
    elif _addPath == TRUE:
        AddOrbacusPath()
    elif _addRegistry == TRUE:
        CreateOrbacusRegistry()

    else:
        PrintUsageAndExit()

#----------------------------------------------------------------------------
# getopts
#   Get options from command line
# 
def getopts( ):
    global _initializeOrbacus
    global _corbaServerName
    global _createCorbaServer
    global _listCorbaServers
    global _showCorbaServer
    global _startCorbaServer
    global _stopCorbaServer
    global _startIMR
    global _stopIMR
    global _addPath
    global _addRegistry
    global _verbose
    global _debug
    argc = len( sys.argv )
    i = 1
    while i < argc:
        arg = sys.argv[i]
        argUC = string.upper( arg )
        if argUC in [ '-?', '-H', '-HELP' ]:
            PrintUsageAndExit()
        elif argUC == '-INIT':
            _initializeOrbacus = TRUE
        elif argUC == '-CREATE':
            i = i + 1
            if ( i >= argc ):
                PrintUsageAndExit( 'Option %s requires argument' % arg, EXIT_FAILURE )
            _corbaServerName = sys.argv[i]
            _createCorbaServer = TRUE
        elif argUC == '-SHOW':
            _showCorbaServer = TRUE
            i = i + 1
            if ( i >= argc ):
                PrintUsageAndExit( 'Option %s requires argument' % arg, EXIT_FAILURE )
            _corbaServerName = sys.argv[i]

        elif argUC == '-START':
            _startCorbaServer = TRUE
            i = i + 1
            if ( i >= argc ):
                PrintUsageAndExit( 'Option %s requires argument' % arg, EXIT_FAILURE )
            _corbaServerName = sys.argv[i]

        elif argUC == '-STOP':
            _stopCorbaServer = TRUE
            i = i + 1
            if ( i >= argc ):
                PrintUsageAndExit( 'Option %s requires argument' % arg, EXIT_FAILURE )
            _corbaServerName = sys.argv[i]
        elif argUC == '-LIST':
            _listCorbaServers = TRUE
        elif argUC == '-STARTIMR':
            _startIMR = TRUE
        elif argUC == '-STOPIMR':
            _stopIMR = TRUE
        elif argUC == '-ADDPATH':
            _addPath = TRUE
        elif argUC == '-ADDREGISTRY':
            _addRegistry = TRUE
        elif argUC == '-VERBOSE':
            _verbose = TRUE
        elif argUC == '-DEBUG':
            _debug = TRUE
            _verbose = TRUE
        elif argUC[0] == '-':
            PrintUsageAndExit( 'Invalid option: %s' % arg, EXIT_FAILURE )
        else:
            PrintUsageAndExit( 'Extra argument: %s' % arg, EXIT_FAILURE )
        i = i + 1
        
    #print self.opt.node
    #print self.node.Name()


#----------------------------------------------------------------------------
# PrintUsageAndExit
#   Display Help screen and exit.
# 
def PrintUsageAndExit( errmsg='', exitcode=EXIT_SUCCESS ):
    print USAGE_TEXT
    if errmsg:
        print errmsg
        print
    else:
        print HELP_TEXT
    #sys.exit( exitcode )
    sys.exit( 0 )

#----------------------------------------------------------------------------
# install
#   Install will:
#       - Back up current configuration
#       - Install NameService and all other Corba services.
# 
def install( ):
    
    try:
        if IsServiceStopped( 'Araxi' ) == FALSE:
            LogMessage( 'Cannot configure Orbacus, Prinergy is still running.', ERROR )
            raise Exception('Cannot configure Orbacus while Prinergy is running.')

        CreateOrbacusBackupFolder()

        # Configure Orbacus
        ConfigureOrbacus() 

    except:
        raise Exception( err() )

#----------------------------------------------------------------------------
# LogMessage
#   Log message to screen.
#   If verbose enabled, display all messages. Otherwise just display info, warnings and errors.
#  
def LogMessage ( msg, level=0, trace=1 ):

    dateTimeFormat = '%b %d %Y %H:%M:%S: '
    dateTime = time.strftime( dateTimeFormat, time.localtime( time.time( ) ) )

    if level == ERROR:
        msg = 'Error: ' + msg

    elif level == WARNING:
        msg = 'Warning: ' + msg

    elif level == INFO:
        msg = 'Info: ' + msg

    if level != INFO and level != VERBOSE or _verbose == TRUE:
        print msg

    logFile = open( _logFile , 'a' )
    msg = msg + '\n'
    logFile.write( dateTime + msg )
    logFile.close()


#----------------------------------------------------------------------------
# CheckRequirements
#
#   Checks the basic requirements before Major Update can be installed.
#   The idea is to find any exceptions that can prevent us from installing
#   the Major Update.
#   Also, global variables are set in this function.
#
def CheckRequirements( ):
    global _araxiHomeDirectory
    global _machineName
    global _creoHomeDirectory

    try:
        # COMMON section
         
        try:       
            # Get Araxi home directory
            _araxiHomeDirectory = os.environ['ARAXIHOME']
        except:
            LogMessage( "Environment variable 'ARAXIHOME' is not defined.", ERROR )
            raise
        
        try:
            # Get computer name
            _machineName = win32api.GetComputerName()
        except:
            LogMessage( 'Computer name is not defined.', ERROR )
            raise
       
        try:    
            # Get CREOHOME environment variable
            _creoHomeDirectory = os.environ['CREOHOME']
        except:
            LogMessage( "Environment variable 'CREOHOME' is not defined.", ERROR )
            raise
        
    except:

        raise Exception( err() )

  

#----------------------------------------------------------------------------
# ORBACUS
#----------------------------------------------------------------------------

#----------------------------------------------------------------------------
# ConfigureOrbacus
#
#   This function will configure Orbacus, by creating the registry entries, and
#   creating services for Orabcus.  IMR.exe has to be running while we configure
#   Orbacus.
#    
def ConfigureOrbacus():
    # print message to screen and ppu log file
    LogMessage( 'Configuring Orbacus.', ALWAYS )

    # Add Orbacus path to the PATH environment variable.
    AddOrbacusPath( )

    # Create Orbacus registry
    CreateOrbacusRegistry( )
   
    # Generate orbacus config files
    GenerateOrbacusConfigFiles()
    
    # Create Corba Servers
    CreateCorbaServers()


#----------------------------------------------------------------------------
# AddOrbacusPath
#
#   This function will add IonaOrbacus bin path to the path
#   environment variable.  In order for the change to take
#   effect on the workstation a reboot is required.
#
def AddOrbacusPath( ):
    # print message to screen and ppu log file
    LogMessage( 'Adding Orbacus path to PATH environment.', ALWAYS )

    # Generic error message
    errmsg = 'Failed to add Orbacus path to PATH environment variable.'
    
    try:    
        # Orbacus bin path
        orbacusPath = os.path.join( _araxiHomeDirectory, 
                                    'IonaOrbacus', 
                                    'bin' )
        # Add path and create the undo and uninstall action.                                
        AddPathWithoutUndo( orbacusPath ) 
    
    except:
        raise Exception('Error: %s\nDetails: %s' % ( errmsg, err() ) )

#----------------------------------------------------------------------------
# CreateOrbacusRegistry
#
#   This function will create IonaOrbacus registry structure.
#   This is done by creating an Orbacus registry file from the template
#   directory with tokens replaced.  Then this Orbacus registry file is
#   used with regupdate.exe to create the Orbacus registry structure.
#   The registry key for IonaOrbacus is Software\OOC.
#
def CreateOrbacusRegistry( ):
    # print message to screen and ppu log file
    LogMessage( 'Creating/updating Orbacus registry.', ALWAYS )

    # Generic error message
    errmsg = 'Failed to create/update Orbacus registry.'
    
    # Iona Orbacus registry key
    orbacusKeyPath = r'HKEY_LOCAL_MACHINE\SOFTWARE\OOC'
   
    # Registry file
    regFileName = 'OrbacusReg.txt'   
    
    # Iona Orbacus bin directory where regupdate.exe can be found.                             
    orbacusBinPath =  os.path.join( _araxiHomeDirectory, 
                                   'IonaOrbacus', 
                                   'bin' )
    # Full path of regupdate.exe.                           
    regUpdateExe = os.path.join( orbacusBinPath, 'regupdate.exe' )
    
    # Template path where the source registry file
    # can be found.
    templatePath = os.path.join( _araxiHomeDirectory, 
                                'CreoAraxi', 
                                'setup', 
                                'Config', 
                                'TemplateFiles' )
                            
    # Config path where the new registry file will be created
    # with tokens replaced.                            
    orbacusConfigPath = os.path.join( _araxiHomeDirectory, 
                                      'IonaOrbacus', 
                                      'Config' )

    # Full path of the new orbacus registry file with tokens replaced.
    orbacusRegFile = os.path.join( orbacusConfigPath, regFileName )


    
#    # If the Orbacus registry key already exists, check to see if there is already an ip address stored there. 
#    # Substitute ip address in place of host name in Orbacus registry. %HOST_NAME% will be replaced by the ip
#    # address in the Orbacus registry file.
#
#    hostIpAddress = CheckOrbacusIpAddress()
#    LogMessage( 'Using ip address %s for %s.' % ( hostIpAddress, _machineName ), INFO )
#
#    # Create a dictionary that will be used to replace tokens 
#    # in the file with.

    dictionary = { '%HOST_NAME%': _machineName, 
                   '%PRIMARY_SERVER_NAME%': GetPrimaryName() }

#    dictionary = { '%HOST_NAME%': hostIpAddress, 
#                   '%PRIMARY_SERVER_NAME%': GetPrimaryName() }
    
    # Create a new orbacus registry file with tokens replaced.
    try:
        ReplaceTokensInFile( templatePath, regFileName, orbacusConfigPath, regFileName, dictionary )
        
        ExecuteCommand( regUpdateExe, 
                        inputPipeMessage=None,
                        rundir = orbacusBinPath, 
                        executableArguments=r'HKEY_LOCAL_MACHINE %s' % orbacusRegFile )
    except:
        raise Exception('Error: %s\nDetails: %s' % ( errmsg, err() ) )
                              
    # Check the registry to see if the key has been created.
    if ( not DoesKeyExist( orbacusKeyPath ) ):
        raise Exception( 'Error: %s\nDetails: Cannot find Orbacus registry key "%s".' % ( errmsg, orbacusKeyPath ) )
    
     
    # Set Prinergy specific Orbacus registry value always.
    regKeyPath = GetAraxiRegPath( 'JOBL' )
    if ( DoesKeyExist( regKeyPath ) ):
        RegInstallValue(regKeyPath, 
                        'JobConfigFilePath', 
                        win32con.REG_SZ, 
                        r'IonaOrbacus\config\JobServer.config' )
        multiString = ['Where to find the config file for Job Servers.\n','\n\n']
        RegInstallValue(regKeyPath, 
                        'JobConfigFilePathComment', 
                        win32con.REG_MULTI_SZ, 
                        multiString )

    LogMessage( 'Orbacus registry created/updated.', INFO )



#----------------------------------------------------------------------------
# CreateCorbaServers
#
#   This function will start imr and create the corba servers.  Once the servers
#   are created, Imr is stopped and the database configuration is backed up.
#
def CreateCorbaServers():

    # Ensure all Corba servers are stopped before we start.
    StopAllCorbaServers()

    # Ensure NameService is not running from previously failed attempt at configuring Orbacus
    StopNameService()

    # Ensure IMR is not running from previously failed attempt at configuring Orbacus
    StopIMR()

    # Backup Previous Orbacus configuration and delete it.
    RemovePreviousOrbacusConfiguration()

    # Startup the IMR service. 
    StartIMR()
        
    # Add NameService
    AddNameService()
    
    # Add NotificationService only if the executable exists
    serviceExeFile = os.path.join( _araxiHomeDirectory, 'IonaOrbacus', 'bin', 'notserv.exe' )
    if os.path.isfile( serviceExeFile ):
        AddOrbacusService( 'NotificationService', serviceExeFile, 'NotificationService.config')
       
    # Add the remainder CorbaServices 
    AddCorbaServices()
    
    # Kill IMR.exe
    # In the future we should keep the input of the pipe and pass a ^c command to close the
    # imr.exe. Instead of killing the process. 
    KillProcess( 'imr.exe', ignore=1 )
    
    # Backup the configuration files in a backup directory.
    # Just in case later on somehow Orbacus configuration gets
    # corrupted. If this happens the user can copy back the backed up files and 
    # restore the state of Orbacus.
    BackupOrbacusConfiguration()
    

#----------------------------------------------------------------------------
# RemovePreviousOrbacusConfiguration
#
#   This function will remove existing Orbacus configuration
#   if it exists, in preparation of loading the new configuration.
#
def RemovePreviousOrbacusConfiguration():
    # print message to screen and ppu log file
    LogMessage( 'Removing previous Orbacus configuration.', INFO )

    orbacusPath = os.path.join( _araxiHomeDirectory, 
                                   'IonaOrbacus' )

    orbacusBinPath = os.path.join( orbacusPath, 
                                   'bin' )
    
    # Remove the IonaOrbacus\bin\Names.db file.
    EraseFile( orbacusBinPath, 'Names.db' )
    
    # Delete the contents of IonaOrabacus\bin\IMR_Database directory.
    EraseFilesRecursive( os.path.join( orbacusBinPath, 'IMR_Database' ) )
    
      
#----------------------------------------------------------------------------
# GenerateOrbacusConfigFiles
#
#   This function will search the templates directory in CreoAraxi for
#   all Orbacus configuration file (*.config).  For each file it will
#   create a new configuration file in IonaOrbacus\config with tokens
#   replaced.
#
def GenerateOrbacusConfigFiles():
    # print message to screen and ppu log file
    LogMessage( 'Generating Orbacus config files.', ALWAYS )
 
    # Generic error message
    errmsg = 'Cannot create orbacus configuration files.'
    
    # Source path where the template files reside 
    templatePath = os.path.join( _araxiHomeDirectory, 
                           'CreoAraxi', 
                           'setup', 
                           'config', 
                           'TemplateFiles' )
    # Destination path where the new files will be created with 
    # tokens replaced.                           
    orbacusConfigPath = os.path.join( _araxiHomeDirectory, 
                             'IonaOrbacus', 
                             'config' )
    
    # Orbacus path
    orbacusPath = os.path.join( _araxiHomeDirectory, 
                                'IonaOrbacus' )
    
    
    # path in the config file has to be separated by \\ not \
    orbacusPath = string.replace( orbacusPath, '\\', '\\\\' )
    
    # Create a dictionary that will be used to replace tokens 
    # in the file with.
    dictionary = { '%HOST_NAME%': _machineName, 
                   '%PRIMARY_SERVER_NAME%': GetPrimaryName(),
                   '%ORBACUS_HOME%' : orbacusPath }

    try:

        # Set the paths
        orbacusPath = os.path.join( _araxiHomeDirectory, 'IonaOrbacus' )
        orbacusConfigPath = os.path.join( orbacusPath, 'Config' )

        # Current Config folder does not exist
        if not os.path.exists( orbacusConfigPath ):
            raise Exception( 'Orbacus Config folder %s does not exist!' % orbacusConfigPath )

        # No path to backup folder
        if _pathOrbacusLastBackupFolder == None:
            raise Exception( 'Orbacus backup folder was not defined!' )

        LogMessage( 'Backing up Orbacus config folder.', INFO )
        shutil.copytree( orbacusConfigPath, os.path.join( _pathOrbacusLastBackupFolder, 'Config' ))
    
        # create a list with all the files in the template directory.
        dirList = os.listdir( templatePath )
        # Pattern matching string
        pattern =  re.compile('.*\.config$', re.IGNORECASE)
        
        for file in dirList:
            matchString =  pattern.match( file )
            
            # If the file is an Orbacus config file then create a new
            # file with the tokens replaced.    
            if ( matchString ):
                # Make sure it is a file and not a directory
                if ( os.path.isfile( os.path.join( templatePath, file ) ) ) :
                    # File found so create a new file with tokens replaced in the 
                    # IonaOrbacus\config directory.
                    ReplaceTokensInFile( templatePath, file, orbacusConfigPath, file, dictionary )
    except:
        raise Exception( 'Error: %s\nDetails: %s).' % ( errmsg, err() ) )
        
#----------------------------------------------------------------------------
# AddCorbaServices
#
# Description: This function looks in the IonaOrbacus\config directory, finds all
#              the '.config' files (excluding NameService.config and IMR.config).
#              It uses these config files to extract information to properly
#              add and configure each CORBA server to the IMR.  The information extracted
#              includes: serverName, the executable name and whether the server is a
#              singleton server (can run only on the Primary or not) or not.
#
def AddCorbaServices():
    # print message to screen and ppu log file
    LogMessage( 'Adding Corba services to Orbacus.', ALWAYS  )

    # Generic error message
    errmsg = 'Cannot create Corba service.'  
     
    configDir = os.path.join( _araxiHomeDirectory, 
                              'IonaOrbacus', 
                              'config' )
    try:
    
        # create a list with all the files in the directory.
        dirList = os.listdir( configDir )
        pattern =  re.compile('.*\.config$', re.IGNORECASE)
        for file in dirList:
            matchString =  pattern.match( file)
            
            if ( matchString ):
                # Make sure the match is a file and not a directory
                if ( os.path.isfile( os.path.join( configDir, file ) ) ):
                    
                    # Get the service name from the file        
                    retLine = FindLineInFile( configDir + os.sep + file, 'ServerName=', ignoreComment = '#' )
                    if ( not retLine ):
                        LogMessage( 'Could not find string "ServerName=" in file "%s".' % file )
                        LogMessage( 'Config file "%s" skipped.' % file )
                        continue
                    pos = string.find( retLine, '=' )
                    serviceName = string.strip( retLine[pos + 1:] )
                    if serviceName == '...': # the template config file has '...' as service name
                        LogMessage( 'Config file "%s" skipped.' % file )
                        continue
                    
                    # Get the service exe name from the file        
                    retLine = FindLineInFile( configDir + os.sep + file, 'ExeName=', ignoreComment = '#' )
                    if ( not retLine ):
                        raise Exception( 'Could not find string "ExeName=" in file "%s".' % file )
                    pos = string.find( retLine, '=' )
                    serviceExe = string.strip( retLine[pos + 1:] )
                
                    # Get the Singleton from the file        
                    retLine = FindLineInFile( configDir + os.sep + file, 'IsSingleton=', ignoreComment = '#' )
                    if ( not retLine  ):
                        raise Exception( 'Could not find string "IsSingleton=" in file "%s".' % file )
                    pos = string.find( retLine, '=' )
                    serviceSingletonStr = string.strip( retLine[pos + 1:] )
                    if ( string.lower( serviceSingletonStr ) == 'true' ):
                        bServiceSingleton = TRUE
                    elif( string.lower( serviceSingletonStr ) == 'false' ):
                        bServiceSingleton = FALSE
                    else:
                        raise Exception( 'Found string "IsSingleton=" in file "%s".\nCannot get singleton type (true, false).' % file )
                        
                    # Remember, we only want to do add Singleton servers to the IMR if we're on the
                    # Primary server. 
                    if ( IsPrimaryServer() or IsEPMServer() ):
                        serviceExeFile = os.path.join( _araxiHomeDirectory, 'CreoAraxi', 'bin', serviceExe )
                        # Create the Orbacus service
                        AddOrbacusService( serviceName, serviceExeFile, file )      
                    elif( not bServiceSingleton ):
                        # Secondary install we only create service if it is not a singleton
                        serviceExeFile = os.path.join( _araxiHomeDirectory, 'CreoAraxi', 'bin', serviceExe )
                        AddOrbacusService( serviceName, serviceExeFile, file )      
                    
    except:
        raise Exception( 'Error: %s\nDetails: %s).' % ( errmsg, err() ) )
                    
      
#----------------------------------------------------------------------------
# AddOrbacusService
#
# Description: This function adds a single Orbacus service, sets the argument
#              parameters, and updates the Max-spawns.
#
def AddOrbacusService( serviceName, serviceExeFile, serviceConfig ):
    # print message to screen and ppu log file
    LogMessage( 'Adding "%s" service to Orbacus.' % serviceName , ALWAYS )
   
    # Generic error message
    errmsg = 'Cannot add orbacus service "%s".' % serviceName
    
    # Full path to the service configuration file
    configFile = os.path.join( _araxiHomeDirectory, 
                               'IonaOrbacus', 
                               'config', 
                               serviceConfig )
    
    # Make sure the service executable exists.
    if ( not os.path.isfile( serviceExeFile ) ):
        raise Exception( 'Error: %s\nFile %s does not exist.' % ( errmsg, serviceExeFile ) )
    
    # Make sure the service config exists.
    if ( not os.path.isfile( configFile ) ):
        raise Exception( 'Error: %s\nFile %s does not exist.' % ( errmsg, configFile ) )
    
    #  
    #** Create Orbacus service
    ExecuteIMRAdmin( IMRADMIN_ADD, serviceName, serviceExeFile )
    # Check if Service has been added
    CheckOrbacusConfiguration( serviceName, ORBACUS_INFO_PATH, serviceExeFile )
   
    
    #** Change service arguments
    ExecuteIMRAdmin( IMRADMIN_SET, serviceName, 'args "-ORBconfig %s"' % configFile )
    # Check if Name Service argument has been added
    CheckOrbacusConfiguration( serviceName, ORBACUS_INFO_ARGUMENTS, '-ORBconfig %s' % configFile )
    
    
    #** Change maximum spawn to 32767
    ExecuteIMRAdmin( IMRADMIN_SET, serviceName, 'max-spawns 32767' )
    # Check if max spawns has be set to 32767
    CheckOrbacusConfiguration( serviceName, ORBACUS_INFO_MAX_SPAWN, '32767' )

#----------------------------------------------------------------------------
# AddNameService
#
#   This function will create and configure the NameService for Orbacus.
#
def AddNameService():
    # print message to screen and ppu log file
    LogMessage( 'Adding "NameService" service to Orbacus.', ALWAYS )

    # Service Name
    serviceName = ORBACUS_SERVER_NAMESERVICE
    
    # Generic errmsg
    errmsg = 'Cannot create orbacus service %s.' % serviceName
    
   
    # Full path of the name service executable
    orbacusBinDir = os.path.join( _araxiHomeDirectory, 
                                   'IonaOrbacus', 
                                   'bin') 
    nameServiceExe = os.path.join( orbacusBinDir, 
                                   'nameserv.exe' )
    
    # Full path of the name service configuration file
    orbacusConfigDir = os.path.join( _araxiHomeDirectory, 
                               'IonaOrbacus', 
                               'config' )
    configFile = os.path.join( orbacusConfigDir, 
                               'NameService.config' )
    try:
        
        # Make sure name service executable exists.
        if ( not os.path.isfile( nameServiceExe ) ):
            raise Exception( 'File "%s" does not exist.' % nameServiceExe )
        
        # Make sure the service configuration file exists.
        if ( not os.path.isfile( configFile ) ):
            raise Exception( 'File "%s" does not exist.' % configFile )
        #  
        #** Create Name Service
        #  We try for a number of time.  There is some timing issues and the
        # only way to get around it is to try it serveral times.
        for loopcount in range( MAX_NAMESERVICE_TRY ):
            output = ExecuteIMRAdmin( IMRADMIN_ADD, serviceName, nameServiceExe )
            if ( string.find( string.upper( output ), 'OBJECT_NOT_EXIST' ) != -1 ):
                LogMessage( 'Failed to create NameService attempt %d of %d' % ( loopcount, MAX_NAMESERVICE_TRY ) )
                # failed so we delete the 
                KillProcess( 'imr.exe', ignore=1 )
                RemoveDir( os.path.join( orbacusBinDir, 'IMR_Database' ) )
                StartIMR()
            else:
                break
    
        # Check if Name Service has been added
        CheckOrbacusConfiguration( serviceName, ORBACUS_INFO_PATH, nameServiceExe )
        
        #** Change service arguments
        ExecuteIMRAdmin( IMRADMIN_SET, serviceName, 'args "-ORBconfig %s -s"' % configFile )
        # Check if Name Service argument has been added
        CheckOrbacusConfiguration( serviceName, ORBACUS_INFO_ARGUMENTS, '-ORBconfig %s -s' % configFile )
         
        #** Change mode of NameService to persistent
        ExecuteIMRAdmin( IMRADMIN_SET, serviceName, 'mode persistent' )
        # Check if Name Service mode has been modified to persistent
        CheckOrbacusConfiguration( serviceName, ORBACUS_INFO_ACTIVATION_MODE, 'persistent' )
        
        #** Change maximum spawn to 32767
        ExecuteIMRAdmin( IMRADMIN_SET, serviceName, 'max-spawns 32767' )
        # Check if Name Service max spawns has be set to 32767
        CheckOrbacusConfiguration( serviceName, ORBACUS_INFO_MAX_SPAWN, '32767' )
        
        # if we get to here, then the NameService was added properly.
        # Note: it will have been started up by the IMR once it was created
        # as a persistent server.  This will intialize the NameService database.
        # now we can stop the NameService and reset the arguments.
        # Stop NameService
        ExecuteIMRAdmin( IMRADMIN_STOP, serviceName )
        if ( not WaitForProcessToStop( 'nameserv.exe',ignore=1 ) ):
            # try again the second time and it will work.
            ExecuteIMRAdmin( IMRADMIN_STOP, serviceName )
           
        #** Change service arguments
        ExecuteIMRAdmin( IMRADMIN_SET, serviceName, 'args "-ORBconfig %s"' % configFile )
        CheckOrbacusConfiguration( serviceName, ORBACUS_INFO_ARGUMENTS, '-ORBconfig %s' % configFile )
    except:
        raise Exception( 'Error: %s\nDetails: %s' % ( errmsg, err() ) )
            
            
#----------------------------------------------------------------------------
# CheckOrbacusConfiguration
#
#   This function will check if Orbacus service has be configure
#   properly by retrieving the Service information and comparing
#   with the string supplied.
#
def CheckOrbacusConfiguration( serviceName, orbacusinfo, strToken ):

    # Generic error message
    # Get name service path information
    bInfoFound, retString, output = GetIMRInfo( serviceName, orbacusinfo )
   
    if ( not bInfoFound ):
        raise Exception( '"%s" could not be found.\nOutput: %s' % (orbacusinfo, output ) )
    elif( string.upper( retString ) != string.upper( strToken ) ):
        raise Exception( '"%s" does not match "%s".\nOutput: %s' % ( strToken, retString, output ) )


    
#----------------------------------------------------------------------------
# 
# StartIMR
#
#   This function will start IMR.exe.  IMR.exe has to remain running while
#   Orbacus is being configured.  Therefore, we do not monitor the output
#   or else the process will wait for IMR.exe to finish.  Instead we let
#   it run and later on we kill the process.
#
def StartIMR( wait=IMR_SLEEP ):
    # print message to screen and ppu log file
    LogMessage( 'Starting IMR.', ALWAYS )

    # Generic error message
    errmsg = 'Cannot start IMR.'
    
    configFile = os.path.join( _araxiHomeDirectory, 
                               'IonaOrbacus', 
                               'config', 
                               'IMR.config' )
    
    try:                           
        # Make sure the config file exists.
        if ( not os.path.isfile( configFile ) ):
            raise Exception( 'File "%s" does not exist.' % configFile )
    
        # Create the command line to launch.
        commandLine = 'imr.exe -ORBconfig %s' % configFile
       
        # Change path before launching command line.
        # IMR.exe has to be launched from IonaOrbacus\bin directory
        destPath = os.path.join( _araxiHomeDirectory, 'IonaOrbacus', 'bin' )
        
        LogMessage( 'Executing: %s' % commandLine )
        currentDir = PushDir( destPath )
          
        try:
            # Execute commandLine and pipe stderror and stdout to a variable
            pipeOut = win32pipe.popen(commandLine ,'r')
        finally:
            PopDir( currentDir )
        
        # Wait few seconds before we check the process
        time.sleep( wait )
                
        # We don't check the ouput, because we need IMR.exe running while
        # we continue on and configure Orbacus.

        # Check if IMR Process is running
        WaitForProcessToStart( 'imr.exe' )
        
    except:
        raise Exception( 'Error: %s\nDetails: %s' % ( errmsg, err() ) )
    
    LogMessage( 'IMR Started.', INFO )


def StopIMR():
    # print message to screen and ppu log file
    LogMessage( 'Stopping IMR.', ALWAYS )

    # Generic error message
    errmsg = 'Cannot stop IMR.'

    process = 'imr.exe'
    try:     
        if IsProcessRunning( process ) == TRUE:
            KillProcess( process, ignore=1 )
        time.sleep(1)
        if IsProcessRunning( process) != FALSE:
            raise
    except:
        LogMessage( 'IMR is still running, could not stop IMR.EXE', ERROR )
        raise Exception('Could not kill IMR.')


def StopNameService():
    # print message to screen and ppu log file
    LogMessage( 'Stopping NameService.', INFO )

    # Generic error message
    errmsg = 'Cannot stop NameService.'

    process = 'nameserv.exe'
    try:     
        if IsProcessRunning( process ) == TRUE:
            KillProcess( process, ignore=1 )
        time.sleep(1)
        if IsProcessRunning( process) != FALSE:
            raise
    except:
        LogMessage( 'NameService is still running, could not stop NAMESERV.EXE', ERROR )
        raise Exception('Could not kill NameService.')



#----------------------------------------------------------------------------
# ListCorbaServers
#
#    Display list of all Corba servers.
#    Display only to screen.
#    
def ListCorbaServers( ):

    # print message to screen and ppu log file
    LogMessage( 'Listing all Corba servers.', ALWAYS )

    # Generic error message
    errmsg = 'Failed to get list of Corba servers.'

    try:
        processName = 'imr.exe'
        wasIMRRunning = IsProcessRunning( processName )
        if wasIMRRunning == FALSE:
            StartIMR()
        ExecuteIMRAdmin( IMRADMIN_START, ORBACUS_SERVER_NAMESERVICE )

        corbaServers = _ListCorbaServers()
        print 'Corba servers:'
        for server in corbaServers:
            print '    %s' % server

        # Stop IMR if it was initially stopped
        if wasIMRRunning == FALSE:
            ExecuteIMRAdmin( IMRADMIN_STOP, ORBACUS_SERVER_NAMESERVICE )
            if ( not WaitForProcessToStop( 'nameserv.exe',ignore=1 ) ):
                # try again the second time and it will work.
                ExecuteIMRAdmin( IMRADMIN_STOP, ORBACUS_SERVER_NAMESERVICE )
            StopIMR()

    except:
        raise Exception( 'Error: %s\nDetails: %s).' % ( errmsg, err() ) )


#----------------------------------------------------------------------------
# _ListCorbaServers
#
#    Return list of all Corba servers.
#    Start and stop the IMR and Name Service as required.
#  
def _ListCorbaServers( ):
    output = ExecuteIMRAdmin ( IMRADMIN_LIST )
    serverList = string.split( output, '\n')
    serverNames = []
    for line in serverList:
        name = string.strip( line ) 
        # don't include heading 'Server records:' in list
        if name != 'Server records:':
            serverNames.append ( string.strip( line ) )
    return serverNames


#----------------------------------------------------------------------------
# DisplayCorbaServer
#
#    Display information of an individual Corba server.
#    Start and stop the IMR and Name Service as required.
#    Display only to screen
#
def DisplayCorbaServer( serverName ):

    # print message to screen and ppu log file
    LogMessage( 'Displaying status of Corba server.', ALWAYS )

    # Generic error message
    errmsg = 'Failed to display status of Corba server.'

    try:
 
        processName = 'imr.exe'
        wasIMRRunning = IsProcessRunning( processName )
        if wasIMRRunning == FALSE:
            StartIMR()
            ExecuteIMRAdmin( IMRADMIN_START, ORBACUS_SERVER_NAMESERVICE )
        bInfoFound, retString, output = GetIMRInfo( serverName, '' )

        print output

        # Stop IMR if it was initially stopped
        if wasIMRRunning == FALSE:
            ExecuteIMRAdmin( IMRADMIN_STOP, ORBACUS_SERVER_NAMESERVICE )
            if ( not WaitForProcessToStop( 'nameserv.exe',ignore=1 ) ):
                # try again the second time and it will work.
                ExecuteIMRAdmin( IMRADMIN_STOP, ORBACUS_SERVER_NAMESERVICE )
            StopIMR()

    except:
        raise Exception( 'Error: %s\nDetails: %s).' % ( errmsg, err() ) )

#----------------------------------------------------------------------------
# GetCorbaServerStatus
#
#    Display information of an individual Corba server.
#  
def GetCorbaServerStatus( serverName, serverAttribute ):

    bInfoFound, retString, output = GetIMRInfo( serverName, '' )

    info = string.split( output, '\n')
    attributeValue = None
    for line in info:
        result = re.split( '(:)', line )
        if len(result)==3:
            thisAttribute = string.strip( result[0]+result[1] )
            if thisAttribute == serverAttribute:
                attributeValue = string.strip( result[2] )
    return attributeValue



#----------------------------------------------------------------------------
# StartCorbaServer
#
#    Start an individual Corba server.
#    Start the IMR and Name Service as required.
#  
def StartCorbaServer( serverName ):
    processName = 'imr.exe'
    wasIMRRunning = IsProcessRunning( processName )
    if wasIMRRunning == FALSE:
        StartIMR()
        ExecuteIMRAdmin( IMRADMIN_START, ORBACUS_SERVER_NAMESERVICE )
    LogMessage( 'Starting Corba server %s.' % serverName , ALWAYS )
    output = ExecuteIMRAdmin( IMRADMIN_START, serverName )
    if output != '':
        LogMessage ( output, ERROR )


   
#----------------------------------------------------------------------------
# StopCorbaServer
#
#    Stop an individual Corba server.
#    Start and stop the IMR and Name Service as required.
#  
def StopCorbaServer( serverName ):
    processName = 'imr.exe'
    wasIMRRunning = IsProcessRunning( processName )
    if wasIMRRunning == FALSE:
        StartIMR()
        ExecuteIMRAdmin( IMRADMIN_START, ORBACUS_SERVER_NAMESERVICE )

    # Stop the server
    LogMessage( 'Stopping Corba server %s.' % serverName , ALWAYS )
    output = ExecuteIMRAdmin( IMRADMIN_STOP, serverName )
    if output != '':
        LogMessage ( output, ERROR )

    # Stop IMR if it was initially stopped
    if wasIMRRunning == FALSE:
        ExecuteIMRAdmin( IMRADMIN_STOP, ORBACUS_SERVER_NAMESERVICE )
        if ( not WaitForProcessToStop( 'nameserv.exe',ignore=1 ) ):
            # try again the second time and it will work.
            ExecuteIMRAdmin( IMRADMIN_STOP, ORBACUS_SERVER_NAMESERVICE )
        StopIMR()

#----------------------------------------------------------------------------
# StopAllCorbaServers
#
#    Stop all Corba servers.
#  
def StopAllCorbaServers():

    # print message to screen and ppu log file
    LogMessage( 'Stopping all Corba servers.', ALWAYS )

    # Generic error message
    errmsg = 'Failed to stop all Corba servers.'

    try:

        # Get list of Corba servers
        servers = _ListCorbaServers()

        # Stop all servers except NameService
        if servers != []:
            for corbaServer in servers:
                if corbaServer != ORBACUS_SERVER_NAMESERVICE:
                    status = GetCorbaServerStatus( corbaServer, ORBACUS_INFO_STATUS )
                    if status == ORBACUS_INFO_RUNNING:
                        LogMessage( 'Stopping Corba server %s.' % corbaServer, ALWAYS )
                        output = ExecuteIMRAdmin( IMRADMIN_STOP, corbaServer )
                        if output != '':
                            LogMessage ( output, ERROR )

            # Stop NameService
            status = GetCorbaServerStatus( ORBACUS_SERVER_NAMESERVICE, ORBACUS_INFO_STATUS )
            if status == ORBACUS_INFO_RUNNING:
                LogMessage( 'Stopping Corba server %s.' % ORBACUS_SERVER_NAMESERVICE, ALWAYS )
                output = ExecuteIMRAdmin( IMRADMIN_STOP, ORBACUS_SERVER_NAMESERVICE )
                if output != '':
                    LogMessage ( output, ERROR )
    except:
        raise Exception( 'Error: %s\nDetails: %s).' % ( errmsg, err() ) )


#----------------------------------------------------------------------------
# ExecuteIMRAdmin
#
#   This function executes an IMRAdmin function with specified commands.
#
def ExecuteIMRAdmin( command, serviceName=None, arguments=None ):
    # print message to screen and ppu log file
    LogMessage( 'Execute IMRAdmin %s, %s, %s' % ( command, serviceName, arguments ) )
          
    # Generic error message
    errmsg = 'Failed to execute imradmin.exe.'
              
    # Path where executable can be found
    orbacusBinPath = os.path.join( _araxiHomeDirectory, 
                                   'IonaOrbacus', 
                                   'bin' )
    
    # Full path of executable to launch 
    imrAdminExe = os.path.join( orbacusBinPath, 'imradmin.exe' )
    
    # Create the command line parameters
    if ( arguments ):   
        commandLineParam = r'%s %s %s' % (command, serviceName, arguments)
    elif (serviceName):
        commandLineParam = r'%s %s' % (command, serviceName)
    else:
        commandLineParam = r'%s' % (command)
    
    try:
        # Don't try and catch any errors.  Later on we will parse
        # the output for errors.
        msg, errorsIgnored = ExecuteCommand( executableToRun=imrAdminExe, 
                                             inputPipeMessage=None,
                                             rundir=orbacusBinPath, 
                                             executableArguments=commandLineParam, 
                                             errorsToCatch=[], 
                                             errorsToIgnore=[],
                                             ignoreErr=FALSE )
        # Log Output 
        LogMessage( 'Output = ' + msg )               
    except:
        raise Exception('Error: %s\nDetails: %s' % ( errmsg, err() ) )
    
    # return the output string
    return msg
    
#----------------------------------------------------------------------------
# GetIMRInfo
#
#   This function returns information about an orbacus service.
#
#   Inputs:
#       serviceName = Orbacus service name.
#       info = information returned.
#
#   Outputs:
#       bFound = TRUE if the information exists else FALSE.
#       retString = information string returned.  Blank if no info.
#
def GetIMRInfo( serviceName, info ):
    # print message to screen and ppu log file
    LogMessage( 'GetIMRInfo( %s, %s )' % ( serviceName, info ), INFO )
    
    # Execute the IMRAdmin command. 
    # strOut is the ouput of the pipe command.
    strOut = ExecuteIMRAdmin( IMRADMIN_GET, serviceName )
    
    bFound = FALSE
    retString = ''
    
    # split the the output string by carriage returns
    strList = string.split(strOut, '\n')
    
    # Parse strOut for a specific line and return the string after ':' character.
    for strLine in strList:
        
        # parse for error or output
        pos = string.find(string.upper( strLine ), string.upper( info ) )
        if ( pos != -1 ):
            # parse the string and only return the string after the info string.
            pos = string.find( strLine, ':' )
            retString = strLine[pos + 1:]
            bFound = TRUE
            break
        
    # Log the string to the ppu log file
    LogMessage( 'String = ' + string.strip( retString ) )
    return bFound, string.strip( retString ), strOut


#----------------------------------------------------------------------------
# BackupOrbacusConfiguration
#
#   Backup all Orbacus configuration files.  This is done as a precaution.
#   For some reason if orbacus configuration gets corrupted, the user
#   can copy back the backup files.
#
def BackupOrbacusConfiguration( ):
    # print message to screen and ppu log file
    LogMessage( 'Backing up Orbacus configuration files.', ALWAYS )
    
    # Generic error message
    errmsg = 'Failed to backup Orbacus configuration files.'
     
    # Create the backup directory structure.
    try:
        # Set the paths
        orbacusPath = os.path.join( _araxiHomeDirectory, 'IonaOrbacus' )
        orbacusBinPath = os.path.join( orbacusPath, 'bin' )
        orbacusDatabasePath =  os.path.join( orbacusBinPath, 'IMR_Database' )   
        orbacusBackupPath = os.path.join( orbacusPath, 'backup' )
        orbacusBackupBinPath = os.path.join( orbacusBackupPath, 'bin' )
        orbacusBackupDatabasePath = os.path.join( orbacusBackupBinPath, 'IMR_Database' )
  
        EnsureDirectoryExists( orbacusBackupDatabasePath )

        if _pathOrbacusLastBackupFolder == None:
            raise Exception ( 'Orbacus backup folder was not defined!') 

        # back up files in backup folder
        fileList = []
        fileList = os.listdir( orbacusBackupPath )
#        print fileList
        for file in fileList:
            if os.path.isfile( os.path.join ( orbacusBackupPath, file )):
                CopyFile ( os.path.join( orbacusBackupPath, file), os.path.join( _pathOrbacusLastBackupFolder, file ))

        shutil.copytree( orbacusBackupBinPath, os.path.join( _pathOrbacusLastBackupFolder, 'bin') )    
        
        #
        #   Backup Names.Db only if the backup does not exist.
        #        
        fileName = 'Names.Db'
        srcfile = os.path.join( orbacusBinPath, fileName )
        backupfile = os.path.join( orbacusBackupBinPath, fileName )
        
        LogMessage('Copying: ' + srcfile + ' to ' + backupfile, INFO)
        CopyFile( srcfile, backupfile )
        
        #
        #   Backup all files in the IMR_Database directory.  
        #
        dirList = os.listdir( orbacusDatabasePath )
        for fileName in dirList:
            if ( os.path.isfile( os.path.join( orbacusDatabasePath, fileName ) ) ):
                srcfile = os.path.join( orbacusDatabasePath, fileName )
                backupfile =os.path.join( orbacusBackupDatabasePath, fileName )
                LogMessage('Copying: ' + srcfile + ' to ' + backupfile, INFO)
                CopyFile( srcfile, backupfile )
    except:
        raise Exception('Error: %s\nDetails: %s' % ( errmsg, err() ) )

#----------------------------------------------------------------------------
# CreateOrbacusBackupFolder
#
#   Create a folder to hold all the current Orbacus configuration files.
#   The format of the folder name is: backup_<date>_<time>
#
def CreateOrbacusBackupFolder():
    global _pathOrbacusLastBackupFolder

    # print message to screen and ppu log file
    LogMessage( 'Creating backup folder for current Orbacus configuration files.', INFO )
    
    # Generic error message
    errmsg = 'Failed to create backup folder for Orbacus configuration files.'
     
    # Create the backup folder.
    try:

        # Set the paths
        orbacusPath = os.path.join( _araxiHomeDirectory, 'IonaOrbacus' )
        orbacusBackupPath = os.path.join( orbacusPath, 'backup' )
#        print orbacusBackupPath

 
        # Create a backup folder with the format: backup_<date>_<time>.
        # Cannot use time format HH:MM:SS for the time, use HH.MM.SS instead.
        dateTimeFormat = '%Y%b%d_%H.%M.%S'
        backupDateTime = time.strftime(dateTimeFormat, time.localtime(time.time()))
        lastBackupPath = os.path.join( orbacusPath, 'backup_' + backupDateTime )



        # folder shouldn't exist, but check anyways
        while os.path.exists( lastBackupPath ):
            # allow some time to elapse before we try again
            time.sleep(2)
            backupDateTime = time.strftime(dateTimeFormat, time.localtime(time.time()))
            lastBackupPath = os.path.join( orbacusPath, 'backup_' + backupDateTime )

        _pathOrbacusLastBackupFolder = lastBackupPath

        EnsureDirectoryExists( _pathOrbacusLastBackupFolder )

    except:
        raise Exception('Error: %s\nDetails: %s' % ( errmsg, err() ) )


#----------------------------------------------------------------------------
# KillProcess
#
#   Kill the process and make sure the process is not running.
#
def KillProcess( process, ignore=0, maxLoopCount=PROCESS_LOOP_COUNT, waitEachLoop=PROCESS_SLEEP):
    # print message to screen and ppu log file
    LogMessage( 'Stopping "%s".' % process, ALWAYS )

    # Generic error message
    errmsg = 'Failed to stop "%s".' % process
    
    # Path where executable can be found
    araxiBinPath = os.path.join( _araxiHomeDirectory, 
                                   'CreoAraxi', 
                                   'bin' )
 
    # Full path of kill.exe
    killExe = os.path.join( araxiBinPath, 'Kill.exe' )
    
    
    try:
        ExecuteCommand( executableToRun=killExe, 
                        inputPipeMessage=None,
                        rundir=araxiBinPath,
                        executableArguments='-f %s' % process ) 
        
        # Check if process is running
        WaitForProcessToStop( process, 
                              ignore=ignore,
                              maxLoopCount=PROCESS_LOOP_COUNT, 
                              waitEachLoop=PROCESS_SLEEP )
    
    except:
        raise Exception('Error: %s\nDetails: %s' % ( errmsg, err() ) )
  

   
    LogMessage( 'Process "%s" stopped.' % process )
   


#----------------------------------------------------------------------------
# SERVICE
#----------------------------------------------------------------------------

def IsServiceRunning( serviceName, computerName=None ):
    '''
    given service key name (not display name) and optional computer name (defaults
    to local computer), return TRUE or FALSE, else raise exception
    '''
    status = GetServiceStatus( serviceName, computerName )
    if status:
        if status[1] == win32service.SERVICE_RUNNING:
            return TRUE

    return FALSE

def IsServiceStopped( serviceName, computerName=None ):
    '''
    given service key name (not display name) and optional computer name (defaults
    to local computer), return TRUE or FALSE, else raise exception
    '''
    status = GetServiceStatus( serviceName, computerName )
    if status:
        if status[1] == win32service.SERVICE_STOPPED:
            return TRUE

    return FALSE


def GetServiceStatus( serviceName, computerName=None ):
    SH = ServiceHandles( serviceName, computerName, win32con.GENERIC_READ, win32service.SERVICE_QUERY_STATUS )
    status = win32service.QueryServiceStatus( SH.hs )
    return status


# -----------------------------------------------------------------------------
# service handles with automatic cleanup
# userfriendlyexception=1 raise nice exception
# userfriendlyexception=0 preserve original win32error (useful for some calling functions)
class ServiceHandles:

    def __init__( self, serviceName, computerName=None, SCManagerDesiredAccess=win32service.SC_MANAGER_ALL_ACCESS, ServiceDesiredAccess=win32service.SERVICE_ALL_ACCESS, userfriendlyexception=1 ):
        self.hSCManager = None
        self.hs = None
        
        try:
            self.hSCManager = win32service.OpenSCManager( computerName, None, SCManagerDesiredAccess )
        except win32api.error, err:
            if userfriendlyexception:
                raise Exception('Could not open service manager: %s' % err )
            else:
                raise
        
        try:
            self.hs = win32service.OpenService( self.hSCManager, serviceName, ServiceDesiredAccess )
        except win32api.error, err:
            if userfriendlyexception:
                raise Exception('Could not open service %s: %s' % ( serviceName, err ) )
            else:
                raise
    
    def __del__( self ):
        if self.hs:
            win32service.CloseServiceHandle( self.hs )
        if self.hSCManager:
            win32service.CloseServiceHandle( self.hSCManager )

        
#----------------------------------------------------------------------------
# FILE AND DIRECTORY
#----------------------------------------------------------------------------


#----------------------------------------------------------------------------
# CopyFile
#
#   This function will copy a file from the source to the target. 
#
def CopyFile( sourceFile, targetFile ):

     # Check if the source file exists.
    if ( not os.path.isfile( sourceFile ) ):
        raise Exception('File %s does not exist.' % sourceFile )

    try:
        targetFailOnExist = 0
        win32api.CopyFile( sourceFile, targetFile, targetFailOnExist )    
    
    except:
        raise Exception( 'Error: %s\nDetails: %s' % (errmsg, err() ) ) 
    

#----------------------------------------------------------------------------
# EnsureDirectoryExists
#
#   This function creates the directory path if it does not exist.
#
def EnsureDirectoryExists( dirpath ):
    
    # Make sure dirpath is not blank or null
    if ( not dirpath ):
        raise Exception( 'Directory to verfiy is blank.')
        
    # Make sure trailing "\" is removed
    if ( dirpath > 0 ):
        while ( dirpath[-1] == os.sep ):
            dirpath = dirpath[0:-1] # Remove trailing backslash
     
    # Check if directory exists.
    if ( os.path.isdir( dirpath ) ):
        pass
    # Check if path exists, Since we already checked directory it must be a file path.
    elif os.path.exists( dirpath ):
        raise Exception( 'File "%s" exists, but it is not a directory.' % dirpath )  
    else: 
        LogMessage( 'Creating Directory "%s".' % dirpath )
        try:
            os.makedirs( dirpath )
        except:
            LogMessage ( 'Failed to create Directory "%s".' % dirpath, ERROR )
            raise 
            
            
#----------------------------------------------------------------------------
# PushDir
#
def PushDir( newdir ):
    olddir = os.getcwd()
    os.chdir( newdir )
    LogMessage( 'Changing directory to: "%s"' % newdir )
    
    return olddir        

#----------------------------------------------------------------------------
# PopDir
#
def PopDir( prevdir ):
    os.chdir( prevdir )
    LogMessage( 'Changing directory to: "%s"' % prevdir )

#----------------------------------------------------------------------------
# EraseFilesRecursive
#
#  Erase all files in a directory recursively
#
def EraseFilesRecursive( dirPath ):

    if ( os.path.isdir( dirPath ) ):
        LogMessage( 'Removing files in directory "%s".' % dirPath, INFO )
        try:
            os.path.walk( dirPath, EraseFiles ,'' )
        except:
            LogMessage( 'Failed to remove files in directory "%s".' % dirPath, ERROR )
            raise
 
#----------------------------------------------------------------------------
# EraseFiles
#
#   Delete all files.
#
#   Input: arg = None 
#          dirname = directory where the files can be found.
#          filenames = list with all the files without the directory.
#
def EraseFiles(arg, dirname, filenames):

    LogMessage( 'Erasing files in folder %s' % dirname, INFO )

    for sfile in filenames:
        EraseFile( dirname, sfile )

    
#----------------------------------------------------------------------------
# EraseFile
#
#   This function will remove the file if it exists. If the file does
#   not exit, then it is ignored. The function returns TRUE if the file has been removed
#   else if the file does not exist the function returns FALSE.
#
def EraseFile( srcPath, srcFile ): 

    LogMessage( 'Erasing file %s' % srcPath+'\\'+srcFile, INFO )

    # Set variable to FALSE
    bFileErased = FALSE
    # Check if file exists in target path
    # if it does then erase it
    if ( os.path.isfile( srcPath + os.sep + srcFile ) ):
        os.unlink(os.path.join(srcPath, srcFile))
        bFileErased = TRUE
  
    return bFileErased                                 
    
    
   
#----------------------------------------------------------------------------
# FindLineInFile
#
#   This function will return a line where the first occurence of the
#   is found.  If the string does not exist in the file None is 
#   returned.  The string comparison is case insensitive.
#
#   Inputs:
#       file = Full path of the file to search.
#       stringToFind = String to search in the file.
#       ignoreComment = Specify the comment token to ignore.  Any lines that begin
#                       with the comment token will be ingored.
#       ignoreWhiteSpace = TRUE to ignore all white spaces from the string and line 
#                          from the file. eg '  e \ta\nt  ' = 'eat' 
#
#   Outputs:
#       retLine = The line of the first occurence of the string,
#                 or None is returned if no string is found.  
#
def FindLineInFile( file, stringToFind, ignoreComment='', ignoreWhiteSpace=1 ):
    """This function searches for the first occurence of the string in the 
       file and return the line.  If no string is found then None is returned.
    """
    # return line
    retLine = None
    
    try:
        errmsg = 'Failed to find line in file "%s" with string "%s".' % ( file, stringToFind )
    
        # make sure the source file exists
        if ( not os.path.isfile( file ) ):
            raise Exception( 'File "%s" does not exist.' % file )
        
        # if ignoreWhiteSpace then remove all white spaces in the string
        if ( ignoreWhiteSpace ):
            cleanStringToFind = RemoveWhiteSpace( stringToFind )
        else:
            cleanStringToFind = stringToFind
            
        # Create new file and replace tokens.
        f = open( file, 'r' )
        
        aLine = f.readline()
        while aLine and  not retLine:
            # searh for the string
            if ( ignoreWhiteSpace ):
                cleanLine = RemoveWhiteSpace( aLine )
            else:
                cleanLine = aLine
            if ( string.find( string.lower( cleanLine ), string.lower( cleanStringToFind ) ) != -1 ):
                # make sure to check if it is a comment
                if ( ignoreComment ):
                    if ( string.strip( cleanLine )[0] != ignoreComment ):
                        retLine = aLine                
                else:
                    retLine = aLine
            aLine = f.readline()
            
        # Close the file    
        f.close()
    except:
        raise Exception( 'Error: %s\nDetails: %s' % (errmsg, err() ) )
       
    return retLine
  
#----------------------------------------------------------------------------
# RemoveWhiteSpace
#
#   Remove all white spaces from the string.
#
def RemoveWhiteSpace( strIn ):
    whiteSpaceCharacters = [ ' ', '\t', '\n' ]
    
    strTemp = strIn
    
    for eachCharacter in whiteSpaceCharacters:
        strTempList = string.split( strTemp, eachCharacter )
        strTemp = string.join( strTempList, '' )
    
    return strTemp
  

    
#----------------------------------------------------------------------------
# ReplaceTokensInFile
#
#   This function creates a new file from the souce file with tokens 
#   replaced.  If the destination file exists, it will be backed up
#   and restored in an undo or an uninstall.
#
#   Inputs:
#       srcPath = Full path of where the source file is to be found.
#       srcFile = File name of the source file.
#       destPath = Full path of where the destination file is to be found.
#       destFile = Full name of the destination file.
#       dictionary = Dictionary {'key1':'value1','key2':'value2' ...} format.
#
def ReplaceTokensInFile( srcPath, srcFile, destPath, destFile, dictionary ):
    """This function creates a new file from the srcfile with tokens replaced.
       Tokens to replace is identified as a dictionary.
    """
    # Generic error message 
    errmsg = 'Failed to replace token in file "%s".' % (srcPath + os.sep + srcFile) 
    
    try:   
        # Make sure the source file exists.
        if ( not os.path.isfile( srcPath + os.sep + srcFile ) ):
            raise Exception( 'File "%s" does not exist.' % os.path.join( srcPath, srcFile ) )
       
        # If the destination file exists then back it up for undo and uninstall.
        if ( os.path.isfile( destPath+ os.sep + destFile ) ):
            EraseFile( destPath, destFile )
            
        # If the destination dir does not exist, create one.
        EnsureDirectoryExists( destPath )
        
        # Create new file and replace tokens.
        f = open( srcPath + os.sep + srcFile, 'r' )
        fNew = open( destPath + os.sep + destFile , 'w' )
    
        line = f.readline()
        keyList = dictionary.keys()
        while line:
            newLine = line
            # replace all tokens in the line
            for eachKey in keyList:
                newLine = string.replace (newLine, eachKey, dictionary[eachKey] ) 
            # write line to the file
            fNew.write( newLine )
            line = f.readline()
        f.close()
        fNew.close()
    except:
        raise Exception( 'Error: %s\nDetails: %s' % (errmsg, err() ) )
    
#----------------------------------------------------------------------------
# RemoveDir
#
#   Remove the specified directory recursively without backing up for
#   undo or uninstall.
#
def RemoveDir( sPath ):

    # print message to screen and ppu log file
    LogMessage( 'Removing directory %s.' % sPath, INFO  )

    # Make sure trailing "\" is removed
    if ( sPath > 0 ):
        while ( sPath[-1] == os.sep ):
            sPath = sPath[0:-1] # Remove trailing backslash

    if ( os.path.isdir( sPath ) ):
        try:
            # remove directory recursively
            shutil.rmtree( sPath )
        except:
            LogMessage( 'Failed to remove directory "%s".' % sPath, WARNING )
    else:
        LogMessage( 'Directory "%s" already been removed.' % sPath )
    


#----------------------------------------------------------------------------
# PROCESS
#----------------------------------------------------------------------------

#----------------------------------------------------------------------------
# WaitForProcessToStart
#
#   Constantly checks if the process is running.  If the process has not
#   started in the predefined time an exception is raised.
#
#   global variables PROCESS_SLEEP and PROCESS_LOOP_COUNT defines the wait 
#   time.
#
def WaitForProcessToStart( process ):
   # print message to screen
    LogMessage( 'Waiting for process "%s" to start.' % process, INFO )

    timeCounter = 0
    while timeCounter <= PROCESS_LOOP_COUNT:
        if ( not IsProcessRunning( process ) ):
            timeCounter = timeCounter + 1
            time.sleep( PROCESS_SLEEP )
            print '.',
        else:
            break         
        
    if ( timeCounter > PROCESS_LOOP_COUNT ):
        raise Exception( 'Process "%s" did not start in time.' % process )
    else:
        LogMessage( 'process "%s" successfully started.' % process )

#----------------------------------------------------------------------------
# WaitForProcessToStop
#
#   Constantly checks if the process is running.  If the process has not
#   stopped in the predefined time FALSE is returned.  Else, if the process
#   is not running it returns TRUE.
#
#   global variables PROCESS_SLEEP and PROCESS_LOOP_COUNT defines the wait 
#   time.
#
def WaitForProcessToStop( process, ignore=0, maxLoopCount=PROCESS_LOOP_COUNT, waitEachLoop=PROCESS_SLEEP ):
   # print message to screen
    LogMessage( 'Waiting for process "%s" to stop.' % process, INFO )

    timeCounter = 0
    while timeCounter <= maxLoopCount:
        if ( IsProcessRunning( process ) ):
            timeCounter = timeCounter + 1
            time.sleep( waitEachLoop )
            print '.',
        else:
            break
                    
    if  ( timeCounter > maxLoopCount ):
        if ( not ignore ):
            raise Exception( 'Process "%s" did not stop in time.' % process )
        else:
            LogMessage( 'process "%s" did not stop in time.' % process )
            return FALSE # Process did not stop
    
    LogMessage( 'process "%s" successfully stopped.' % process )
    return TRUE # Process did stop     
         
#----------------------------------------------------------------------------
# IsProcessRunning
#
#   Check if the specified process is running.
#   Returns TRUE if the process is running, else returns FALSE.
#
def IsProcessRunning( processName ):
    # Create a list of all the running processes.
    pidlist = pypsapi.EnumProcesses()
    
    processName = FormatProcessName( processName )
    
    win32x.EnableDebugPrivilege()
    bProcessRunning = FALSE
    for pid in pidlist:
        # Skip the current ppu process ID.
        if pid != os.getpid():
            try:
                name = pypsapi.GetProcessName( pid )
                name = FormatProcessName( name )
                if name == processName:
                    bProcessRunning = TRUE
            except win32api.error:
                # somtimes we have a process list with all the running
                # processes, but when we try to get the name of the 
                # process, the process is removed, which results in a
                # OpenProcess error 87.  Other unknown cases may exist
                # so we want to be safe and do the "pass" for all 
                # win32api.errorexceptions.
                pass
            
            # If the process is found running then exit loop    
            if ( bProcessRunning ):
                break    
                
    win32x.DisableDebugPrivilege()            
    return bProcessRunning
      
     
#----------------------------------------------------------------------------
# FormatProcessName
#
def FormatProcessName( processName ):
    head, tail = os.path.split( processName )
    name, ext = os.path.splitext( tail )
    formatedName = (string.upper( name ) + '.EXE')[:15]
    return formatedName

#----------------------------------------------------------------------------
# COMMON
#----------------------------------------------------------------------------

#----------------------------------------------------------------------------
# IsErrorEx
#
#   This function handles the error checking of pipe outputs.
#
#   Inputs:
#       output = Pipe output to check.
#       errorsToCatch = list of strings to catch and flag as an error.
#       errorsToIngore = list of string to ignore if an error is caught.
#       ignoreErr = True to ignore all errors.
#
#   Outputs:
#       bError = True if an error is caught else False.
#       postLine = The output in string format.
#       errsIgnored = List of all the errors that was ignored.
#
def IsErrorEx( output, 
               errorsToCatch=['Failed', 'Warning', 'Exception', 'Error'], 
               errorsToIgnore=[], 
               ignoreErr = FALSE ):
                   
    # print message to screen and ppu log file
    LogMessage( 'Checking output for Errors Please be patient.' )
    
    printDotLineNumber = 100
    lineCounter = 0
    
    # Generic error message 
    errmsg = 'Failed to parse output for errors.'
    
    bError = FALSE
    postLine = ''
    errsIgnored = []
    
    # convert errors to one-item lists if user specified them as a single string
    if type( errorsToCatch ) == types.StringType:
        errorsToCatch = [ errorsToCatch ]
    if type( errorsToIgnore ) == types.StringType:
        errorsToIgnore = [ errorsToIgnore ]

    try:
        aLine = output.readline()
        while ( aLine ):
            postLine = postLine + aLine
            lineCounter = lineCounter + 1
            if ( ( lineCounter % printDotLineNumber ) == 0 ):
                print '.',
            for err in errorsToCatch:
                if ( string.find( string.lower(aLine), string.lower(err) ) != -1 ):
                    for errIgnored in errorsToIgnore:
                        if ( string.find( string.lower(aLine), string.lower(errIgnored) ) != -1 ):
                            ignoreErr = TRUE             
                            errsIgnored.append( string.strip(aLine) )
                    if not ignoreErr:      
                        bError = TRUE
    
            aLine = output.readline()
    except:
        raise Exception('Error: %s\nDetails: %s' % ( errmsg, err() ) )
    
    if ( lineCounter >= printDotLineNumber ):
        print ' ' # got to next line after the last '.'
        
    LogMessage('%d lines have been parsed for errors.' %   lineCounter )  
    return bError, postLine, errsIgnored        

#----------------------------------------------------------------------------
# GetOriginalPrimaryNodeName
#
#   Get the original Primary name for the current workstation.
#   If EPM was not installed on the current workstation then 
#   GetOriginalPrimaryNodeName will return None.
#
#
def GetOriginalPrimaryNodeName(): 
    # print message to screen and ppu log file
    LogMessage( 'Getting EPM original primary server name.', INFO  )

    # Generic error message
    errmsg = 'Failed to retrieve EPM original Primary server name.'
    
    try:  
        node = PrinergyNodeInfo( None )
        node.Query()
    except:
        raise Exception( 'Error: %s\nDetails: %s' % ( errmsg, err() ) )
    
    # return EPM original Primary name
    # return value of class member.  It's not a function call!
    return node.OriginalPrimaryNodeName
        
#----------------------------------------------------------------------------
# GetPrimaryName
#
#   Get the Primary for the current workstation.
#
def GetPrimaryName(): 
    # print message to screen and ppu log file
    LogMessage( 'Getting primary server name.', INFO  )

    # Generic error message
    errmsg = 'Failed to retrieve Primary server name.'
    
    try:  
        node = PrinergyNodeInfo( None )
        node.Query()
    except:
        raise Exception( 'Error: %s\nDetails: %s' % ( errmsg, err() ) )
    
    # return Primary name            
    return node.GetPrimaryNodeName()

#----------------------------------------------------------------------------
# IsPrimaryServer
#
#   returns 1 if running on the primary, and 0 otherwise.
#
def IsPrimaryServer( ):
    node = PrinergyNodeInfo( None )
    node.Query()
       
    if string.upper( node.GetPrimaryNodeName() ) == string.upper( node.GetNodeName() ):
        return 1
    return 0


#----------------------------------------------------------------------------
# IsEPMServer
#
#   returns 1 if running on an Emergency Plate Making machine, and 0 otherwise.
#
def IsEPMServer( ):
    node = PrinergyNodeInfo( None )
    node.Query()
       
    if node.OriginalPrimaryNodeName is None:
        return 0
    else:
        return 1

#----------------------------------------------------------------------------
# AddPathWithoutUndo
#
#   
def AddPathWithoutUndo( newPath ):
    
    # Print message to screen ppu log
    LogMessage( 'Adding "%s" to Path environment variable.' % newPath, INFO )

    if ( AddPath( newPath ) ):
        LogMessage( 'Path "%s" added to the Path environment variable.' % newPath )
        
    else:
        LogMessage( 'Path "%s" already exists in Path environment variable.' % newPath )


#----------------------------------------------------------------------------
# AddPath
#
#   Add new path to the path environment variable if it does not exist.
#   The new path will be inserted at begining of the path. The new path is
#   saved in a custom ini file for unistall.  This function does not use
#   ppu registry function to save the old registry function, because other
#   programs can modify the path environment variable as well and these 
#   changes can be lost when uninstalling the Major Update.
#
def AddPath( newPath ):
    
    # Grab the current path from the registry
    if ( DoesKeyExist( REG_ENVIRONMENT_KEY ) ):
        pathValue = QueryValueEx( REG_ENVIRONMENT_KEY, 
                                                   REG_PATH_VALUE, 
                                                   None )
        if ( pathValue ):
            currentPath = pathValue[0]
            regType = pathValue[1]
            # make sure newPath and currentPath is terminated with a semicolon
            if ( newPath[-1] != ';'):
                newPath = newPath + ';'
            if (currentPath[-1] != ';'):
                currentPath = currentPath + ';' 
        
            # Make sure new path does not exist in current path.
            # And if the path does not exist then insert the new path
            # at the beginning of the path environment.
            if ( string.find( string.upper( currentPath ), string.upper( newPath ) ) == -1 ):
                newCurrentPath = newPath + currentPath
                
                # The ppu Registry function will automatically save the old registry value
                # for undo and uninstall.  We do not want this behavior when replacing
                # the path environment variables, because other programs can modify the path
                # registry value as well. 
                # Instead we call RegSetValueEx so PPU will not save the replaced path
                # environment variable.
                SetValueEx(REG_ENVIRONMENT_KEY, REG_PATH_VALUE, regType, newCurrentPath)
 
                return TRUE
            else:
                return FALSE
        else:
            errMsg = 'Error: Could not remove path environment variable.\nRegistry value %s does not exist.' % REG_PATH_VALUE
            raise Exception( errMsg )
    else:
        errMsg = 'Error: Could not remove path environment variable.\nRegistry key %s does not exist.' % REG_ENVIRONMENT_KEY
        raise Exception( errMsg )

# Creates a full araxi registry path.  The passed regkeypath is considered to be relative to
# \HKEY_LOCAL_MACHINE\Software\Creo\Araxi\<currentRelease>\Default\
#
def GetAraxiRegPath( regkeypath='' ):
    # Using product=None because we want to have the Araxi based, and NOT the product based CurrentRelease.
    currentRelease = ArxRegGetCurrentRelease( None, product=None )
    return os.path.join( 'HKEY_LOCAL_MACHINE', 'Software', 'Creo', 'Araxi', currentRelease, 'Default', regkeypath )

# return the Araxi CurrentRelease registry value on a remote computer,
# or the local computer if the argument is empty
#
# The product parameter may be used to get the product specific release.
# If the key doesn't exit or no product is specified, it will fall back to the 
# default Creo\Araxi\CurrentRelease for backwards compatibility.
#
def ArxRegGetCurrentRelease( node=None, product=None ):
    hkroot = win32con.HKEY_LOCAL_MACHINE
    if node:
        hkroot = win32api.RegConnectRegistry( node, win32con.HKEY_LOCAL_MACHINE )
    subkey = 'Software\\Creo\\Araxi'
    # default hkey to Araxi CurrentRelease
    hkey = win32api.RegOpenKey( hkroot, subkey, 0, win32con.KEY_QUERY_VALUE )
    if product:
        # see if we can get to the Product key
        try:
            productsubkey = os.path.join( subkey, 'Products', product )
            # verify if the key is available
            product_hkey = win32api.RegOpenKey( hkroot, productsubkey, 0, win32con.KEY_QUERY_VALUE )
            # verify if CurrentRelease is available
            c, t = win32api.RegQueryValueEx( product_hkey, 'CurrentRelease' )
            # if we get here, it means that we have a Product registry key and the CurrentRelease value available
            hkey = product_hkey
        except:
            # fall back to default
            pass
        
    CurrentRelease, t = win32api.RegQueryValueEx( hkey, 'CurrentRelease' )
    assert t == win32con.REG_SZ
    return CurrentRelease

#----------------------------------------------------------------------------
# _RegPathSplit
#
def _RegPathSplit( regkeypath ):
    """For a given registry key path, returns the rootkey and subkey.
    """
    names = string.split( regkeypath, '\\' )
    rootkeyname = names[0]
    subkeypath = string.join( names[1:], '\\' )
    return rootkeyname, subkeypath


#----------------------------------------------------------------------------
# _OpenRootKeyByName
#
def _OpenRootKeyByName( rootkeyname, mode=win32con.KEY_READ|win32con.KEY_WRITE, server=None ):
    """Opens root of registry key and returns its handle.

        Supported options for root key are:
        - local machine: HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CURRENT_USER, HKEY_ARAXI_ROOT
        - remote machine (server is specified): HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_ARAXI_ROOT
        
        HKEY_ARAXI_ROOT is relative to \HKEY_LOCAL_MACHINE\Software\Creo\Araxi\<CurrentRelease>\Default\
    """
    root = None
    if rootkeyname == 'HKEY_LOCAL_MACHINE':
        root = win32con.HKEY_LOCAL_MACHINE
    elif rootkeyname == 'HKEY_USERS':
        root = win32con.HKEY_USERS        
    elif rootkeyname == 'HKEY_CURRENT_USER' and not server:
        root = win32con.HKEY_CURRENT_USER
    elif rootkeyname == 'HKEY_ARAXI_ROOT':
        root = win32con.HKEY_LOCAL_MACHINE
        hkey = win32api.RegConnectRegistry( server, root )    # defaults to local machine if server is None
        subkey = 'Software\\Creo\\Araxi'
        hkey = win32api.RegOpenKey( hkey, subkey, 0, win32con.KEY_READ )
        CurrentRelease, regtype = win32api.RegQueryValueEx( hkey, 'CurrentRelease' )
        assert regtype == win32con.REG_SZ
        hkey = win32api.RegOpenKey( hkey, CurrentRelease, 0, mode )
        return win32api.RegOpenKey( hkey, 'Default', 0, mode )
    else:
        raise ValueError( 'unsupported root key name: ' + str(rootkeyname) )

    if server:
        return win32api.RegConnectRegistry( server, root )
    else:
        return win32api.RegOpenKey( root, None, 0, mode )


#----------------------------------------------------------------------------
# EnumSubkeys
#
def EnumSubkeys( regkeypath, server=None ):
    """Enumerates over subkeys in a key and returns a list of strings specifying 
        the full paths to all subkeys. Returns an empty list [] if no subkeys were found.
    """
    mode = win32con.KEY_READ
    rootkeyname, subkeypath = _RegPathSplit( regkeypath )
    hrootkey = _OpenRootKeyByName( rootkeyname, mode, server )
    hsourcekey = win32api.RegOpenKey( hrootkey, subkeypath, 0, mode )    

    subkeylist = []
    index = 0
    stop_enum = FALSE
    while not stop_enum:   # enumerate over key until we error out
        try:
            enumsubkey = win32api.RegEnumKey( hsourcekey, index )
            # found a subkey
            newsubkey = regkeypath + '\\' + enumsubkey
            subkeylist.append(newsubkey)
            index = index + 1
        except:
            stop_enum = TRUE
    return subkeylist

        
def MyRegQueryValue( rootkey, subkeypath, valuename, expectedtype=None ):
    '''
    get a registry value and type, optionally checking for expected type,
    and formatting a nice error message if not found or wrong type
    '''
    hkey = None
    try:
        try:
            hkey = win32api.RegOpenKey( rootkey, subkeypath, RESERVED, win32con.KEY_QUERY_VALUE )
        except win32api.error, err:
            if err[0] == winerror.ERROR_FILE_NOT_FOUND:
                raise KeyError( 'registry key not found' )
            else:
                raise err
                
        try:
            v, t = win32api.RegQueryValueEx( hkey, valuename )
            if expectedtype and t != expectedtype:
                raise TypeError( 'wrong registry value type found: %d expected: %d' % ( t, expectedtype ) )
            return v
        except win32api.error, err:
            if err[0] == winerror.ERROR_FILE_NOT_FOUND:
                raise KeyError( 'registry value not found' )
            else:
                raise err
    except win32api.error, err:
        if err[0] == winerror.ERROR_ACCESS_DENIED: # show user friendly message when access is denied
            raise Exception( 'Registry access denied, please log in as an administrator and try again!' )
        else:
            raise



def OpenKeyByName( rootkeyname, mode=win32con.KEY_READ|win32con.KEY_WRITE ):
    if rootkeyname == 'HKEY_LOCAL_MACHINE':
        return win32api.RegOpenKey( win32con.HKEY_LOCAL_MACHINE, None, 0, mode )
    elif rootkeyname == 'HKEY_ARAXI_ROOT':
        subkey = 'Software\\Creo\\Araxi'
        hkey = win32api.RegOpenKey( win32con.HKEY_LOCAL_MACHINE, subkey, 0, win32con.KEY_READ )
        CurrentRelease, regtype = win32api.RegQueryValueEx( hkey, 'CurrentRelease' )
        assert regtype == win32con.REG_SZ
        return win32api.RegOpenKey( hkey, CurrentRelease, 0, mode )
    else:
        raise ValueError( 'unsupported root key name: ' + str(rootkeyname) )


def DoesKeyExist( regkeypath ):
    rootkeyname, subkeypath = PathSplit( regkeypath )
    hkey = OpenKeyByName( rootkeyname, win32con.KEY_READ )
    try:
        hkey = win32api.RegOpenKey( hkey, subkeypath, 0, win32con.KEY_READ )
    except win32api.error, err:
        if err[0] != winerror.ERROR_FILE_NOT_FOUND:
            raise err
        else:
            return 0

    return 1


# Creates a new registry key.
def RegInstallKey( regkeypath ):
    if not DoesKeyExist( regkeypath ):
        CreateKey( regkeypath )


# Writes a registry value.  
#
def RegInstallValue( regkeypath, valname, regtype, newvalue, AutoRemove_AutoCreatedKey=1 ):
    regfullpath = regkeypath
    if valname:
        regfullpath = regfullpath + '\\' + valname
    # create the key if it doesn't exist
    if not DoesKeyExist( regkeypath ):
            CreateKey( regkeypath )
       
    SetValueEx( regkeypath, valname, regtype, newvalue )


def SetValueEx( regkeypath, valname, regtype, newvalue ):
    # create the key if it doesn't exist
    if not DoesKeyExist( regkeypath ):
        CreateKey( regkeypath )
    
    rootkeyname, subkeypath = PathSplit( regkeypath )
    hkey = OpenKeyByName( rootkeyname, win32con.KEY_WRITE )
    
    hkey = win32api.RegOpenKey( hkey, subkeypath, 0, win32con.KEY_WRITE )
    if valname:
        win32api.RegSetValueEx( hkey, valname, 0, regtype, newvalue )
    else:
        win32api.RegSetValue( hkey, None, regtype, newvalue )


def DeleteValue( regkeypath, valname=None ):
    rootkeyname, subkeypath = PathSplit( regkeypath )
    hkey = OpenKeyByName( rootkeyname, win32con.KEY_WRITE )

    try:
        hkey = win32api.RegOpenKey( hkey, subkeypath, 0, win32con.KEY_WRITE )
        win32api.RegDeleteValue( hkey, valname )
    except win32api.error, err:
        if err[0] != winerror.ERROR_FILE_NOT_FOUND:
            raise err

def QueryValueEx( regkeypath, valname=None, regtype=None ):
    rootkeyname, subkeypath = PathSplit( regkeypath )
    hkey = OpenKeyByName( rootkeyname, win32con.KEY_READ )
    hkey = win32api.RegOpenKey( hkey, subkeypath, 0, win32con.KEY_READ )

    try:
        oldval, oldtype = win32api.RegQueryValueEx( hkey, valname )
        if regtype and oldtype != regtype:
            raise TypeError
        return oldval, oldtype
    except win32api.error, err:
        if err[0] != winerror.ERROR_FILE_NOT_FOUND:
            raise err
        else:
            return None


def CreateKey( regkeypath ):
    rootkeyname, subkeypath = PathSplit( regkeypath )
    hkey = OpenKeyByName( rootkeyname, win32con.KEY_WRITE )
    hkey = win32api.RegCreateKey( hkey, subkeypath )


def PathSplit( regkeypath ):
    names = string.split( regkeypath, '\\' )
    rootkeyname = names[0]
    subkeypath = string.join( names[1:], '\\' )
    return rootkeyname, subkeypath



##################
# helper function GetHostIPaddress, get's host name or ip address and ensures that it will returns one of its ip addresses
# Code plucked from CheckNetworkConfig.cmd
def GetHostIPaddress( host ):
    # Check to see if the Orbacus host value is currently storing an IP address or a host name.
    if string.find( host, '.' ) == -1:
        # This indicates that a host name is being stored.
        name, alias, hostIp = socket.gethostbyname_ex( host )
        newIp = hostIp[0]
    else:
        # This indicates that the IP address is being stored.
        # Compare this IP addres with the IP address of the machine
        # to see if the address has changed.
        machineName = socket.gethostname()
        machine, machineAlias, machineIp = socket.gethostbyname_ex(machineName)
        for ip in machineIp:
            if ip in machineIp:
                newIp = ip
                return newIp
        # wasn't in list just return the first ip
        newIp = machineIp[0]
    return newIp
# end helper function GetHostIPaddress
##################


##################
# helper function ReplaceHostNameToHostIPinOrbacusParamString
# looks for 'lookFor' within the string delimited by 'paramDelimiter', then
# threats the part after the string passed as lookFor as a host.
# If the host is not an IP address, it will be replaced by the host's IP.
# The return string will then be reconstructed with the IP instead of the host name.
# parameterString, the Orbacus parameter string, which we usually get from the registry.
# paramDelimiter, default ' --' : the registry value should be a list of parameters separated by paramDelimiter
# lookFor, default 'host ' : identifies what parameter within the registry value that identifies the host name or the host ip address
#
# NOTE 1: The spaces in paramDelimiter, and lookFor are important
# NOTE 2: Leading and trailing whitespaces will be removed during this conversion from all parameters as well.
# Code plucked from CheckNetworkConfig.cmd

def ReplaceHostNameToHostIPinOrbacusParamString( parameterString, lookFor='host ', paramDelimiter=' --' ):

#    print 'Checking Orbacus IP Address . . .',
    parameterList = string.split( parameterString, paramDelimiter )
    newParameterList = []

    # find the parameter that starts with lookFor
    for ListItem in parameterList:

        ListItem = string.strip( ListItem )
        position = string.find( ListItem, lookFor )

        if position == 0:
            # if we've found it, get the IP address for that host
            hostParameter = ListItem[ len(lookFor): ]
            hostIP = GetHostIPaddress( hostParameter )

            # replace the list item that contains the host name with a list item with the IP address
            ListItem = lookFor + hostIP
            LogMessage( 'Using IP address %s for host %s' % ( hostIP, hostParameter ), INFO )
#            print "Using IP address %s for host %s" % ( hostIP, hostParameter )

        elif position <> -1:
            raise Exception( "Invalid configuration string detected in %s" % parameterString )

        # append the resultin ListItem to the new endpoint list
        newParameterList.append( ListItem )

    # rebuild the parameter sring from the list
    newparameterString = string.join( newParameterList, paramDelimiter )
#    return newparameterString
    return hostIP

# end helper function ReplaceHostNameToHostIPinOrbacusParamString
##################


# -----------------------------------------------------------------------
# This function receives a list of IP addresses and checks to see if at
# least one of the addresses are stored in the registry.  If not, the
# current IP address stored in the registry is over-written.
# Code plucked from CheckNetworkConfig.cmd
def CheckOrbacusIpAddress():

    # Retrieve the IP address from the registry. Just check IMR registry key.
    imrReg = r'HKEY_LOCAL_MACHINE\SOFTWARE\OOC\Properties\ooc\imr'
    try:
        if  DoesKeyExist( imrReg ):
            hkey, endpointString = GetKeyValue(win32con.HKEY_LOCAL_MACHINE,
                                     "SOFTWARE\\OOC\\Properties\\ooc\\imr",
                                     'admin_endpoint')
            ipAddress = ReplaceHostNameToHostIPinOrbacusParamString( endpointString )
        else:
            LogMessage( 'Getting ip address for %s' % _machineName, INFO )
#            print 'Getting ip address for %s' % _machineName
            ipAddress = GetHostIPaddress( _machineName )
        return ipAddress

    except Exception, err:
        print 'ERROR - Could not check Orbacus IP address.  Cause: %s' % err
        return FALSE

# -----------------------------------------------------------------------
def GetKeyValue(hkey, subkey, value):

    # This function is used to check the registry for a particular key value
    # (as specified by the calling function).

    # Retrieve the key from the registry.
    try:
        key = win32api.RegOpenKey(hkey,
                                  subkey,
                                  RESERVED,
                                  win32con.KEY_READ | win32con.KEY_WRITE)

    except win32api.error, err:
        if err[0] == winerror.ERROR_ACCESS_DENIED:
            print err[2]
            sys.exit(1)
        else:
            raise

    # Access the key value
    try:
        data, keyType = win32api.RegQueryValueEx(key, value)

    except win32api.error, err:
        if err[0] == winerror.ERROR_ACCESS_DENIED:
            print err[2]
            sys.exit(1)
        else:
            raise

    return key, data



#
# produce an NT computer name in 'canonical' form, without leading backslashes
# optional argument may have leading or missing double backslashes
# '.', '' or None is interpreted as the local computer
#
def NodeName( name=None ):
    nodename = ''
    if name:
        nodename = name
    if nodename[:2] == '\\\\':
        nodename = nodename[2:]
        if not nodename:
            nodename = name
            #raise ValueError( 'invalid node name' )
    if nodename == '.':
        nodename = ''
    if not nodename:
        nodename = win32api.GetComputerName()
    return string.upper( nodename )

    
#
# Get basic information about a Prinergy node.
# Divided into 4 steps:
# 1) Constructor just stores arguments
# 2) Query retrieves information, possibly raising exception
# 3) Format produces text, but does not write to a particular I/O device
# 4) Print writes formatted text to stdout
#
class PrinergyNodeInfo:
    def __init__( self, nodename ):
        self.nodename = NodeName( nodename )
        self.queried = FALSE
        
    def Query( self ):
        hklm = win32api.RegConnectRegistry( self.nodename, win32con.HKEY_LOCAL_MACHINE )
    
        subkey_Araxi = 'Software\\Creo\\Araxi'
        self.CurrentRelease = MyRegQueryValue( hklm, subkey_Araxi, 'CurrentRelease', win32con.REG_SZ )
    
        subkey_Araxi_CurrentRelease = subkey_Araxi + '\\' + self.CurrentRelease
        subkey_Araxi_CurrentRelease_Default = subkey_Araxi_CurrentRelease + '\\' + 'Default'
        subkey_Araxi_CurrentRelease_Default_System = subkey_Araxi_CurrentRelease_Default + '\\' + 'System'
        
        self.PrimaryNodeName = MyRegQueryValue( hklm, subkey_Araxi_CurrentRelease_Default_System, 'PrimaryNodeName', win32con.REG_SZ )
        self.NodeList = MyRegQueryValue( hklm, subkey_Araxi_CurrentRelease_Default_System, 'NodeList', win32con.REG_SZ )
        self.AraxiRootDirectory = MyRegQueryValue( hklm, subkey_Araxi_CurrentRelease_Default_System, 'AraxiRootDirectory', win32con.REG_SZ )
        try:
            # if the machine is a Emergency Plate Making (EPM) machine the OriginalPrimaryNodeName value will exist
            self.OriginalPrimaryNodeName = MyRegQueryValue( hklm, subkey_Araxi_CurrentRelease_Default_System, 'OriginalPrimaryNodeName', win32con.REG_SZ )
        except:
            self.OriginalPrimaryNodeName = None
        
        subkey = 'System\\CurrentControlSet\\Control\\Session Manager\\Environment'
        self.AraxiHomeEnvVar = MyRegQueryValue( hklm, subkey, 'AraxiHome', win32con.REG_SZ )
        
        subkey = 'System\\CurrentControlSet\\Services\\LanmanServer\\Shares'
        AraxiHomeShareInfo = MyRegQueryValue( hklm, subkey, 'AraxiHome', win32con.REG_MULTI_SZ )
        m = re.match( 'Path=(.+)', AraxiHomeShareInfo[1] )
        if m:
            self.AraxiHomeSharePath = m.group( 1 )
        else:
            self.AraxiHomeSharePath = '<Unavailable>'
        
        self.queried = 1
       
    
    def GetPrimaryNodeName( self ):
        if not self.queried:
            return '<Not queried>'

        return self.PrimaryNodeName
        
    def GetNodeName( self ):
        return self.nodename



#----------------------------------------------------------------------------
# ExecuteCommand
#
#   Executes a command line and sends input to the input pipe.  The output
#   pipe will be parsed for any erros
#
def ExecuteCommand( executableToRun, 
                    inputPipeMessage=None,
                    rundir=None, 
                    executableArguments='', 
                    errorsToCatch=['Failed', 'Warning', 'Exception', 'Error'], 
                    errorsToIgnore=[],
                    ignoreErr=FALSE ):

    try :
    
        # spit the executableToRun into the path portion and the executable portion
        executablePath, executableFile = os.path.split( executableToRun )
        
        # if there is a path, then check if file exists.
        if ( executablePath ):
            if ( not os.path.isfile( executableToRun ) ):
                raise Exception('Cannot find file "%s".' % executableToRun )
        
        # Make sure if no agrumets passed then the argument list is blank
        if ( not executableArguments ):
            executableArguments = ''
        
        # Create the command line string to be launched
        commandLine = r'%s %s' % ( executableToRun, executableArguments )
        
        if ( rundir ):
            if ( not os.path.isdir( rundir ) ):
                raise Exception('Cannot execute command "%s" in directory "%s".\nDirectory "%s" does not exist.' % ( commandLine, rundir, rundir ) )
        
            currentDir = PushDir( rundir )
            
        try:
            # Print commandline to the log file
            LogMessage( 'Executing command: %s' % commandLine )

            pipeIn, pipeOut = win32pipe.popen4(commandLine ,'t')
            if ( inputPipeMessage ):
                pipeIn.write( inputPipeMessage )
                
        finally:
            if ( rundir ):
                PopDir( currentDir )
     
        
        # Parse the output for any errors
        bError, postLine, errorsIgnored = IsErrorEx( pipeOut,
                                                     errorsToCatch=errorsToCatch,
                                                     errorsToIgnore=errorsToIgnore, 
                                                     ignoreErr=ignoreErr )
        if ( bError ):
            raise Exception('Error in output %s' %  postLine )
        
        # Close the pipes 
        pipeOut.close()
        pipeIn.close()
        
    except:
        raise Exception( 'Error: Failed to execute command.\nDetails: %s' % err() )
    
    # return the output
    return postLine, errorsIgnored


# -----------------------------------------------------------------------------

#
# exception reporting
#


# Utility function for printing error message
#       Instead of          ---         Use
#       ----------                      ---
#   try:                    -->     try:
#   except Exception, err:  -->     except:
#       print err           -->         print err()
#
# This ensures that non-standard exceptions (which aren't subclassed
# from Exception) can't slip out of the exception handler.


def err():
    strErr = None
    try:
        a, b, c = sys.exc_info()
        #print
        #print
        #print "'%s' --- '%s' --- '%s'" % (a, b, c)
        if b and str(b):
            if a and str(a):
                a_str = str(a)
                if str(a) == str(b)[:len(a_str)]:
                    # added this case to avoid repetition of the "exceptions.Exception" string in the message
                    strErr = "%s" % b
                else:
                    strErr = "%s %s" % (a, b)
            else:
                strErr = "%s" % b
        elif a and str(a):
            strErr = "'%s' raised" % a
        else:
            strErr = '[unknown exception]: %s' % c
    except:
        strErr = '[unknown exception]'
    return strErr


def format_traceback():
    import sys
    x = sys.exc_info()
    if x:
        import os
        import thread
        import traceback
        tid = thread.get_ident()
        pid = os.getpid()
        buf = 'Exception in process %d, thread %d\n' % ( pid, tid )
        buf = buf + 'Traceback (innermost last):\n'
        tblist = traceback.extract_tb( x[2] )
        for tb in tblist:
            buf = buf + '  File "%s", line %d, in %s\n    %s\n' % tb
        buf = buf + '%s: %s\n' % ( x[0].__name__, str( x[1] ) )
        return buf
    else:
        return 'No traceback'

def print_traceback():
    print format_traceback()


if __name__ == '__main__':
    main()
