From c505d24a8d00491f172a726c2cf59610a2f64654 Mon Sep 17 00:00:00 2001
From: Matthew Harmsen <mharmsen@redhat.com>
Date: Mon, 23 Apr 2012 13:39:04 -0700
Subject: [PATCH] * Completed the following six 'scriptlets':   Dogtag 10:
 Python 'initialization.py' Installation Scriptlet      
 (https://fedorahosted.org/pki/ticket/147)   Dogtag 10:
 Python 'instance_layout.py' Installation Scriptlet      
 (https://fedorahosted.org/pki/ticket/75)   Dogtag 10:
 Python 'webserver_layout.py' Installation Scriptlet      
 (https://fedorahosted.org/pki/ticket/140)   Dogtag 10:
 Python 'subsystem_layout.py' Installation Scriptlet      
 (https://fedorahosted.org/pki/ticket/141)   Dogtag 10:
 Python 'war_explosion.py' Installation Scriptlet      
 (https://fedorahosted.org/pki/ticket/76)   Dogtag 10:
 Python 'finalization.py' Installation Scriptlet      
 (https://fedorahosted.org/pki/ticket/148) * Created
 numerous PKI deployment helper utilities. * Augmented
 logging to provide indentation. * Generated logic for
 installation 'manifest'. * Tested logic using '--dry_run'
 option and '-p' prefix options. * Per initial review,
 removed numerous "constants" and consolidated   logic into
 "master" dictionary. * Corrected the following ticket:  
 Dogtag 10: Fix 'build_dogtag_pki' script to account for
 'pki-deploy' RPM      
 (https://fedorahosted.org/pki/ticket/138)       Resolves
 Bugzilla Bug #810047 - build_dogtag_pki fails with      
 requirements for pki-deploy      
 (https://bugzilla.redhat.com/show_bug.cgi?id=810047) *
 Created the following three 'scriptlets' as 'NOT YET
 IMPLEMENTED'   place-holders:   Dogtag 10: Python
 'security_databases.py' Installation Scriptlet      
 (https://fedorahosted.org/pki/ticket/136)   Dogtag 10:
 Python 'slot_assignment.py' Installation Scriptlet      
 (https://fedorahosted.org/pki/ticket/146)   Dogtag 10:
 Python 'configuration.py' Configuration Scriptlet      
 (https://fedorahosted.org/pki/ticket/137)

---
 base/deploy/CMakeLists.txt                       |  202 +++--
 base/deploy/config/pkideployment.cfg             |    8 +
 base/deploy/src/pkidestroy                       |  106 ++-
 base/deploy/src/pkispawn                         |  128 ++-
 base/deploy/src/scriptlets/configuration.py      |   52 +
 base/deploy/src/scriptlets/finalization.py       |  109 ++
 base/deploy/src/scriptlets/initialization.py     |   68 ++
 base/deploy/src/scriptlets/instance.py           |  105 --
 base/deploy/src/scriptlets/instance_layout.py    |   97 ++
 base/deploy/src/scriptlets/pkiconfig.py          |   64 +-
 base/deploy/src/scriptlets/pkihelper.py          | 1195 ++++++++++++++++++----
 base/deploy/src/scriptlets/pkilogging.py         |   30 +-
 base/deploy/src/scriptlets/pkimanifest.py        |   97 ++
 base/deploy/src/scriptlets/pkimessages.py        |  132 ++-
 base/deploy/src/scriptlets/pkiparser.py          |  362 +++++++
 base/deploy/src/scriptlets/pkiscriptlet.py       |    3 +-
 base/deploy/src/scriptlets/security_databases.py |   54 +-
 base/deploy/src/scriptlets/slot_assignment.py    |   52 +
 base/deploy/src/scriptlets/subsystem_layout.py   |  107 ++
 base/deploy/src/scriptlets/war_explosion.py      |   69 ++
 base/deploy/src/scriptlets/webserver_layout.py   |  119 +++
 scripts/build_dogtag_pki                         |    3 +-
 specs/pki-core.spec                              |   31 +-
 23 files changed, 2639 insertions(+), 554 deletions(-)
 create mode 100644 base/deploy/src/scriptlets/configuration.py
 create mode 100644 base/deploy/src/scriptlets/finalization.py
 create mode 100644 base/deploy/src/scriptlets/initialization.py
 delete mode 100644 base/deploy/src/scriptlets/instance.py
 create mode 100644 base/deploy/src/scriptlets/instance_layout.py
 create mode 100644 base/deploy/src/scriptlets/pkimanifest.py
 create mode 100644 base/deploy/src/scriptlets/pkiparser.py
 create mode 100644 base/deploy/src/scriptlets/slot_assignment.py
 create mode 100644 base/deploy/src/scriptlets/subsystem_layout.py
 create mode 100644 base/deploy/src/scriptlets/war_explosion.py
 create mode 100644 base/deploy/src/scriptlets/webserver_layout.py

diff --git a/base/deploy/CMakeLists.txt b/base/deploy/CMakeLists.txt
index 65c3eac..397c9a3 100644
--- a/base/deploy/CMakeLists.txt
+++ b/base/deploy/CMakeLists.txt
@@ -1,5 +1,26 @@
 project(deploy)
 
+set(PKI_SUBSYSTEMS
+    ca
+    kra
+    ocsp
+    ra
+    tks
+    tps
+)
+
+set(TOMCAT_SUBSYSTEMS
+    ca
+    kra
+    ocsp
+    tks
+)
+
+set(APACHE_SUBSYSTEMS
+    ra
+    tps
+)
+
 install(
     FILES
         src/pkispawn
@@ -34,13 +55,22 @@ execute_process(
 )
 install(
     FILES
-        src/scriptlets/instance.py
+        src/scriptlets/configuration.py
+        src/scriptlets/finalization.py
+        src/scriptlets/initialization.py
+        src/scriptlets/instance_layout.py
         src/scriptlets/pkiconfig.py
         src/scriptlets/pkihelper.py
-        src/scriptlets/pkimessages.py
         src/scriptlets/pkilogging.py
+        src/scriptlets/pkimanifest.py
+        src/scriptlets/pkimessages.py
+        src/scriptlets/pkiparser.py
         src/scriptlets/pkiscriptlet.py
         src/scriptlets/security_databases.py
+        src/scriptlets/slot_assignment.py
+        src/scriptlets/subsystem_layout.py
+        src/scriptlets/war_explosion.py
+        src/scriptlets/webserver_layout.py
     DESTINATION
         ${PYTHON_SITE_PACKAGES}/pki/deployment
     PERMISSIONS
@@ -67,60 +97,114 @@ install(
 #install(CODE "file(MAKE_DIRECTORY \$ENV{DESTDIR}${VAR_INSTALL_DIR}/lock/pki)")
 #install(CODE "file(MAKE_DIRECTORY \$ENV{DESTDIR}${VAR_INSTALL_DIR}/run/pki)")
 
-# install subsystem directories for pkispawn
-install(CODE "file(MAKE_DIRECTORY \$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/spawn/ca)")
-install(CODE "file(MAKE_DIRECTORY \$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/spawn/kra)")
-install(CODE "file(MAKE_DIRECTORY \$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/spawn/ocsp)")
-install(CODE "file(MAKE_DIRECTORY \$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/spawn/ra)")
-install(CODE "file(MAKE_DIRECTORY \$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/spawn/tks)")
-install(CODE "file(MAKE_DIRECTORY \$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/spawn/tps)")
-
-# install subsystem directories for pkidestroy
-install(CODE "file(MAKE_DIRECTORY \$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/destroy/ca)")
-install(CODE "file(MAKE_DIRECTORY \$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/destroy/kra)")
-install(CODE "file(MAKE_DIRECTORY \$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/destroy/ocsp)")
-install(CODE "file(MAKE_DIRECTORY \$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/destroy/ra)")
-install(CODE "file(MAKE_DIRECTORY \$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/destroy/tks)")
-install(CODE "file(MAKE_DIRECTORY \$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/destroy/tps)")
-
-# generate and install symbolic links for pkispawn CA
-install(
-    CODE
-        "execute_process(
-            COMMAND
-                ${CMAKE_COMMAND} -E create_symlink
-                \"${PYTHON_SITE_PACKAGES}/pki/deployment/instance.py\"
-                \"\$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/spawn/ca/0010_instance\")"
-)
-install(
-    CODE
-        "execute_process(
-            COMMAND
-                ${CMAKE_COMMAND} -E create_symlink
-                    \"${PYTHON_SITE_PACKAGES}/pki/deployment/security_databases.py\"
-                    \"\$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/spawn/ca/0020_security_databases\")"
-)
-
-# generate and install symbolic links for pkispawn KRA
-
-# generate and install symbolic links for pkispawn OCSP
-
-# generate and install symbolic links for pkispawn RA
-
-# generate and install symbolic links for pkispawn TKS
-
-# generate and install symbolic links for pkispawn TPS
-
-
-# generate and install symbolic links for pkidestroy CA
-
-# generate and install symbolic links for pkidestroy KRA
-
-# generate and install symbolic links for pkidestroy OCSP
-
-# generate and install symbolic links for pkidestroy RA
-
-# generate and install symbolic links for pkidestroy TKS
-
-# generate and install symbolic links for pkidestroy TPS
-
+# install subsystem directories for pkispawn and pkidestroy
+foreach(PKI_SUBSYSTEM ${PKI_SUBSYSTEMS})
+    install(CODE "file(MAKE_DIRECTORY \$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/spawn/${PKI_SUBSYSTEM})")
+    install(CODE "file(MAKE_DIRECTORY \$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/destroy/${PKI_SUBSYSTEM})")
+endforeach(PKI_SUBSYSTEM ${PKI_SUBSYSTEMS})
+
+# generate and install shared ordered 'scriptlet' symbolic links
+# for CA, KRA, OCSP, and TKS for pkispawn
+foreach(TOMCAT_SUBSYSTEM ${TOMCAT_SUBSYSTEMS})
+    install(CODE "execute_process(COMMAND
+        ${CMAKE_COMMAND} -E create_symlink
+        \"${PYTHON_SITE_PACKAGES}/pki/deployment/initialization.py\"
+        \"\$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/spawn/${TOMCAT_SUBSYSTEM}/000_initialization\")"
+    )
+    install(CODE "execute_process(COMMAND
+        ${CMAKE_COMMAND} -E create_symlink
+        \"${PYTHON_SITE_PACKAGES}/pki/deployment/instance_layout.py\"
+        \"\$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/spawn/${TOMCAT_SUBSYSTEM}/010_instance_layout\")"
+    )
+    install(CODE "execute_process(COMMAND
+        ${CMAKE_COMMAND} -E create_symlink
+        \"${PYTHON_SITE_PACKAGES}/pki/deployment/webserver_layout.py\"
+        \"\$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/spawn/${TOMCAT_SUBSYSTEM}/020_webserver_layout\")"
+    )
+    install(CODE "execute_process(COMMAND
+        ${CMAKE_COMMAND} -E create_symlink
+        \"${PYTHON_SITE_PACKAGES}/pki/deployment/subsystem_layout.py\"
+        \"\$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/spawn/${TOMCAT_SUBSYSTEM}/030_subsystem_layout\")"
+    )
+    install(CODE "execute_process(COMMAND
+        ${CMAKE_COMMAND} -E create_symlink
+        \"${PYTHON_SITE_PACKAGES}/pki/deployment/war_explosion.py\"
+        \"\$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/spawn/${TOMCAT_SUBSYSTEM}/040_war_explosion\")"
+    )
+    install(CODE "execute_process(COMMAND
+        ${CMAKE_COMMAND} -E create_symlink
+        \"${PYTHON_SITE_PACKAGES}/pki/deployment/security_databases.py\"
+        \"\$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/spawn/${TOMCAT_SUBSYSTEM}/050_security_databases\")"
+    )
+    install(CODE "execute_process(COMMAND
+        ${CMAKE_COMMAND} -E create_symlink
+        \"${PYTHON_SITE_PACKAGES}/pki/deployment/slot_assignment.py\"
+        \"\$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/spawn/${TOMCAT_SUBSYSTEM}/060_slot_assignment\")"
+    )
+    install(CODE "execute_process(COMMAND
+        ${CMAKE_COMMAND} -E create_symlink
+        \"${PYTHON_SITE_PACKAGES}/pki/deployment/configuration.py\"
+        \"\$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/spawn/${TOMCAT_SUBSYSTEM}/070_configuration\")"
+    )
+    install(CODE "execute_process(COMMAND
+        ${CMAKE_COMMAND} -E create_symlink
+        \"${PYTHON_SITE_PACKAGES}/pki/deployment/finalization.py\"
+        \"\$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/spawn/${TOMCAT_SUBSYSTEM}/999_finalization\")"
+    )
+endforeach(TOMCAT_SUBSYSTEM ${TOMCAT_SUBSYSTEMS})
+
+# generate and install shared ordered 'scriptlet' symbolic links
+# for RA, and TPS for pkispawn
+
+# generate and install shared ordered 'scriptlet' symbolic links
+# for CA, KRA, OCSP, and TKS for pkidestroy
+foreach(TOMCAT_SUBSYSTEM ${TOMCAT_SUBSYSTEMS})
+    install(CODE "execute_process(COMMAND
+        ${CMAKE_COMMAND} -E create_symlink
+        \"${PYTHON_SITE_PACKAGES}/pki/deployment/initialization.py\"
+        \"\$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/destroy/${TOMCAT_SUBSYSTEM}/000_initialization\")"
+    )
+    install(CODE "execute_process(COMMAND
+        ${CMAKE_COMMAND} -E create_symlink
+        \"${PYTHON_SITE_PACKAGES}/pki/deployment/configuration.py\"
+        \"\$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/destroy/${TOMCAT_SUBSYSTEM}/930_configuration\")"
+    )
+#   install(CODE "execute_process(COMMAND
+#       ${CMAKE_COMMAND} -E create_symlink
+#       \"${PYTHON_SITE_PACKAGES}/pki/deployment/slot_assignment.py\"
+#       \"\$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/destroy/${TOMCAT_SUBSYSTEM}/940_slot_assignment\")"
+#   )
+    install(CODE "execute_process(COMMAND
+        ${CMAKE_COMMAND} -E create_symlink
+        \"${PYTHON_SITE_PACKAGES}/pki/deployment/security_databases.py\"
+        \"\$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/destroy/${TOMCAT_SUBSYSTEM}/950_security_databases\")"
+    )
+    install(CODE "execute_process(COMMAND
+        ${CMAKE_COMMAND} -E create_symlink
+        \"${PYTHON_SITE_PACKAGES}/pki/deployment/war_explosion.py\"
+        \"\$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/destroy/${TOMCAT_SUBSYSTEM}/960_war_explosion\")"
+    )
+    install(CODE "execute_process(COMMAND
+        ${CMAKE_COMMAND} -E create_symlink
+        \"${PYTHON_SITE_PACKAGES}/pki/deployment/subsystem_layout.py\"
+        \"\$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/destroy/${TOMCAT_SUBSYSTEM}/970_subsystem_layout\")"
+    )
+    install(CODE "execute_process(COMMAND
+        ${CMAKE_COMMAND} -E create_symlink
+        \"${PYTHON_SITE_PACKAGES}/pki/deployment/webserver_layout.py\"
+        \"\$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/destroy/${TOMCAT_SUBSYSTEM}/980_webserver_layout\")"
+    )
+    install(CODE "execute_process(COMMAND
+        ${CMAKE_COMMAND} -E create_symlink
+        \"${PYTHON_SITE_PACKAGES}/pki/deployment/instance_layout.py\"
+        \"\$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/destroy/${TOMCAT_SUBSYSTEM}/990_instance_layout\")"
+    )
+    install(CODE "execute_process(COMMAND
+        ${CMAKE_COMMAND} -E create_symlink
+        \"${PYTHON_SITE_PACKAGES}/pki/deployment/finalization.py\"
+        \"\$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/destroy/${TOMCAT_SUBSYSTEM}/999_finalization\")"
+    )
+endforeach(TOMCAT_SUBSYSTEM ${TOMCAT_SUBSYSTEMS})
+
+# generate and install shared ordered 'scriptlet' symbolic links
+# for RA, and TPS for pkidestroy
diff --git a/base/deploy/config/pkideployment.cfg b/base/deploy/config/pkideployment.cfg
index db9ce54..954fa93 100644
--- a/base/deploy/config/pkideployment.cfg
+++ b/base/deploy/config/pkideployment.cfg
@@ -1,5 +1,8 @@
 [Common]
+pki_source_root=/usr/share/pki
 pki_instance_root=/var/lib/pki
+pki_instance_configuration_root=/etc/pki
+pki_instance_log_root=/var/log/pki
 pki_instance_name=instance
 pki_user=pkiuser
 pki_group=pkiuser
@@ -10,19 +13,24 @@ pki_http_port=80
 pki_https_port=443
 [Tomcat]
 pki_web_server=Tomcat
+pki_tomcat_root=/usr/share/tomcat
 pki_http_port=8080
 pki_https_port=8443
 pki_ajp_port=8009
 pki_security_manager=true
 [CA]
 pki_subsystem=CA
+pki_war_name=ca.war
 [KRA]
 pki_subsystem=KRA
+pki_war_name=kra.war
 [OCSP]
 pki_subsystem=OCSP
+pki_war_name=ocsp.war
 [RA]
 pki_subsystem=RA
 [TKS]
 pki_subsystem=TKS
+pki_war_name=tks.war
 [TPS]
 pki_subsystem=TPS
diff --git a/base/deploy/src/pkidestroy b/base/deploy/src/pkidestroy
index a762e14..6d0afd2 100755
--- a/base/deploy/src/pkidestroy
+++ b/base/deploy/src/pkidestroy
@@ -23,15 +23,16 @@
 import sys
 if not hasattr(sys, "hexversion") or sys.hexversion < 0x020700f0:
     print "Python version %s.%s.%s is too old." % sys.version_info[:3]
-    print "Please upgrade to at least Python 2.7.0." 
+    print "Please upgrade to at least Python 2.7.0."
     sys.exit(1)
 try:
     import argparse
     import logging
     import os
     import pprint
+    from time import strftime as date
     from pki.deployment import pkiconfig as config
-    from pki.deployment import pkihelper
+    from pki.deployment import pkiparser as parse
     from pki.deployment import pkilogging
     from pki.deployment import pkimessages as log
 except ImportError:
@@ -52,23 +53,34 @@ def main(argv):
     if not os.geteuid() == 0:
         sys.exit("'%s' must be run as root!" % argv[0])
 
+    # Set the umask
+    os.umask(config.PKI_DEPLOYMENT_DEFAULT_UMASK)
+
+    # Generate a timestamp
+    config.pki_timestamp = date('%Y%m%d%H%M%S')
+
     # Initialize 'pretty print' for objects
     pp = pprint.PrettyPrinter(indent=4)
 
     # Read and process command-line arguments.
-    pkihelper.process_command_line_arguments(argv)
+    parse.process_command_line_arguments(argv)
 
     # Enable 'pkidestroy' logging.
-    config.pki_log_dir = config.pki_root_prefix +\
-                         config.PKIDESTROY_LOG_PATH
-    config.pki_log_name = config.PKIDESTROY_LOG_PREFIX +\
-                          config.pki_subsystem.lower() +\
-                          config.PKIDESTROY_LOG_SUFFIX
+    if not config.pki_dry_run_flag:
+        config.pki_log_dir = config.pki_root_prefix +\
+                             "/var/log"
+        config.pki_log_name = "pki" + "-" +\
+                              config.pki_subsystem.lower() +\
+                              "-" + "destroy" + "." +\
+                              config.pki_timestamp + "." + "log"
+    else:
+        config.pki_log_dir = "/dev"
+        config.pki_log_name = "null"
     rv = pkilogging.enable_pki_logger(config.pki_log_dir,
                                       config.pki_log_name,
                                       config.pki_log_level,
                                       config.pki_console_log_level,
-                                      config.PKIDESTROY_LOGGER)
+                                      "pkidestroy")
     if rv != OSError:
         config.pki_log = rv
     else:
@@ -76,17 +88,24 @@ def main(argv):
         sys.exit(1)
 
     # Read the specified PKI configuration file.
-    rv = pkihelper.read_pki_configuration_file()
+    rv = parse.read_pki_configuration_file()
     if rv != 0:
-        config.pki_log.error(PKI_UNABLE_TO_PARSE_1, rv)
+        config.pki_log.error(PKI_UNABLE_TO_PARSE_1, rv,
+                             extra=config.PKI_INDENTATION_LEVEL_0)
         sys.exit(1)
     else:
-        config.pki_log.debug(log.PKI_DICTIONARY_COMMON)
-        config.pki_log.debug(pp.pformat(config.pki_common_dict))
-        config.pki_log.debug(log.PKI_DICTIONARY_WEB_SERVER)
-        config.pki_log.debug(pp.pformat(config.pki_web_server_dict))
-        config.pki_log.debug(log.PKI_DICTIONARY_SUBSYSTEM)
-        config.pki_log.debug(pp.pformat(config.pki_subsystem_dict))
+        config.pki_log.debug(log.PKI_DICTIONARY_COMMON,
+                             extra=config.PKI_INDENTATION_LEVEL_0)
+        config.pki_log.debug(pp.pformat(config.pki_common_dict),
+                             extra=config.PKI_INDENTATION_LEVEL_0)
+        config.pki_log.debug(log.PKI_DICTIONARY_WEB_SERVER,
+                             extra=config.PKI_INDENTATION_LEVEL_0)
+        config.pki_log.debug(pp.pformat(config.pki_web_server_dict),
+                             extra=config.PKI_INDENTATION_LEVEL_0)
+        config.pki_log.debug(log.PKI_DICTIONARY_SUBSYSTEM,
+                             extra=config.PKI_INDENTATION_LEVEL_0)
+        config.pki_log.debug(pp.pformat(config.pki_subsystem_dict),
+                             extra=config.PKI_INDENTATION_LEVEL_0)
 
     # Override PKI configuration file values with 'custom' command-line values.
     if not config.pki_instance_name is None:
@@ -101,51 +120,56 @@ def main(argv):
     if not config.pki_ajp_port is None:
         config.pki_web_server_dict['pki_ajp_port'] =\
             config.pki_ajp_port
-    config.pki_log.debug(log.PKI_DICTIONARY_COMMON)
-    config.pki_log.debug(pp.pformat(config.pki_common_dict))
-    config.pki_log.debug(log.PKI_DICTIONARY_WEB_SERVER)
-    config.pki_log.debug(pp.pformat(config.pki_web_server_dict))
-    config.pki_log.debug(log.PKI_DICTIONARY_SUBSYSTEM)
-    config.pki_log.debug(pp.pformat(config.pki_subsystem_dict))
+    config.pki_log.debug(log.PKI_DICTIONARY_COMMON,
+                         extra=config.PKI_INDENTATION_LEVEL_0)
+    config.pki_log.debug(pp.pformat(config.pki_common_dict),
+                         extra=config.PKI_INDENTATION_LEVEL_0)
+    config.pki_log.debug(log.PKI_DICTIONARY_WEB_SERVER,
+                         extra=config.PKI_INDENTATION_LEVEL_0)
+    config.pki_log.debug(pp.pformat(config.pki_web_server_dict),
+                         extra=config.PKI_INDENTATION_LEVEL_0)
+    config.pki_log.debug(log.PKI_DICTIONARY_SUBSYSTEM,
+                         extra=config.PKI_INDENTATION_LEVEL_0)
+    config.pki_log.debug(pp.pformat(config.pki_subsystem_dict),
+                         extra=config.PKI_INDENTATION_LEVEL_0)
 
     # Combine the various sectional dictionaries into a PKI master dictionary
-    pkihelper.create_pki_master_dictionary()
-    config.pki_log.debug(log.PKI_DICTIONARY_MASTER)
-    config.pki_log.debug(pp.pformat(config.pki_master_dict))
+    parse.compose_pki_master_dictionary()
+    config.pki_master_dict['pki_destroy_log'] = config.pki_log_dir + "/" +\
+                                                config.pki_log_name
+    config.pki_log.debug(log.PKI_DICTIONARY_MASTER,
+                         extra=config.PKI_INDENTATION_LEVEL_0)
+    config.pki_log.debug(pp.pformat(config.pki_master_dict),
+                         extra=config.PKI_INDENTATION_LEVEL_0)
 
     # Remove the specified PKI subsystem.
-    pki_scriptlets_path = config.pki_root_prefix +\
-                          config.PKIDESTROY_PATH +\
+    pki_scriptlets_path = "/usr/share/pki/deployment/destroy" +\
                           "/" + config.pki_subsystem.lower()
     if not os.path.exists(pki_scriptlets_path) or\
        not os.path.isdir(pki_scriptlets_path):
         config.pki_log.error(log.PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1,
-                             pki_scriptlets_path)
+                             pki_scriptlets_path,
+                             extra=config.PKI_INDENTATION_LEVEL_0)
         sys.exit(1)
     pki_subsystem_scriptlets = os.listdir(pki_scriptlets_path)
     pki_subsystem_scriptlets.sort()
 
     # Process the various "scriptlets" for the specified PKI subsystem.
     rv = 0
-    config.pki_log.info(log.PKIDESTROY_BEGIN_MESSAGE_2,
-                        config.pki_subsystem,
-                        config.pki_master_dict['pki_instance_name'])
     for pki_scriptlet in pki_subsystem_scriptlets:
-        scriptlet = __import__(config.PKI_DEPLOYMENT_SCRIPTLETS_MODULE +\
-                               '.' + pki_scriptlet[5:],
-                               fromlist = [pki_scriptlet[5:]])
+        scriptlet = __import__("pki.deployment" +\
+                               "." + pki_scriptlet[4:],
+                               fromlist = [pki_scriptlet[4:]])
         instance = scriptlet.PkiScriptlet()
         rv = instance.destroy()
         if rv != 0:
             sys.exit(1)
-    config.pki_log.info(log.PKIDESTROY_END_MESSAGE_2,
-                        config.pki_subsystem,
-                        config.pki_master_dict['pki_instance_name'])
-    config.pki_log.debug(log.PKI_DICTIONARY_MASTER)
-    config.pki_log.debug(pp.pformat(config.pki_master_dict))
+    config.pki_log.debug(log.PKI_DICTIONARY_MASTER,
+                         extra=config.PKI_INDENTATION_LEVEL_0)
+    config.pki_log.debug(pp.pformat(config.pki_master_dict),
+                         extra=config.PKI_INDENTATION_LEVEL_0)
 
 
 # PKI Deployment Entry Point
 if __name__ == "__main__":
     main(sys.argv)
-
diff --git a/base/deploy/src/pkispawn b/base/deploy/src/pkispawn
index a48a475..2ac21a6 100755
--- a/base/deploy/src/pkispawn
+++ b/base/deploy/src/pkispawn
@@ -23,15 +23,16 @@
 import sys
 if not hasattr(sys, "hexversion") or sys.hexversion < 0x020700f0:
     print "Python version %s.%s.%s is too old." % sys.version_info[:3]
-    print "Please upgrade to at least Python 2.7.0." 
+    print "Please upgrade to at least Python 2.7.0."
     sys.exit(1)
 try:
     import argparse
     import logging
     import os
     import pprint
+    from time import strftime as date
     from pki.deployment import pkiconfig as config
-    from pki.deployment import pkihelper
+    from pki.deployment import pkiparser as parse
     from pki.deployment import pkilogging
     from pki.deployment import pkimessages as log
 except ImportError:
@@ -55,40 +56,49 @@ def main(argv):
     # Set the umask
     os.umask(config.PKI_DEPLOYMENT_DEFAULT_UMASK)
 
+    # Generate a timestamp
+    config.pki_timestamp = date('%Y%m%d%H%M%S')
+
     # Initialize 'pretty print' for objects
     pp = pprint.PrettyPrinter(indent=4)
 
     # Read and process command-line arguments.
-    pkihelper.process_command_line_arguments(argv)
+    parse.process_command_line_arguments(argv)
 
     # Enable 'pkispawn' logging.
     rv = 0
     if not config.pki_update_flag:
-        pki_deployment_begin = log.PKISPAWN_BEGIN_MESSAGE_2
-        pki_deployment_end = log.PKISPAWN_END_MESSAGE_2
-        config.pki_log_dir = config.pki_root_prefix +\
-                             config.PKISPAWN_LOG_PATH
-        config.pki_log_name = config.PKISPAWN_LOG_PREFIX +\
-                              config.pki_subsystem.lower() +\
-                              config.PKISPAWN_LOG_SUFFIX
+        if not config.pki_dry_run_flag:
+            config.pki_log_dir = config.pki_root_prefix +\
+                                 "/var/log"
+            config.pki_log_name = "pki" + "-" +\
+                                  config.pki_subsystem.lower() +\
+                                  "-" + "spawn" + "." +\
+                                  config.pki_timestamp + "." + "log"
+        else:
+            config.pki_log_dir = "/dev"
+            config.pki_log_name = "null"
         rv = pkilogging.enable_pki_logger(config.pki_log_dir,
                                           config.pki_log_name,
                                           config.pki_log_level,
                                           config.pki_console_log_level,
-                                          config.PKISPAWN_LOGGER)
+                                          "pkispawn")
     else:
-        pki_deployment_begin = log.PKIRESPAWN_BEGIN_MESSAGE_2
-        pki_deployment_end = log.PKIRESPAWN_END_MESSAGE_2
-        config.pki_log_dir = config.pki_root_prefix +\
-                             config.PKIRESPAWN_LOG_PATH
-        config.pki_log_name = config.PKIRESPAWN_LOG_PREFIX +\
-                              config.pki_subsystem.lower() +\
-                              config.PKIRESPAWN_LOG_SUFFIX
+        if not config.pki_dry_run_flag:
+            config.pki_log_dir = config.pki_root_prefix +\
+                                 "/var/log"
+            config.pki_log_name = "pki" + "-" +\
+                                  config.pki_subsystem.lower() +\
+                                  "-" + "respawn" + "." +\
+                                  config.pki_timestamp + "." + "log"
+        else:
+            config.pki_log_dir = "/dev"
+            config.pki_log_name = "null"
         rv = pkilogging.enable_pki_logger(config.pki_log_dir,
                                           config.pki_log_name,
                                           config.pki_log_level,
                                           config.pki_console_log_level,
-                                          config.PKIRESPAWN_LOGGER)
+                                          "pkirespawn")
     if rv != OSError:
         config.pki_log = rv
     else:
@@ -96,17 +106,24 @@ def main(argv):
         sys.exit(1)
 
     # Read the specified PKI configuration file.
-    rv = pkihelper.read_pki_configuration_file()
+    rv = parse.read_pki_configuration_file()
     if rv != 0:
-        config.pki_log.error(PKI_UNABLE_TO_PARSE_1, rv)
+        config.pki_log.error(PKI_UNABLE_TO_PARSE_1, rv,
+                             extra=config.PKI_INDENTATION_LEVEL_0)
         sys.exit(1)
     else:
-        config.pki_log.debug(log.PKI_DICTIONARY_COMMON)
-        config.pki_log.debug(pp.pformat(config.pki_common_dict))
-        config.pki_log.debug(log.PKI_DICTIONARY_WEB_SERVER)
-        config.pki_log.debug(pp.pformat(config.pki_web_server_dict))
-        config.pki_log.debug(log.PKI_DICTIONARY_SUBSYSTEM)
-        config.pki_log.debug(pp.pformat(config.pki_subsystem_dict))
+        config.pki_log.debug(log.PKI_DICTIONARY_COMMON,
+                             extra=config.PKI_INDENTATION_LEVEL_0)
+        config.pki_log.debug(pp.pformat(config.pki_common_dict),
+                             extra=config.PKI_INDENTATION_LEVEL_0)
+        config.pki_log.debug(log.PKI_DICTIONARY_WEB_SERVER,
+                             extra=config.PKI_INDENTATION_LEVEL_0)
+        config.pki_log.debug(pp.pformat(config.pki_web_server_dict),
+                             extra=config.PKI_INDENTATION_LEVEL_0)
+        config.pki_log.debug(log.PKI_DICTIONARY_SUBSYSTEM,
+                             extra=config.PKI_INDENTATION_LEVEL_0)
+        config.pki_log.debug(pp.pformat(config.pki_subsystem_dict),
+                             extra=config.PKI_INDENTATION_LEVEL_0)
 
     # Override PKI configuration file values with 'custom' command-line values.
     if not config.pki_instance_name is None:
@@ -121,39 +138,50 @@ def main(argv):
     if not config.pki_ajp_port is None:
         config.pki_web_server_dict['pki_ajp_port'] =\
             config.pki_ajp_port
-    config.pki_log.debug(log.PKI_DICTIONARY_COMMON)
-    config.pki_log.debug(pp.pformat(config.pki_common_dict))
-    config.pki_log.debug(log.PKI_DICTIONARY_WEB_SERVER)
-    config.pki_log.debug(pp.pformat(config.pki_web_server_dict))
-    config.pki_log.debug(log.PKI_DICTIONARY_SUBSYSTEM)
-    config.pki_log.debug(pp.pformat(config.pki_subsystem_dict))
+    config.pki_log.debug(log.PKI_DICTIONARY_COMMON,
+                         extra=config.PKI_INDENTATION_LEVEL_0)
+    config.pki_log.debug(pp.pformat(config.pki_common_dict),
+                         extra=config.PKI_INDENTATION_LEVEL_0)
+    config.pki_log.debug(log.PKI_DICTIONARY_WEB_SERVER,
+                         extra=config.PKI_INDENTATION_LEVEL_0)
+    config.pki_log.debug(pp.pformat(config.pki_web_server_dict),
+                         extra=config.PKI_INDENTATION_LEVEL_0)
+    config.pki_log.debug(log.PKI_DICTIONARY_SUBSYSTEM,
+                         extra=config.PKI_INDENTATION_LEVEL_0)
+    config.pki_log.debug(pp.pformat(config.pki_subsystem_dict),
+                         extra=config.PKI_INDENTATION_LEVEL_0)
 
     # Combine the various sectional dictionaries into a PKI master dictionary
-    pkihelper.create_pki_master_dictionary()
-    config.pki_log.debug(log.PKI_DICTIONARY_MASTER)
-    config.pki_log.debug(pp.pformat(config.pki_master_dict))
+    parse.compose_pki_master_dictionary()
+    if not config.pki_update_flag:
+        config.pki_master_dict['pki_spawn_log'] = config.pki_log_dir + "/" +\
+                                                  config.pki_log_name
+    else:
+        config.pki_master_dict['pki_respawn_log'] = config.pki_log_dir + "/" +\
+                                                    config.pki_log_name
+    config.pki_log.debug(log.PKI_DICTIONARY_MASTER,
+                         extra=config.PKI_INDENTATION_LEVEL_0)
+    config.pki_log.debug(pp.pformat(config.pki_master_dict),
+                         extra=config.PKI_INDENTATION_LEVEL_0)
 
     # Install and configure the specified PKI subsystem.
-    pki_scriptlets_path = config.pki_root_prefix +\
-                          config.PKISPAWN_PATH +\
+    pki_scriptlets_path = "/usr/share/pki/deployment/spawn" +\
                           "/" + config.pki_subsystem.lower()
     if not os.path.exists(pki_scriptlets_path) or\
        not os.path.isdir(pki_scriptlets_path):
         config.pki_log.error(log.PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1,
-                             pki_scriptlets_path)
+                             pki_scriptlets_path,
+                             extra=config.PKI_INDENTATION_LEVEL_0)
         sys.exit(1)
     pki_subsystem_scriptlets = os.listdir(pki_scriptlets_path)
     pki_subsystem_scriptlets.sort()
 
     # Process the various "scriptlets" for the specified PKI subsystem.
     rv = 0
-    config.pki_log.info(pki_deployment_begin,
-                        config.pki_subsystem,
-                        config.pki_master_dict['pki_instance_name'])
     for pki_scriptlet in pki_subsystem_scriptlets:
-        scriptlet = __import__(config.PKI_DEPLOYMENT_SCRIPTLETS_MODULE +\
-                               '.' + pki_scriptlet[5:],
-                               fromlist = [pki_scriptlet[5:]])
+        scriptlet = __import__("pki.deployment" +\
+                               "." + pki_scriptlet[4:],
+                               fromlist = [pki_scriptlet[4:]])
         instance = scriptlet.PkiScriptlet()
         if not config.pki_update_flag:
             rv = instance.spawn()
@@ -161,14 +189,12 @@ def main(argv):
             rv = instance.respawn()
         if rv != 0:
             sys.exit(1)
-    config.pki_log.info(pki_deployment_end,
-                        config.pki_subsystem,
-                        config.pki_master_dict['pki_instance_name'])
-    config.pki_log.debug(log.PKI_DICTIONARY_MASTER)
-    config.pki_log.debug(pp.pformat(config.pki_master_dict))
+    config.pki_log.debug(log.PKI_DICTIONARY_MASTER,
+                         extra=config.PKI_INDENTATION_LEVEL_0)
+    config.pki_log.debug(pp.pformat(config.pki_master_dict),
+                         extra=config.PKI_INDENTATION_LEVEL_0)
 
 
 # PKI Deployment Entry Point
 if __name__ == "__main__":
     main(sys.argv)
-
diff --git a/base/deploy/src/scriptlets/configuration.py b/base/deploy/src/scriptlets/configuration.py
new file mode 100644
index 0000000..45b325c
--- /dev/null
+++ b/base/deploy/src/scriptlets/configuration.py
@@ -0,0 +1,52 @@
+#!/usr/bin/python -t
+# Authors:
+#     Matthew Harmsen <mharmsen@redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2012 Red Hat, Inc.
+# All rights reserved.
+#
+
+# PKI Deployment Imports
+import pkiconfig as config
+from pkiconfig import pki_master_dict as master
+import pkimessages as log
+import pkiscriptlet
+
+
+# PKI Deployment Instance Population Classes
+class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
+    rv = 0
+
+    def spawn(self):
+        config.pki_log.info(log.CONFIGURATION_SPAWN_1, __name__,
+                            extra=config.PKI_INDENTATION_LEVEL_1)
+        config.pki_log.info("NOT YET IMPLEMENTED",
+                            extra=config.PKI_INDENTATION_LEVEL_2)
+        return self.rv
+
+    def respawn(self):
+        config.pki_log.info(log.CONFIGURATION_RESPAWN_1, __name__,
+                            extra=config.PKI_INDENTATION_LEVEL_1)
+        config.pki_log.info("NOT YET IMPLEMENTED",
+                            extra=config.PKI_INDENTATION_LEVEL_2)
+        return self.rv
+
+    def destroy(self):
+        config.pki_log.info(log.CONFIGURATION_DESTROY_1, __name__,
+                            extra=config.PKI_INDENTATION_LEVEL_1)
+        config.pki_log.info("NOT YET IMPLEMENTED",
+                            extra=config.PKI_INDENTATION_LEVEL_2)
+        return self.rv
diff --git a/base/deploy/src/scriptlets/finalization.py b/base/deploy/src/scriptlets/finalization.py
new file mode 100644
index 0000000..6007326
--- /dev/null
+++ b/base/deploy/src/scriptlets/finalization.py
@@ -0,0 +1,109 @@
+#!/usr/bin/python -t
+# Authors:
+#     Matthew Harmsen <mharmsen@redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2012 Red Hat, Inc.
+# All rights reserved.
+#
+
+# PKI Deployment Imports
+import pkiconfig as config
+from pkiconfig import pki_master_dict as master
+import pkihelper as util
+import pkimanifest as manifest
+import pkimessages as log
+import pkiscriptlet
+
+
+# PKI Deployment Instance Population Classes
+class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
+    rv = 0
+
+    def spawn(self):
+        config.pki_log.info(log.FINALIZATION_SPAWN_1, __name__,
+                            extra=config.PKI_INDENTATION_LEVEL_1)
+        # Save a copy of the configuration file used by this process
+        # (which may be used later by 'pkidestroy')
+        util.file.copy(config.pkideployment_cfg,
+                       master['pki_subsystem_registry_path'] +\
+                       "/" + config.PKI_DEPLOYMENT_DEFAULT_CONFIGURATION_FILE)
+        # Save a timestamped copy of the installation manifest file
+        filename = master['pki_root_prefix'] +\
+                   config.PKI_DEPLOYMENT_REGISTRY_ROOT + "/" +\
+                   master['pki_instance_name'] + "/" +\
+                   master['pki_subsystem'].lower() +"/" +\
+                   "spawn" + "_" + "manifest" + "." +\
+                   master['pki_timestamp'] + "." + "csv"
+        config.pki_log.info(log.PKI_MANIFEST_MESSAGE_1, filename,
+                            extra=config.PKI_INDENTATION_LEVEL_2)
+        # for record in manifest.database:
+        #     print tuple(record)
+        if not config.pki_dry_run_flag:
+            manifest.file.register(filename)
+            manifest.file.write()
+            util.file.modify(filename, silent=True)
+        # Log final process messages
+        config.pki_log.info(log.PKISPAWN_END_MESSAGE_2,
+                            master['pki_subsystem'],
+                            master['pki_instance_name'],
+                            extra=config.PKI_INDENTATION_LEVEL_0)
+        if not config.pki_dry_run_flag:
+            util.file.modify(master['pki_spawn_log'], silent=True)
+        return self.rv
+
+    def respawn(self):
+        config.pki_log.info(log.FINALIZATION_RESPAWN_1, __name__,
+                            extra=config.PKI_INDENTATION_LEVEL_1)
+        # Save a copy of the configuration file used by this process
+        # (which may be used later by 'pkidestroy')
+        util.file.copy(config.pkideployment_cfg,
+                       master['pki_subsystem_registry_path'] +\
+                       "/" + config.PKI_DEPLOYMENT_DEFAULT_CONFIGURATION_FILE,
+                       overwrite_flag=True)
+        # Save a timestamped copy of the updated manifest file
+        filename = master['pki_root_prefix'] +\
+                   config.PKI_DEPLOYMENT_REGISTRY_ROOT + "/" +\
+                   master['pki_instance_name'] + "/" +\
+                   master['pki_subsystem'].lower() +"/" +\
+                   "respawn" + "_" + "manifest" + "." +\
+                   master['pki_timestamp'] + "." + "csv"
+        config.pki_log.info(log.PKI_MANIFEST_MESSAGE_1, filename,
+                            extra=config.PKI_INDENTATION_LEVEL_2)
+        # for record in manifest.database:
+        #     print tuple(record)
+        if not config.pki_dry_run_flag:
+            manifest.file.register(filename)
+            manifest.file.write()
+            util.file.modify(filename, silent=True)
+        # Log final process messages
+        config.pki_log.info(log.PKIRESPAWN_END_MESSAGE_2,
+                            master['pki_subsystem'],
+                            master['pki_instance_name'],
+                            extra=config.PKI_INDENTATION_LEVEL_0)
+        if not config.pki_dry_run_flag:
+            util.file.modify(master['pki_respawn_log'], silent=True)
+        return self.rv
+
+    def destroy(self):
+        config.pki_log.info(log.FINALIZATION_DESTROY_1, __name__,
+                            extra=config.PKI_INDENTATION_LEVEL_1)
+        config.pki_log.info(log.PKIDESTROY_END_MESSAGE_2,
+                            master['pki_subsystem'],
+                            master['pki_instance_name'],
+                            extra=config.PKI_INDENTATION_LEVEL_0)
+        if not config.pki_dry_run_flag:
+            util.file.modify(master['pki_destroy_log'], silent=True)
+        return self.rv
diff --git a/base/deploy/src/scriptlets/initialization.py b/base/deploy/src/scriptlets/initialization.py
new file mode 100644
index 0000000..67c9325
--- /dev/null
+++ b/base/deploy/src/scriptlets/initialization.py
@@ -0,0 +1,68 @@
+#!/usr/bin/python -t
+# Authors:
+#     Matthew Harmsen <mharmsen@redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2012 Red Hat, Inc.
+# All rights reserved.
+#
+
+# PKI Deployment Imports
+import pkiconfig as config
+from pkiconfig import pki_master_dict as master
+import pkihelper as util
+import pkimessages as log
+import pkiscriptlet
+
+
+# PKI Deployment Instance Population Classes
+class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
+    rv = 0
+
+    def spawn(self):
+        config.pki_log.info(log.PKISPAWN_BEGIN_MESSAGE_2,
+                            master['pki_subsystem'],
+                            master['pki_instance_name'],
+                            extra=config.PKI_INDENTATION_LEVEL_0)
+        config.pki_log.info(log.INITIALIZATION_SPAWN_1, __name__,
+                            extra=config.PKI_INDENTATION_LEVEL_1)
+        # establish 'uid' and 'gid'
+        util.identity.set_uid(master['pki_user'])
+        util.identity.set_gid(master['pki_group'])
+        return self.rv
+
+    def respawn(self):
+        config.pki_log.info(log.PKIRESPAWN_BEGIN_MESSAGE_2,
+                            master['pki_subsystem'],
+                            master['pki_instance_name'],
+                            extra=config.PKI_INDENTATION_LEVEL_0)
+        config.pki_log.info(log.INITIALIZATION_RESPAWN_1, __name__,
+                            extra=config.PKI_INDENTATION_LEVEL_1)
+        # establish 'uid' and 'gid'
+        util.identity.set_uid(master['pki_user'])
+        util.identity.set_gid(master['pki_group'])
+        return self.rv
+
+    def destroy(self):
+        config.pki_log.info(log.PKIDESTROY_BEGIN_MESSAGE_2,
+                            master['pki_subsystem'],
+                            master['pki_instance_name'],
+                            extra=config.PKI_INDENTATION_LEVEL_0)
+        config.pki_log.info(log.INITIALIZATION_DESTROY_1, __name__,
+                            extra=config.PKI_INDENTATION_LEVEL_1)
+        # establish 'uid' and 'gid'
+        util.identity.set_uid(master['pki_user'])
+        util.identity.set_gid(master['pki_group'])
+        return self.rv
diff --git a/base/deploy/src/scriptlets/instance.py b/base/deploy/src/scriptlets/instance.py
deleted file mode 100644
index a7ca35c..0000000
--- a/base/deploy/src/scriptlets/instance.py
+++ /dev/null
@@ -1,105 +0,0 @@
-#!/usr/bin/python -t
-# Authors:
-#     Matthew Harmsen <mharmsen@redhat.com>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; version 2 of the License.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# Copyright (C) 2011 Red Hat, Inc.
-# All rights reserved.
-#
-
-# System Imports
-import os
-import grp
-import pwd
-
-# PKI Deployment Imports
-import pkiconfig as config
-import pkimessages as log
-import pkiscriptlet
-
-
-# PKI Deployment Instance Population Classes
-class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
-    rv = 0
-    pki_path = config.pki_root_prefix +\
-               config.pki_master_dict['pki_instance_root']
-    pki_instance_path = pki_path + "/" +\
-                        config.pki_master_dict['pki_instance_name']
-    pki_subsystem_path = pki_instance_path + "/" +\
-                         config.pki_master_dict['pki_subsystem'].lower()
-
-    def spawn(self):
-        if not os.path.exists(self.pki_subsystem_path):
-            config.pki_log.info(log.INSTANCE_SPAWN_1, __name__)
-            config.pki_log.info(log.INSTANCE_SPAWN_MKDIR_1,
-                                self.pki_subsystem_path)
-            if not config.pki_dry_run_flag:
-                try:
-                    pki_gid = grp.getgrnam(
-                                  config.pki_master_dict['pki_group'])[2]
-                    pki_uid = pwd.getpwnam(
-                                  config.pki_master_dict['pki_user'])[2]
-                    os.mkdir(self.pki_path,
-                             config.PKI_DEPLOYMENT_DEFAULT_DIR_PERMISSIONS)
-                    os.chown(self.pki_path,
-                             pki_uid,
-                             pki_gid)
-                    os.mkdir(self.pki_instance_path,
-                             config.PKI_DEPLOYMENT_DEFAULT_DIR_PERMISSIONS)
-                    os.chown(self.pki_instance_path,
-                             pki_uid,
-                             pki_gid)
-                    os.mkdir(self.pki_subsystem_path,
-                             config.PKI_DEPLOYMENT_DEFAULT_DIR_PERMISSIONS)
-                    os.chown(self.pki_subsystem_path,
-                             pki_uid,
-                             pki_gid)
-                except KeyError:
-                    self.rv = KeyError
-                except OSError:
-                    self.rv = OSError
-        elif not os.path.isdir(self.pki_subsystem_path):
-            config.pki_log.error(
-                log.PKI_DIRECTORY_ALREADY_EXISTS_NOT_A_DIRECTORY_1,
-                self.pki_subsystem_path)
-            self.rv = -1
-        else:
-            config.pki_log.error(log.PKI_DIRECTORY_ALREADY_EXISTS_1,
-                                    self.pki_subsystem_path)
-            self.rv = -1
-        return self.rv
-
-    def respawn(self):
-        if not os.path.exists(self.pki_subsystem_path) or\
-           not os.path.isdir(self.pki_subsystem_path):
-            config.pki_log.error(
-                log.PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1,
-                self.pki_subsystem_path)
-            self.rv = -1
-        else:
-            config.pki_log.info(log.INSTANCE_RESPAWN_1, __name__)
-        return self.rv
-
-    def destroy(self):
-        if not os.path.exists(self.pki_subsystem_path) or\
-           not os.path.isdir(self.pki_subsystem_path):
-            config.pki_log.error(
-                log.PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1,
-                self.pki_subsystem_path)
-            self.rv = -1
-        else:
-            config.pki_log.info(log.INSTANCE_DESTROY_1, __name__)
-        return self.rv
-
diff --git a/base/deploy/src/scriptlets/instance_layout.py b/base/deploy/src/scriptlets/instance_layout.py
new file mode 100644
index 0000000..38234f4
--- /dev/null
+++ b/base/deploy/src/scriptlets/instance_layout.py
@@ -0,0 +1,97 @@
+#!/usr/bin/python -t
+# Authors:
+#     Matthew Harmsen <mharmsen@redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2012 Red Hat, Inc.
+# All rights reserved.
+#
+
+# PKI Deployment Imports
+import pkiconfig as config
+from pkiconfig import pki_master_dict as master
+import pkihelper as util
+import pkimessages as log
+import pkiscriptlet
+
+
+# PKI Deployment Instance Population Classes
+class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
+    rv = 0
+
+    def spawn(self):
+        config.pki_log.info(log.INSTANCE_SPAWN_1, __name__,
+                            extra=config.PKI_INDENTATION_LEVEL_1)
+        # establish instance base
+        util.directory.create(master['pki_path'])
+        util.directory.create(master['pki_instance_path'])
+        # establish instance logs
+        util.directory.create(master['pki_log_path'])
+        util.directory.create(master['pki_instance_log_path'])
+        # establish instance configuration
+        if master['pki_configuration_path'] !=\
+           config.PKI_SHARED_CONFIGURATION_ROOT:
+            util.directory.create(master['pki_configuration_path'])
+        util.directory.create(master['pki_instance_configuration_path'])
+        # establish instance registry
+        util.directory.create(master['pki_registry_path'])
+        util.directory.create(master['pki_instance_registry_path'])
+        return self.rv
+
+    def respawn(self):
+        config.pki_log.info(log.INSTANCE_RESPAWN_1, __name__,
+                            extra=config.PKI_INDENTATION_LEVEL_1)
+        # update instance base
+        util.directory.modify(master['pki_path'])
+        util.directory.modify(master['pki_instance_path'])
+        # update instance logs
+        util.directory.modify(master['pki_log_path'])
+        util.directory.modify(master['pki_instance_log_path'])
+        # update instance configuration
+        if master['pki_configuration_path'] !=\
+           config.PKI_SHARED_CONFIGURATION_ROOT:
+            util.directory.modify(master['pki_configuration_path'])
+        util.directory.modify(master['pki_instance_configuration_path'])
+        # update instance registry
+        util.directory.modify(master['pki_registry_path'])
+        util.directory.modify(master['pki_instance_registry_path'])
+        return self.rv
+
+    def destroy(self):
+        config.pki_log.info(log.INSTANCE_DESTROY_1, __name__,
+                            extra=config.PKI_INDENTATION_LEVEL_1)
+        # remove instance base
+        if util.directory.is_empty(master['pki_instance_path']):
+            util.directory.delete(master['pki_instance_path'])
+        if util.directory.is_empty(master['pki_path']):
+            util.directory.delete(master['pki_path'])
+        # remove instance logs
+        if util.directory.is_empty(master['pki_instance_log_path']):
+            util.directory.delete(master['pki_instance_log_path'])
+        if util.directory.is_empty(master['pki_log_path']):
+            util.directory.delete(master['pki_log_path'])
+        # remove instance configuration
+        if util.directory.is_empty(master['pki_instance_configuration_path']):
+            util.directory.delete(master['pki_instance_configuration_path'])
+        if util.directory.is_empty(master['pki_configuration_path']) and\
+           master['pki_configuration_path'] !=\
+           config.PKI_SHARED_CONFIGURATION_ROOT:
+            util.directory.delete(master['pki_configuration_path'])
+        # remove instance registry
+        if util.directory.is_empty(master['pki_instance_registry_path']):
+            util.directory.delete(master['pki_instance_registry_path'])
+        if util.directory.is_empty(master['pki_registry_path']):
+            util.directory.delete(master['pki_registry_path'])
+        return self.rv
diff --git a/base/deploy/src/scriptlets/pkiconfig.py b/base/deploy/src/scriptlets/pkiconfig.py
index 7d676c0..505bfc0 100644
--- a/base/deploy/src/scriptlets/pkiconfig.py
+++ b/base/deploy/src/scriptlets/pkiconfig.py
@@ -15,7 +15,7 @@
 # with this program; if not, write to the Free Software Foundation, Inc.,
 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 #
-# Copyright (C) 2011 Red Hat, Inc.
+# Copyright (C) 2012 Red Hat, Inc.
 # All rights reserved.
 #
 
@@ -24,51 +24,50 @@ import logging
 
 
 # PKI Deployment Constants
-PKI_DEPLOYMENT_PATH = "/usr/share/pki/deployment"
-PKI_DEPLOYMENT_CONFIG_PATH = PKI_DEPLOYMENT_PATH + "/" + "config"
-PKI_DEPLOYMENT_SCRIPTLETS_MODULE = "pki.deployment"
-PKI_DEPLOYMENT_VERBOSITY=\
-"VERBOSITY FLAGS    CONSOLE MESSAGE LEVEL       LOG MESSAGE LEVEL\n"\
-"=======================================================================\n"\
-"  NONE             error|warning               error|warning|info\n"\
-"  -v               error|warning|info          error|warning|info\n"\
-"  -vv              error|warning|info          error|warning|info|debug\n"\
-"  -vvv             error|warning|info|debug    error|warning|info|debug\n"\
-" "
 PKI_DEPLOYMENT_DEFAULT_DIR_PERMISSIONS = 00770
 PKI_DEPLOYMENT_DEFAULT_EXE_PERMISSIONS = 00770
 PKI_DEPLOYMENT_DEFAULT_FILE_PERMISSIONS = 00660
+PKI_DEPLOYMENT_DEFAULT_SGID_DIR_PERMISSIONS = 02770
+PKI_DEPLOYMENT_DEFAULT_SYMLINK_PERMISSIONS = 00777
 PKI_DEPLOYMENT_DEFAULT_UMASK = 00002
 
-PKIDESTROY_PATH = PKI_DEPLOYMENT_PATH + "/" + "destroy"
-PKIDESTROY_LOG_PATH = "/var/log"
-PKIDESTROY_LOG_PREFIX = "pki-"
-PKIDESTROY_LOG_SUFFIX = "-destroy.log"
-PKIDESTROY_LOGGER = "pkidestroy"
-
-PKIRESPAWN_PATH = PKI_DEPLOYMENT_PATH + "/" + "spawn"
-PKIRESPAWN_LOG_PATH = "/var/log"
-PKIRESPAWN_LOG_PREFIX = "pki-"
-PKIRESPAWN_LOG_SUFFIX = "-respawn.log"
-PKIRESPAWN_LOGGER = "pkirespawn"
-
-PKISPAWN_PATH = PKI_DEPLOYMENT_PATH + "/" + "spawn"
-PKISPAWN_LOG_PATH = "/var/log"
-PKISPAWN_LOG_PREFIX = "pki-"
-PKISPAWN_LOG_SUFFIX = "-spawn.log"
-PKISPAWN_LOGGER = "pkispawn"
-
-PKI_SECURITY_DATABASE_DIR = "alias"
 PKI_SUBSYSTEMS = ["CA","KRA","OCSP","RA","TKS","TPS"]
+PKI_SIGNED_AUDIT_SUBSYSTEMS = ["CA","KRA","OCSP","TKS","TPS"]
 PKI_APACHE_SUBSYSTEMS = ["RA","TPS"]
 PKI_TOMCAT_SUBSYSTEMS = ["CA","KRA","OCSP","TKS"]
 
+PKI_INDENTATION_LEVEL_0 = {'indent' : ''}
+PKI_INDENTATION_LEVEL_1 = {'indent' : '... '}
+PKI_INDENTATION_LEVEL_2 = {'indent' : '....... '}
+PKI_INDENTATION_LEVEL_3 = {'indent' : '........... '}
+PKI_INDENTATION_LEVEL_4 = {'indent' : '............... '}
+
+# NOTE:  Well-known 'registry root', default 'instance', and default
+#        'configuration file' names MUST be created in order to potentially
+#        obtain an instance-specific configuration file
+#        (presuming one has not been specified during command-line parsing)
+#        because command-line parsing happens prior to reading any
+#        configuration files.  Although the 'registry root' MUST remain fixed,
+#        the default 'instance' name may be overridden by the value specified
+#        in the configuration file (the value in the default configuration file
+#        should always match the 'default' instance name specified below).
+PKI_DEPLOYMENT_REGISTRY_ROOT = "/etc/sysconfig/pki"
+PKI_DEPLOYMENT_DEFAULT_INSTANCE_NAME = "instance"
+PKI_DEPLOYMENT_DEFAULT_CONFIGURATION_FILE = "pkideployment.cfg"
+
+# NOTE: Top-level "/etc/pki" is owned by the "filesystem" package!
+PKI_SHARED_CONFIGURATION_ROOT = "/etc/pki"
+
+
+# PKI Deployment Global Variables
+pki_timestamp = None
+
 
 # PKI Deployment "Mandatory" Command-Line Variables
 pki_subsystem = None
 
 # PKI Deployment "Optional" Command-Line Variables
-pkideployment_cfg = PKI_DEPLOYMENT_CONFIG_PATH + "/" + "pkideployment.cfg"
+pkideployment_cfg = "/usr/share/pki/deployment/config/pkideployment.cfg"
 pki_dry_run_flag = False
 pki_root_prefix = None
 pki_update_flag = False
@@ -93,4 +92,3 @@ pki_common_dict = None
 pki_web_server_dict = None
 pki_subsystem_dict = None
 pki_master_dict = None
-
diff --git a/base/deploy/src/scriptlets/pkihelper.py b/base/deploy/src/scriptlets/pkihelper.py
index ee2bdd2..95b5bf0 100644
--- a/base/deploy/src/scriptlets/pkihelper.py
+++ b/base/deploy/src/scriptlets/pkihelper.py
@@ -15,208 +15,1021 @@
 # with this program; if not, write to the Free Software Foundation, Inc.,
 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 #
-# Copyright (C) 2011 Red Hat, Inc.
+# Copyright (C) 2012 Red Hat, Inc.
 # All rights reserved.
 #
 
 # System Imports
-import ConfigParser
-import argparse
-import logging
+import errno
+import sys
 import os
+import shutil
+from grp import getgrnam
+from pwd import getpwnam
+import zipfile
 
 
 # PKI Deployment Imports
 import pkiconfig as config
+from pkiconfig import pki_master_dict as master
+import pkimanifest as manifest
 import pkimessages as log
 
 
-# PKI Deployment Helper Functions
-def process_command_line_arguments(argv):
-    "Read and process command-line options"
-    description = None
-    if os.path.basename(argv[0]) == 'pkispawn':
-        description = 'PKI Instance Installation and Configuration'
-    elif os.path.basename(argv[0]) == 'pkidestroy':
-        description = 'PKI Instance Removal'
-    parser = argparse.ArgumentParser(
-                 description=description,
-                 add_help=False,
-                 formatter_class=argparse.RawDescriptionHelpFormatter,
-                 epilog=config.PKI_DEPLOYMENT_VERBOSITY)
-    mandatory = parser.add_argument_group('mandatory arguments')
-    mandatory.add_argument('-s',
-                           dest='pki_subsystem', action='store',
-                           nargs=1, choices=config.PKI_SUBSYSTEMS,
-                           required=True, metavar='<subsystem>',
-                           help='where <subsystem> is '
-                                'CA, KRA, OCSP, RA, TKS, or TPS')
-    optional = parser.add_argument_group('optional arguments')
-    optional.add_argument('--dry_run',
-                          dest='pki_dry_run_flag', action='store_true',
-                          help='do not actually perform any actions')
-    optional.add_argument('-f',
-                          dest='pkideployment_cfg', action='store',
-                          nargs=1, metavar='<file>',
-                          help='overrides default configuration filename')
-    optional.add_argument('-h', '--help',
-                          dest='help', action='help',
-                          help='show this help message and exit')
-    optional.add_argument('-p',
-                          dest='pki_root_prefix', action='store',
-                          nargs=1, metavar='<prefix>',
-                          help='directory prefix to specify local directory')
-    if os.path.basename(argv[0]) == 'pkispawn':
-        optional.add_argument('-u',
-                              dest='pki_update_flag', action='store_true',
-                              help='update instance of specified subsystem')
-    optional.add_argument('-v',
-                          dest='pki_verbosity', action='count',
-                          help='display verbose information (details below)')
-    custom = parser.add_argument_group('custom arguments '
-                                       '(OVERRIDES configuration file values)')
-    custom.add_argument('-i',
-                        dest='pki_instance_name', action='store',
-                        nargs=1, metavar='<instance>',
-                        help='PKI instance name (MUST specify REQUIRED ports)')
-    custom.add_argument('--http_port',
-                        dest='pki_http_port', action='store',
-                        nargs=1, metavar='<port>',
-                        help='HTTP port (CA, KRA, OCSP, RA, TKS, TPS)')
-    custom.add_argument('--https_port',
-                        dest='pki_https_port', action='store',
-                        nargs=1, metavar='<port>',
-                        help='HTTPS port (CA, KRA, OCSP, RA, TKS, TPS)')
-    custom.add_argument('--ajp_port',
-                        dest='pki_ajp_port', action='store',
-                        nargs=1, metavar='<port>',
-                        help='AJP port (CA, KRA, OCSP, TKS)')
-    args = parser.parse_args()
-
-    config.pki_subsystem = str(args.pki_subsystem).strip('[\']')
-    if args.pki_dry_run_flag:
-        config.pki_dry_run_flag = args.pki_dry_run_flag
-    if not args.pkideployment_cfg is None:
-        config.pkideployment_cfg = str(args.pkideployment_cfg).strip('[\']')
-    if not os.path.exists(config.pkideployment_cfg) or\
-       not os.path.isfile(config.pkideployment_cfg):
-        print "ERROR:  " +\
-              log.PKI_FILE_MISSING_OR_NOT_A_FILE_1 %\
-              config.pkideployment_cfg
-        print
-        parser.print_help()
-        parser.exit(-1);
-    if not args.pki_root_prefix is None:
-        config.pki_root_prefix = str(args.pki_root_prefix).strip('[\']')
-    if config.pki_root_prefix is None or\
-       len(config.pki_root_prefix) == 0:
-        config.pki_root_prefix = ""
-    elif not os.path.exists(config.pki_root_prefix) or\
-         not os.path.isdir(config.pki_root_prefix):
-        print "ERROR:  " +\
-              log.PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1 %\
-              config.pki_root_prefix
-        print
-        parser.print_help()
-        parser.exit(-1);
-    if os.path.basename(argv[0]) == 'pkispawn':
-        if args.pki_update_flag:
-            config.pki_update_flag = args.pki_update_flag
-    if args.pki_verbosity == 1:
-        config.pki_console_log_level = logging.INFO
-        config.pki_log_level = logging.INFO
-    elif args.pki_verbosity == 2:
-        config.pki_console_log_level = logging.INFO
-        config.pki_log_level = logging.DEBUG
-    elif args.pki_verbosity == 3:
-        config.pki_console_log_level = logging.DEBUG
-        config.pki_log_level = logging.DEBUG
-    elif args.pki_verbosity > 3:
-        print "ERROR:  " + log.PKI_VERBOSITY_LEVELS_MESSAGE
-        print
-        parser.print_help()
-        parser.exit(-1);
-    if not args.pki_instance_name is None:
-        config.pki_instance_name = str(args.pki_instance_name).strip('[\']')
-    if not args.pki_http_port is None:
-        config.pki_http_port = str(args.pki_http_port).strip('[\']')
-    if not args.pki_https_port is None:
-        config.pki_https_port = str(args.pki_https_port).strip('[\']')
-    if not args.pki_ajp_port is None:
-        if config.pki_subsystem in config.PKI_TOMCAT_SUBSYSTEMS:
-            config.pki_ajp_port = str(args.pki_ajp_port).strip('[\']')
-        else:
-            print "ERROR:  " +\
-                  log.PKI_CUSTOM_TOMCAT_AJP_PORT_1 %\
-                  config.pki_subsystem
-            print
-            parser.print_help()
-            parser.exit(-1);
-    if not args.pki_instance_name is None or\
-       not args.pki_http_port is None or\
-       not args.pki_https_port is None or\
-       not args.pki_ajp_port is None:
-        if config.pki_subsystem in config.PKI_APACHE_SUBSYSTEMS:
-            if args.pki_instance_name is None or\
-               args.pki_http_port is None or\
-               args.pki_https_port is None:
-                print "ERROR:  " + log.PKI_CUSTOM_APACHE_INSTANCE_1 %\
-                      config.pki_subsystem
-                print
-                parser.print_help()
-                parser.exit(-1);
-        elif config.pki_subsystem in config.PKI_TOMCAT_SUBSYSTEMS:
-            if args.pki_instance_name is None or\
-               args.pki_http_port is None or\
-               args.pki_https_port is None or\
-               args.pki_ajp_port is None:
-                print "ERROR:  " + log.PKI_CUSTOM_TOMCAT_INSTANCE_1 %\
-                      config.pki_subsystem
-                print
-                parser.print_help()
-                parser.exit(-1);
-
-
-def read_pki_configuration_file():
-    "Read configuration file sections into dictionaries"
-    rv = 0
+# PKI Deployment Functions
+def pki_copytree(src, dst, symlinks=False, ignore=None):
+    """Recursively copy a directory tree using copy2().
+
+    PATCH:  This code was copied from 'shutil.py' and patched to
+            allow 'The destination directory to already exist.'
+
+    If exception(s) occur, an Error is raised with a list of reasons.
+
+    If the optional symlinks flag is true, symbolic links in the
+    source tree result in symbolic links in the destination tree; if
+    it is false, the contents of the files pointed to by symbolic
+    links are copied.
+
+    The optional ignore argument is a callable. If given, it
+    is called with the `src` parameter, which is the directory
+    being visited by pki_copytree(), and `names` which is the list of
+    `src` contents, as returned by os.listdir():
+
+        callable(src, names) -> ignored_names
+
+    Since pki_copytree() is called recursively, the callable will be
+    called once for each directory that is copied. It returns a
+    list of names relative to the `src` directory that should
+    not be copied.
+
+    XXX Consider this example code rather than the ultimate tool.
+
+    """
+    names = os.listdir(src)
+    if ignore is not None:
+        ignored_names = ignore(src, names)
+    else:
+        ignored_names = set()
+
+    # PATCH:  ONLY execute 'os.makedirs(dst)' if the top-level
+    #         destination directory does NOT exist!
+    if not os.path.exists(dst):
+        os.makedirs(dst)
+    errors = []
+    for name in names:
+        if name in ignored_names:
+            continue
+        srcname = os.path.join(src, name)
+        dstname = os.path.join(dst, name)
+        try:
+            if symlinks and os.path.islink(srcname):
+                linkto = os.readlink(srcname)
+                os.symlink(linkto, dstname)
+            elif os.path.isdir(srcname):
+                pki_copytree(srcname, dstname, symlinks, ignore)
+            else:
+                # Will raise a SpecialFileError for unsupported file types
+                shutil.copy2(srcname, dstname)
+        # catch the Error from the recursive pki_copytree so that we can
+        # continue with other files
+        except Error, err:
+            errors.extend(err.args[0])
+        except EnvironmentError, why:
+            errors.append((srcname, dstname, str(why)))
     try:
-        parser = ConfigParser.ConfigParser()
-        parser.read(config.pkideployment_cfg)
-        config.pki_common_dict = dict(parser._sections['Common'])
-        if config.pki_subsystem == "CA":
-            config.pki_web_server_dict = dict(parser._sections['Tomcat'])
-            config.pki_subsystem_dict = dict(parser._sections['CA'])
-        elif config.pki_subsystem == "KRA":
-            config.pki_web_server_dict = dict(parser._sections['Tomcat'])
-            config.pki_subsystem_dict = dict(parser._sections['KRA'])
-        elif config.pki_subsystem == "OCSP":
-            config.pki_web_server_dict = dict(parser._sections['Tomcat'])
-            config.pki_subsystem_dict = dict(parser._sections['OCSP'])
-        elif config.pki_subsystem == "RA":
-            config.pki_web_server_dict = dict(parser._sections['Apache'])
-            config.pki_subsystem_dict = dict(parser._sections['RA'])
-        elif config.pki_subsystem == "TKS":
-            config.pki_web_server_dict = dict(parser._sections['Tomcat'])
-            config.pki_subsystem_dict = dict(parser._sections['TKS'])
-        elif config.pki_subsystem == "TPS":
-            config.pki_web_server_dict = dict(parser._sections['Apache'])
-            config.pki_subsystem_dict = dict(parser._sections['TPS'])
-        # Insert empty record into dictionaries for "pretty print" statements
-        config.pki_common_dict[0] = None
-        config.pki_web_server_dict[0] = None
-        config.pki_subsystem_dict[0] = None
-    except ConfigParser.ParsingError, err:
-        rv = err
-    return rv
-
-
-def create_pki_master_dictionary():
-    "Create a single master PKI dictionary from the sectional dictionaries"
-    config.pki_master_dict = dict()
-    config.pki_master_dict.update(config.pki_common_dict)
-    config.pki_master_dict.update(config.pki_web_server_dict)
-    config.pki_master_dict.update(config.pki_subsystem_dict)
-    config.pki_master_dict.update(__name__="PKI Master Dictionary")
-    return
+        shutil.copystat(src, dst)
+    except OSError, why:
+        if WindowsError is not None and isinstance(why, WindowsError):
+            # Copying file access times may fail on Windows
+            pass
+        else:
+            errors.extend((src, dst, str(why)))
+    if errors:
+        raise Error, errors
+
+
+# PKI Deployment Identity Class
+class identity:
+    def get_uid(self, critical_failure=True):
+        try:
+            pki_uid = master['pki_uid']
+        except KeyError as exc:
+            config.pki_log.error(log.PKI_KEYERROR_1, exc,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            if critical_failure == True:
+                sys.exit(1)
+        return pki_uid
+
+    def get_gid(self, critical_failure=True):
+        try:
+            pki_gid = master['pki_gid']
+        except KeyError as exc:
+            config.pki_log.error(log.PKI_KEYERROR_1, exc,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            if critical_failure == True:
+                sys.exit(1)
+        return pki_gid
+
+    def set_uid(self, name, critical_failure=True):
+        try:
+            config.pki_log.debug(log.PKIHELPER_USER_1, name,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            # id -u <name>
+            pki_uid = getpwnam(name)[2]
+            master['pki_uid']=pki_uid
+            config.pki_log.debug(log.PKIHELPER_UID_2, name, pki_uid,
+                                 extra=config.PKI_INDENTATION_LEVEL_3)
+        except KeyError as exc:
+            config.pki_log.error(log.PKI_KEYERROR_1, exc,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            if critical_failure == True:
+                sys.exit(1)
+        return pki_uid
+
+    def set_gid(self, name, critical_failure=True):
+        try:
+            config.pki_log.debug(log.PKIHELPER_GROUP_1, name,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            # id -g <name>
+            pki_gid = getgrnam(name)[2]
+            master['pki_gid']=pki_gid
+            config.pki_log.debug(log.PKIHELPER_GID_2, name, pki_gid,
+                                 extra=config.PKI_INDENTATION_LEVEL_3)
+        except KeyError as exc:
+            config.pki_log.error(log.PKI_KEYERROR_1, exc,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            if critical_failure == True:
+                sys.exit(1)
+        return pki_gid
+
+
+# PKI Deployment Instance Class
+class instance:
+    def apache_instances(self, name):
+        rv = 0
+        try:
+            if not os.path.exists(name) or not os.path.isdir(name):
+                config.pki_log.error(
+                    log.PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1, name,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                sys.exit(1)
+            # count number of PKI subsystems present
+            # within the specfied Apache instance
+            for subsystem in config.PKI_APACHE_SUBSYSTEMS:
+                path = name + "/" + subsystem.lower()
+                if os.path.exists(path) and os.path.isdir(path):
+                    rv = rv + 1
+            # always display correct information (even during dry_run)
+            if config.pki_dry_run_flag and rv > 0:
+                config.pki_log.debug(log.PKIHELPER_APACHE_INSTANCES_2,
+                                     name, rv - 1,
+                                     extra=config.PKI_INDENTATION_LEVEL_2)
+            else:
+                config.pki_log.debug(log.PKIHELPER_APACHE_INSTANCES_2, name,
+                                     rv, extra=config.PKI_INDENTATION_LEVEL_2)
+        except OSError as exc:
+            config.pki_log.error(log.PKI_OSERROR_1, exc,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            sys.exit(1)
+        return rv
+
+    def pki_subsystem_instances(self, name):
+        rv = 0
+        try:
+            if not os.path.exists(name) or not os.path.isdir(name):
+                config.pki_log.error(
+                    log.PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1, name,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                sys.exit(1)
+            # count total number of PKI subsystems present
+            # within the specfied PKI instance
+            for subsystem in config.PKI_SUBSYSTEMS:
+                path = name + "/" + subsystem.lower()
+                if os.path.exists(path) and os.path.isdir(path):
+                    rv = rv + 1
+            # always display correct information (even during dry_run)
+            if config.pki_dry_run_flag and rv > 0:
+                config.pki_log.debug(log.PKIHELPER_PKI_SUBSYSTEM_INSTANCES_2,
+                                     name, rv - 1,
+                                     extra=config.PKI_INDENTATION_LEVEL_2)
+            else:
+                config.pki_log.debug(log.PKIHELPER_PKI_SUBSYSTEM_INSTANCES_2,
+                                     name, rv,
+                                     extra=config.PKI_INDENTATION_LEVEL_2)
+        except OSError as exc:
+            config.pki_log.error(log.PKI_OSERROR_1, exc,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            sys.exit(1)
+        return rv
+
+    def tomcat_instances(self, name):
+        rv = 0
+        try:
+            if not os.path.exists(name) or not os.path.isdir(name):
+                config.pki_log.error(
+                    log.PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1, name,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                sys.exit(1)
+            # count number of PKI subsystems present
+            # within the specfied Tomcat instance
+            for subsystem in config.PKI_TOMCAT_SUBSYSTEMS:
+                path = name + "/" + subsystem.lower()
+                if os.path.exists(path) and os.path.isdir(path):
+                    rv = rv + 1
+            # always display correct information (even during dry_run)
+            if config.pki_dry_run_flag and rv > 0:
+                config.pki_log.debug(log.PKIHELPER_TOMCAT_INSTANCES_2,
+                                     name, rv - 1,
+                                     extra=config.PKI_INDENTATION_LEVEL_2)
+            else:
+                config.pki_log.debug(log.PKIHELPER_TOMCAT_INSTANCES_2, name,
+                                     rv, extra=config.PKI_INDENTATION_LEVEL_2)
+        except OSError as exc:
+            config.pki_log.error(log.PKI_OSERROR_1, exc,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            sys.exit(1)
+        return rv
+
+
+# PKI Deployment Directory Class
+class directory:
+    def create(self, name, uid=None, gid=None,
+               perms=config.PKI_DEPLOYMENT_DEFAULT_DIR_PERMISSIONS,
+               acls=None, critical_failure=True):
+        try:
+            if not os.path.exists(name):
+                # mkdir -p <name>
+                config.pki_log.info(log.PKIHELPER_MKDIR_1, name,
+                                    extra=config.PKI_INDENTATION_LEVEL_2)
+                if not config.pki_dry_run_flag:
+                    os.makedirs(name)
+                # chmod <perms> <name>
+                config.pki_log.debug(log.PKIHELPER_CHMOD_2, perms, name,
+                                     extra=config.PKI_INDENTATION_LEVEL_3)
+                if not config.pki_dry_run_flag:
+                    os.chmod(name, perms)
+                # chown <uid>:<gid> <name>
+                if uid == None:
+                    uid = identity.get_uid()
+                if gid == None:
+                    gid = identity.get_gid()
+                config.pki_log.debug(log.PKIHELPER_CHOWN_3,
+                                     uid, gid, name,
+                                     extra=config.PKI_INDENTATION_LEVEL_3)
+                if not config.pki_dry_run_flag:
+                    os.chown(name, uid, gid)
+                # Store record in installation manifest
+                record = manifest.record()
+                record.name = name
+                record.type = manifest.RECORD_TYPE_DIRECTORY
+                record.user = master['pki_user']
+                record.group = master['pki_group']
+                record.uid = uid
+                record.gid = gid
+                record.permissions = perms
+                record.acls = acls
+                manifest.database.append(record)
+            elif not os.path.isdir(name):
+                config.pki_log.error(
+                    log.PKI_DIRECTORY_ALREADY_EXISTS_NOT_A_DIRECTORY_1, name,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                if critical_failure == True:
+                    sys.exit(1)
+            else:
+                config.pki_log.error(log.PKI_DIRECTORY_ALREADY_EXISTS_1, name,
+                                     extra=config.PKI_INDENTATION_LEVEL_2)
+                if critical_failure == True:
+                    sys.exit(1)
+        except OSError as exc:
+            if exc.errno == errno.EEXIST:
+                pass
+            else:
+                config.pki_log.error(log.PKI_OSERROR_1, exc,
+                                     extra=config.PKI_INDENTATION_LEVEL_2)
+                if critical_failure == True:
+                    sys.exit(1)
+        return
+
+    def modify(self, name, uid=None, gid=None,
+               perms=config.PKI_DEPLOYMENT_DEFAULT_DIR_PERMISSIONS,
+               acls=None, silent=False, critical_failure=True):
+        try:
+            if os.path.exists(name):
+                if not os.path.isdir(name):
+                    config.pki_log.error(
+                        log.PKI_DIRECTORY_ALREADY_EXISTS_NOT_A_DIRECTORY_1,
+                        name, extra=config.PKI_INDENTATION_LEVEL_2)
+                    if critical_failure == True:
+                        sys.exit(1)
+                # Always re-process each directory whether it needs it or not
+                if not silent:
+                    config.pki_log.info(log.PKIHELPER_MODIFY_DIR_1, name,
+                                        extra=config.PKI_INDENTATION_LEVEL_2)
+                # chmod <perms> <name>
+                if not silent:
+                    config.pki_log.debug(log.PKIHELPER_CHMOD_2, perms, name,
+                                         extra=config.PKI_INDENTATION_LEVEL_3)
+                if not config.pki_dry_run_flag:
+                    os.chmod(name, perms)
+                # chown <uid>:<gid> <name>
+                if uid == None:
+                    uid = identity.get_uid()
+                if gid == None:
+                    gid = identity.get_gid()
+                if not silent:
+                    config.pki_log.debug(log.PKIHELPER_CHOWN_3,
+                                         uid, gid, name,
+                                         extra=config.PKI_INDENTATION_LEVEL_3)
+                if not config.pki_dry_run_flag:
+                    os.chown(name, uid, gid)
+                # Store record in installation manifest
+                if not silent:
+                    record = manifest.record()
+                    record.name = name
+                    record.type = manifest.RECORD_TYPE_DIRECTORY
+                    record.user = master['pki_user']
+                    record.group = master['pki_group']
+                    record.uid = uid
+                    record.gid = gid
+                    record.permissions = perms
+                    record.acls = acls
+                    manifest.database.append(record)
+            else:
+                config.pki_log.error(
+                    log.PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1, name,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                if critical_failure == True:
+                    sys.exit(1)
+        except OSError as exc:
+            config.pki_log.error(log.PKI_OSERROR_1, exc,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            if critical_failure == True:
+                sys.exit(1)
+        return
+
+    def delete(self, name, recursive_flag=True, critical_failure=True):
+        try:
+            if not os.path.exists(name) or not os.path.isdir(name):
+                config.pki_log.error(
+                    log.PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1, name,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                sys.exit(1)
+            else:
+                if recursive_flag == True:
+                    # rm -rf <name>
+                    config.pki_log.info(log.PKIHELPER_RM_RF_1, name,
+                                        extra=config.PKI_INDENTATION_LEVEL_2)
+                    if not config.pki_dry_run_flag:
+                        shutil.rmtree(name)
+                else:
+                    # rmdir <name>
+                    config.pki_log.info(log.PKIHELPER_RMDIR_1, name,
+                                        extra=config.PKI_INDENTATION_LEVEL_2)
+                    if not config.pki_dry_run_flag:
+                        os.rmdir(name)
+        except OSError as exc:
+            config.pki_log.error(log.PKI_OSERROR_1, exc,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            if critical_failure == True:
+                sys.exit(1)
+        return
+
+    def is_empty(self, name):
+        try:
+            if not os.listdir(name):
+                config.pki_log.debug(log.PKIHELPER_DIRECTORY_IS_NOT_EMPTY_1,
+                                     name, extra=config.PKI_INDENTATION_LEVEL_2)
+                return False
+            else:
+                config.pki_log.debug(log.PKIHELPER_DIRECTORY_IS_EMPTY_1,
+                                     name, extra=config.PKI_INDENTATION_LEVEL_2)
+                return True
+        except OSError as exc:
+            config.pki_log.error(log.PKI_OSERROR_1, exc,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            sys.exit(1)
+
+    def set_mode(self, name, uid=None, gid=None,
+                 dir_perms=config.PKI_DEPLOYMENT_DEFAULT_DIR_PERMISSIONS,
+                 file_perms=config.PKI_DEPLOYMENT_DEFAULT_FILE_PERMISSIONS,
+                 symlink_perms=\
+                     config.PKI_DEPLOYMENT_DEFAULT_SYMLINK_PERMISSIONS,
+                 dir_acls=None, file_acls=None, symlink_acls=None,
+                 recursive_flag=True, critical_failure=True):
+        try:
+            if config.pki_dry_run_flag:
+                config.pki_log.info(
+                    log.PKIHELPER_SET_MODE_1, name,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+            elif not os.path.exists(name) or not os.path.isdir(name):
+                config.pki_log.error(
+                    log.PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1, name,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                sys.exit(1)
+            else:
+                config.pki_log.info(
+                    log.PKIHELPER_SET_MODE_1, name,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                if uid == None:
+                    uid = identity.get_uid()
+                if gid == None:
+                    gid = identity.get_gid()
+                if recursive_flag == True:
+                    for root, dirs, files in os.walk(name):
+                        for name in files:
+                            if not os.path.islink(name):
+                                file = os.path.join(root, name)
+                                config.pki_log.debug(
+                                    log.PKIHELPER_IS_A_FILE_1, file,
+                                    extra=config.PKI_INDENTATION_LEVEL_3)
+                                # chmod <file_perms> <name>
+                                config.pki_log.debug(log.PKIHELPER_CHMOD_2,
+                                    file_perms, file,
+                                    extra=config.PKI_INDENTATION_LEVEL_3)
+                                if not config.pki_dry_run_flag:
+                                    os.chmod(file, file_perms)
+                                # chown <uid>:<gid> <name>
+                                config.pki_log.debug(log.PKIHELPER_CHOWN_3,
+                                    uid, gid, file,
+                                    extra=config.PKI_INDENTATION_LEVEL_3)
+                                if not config.pki_dry_run_flag:
+                                    os.chown(file, uid, gid)
+                                # Store record in installation manifest
+                                record = manifest.record()
+                                record.name = name
+                                record.type = manifest.RECORD_TYPE_FILE
+                                record.user = master['pki_user']
+                                record.group = master['pki_group']
+                                record.uid = uid
+                                record.gid = gid
+                                record.permissions = file_perms
+                                record.acls = file_acls
+                                manifest.database.append(record)
+                            else:
+                                symlink = os.path.join(root, name)
+                                config.pki_log.debug(
+                                    log.PKIHELPER_IS_A_SYMLINK_1, symlink,
+                                    extra=config.PKI_INDENTATION_LEVEL_3)
+                                # REMINDER:  Due to POSIX compliance, 'lchmod'
+                                #            is NEVER implemented on Linux
+                                #            systems since 'chmod' CANNOT be
+                                #            run directly against symbolic
+                                #            links!
+                                # chown -h <uid>:<gid> <link>
+                                config.pki_log.debug(log.PKIHELPER_CHOWN_H_3,
+                                    uid, gid, link,
+                                    extra=config.PKI_INDENTATION_LEVEL_3)
+                                if not config.pki_dry_run_flag:
+                                    os.lchown(link, uid, gid)
+                                # Store record in installation manifest
+                                record = manifest.record()
+                                record.name = name
+                                record.type = manifest.RECORD_TYPE_SYMLINK
+                                record.user = master['pki_user']
+                                record.group = master['pki_group']
+                                record.uid = uid
+                                record.gid = gid
+                                record.permissions = symlink_perms
+                                record.acls = symlink_acls
+                                manifest.database.append(record)
+                        for name in dirs:
+                            dir = os.path.join(root, name)
+                            config.pki_log.debug(
+                                log.PKIHELPER_IS_A_DIRECTORY_1, dir,
+                                extra=config.PKI_INDENTATION_LEVEL_3)
+                            # chmod <dir_perms> <name>
+                            config.pki_log.debug(log.PKIHELPER_CHMOD_2,
+                                dir_perms, dir,
+                                extra=config.PKI_INDENTATION_LEVEL_3)
+                            if not config.pki_dry_run_flag:
+                                os.chmod(dir, dir_perms)
+                            # chown <uid>:<gid> <name>
+                            config.pki_log.debug(log.PKIHELPER_CHOWN_3,
+                                uid, gid, dir,
+                                extra=config.PKI_INDENTATION_LEVEL_3)
+                            if not config.pki_dry_run_flag:
+                                os.chown(dir, uid, gid)
+                            # Store record in installation manifest
+                            record = manifest.record()
+                            record.name = name
+                            record.type = manifest.RECORD_TYPE_DIRECTORY
+                            record.user = master['pki_user']
+                            record.group = master['pki_group']
+                            record.uid = uid
+                            record.gid = gid
+                            record.permissions = dir_perms
+                            record.acls = dir_acls
+                            manifest.database.append(record)
+                else:
+                    config.pki_log.debug(
+                        log.PKIHELPER_IS_A_DIRECTORY_1, name,
+                        extra=config.PKI_INDENTATION_LEVEL_3)
+                    name = os.path.join(root, name)
+                    # chmod <dir_perms> <name>
+                    config.pki_log.debug(log.PKIHELPER_CHMOD_2,
+                                         dir_perms, name,
+                                         extra=config.PKI_INDENTATION_LEVEL_3)
+                    if not config.pki_dry_run_flag:
+                        os.chmod(name, dir_perms)
+                    # chown <uid>:<gid> <name>
+                    config.pki_log.debug(log.PKIHELPER_CHOWN_3,
+                                         uid, gid, name,
+                                         extra=config.PKI_INDENTATION_LEVEL_3)
+                    if not config.pki_dry_run_flag:
+                        os.chown(name, uid, gid)
+                    # Store record in installation manifest
+                    record = manifest.record()
+                    record.name = name
+                    record.type = manifest.RECORD_TYPE_DIRECTORY
+                    record.user = master['pki_user']
+                    record.group = master['pki_group']
+                    record.uid = uid
+                    record.gid = gid
+                    record.permissions = dir_perms
+                    record.acls = dir_acls
+                    manifest.database.append(record)
+        except OSError as exc:
+            config.pki_log.error(log.PKI_OSERROR_1, exc,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            if critical_failure == True:
+                sys.exit(1)
+
+    def copy(self, old_name, new_name, uid=None, gid=None,
+             dir_perms=config.PKI_DEPLOYMENT_DEFAULT_DIR_PERMISSIONS,
+             file_perms=config.PKI_DEPLOYMENT_DEFAULT_FILE_PERMISSIONS,
+             symlink_perms=config.PKI_DEPLOYMENT_DEFAULT_SYMLINK_PERMISSIONS,
+             dir_acls=None, file_acls=None, symlink_acls=None,
+             recursive_flag=True, overwrite_flag=False, critical_failure=True):
+        try:
+            if not os.path.exists(old_name) or not os.path.isdir(old_name):
+                config.pki_log.error(
+                    log.PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1, old_name,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                sys.exit(1)
+            elif config.pki_dry_run_flag:
+                if recursive_flag == True:
+                    # cp -rp <old_name> <new_name>
+                    config.pki_log.info(log.PKIHELPER_CP_RP_2,
+                                        old_name, new_name,
+                                        extra=config.PKI_INDENTATION_LEVEL_2)
+                else:
+                    # cp -p <old_name> <new_name>
+                    config.pki_log.info(log.PKIHELPER_CP_P_2,
+                                        old_name, new_name,
+                                        extra=config.PKI_INDENTATION_LEVEL_2)
+                config.pki_log.info(
+                    log.PKIHELPER_SET_MODE_1, new_name,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+            else:
+                if os.path.exists(new_name):
+                    if not overwrite_flag:
+                        config.pki_log.error(
+                            log.PKI_DIRECTORY_ALREADY_EXISTS_1, new_name,
+                            extra=config.PKI_INDENTATION_LEVEL_2)
+                        sys.exit(1)
+                if recursive_flag == True:
+                    # cp -rp <old_name> <new_name>
+                    config.pki_log.info(log.PKIHELPER_CP_RP_2,
+                                        old_name, new_name,
+                                        extra=config.PKI_INDENTATION_LEVEL_2)
+                    # Due to a limitation in the 'shutil.copytree()'
+                    # implementation which requires that
+                    # 'The destination directory must not already exist.',
+                    # an OSError exception is always thrown due to the
+                    # implementation's unchecked call to 'os.makedirs(dst)'.
+                    # Consequently, a 'patched' local copy of this routine has
+                    # been included in this file with the appropriate fix.
+                    pki_copytree(old_name, new_name)
+                else:
+                    # cp -p <old_name> <new_name>
+                    config.pki_log.info(log.PKIHELPER_CP_P_2,
+                                        old_name, new_name,
+                                        extra=config.PKI_INDENTATION_LEVEL_2)
+                    shutil.copy2(old_name, new_name)
+                # set ownerships, permissions, and acls
+                # of newly created top-level directory
+                self.modify(new_name, uid, gid, dir_perms, dir_acls,
+                            True, critical_failure)
+                # set ownerships, permissions, and acls
+                # of contents of newly created top-level directory
+                self.set_mode(new_name, uid, gid,
+                              dir_perms, file_perms, symlink_perms,
+                              dir_acls, file_acls, symlink_acls,
+                              recursive_flag, critical_failure)
+        except OSError as exc:
+            config.pki_log.error(log.PKI_OSERROR_1, exc,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            if critical_failure == True:
+                sys.exit(1)
+        except shutil.Error as exc:
+            config.pki_log.error(log.PKI_SHUTIL_ERROR_1, exc,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            if critical_failure == True:
+                sys.exit(1)
+        return
+
+
+# PKI Deployment File Class (also used for executables)
+class file:
+    def create(self, name, uid=None, gid=None,
+               perms=config.PKI_DEPLOYMENT_DEFAULT_FILE_PERMISSIONS,
+               acls=None, critical_failure=True):
+        try:
+            if not os.path.exists(name):
+                # touch <name>
+                config.pki_log.info(log.PKIHELPER_TOUCH_1, name,
+                                    extra=config.PKI_INDENTATION_LEVEL_2)
+                if not config.pki_dry_run_flag:
+                    open(name, "w").close()
+                # chmod <perms> <name>
+                config.pki_log.debug(log.PKIHELPER_CHMOD_2, perms, name,
+                                     extra=config.PKI_INDENTATION_LEVEL_3)
+                if not config.pki_dry_run_flag:
+                    os.chmod(name, perms)
+                # chown <uid>:<gid> <name>
+                if uid == None:
+                    uid = identity.get_uid()
+                if gid == None:
+                    gid = identity.get_gid()
+                config.pki_log.debug(log.PKIHELPER_CHOWN_3,
+                                     uid, gid, name,
+                                     extra=config.PKI_INDENTATION_LEVEL_3)
+                if not config.pki_dry_run_flag:
+                    os.chown(name, uid, gid)
+                # Store record in installation manifest
+                record = manifest.record()
+                record.name = name
+                record.type = manifest.RECORD_TYPE_FILE
+                record.user = master['pki_user']
+                record.group = master['pki_group']
+                record.uid = uid
+                record.gid = gid
+                record.permissions = perms
+                record.acls = acls
+                manifest.database.append(record)
+            elif not os.path.isfile(name):
+                config.pki_log.error(
+                    log.PKI_FILE_ALREADY_EXISTS_NOT_A_FILE_1, name,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                if critical_failure == True:
+                    sys.exit(1)
+            else:
+                config.pki_log.error(log.PKI_FILE_ALREADY_EXISTS_1, name,
+                                     extra=config.PKI_INDENTATION_LEVEL_2)
+                if critical_failure == True:
+                    sys.exit(1)
+        except OSError as exc:
+            if exc.errno == errno.EEXIST:
+                pass
+            else:
+                config.pki_log.error(log.PKI_OSERROR_1, exc,
+                                     extra=config.PKI_INDENTATION_LEVEL_2)
+                if critical_failure == True:
+                    sys.exit(1)
+        return
+
+    def modify(self, name, uid=None, gid=None,
+               perms=config.PKI_DEPLOYMENT_DEFAULT_FILE_PERMISSIONS,
+               acls=None, silent=False, critical_failure=True):
+        try:
+            if os.path.exists(name):
+                if not os.path.isfile(name):
+                    config.pki_log.error(
+                        log.PKI_FILE_ALREADY_EXISTS_NOT_A_FILE_1,
+                        name, extra=config.PKI_INDENTATION_LEVEL_2)
+                    if critical_failure == True:
+                        sys.exit(1)
+                # Always re-process each file whether it needs it or not
+                if not silent:
+                    config.pki_log.info(log.PKIHELPER_MODIFY_FILE_1, name,
+                                        extra=config.PKI_INDENTATION_LEVEL_2)
+                # chmod <perms> <name>
+                if not silent:
+                    config.pki_log.debug(log.PKIHELPER_CHMOD_2, perms, name,
+                                         extra=config.PKI_INDENTATION_LEVEL_3)
+                if not config.pki_dry_run_flag:
+                    os.chmod(name, perms)
+                # chown <uid>:<gid> <name>
+                if uid == None:
+                    uid = identity.get_uid()
+                if gid == None:
+                    gid = identity.get_gid()
+                if not silent:
+                    config.pki_log.debug(log.PKIHELPER_CHOWN_3,
+                                         uid, gid, name,
+                                         extra=config.PKI_INDENTATION_LEVEL_3)
+                if not config.pki_dry_run_flag:
+                    os.chown(name, uid, gid)
+                # Store record in installation manifest
+                if not silent:
+                    record = manifest.record()
+                    record.name = name
+                    record.type = manifest.RECORD_TYPE_FILE
+                    record.user = master['pki_user']
+                    record.group = master['pki_group']
+                    record.uid = uid
+                    record.gid = gid
+                    record.permissions = perms
+                    record.acls = acls
+                    manifest.database.append(record)
+            else:
+                config.pki_log.error(
+                    log.PKI_FILE_MISSING_OR_NOT_A_FILE_1, name,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                if critical_failure == True:
+                    sys.exit(1)
+        except OSError as exc:
+            config.pki_log.error(log.PKI_OSERROR_1, exc,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            if critical_failure == True:
+                sys.exit(1)
+        return
+
+    def delete(self, name, critical_failure=True):
+        try:
+            if not os.path.exists(name) or not os.path.isfile(name):
+                config.pki_log.error(
+                    log.PKI_FILE_MISSING_OR_NOT_A_FILE_1, name,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                sys.exit(1)
+            else:
+                # rm -f <name>
+                config.pki_log.info(log.PKIHELPER_RM_F_1, name,
+                                    extra=config.PKI_INDENTATION_LEVEL_2)
+                if not config.pki_dry_run_flag:
+                    os.remove(name)
+        except OSError as exc:
+            config.pki_log.error(log.PKI_OSERROR_1, exc,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            if critical_failure == True:
+                sys.exit(1)
+        return
+
+    def copy(self, old_name, new_name, uid=None, gid=None,
+             perms=config.PKI_DEPLOYMENT_DEFAULT_FILE_PERMISSIONS, acls=None,
+             overwrite_flag=False, critical_failure=True):
+        try:
+            if not os.path.exists(old_name) or not os.path.isfile(old_name):
+                config.pki_log.error(
+                    log.PKI_FILE_MISSING_OR_NOT_A_FILE_1, old_name,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                sys.exit(1)
+            elif config.pki_dry_run_flag:
+                # cp -p <old_name> <new_name>
+                config.pki_log.info(log.PKIHELPER_CP_P_2,
+                                    old_name, new_name,
+                                    extra=config.PKI_INDENTATION_LEVEL_2)
+                # chmod <perms> <new_name>
+                config.pki_log.debug(log.PKIHELPER_CHMOD_2,
+                                     perms, new_name,
+                                     extra=config.PKI_INDENTATION_LEVEL_3)
+                # chown <uid>:<gid> <new_name>
+                config.pki_log.debug(log.PKIHELPER_CHOWN_3,
+                                     uid, gid, new_name,
+                                     extra=config.PKI_INDENTATION_LEVEL_3)
+            else:
+                if os.path.exists(new_name):
+                    if not overwrite_flag:
+                        config.pki_log.error(
+                            log.PKI_FILE_ALREADY_EXISTS_1, new_name,
+                            extra=config.PKI_INDENTATION_LEVEL_2)
+                        sys.exit(1)
+                # cp -p <old_name> <new_name>
+                config.pki_log.info(log.PKIHELPER_CP_P_2,
+                                    old_name, new_name,
+                                    extra=config.PKI_INDENTATION_LEVEL_2)
+                shutil.copy2(old_name, new_name)
+                if uid == None:
+                    uid = identity.get_uid()
+                if gid == None:
+                    gid = identity.get_gid()
+                # chmod <perms> <new_name>
+                config.pki_log.debug(log.PKIHELPER_CHMOD_2,
+                                     perms, new_name,
+                                     extra=config.PKI_INDENTATION_LEVEL_3)
+                os.chmod(new_name, perms)
+                # chown <uid>:<gid> <new_name>
+                config.pki_log.debug(log.PKIHELPER_CHOWN_3,
+                                     uid, gid, new_name,
+                                     extra=config.PKI_INDENTATION_LEVEL_3)
+                os.chown(new_name, uid, gid)
+                # Store record in installation manifest
+                record = manifest.record()
+                record.name = new_name
+                record.type = manifest.RECORD_TYPE_FILE
+                record.user = master['pki_user']
+                record.group = master['pki_group']
+                record.uid = uid
+                record.gid = gid
+                record.permissions = perms
+                record.acls = acls
+                manifest.database.append(record)
+        except OSError as exc:
+            config.pki_log.error(log.PKI_OSERROR_1, exc,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            if critical_failure == True:
+                sys.exit(1)
+        except shutil.Error as exc:
+            config.pki_log.error(log.PKI_SHUTIL_ERROR_1, exc,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            if critical_failure == True:
+                sys.exit(1)
+        return
+
+
+# PKI Deployment Symbolic Link Class
+class symlink:
+    def create(self, name, link, uid=None, gid=None,
+               acls=None, allow_dangling_symlink=False, critical_failure=True):
+        try:
+            if not os.path.exists(link):
+                if not config.pki_dry_run_flag:
+                    if not os.path.exists(name):
+                        config.pki_log.warning(
+                            log.PKIHELPER_DANGLING_SYMLINK_2, link, name,
+                            extra=config.PKI_INDENTATION_LEVEL_2)
+                        if not allow_dangling_symlink:
+                            sys.exit(1)
+                # ln -s <name> <link>
+                config.pki_log.info(log.PKIHELPER_LINK_S_2, name, link,
+                                    extra=config.PKI_INDENTATION_LEVEL_2)
+                if not config.pki_dry_run_flag:
+                    os.symlink(name, link)
+                # REMINDER:  Due to POSIX compliance, 'lchmod' is NEVER
+                #            implemented on Linux systems since 'chmod'
+                #            CANNOT be run directly against symbolic links!
+                # chown -h <uid>:<gid> <link>
+                if uid == None:
+                    uid = identity.get_uid()
+                if gid == None:
+                    gid = identity.get_gid()
+                config.pki_log.debug(log.PKIHELPER_CHOWN_H_3,
+                                     uid, gid, link,
+                                     extra=config.PKI_INDENTATION_LEVEL_3)
+                if not config.pki_dry_run_flag:
+                    os.lchown(link, uid, gid)
+                # Store record in installation manifest
+                record = manifest.record()
+                record.name = link
+                record.type = manifest.RECORD_TYPE_SYMLINK
+                record.user = master['pki_user']
+                record.group = master['pki_group']
+                record.uid = uid
+                record.gid = gid
+                record.permissions =\
+                    config.PKI_DEPLOYMENT_DEFAULT_SYMLINK_PERMISSIONS
+                record.acls = acls
+                manifest.database.append(record)
+            elif not os.path.islink(link):
+                config.pki_log.error(
+                    log.PKI_SYMLINK_ALREADY_EXISTS_NOT_A_SYMLINK_1, link,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                if critical_failure == True:
+                    sys.exit(1)
+            else:
+                config.pki_log.error(log.PKI_SYMLINK_ALREADY_EXISTS_1, link,
+                                     extra=config.PKI_INDENTATION_LEVEL_2)
+                if critical_failure == True:
+                    sys.exit(1)
+        except OSError as exc:
+            if exc.errno == errno.EEXIST:
+                pass
+            else:
+                config.pki_log.error(log.PKI_OSERROR_1, exc,
+                                     extra=config.PKI_INDENTATION_LEVEL_2)
+                if critical_failure == True:
+                    sys.exit(1)
+        return
+
+    def modify(self, link, uid=None, gid=None,
+               acls=None, silent=False, critical_failure=True):
+        try:
+            if os.path.exists(link):
+                if not os.path.islink(link):
+                    config.pki_log.error(
+                        log.PKI_SYMLINK_ALREADY_EXISTS_NOT_A_SYMLINK_1,
+                        link, extra=config.PKI_INDENTATION_LEVEL_2)
+                    if critical_failure == True:
+                        sys.exit(1)
+                # Always re-process each link whether it needs it or not
+                if not silent:
+                    config.pki_log.info(log.PKIHELPER_MODIFY_SYMLINK_1, link,
+                                        extra=config.PKI_INDENTATION_LEVEL_2)
+                # REMINDER:  Due to POSIX compliance, 'lchmod' is NEVER
+                #            implemented on Linux systems since 'chmod'
+                #            CANNOT be run directly against symbolic links!
+                # chown -h <uid>:<gid> <link>
+                if uid == None:
+                    uid = identity.get_uid()
+                if gid == None:
+                    gid = identity.get_gid()
+                if not silent:
+                    config.pki_log.debug(log.PKIHELPER_CHOWN_H_3,
+                                         uid, gid, link,
+                                         extra=config.PKI_INDENTATION_LEVEL_3)
+                if not config.pki_dry_run_flag:
+                    os.lchown(link, uid, gid)
+                # Store record in installation manifest
+                if not silent:
+                    record = manifest.record()
+                    record.name = link
+                    record.type = manifest.RECORD_TYPE_SYMLINK
+                    record.user = master['pki_user']
+                    record.group = master['pki_group']
+                    record.uid = uid
+                    record.gid = gid
+                    record.permissions =\
+                        config.PKI_DEPLOYMENT_DEFAULT_SYMLINK_PERMISSIONS
+                    record.acls = acls
+                    manifest.database.append(record)
+            else:
+                config.pki_log.error(
+                    log.PKI_SYMLINK_MISSING_OR_NOT_A_SYMLINK_1, link,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                if critical_failure == True:
+                    sys.exit(1)
+        except OSError as exc:
+            config.pki_log.error(log.PKI_OSERROR_1, exc,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            if critical_failure == True:
+                sys.exit(1)
+        return
+
+    def delete(self, link, critical_failure=True):
+        try:
+            if not os.path.exists(link) or not os.path.islink(link):
+                config.pki_log.error(
+                    log.PKI_SYMLINK_MISSING_OR_NOT_A_SYMLINK_1, link,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                sys.exit(1)
+            else:
+                # rm -f <link>
+                config.pki_log.info(log.PKIHELPER_RM_F_1, link,
+                                    extra=config.PKI_INDENTATION_LEVEL_2)
+                if not config.pki_dry_run_flag:
+                    os.remove(link)
+        except OSError as exc:
+            config.pki_log.error(log.PKI_OSERROR_1, exc,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            if critical_failure == True:
+                sys.exit(1)
+        return
+
+
+# PKI Deployment War File Class
+class war:
+    def explode(self, name, path, critical_failure=True):
+        try:
+            if os.path.exists(name) and os.path.isfile(name):
+                if not zipfile.is_zipfile(name):
+                    config.pki_log.error(
+                        log.PKI_FILE_NOT_A_WAR_FILE_1,
+                        name, extra=config.PKI_INDENTATION_LEVEL_2)
+                    if critical_failure == True:
+                        sys.exit(1)
+                if not config.pki_dry_run_flag:
+                    if not os.path.exists(path) or not os.path.isdir(path):
+                        config.pki_log.error(
+                            log.PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1,
+                            path, extra=config.PKI_INDENTATION_LEVEL_2)
+                        if critical_failure == True:
+                            sys.exit(1)
+                # jar -xf <name> -C <path>
+                config.pki_log.info(log.PKIHELPER_JAR_XF_C_2, name, path,
+                                    extra=config.PKI_INDENTATION_LEVEL_2)
+                if not config.pki_dry_run_flag:
+                    # Open war file
+                    war = zipfile.ZipFile(name, 'r')
+                    # Extract contents of war file to path
+                    war.extractall(path)
+            else:
+                config.pki_log.error(
+                    log.PKI_FILE_MISSING_OR_NOT_A_FILE_1, name,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                if critical_failure == True:
+                    sys.exit(1)
+        except OSError as exc:
+            config.pki_log.error(log.PKI_OSERROR_1, exc,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            if critical_failure == True:
+                sys.exit(1)
+        except zipfile.BadZipfile as exc:
+            config.pki_log.error(log.PKI_BADZIPFILE_ERROR_1, exc,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            if critical_failure == True:
+                sys.exit(1)
+        except zipfile.LargeZipFile as exc:
+            config.pki_log.error(log.PKI_LARGEZIPFILE_ERROR_1, exc,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            if critical_failure == True:
+                sys.exit(1)
+        return
+
 
+# PKI Deployment Helper Class Instances
+identity = identity()
+instance = instance()
+directory = directory()
+file = file()
+symlink = symlink()
+war = war()
diff --git a/base/deploy/src/scriptlets/pkilogging.py b/base/deploy/src/scriptlets/pkilogging.py
index 776677c..9b22ae3 100644
--- a/base/deploy/src/scriptlets/pkilogging.py
+++ b/base/deploy/src/scriptlets/pkilogging.py
@@ -15,7 +15,7 @@
 # with this program; if not, write to the Free Software Foundation, Inc.,
 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 #
-# Copyright (C) 2011 Red Hat, Inc.
+# Copyright (C) 2012 Red Hat, Inc.
 # All rights reserved.
 #
 
@@ -31,16 +31,36 @@ def enable_pki_logger(log_dir, log_name, log_level, console_log_level, logger):
             os.makedirs(log_dir)
         except OSError:
             return OSError
+
+    # Establish 'file' logger using 'basicConfig()'
+    logging.LoggerAdapter(logging.getLogger(''), {'indent' : ''})
     logging.basicConfig(level=log_level,
                         format='%(asctime)s %(name)-12s ' +\
-                               '%(levelname)-8s %(message)s',
+                               '%(levelname)-8s ' +\
+                               '%(indent)s%(message)s',
                         datefmt='%Y-%m-%d %H:%M:%S',
                         filename=log_dir + "/" + log_name,
                         filemode='w')
+
+    # Establish 'console' logger
     console = logging.StreamHandler()
+    logging.LoggerAdapter(console, {'indent' : ''})
     console.setLevel(console_log_level)
-    formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
-    console.setFormatter(formatter)
+    console_format = logging.Formatter('%(name)-12s: ' +\
+                                       '%(levelname)-8s ' +\
+                                       '%(indent)s%(message)s')
+    console.setFormatter(console_format)
     logging.getLogger('').addHandler(console)
-    return logging.getLogger(logger)
 
+    # Establish 'file' logger
+#   file = logging.FileHandler(log_dir + "/" + log_name, 'w')
+#   logging.LoggerAdapter(file, {'indent' : ''})
+#   file.setLevel(log_level)
+#   file_format = logging.Formatter('%(asctime)s %(name)-12s: ' +\
+#                                   '%(levelname)-8s ' +\
+#                                   '%(indent)s%(message)s',
+#                                   '%Y-%m-%d %H:%M:%S')
+#   file.setFormatter(file_format)
+#   logging.getLogger('').addHandler(file)
+
+    return logging.getLogger(logger)
diff --git a/base/deploy/src/scriptlets/pkimanifest.py b/base/deploy/src/scriptlets/pkimanifest.py
new file mode 100644
index 0000000..dfd18fb
--- /dev/null
+++ b/base/deploy/src/scriptlets/pkimanifest.py
@@ -0,0 +1,97 @@
+#!/usr/bin/python -t
+# Authors:
+#     Matthew Harmsen <mharmsen@redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2012 Red Hat, Inc.
+# All rights reserved.
+#
+
+# System Imports
+from collections import namedtuple
+import csv
+
+
+# PKI Deployment Manifest Constants
+RECORD_TYPE_DIRECTORY = "directory"
+RECORD_TYPE_FILE = "file"
+RECORD_TYPE_SYMLINK = "symlink"
+
+
+# PKI Deployment Manifest Record Class
+class record(object):
+    __slots__= "name",\
+               "type",\
+               "user",\
+               "group",\
+               "uid",\
+               "gid",\
+               "permissions",\
+               "acls",
+
+    def items(self):
+        "dict style items"
+        return [
+            (field_name, getattr(self, field_name))
+            for field_name in self.__slots__]
+
+    def __iter__(self):
+        "iterate over fields tuple/list style"
+        for field_name in self.__slots__:
+            yield getattr(self, field_name)
+
+    def __getitem__(self, index):
+        "tuple/list style getitem"
+        return getattr(self, self.__slots__[index])
+
+
+# PKI Deployment Manifest File Class
+class file:
+    """FUTURE:  Consider creating a single manifest file
+       that is always overwritten."""
+    global database
+    filename = None
+
+    def register(self, name):
+        self.filename = name
+
+    def write(self):
+        try:
+            fd = open(self.filename, "wt")
+            c = csv.writer(fd)
+            for record in database:
+                c.writerow(tuple(record))
+            fd.close()
+        except IOError as exc:
+            config.pki_log.error(log.PKI_IOERROR_1, exc,
+                                 extra=config.PKI_INDENTATION_LEVEL_1)
+            sys.exit(1)
+
+    def read(self):
+        try:
+            fd = open(self.filename, "rt")
+            cr = csv.reader(fd)
+            for row in cr:
+                print tuple(row)
+            fd.close()
+        except IOError as exc:
+            config.pki_log.error(log.PKI_IOERROR_1, exc,
+                                 extra=config.PKI_INDENTATION_LEVEL_1)
+            sys.exit(1)
+
+
+# PKI Deployment Global Named Tuples
+database = []
+file = file()
diff --git a/base/deploy/src/scriptlets/pkimessages.py b/base/deploy/src/scriptlets/pkimessages.py
index e6a9f95..81f0ead 100644
--- a/base/deploy/src/scriptlets/pkimessages.py
+++ b/base/deploy/src/scriptlets/pkimessages.py
@@ -15,22 +15,11 @@
 # with this program; if not, write to the Free Software Foundation, Inc.,
 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 #
-# Copyright (C) 2011 Red Hat, Inc.
+# Copyright (C) 2012 Red Hat, Inc.
 # All rights reserved.
 #
 
 # PKI Deployment Engine Messages
-PKI_CUSTOM_APACHE_INSTANCE_1 = "When a custom '%s' subsystem is being "\
-                               "deployed, the 'instance', 'http_port', and "\
-                               "'https_port' must ALL be specified!"
-PKI_CUSTOM_TOMCAT_INSTANCE_1 = "When a custom '%s' subsystem is being "\
-                               "deployed, the 'instance', 'http_port', "\
-                               "'https_port', and 'ajp_port' must ALL be "\
-                               "specified!"
-PKI_CUSTOM_TOMCAT_AJP_PORT_1 = "When a custom '%s' subsystem is being "\
-                               "deployed, ONLY the 'instance', "\
-                               "'http_port', and 'https_port' MUST be "\
-                               "specified; NO 'ajp_port' should be requested!"
 PKI_DICTIONARY_COMMON ="\n"\
 "=====================================================\n"\
 "    DISPLAY CONTENTS OF PKI COMMON DICTIONARY\n"\
@@ -47,14 +36,57 @@ PKI_DICTIONARY_WEB_SERVER="\n"\
 "=====================================================\n"\
 "    DISPLAY CONTENTS OF PKI WEB SERVER DICTIONARY\n"\
 "====================================================="
+
+
+# PKI Deployment Log Messages
+PKI_VERBOSITY=\
+"VERBOSITY FLAGS    CONSOLE MESSAGE LEVEL       LOG MESSAGE LEVEL\n"\
+"=======================================================================\n"\
+"  NONE             error|warning               error|warning|info\n"\
+"  -v               error|warning|info          error|warning|info\n"\
+"  -vv              error|warning|info          error|warning|info|debug\n"\
+"  -vvv             error|warning|info|debug    error|warning|info|debug\n"\
+" "
+
+# PKI Deployment Error Messages
+PKI_BADZIPFILE_ERROR_1 = "zipfile.BadZipFile:  %s!"
+PKI_CUSTOM_APACHE_INSTANCE_1 = "When a custom '%s' subsystem is being "\
+                               "deployed, the 'instance', 'http_port', and "\
+                               "'https_port' must ALL be specified!"
+PKI_CUSTOM_TOMCAT_INSTANCE_1 = "When a custom '%s' subsystem is being "\
+                               "deployed, the 'instance', 'http_port', "\
+                               "'https_port', and 'ajp_port' must ALL be "\
+                               "specified!"
+PKI_CUSTOM_TOMCAT_AJP_PORT_1 = "When a custom '%s' subsystem is being "\
+                               "deployed, ONLY the 'instance', "\
+                               "'http_port', and 'https_port' MUST be "\
+                               "specified; NO 'ajp_port' should be requested!"
 PKI_DIRECTORY_ALREADY_EXISTS_1 = "Directory '%s' already exists!"
 PKI_DIRECTORY_ALREADY_EXISTS_NOT_A_DIRECTORY_1 = "Directory '%s' already "\
                                                  "exists BUT it is NOT a "\
                                                  "directory!"
 PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1 = "Directory '%s' is either "\
                                              "missing or is NOT a directory!"
+PKI_FILE_ALREADY_EXISTS_1 = "File '%s' already exists!"
+PKI_FILE_ALREADY_EXISTS_NOT_A_FILE_1 = "File '%s' already "\
+                                       "exists BUT it is NOT a "\
+                                       "file!"
 PKI_FILE_MISSING_OR_NOT_A_FILE_1 = "File '%s' is either missing "\
                                    "or is NOT a regular file!"
+PKI_FILE_NOT_A_WAR_FILE_1 = "File '%s' is NOT a war file!"
+
+PKI_IOERROR_1 = "IOError:  %s!"
+PKI_KEYERROR_1 = "KeyError:  %s!"
+PKI_LARGEZIPFILE_ERROR_1 = "zipfile.LargeZipFile:  %s!"
+PKI_MANIFEST_MESSAGE_1 = "generating manifest file called '%s'"
+PKI_OSERROR_1 = "OSError:  %s!"
+PKI_SHUTIL_ERROR_1 = "shutil.Error:  %s!"
+PKI_SYMLINK_ALREADY_EXISTS_1 = "Symlink '%s' already exists!"
+PKI_SYMLINK_ALREADY_EXISTS_NOT_A_SYMLINK_1 = "Symlink '%s' already "\
+                                             "exists BUT it is NOT a "\
+                                             "symlink!"
+PKI_SYMLINK_MISSING_OR_NOT_A_SYMLINK_1 = "Symlink '%s' is either missing "\
+                                         "or is NOT a symbolic link!"
 PKI_UNABLE_TO_PARSE_1 = "'Could not parse:  '%s'"
 PKI_UNABLE_TO_CREATE_LOG_DIRECTORY_1 = "Could not create log directory '%s'!"
 PKI_VERBOSITY_LEVELS_MESSAGE = "Only up to 3 levels of verbosity are supported!"
@@ -64,23 +96,77 @@ PKI_VERBOSITY_LEVELS_MESSAGE = "Only up to 3 levels of verbosity are supported!"
 PKIDESTROY_BEGIN_MESSAGE_2 = "BEGIN destroying subsystem '%s' of "\
                              "instance '%s' . . ."
 PKIDESTROY_END_MESSAGE_2 = "END destroying subsystem '%s' of "\
-                           "instance '%s'."
+                           "instance '%s'"
 PKIRESPAWN_BEGIN_MESSAGE_2 = "BEGIN respawning subsystem '%s' of "\
                              "instance '%s' . . ."
 PKIRESPAWN_END_MESSAGE_2 = "END respawning subsystem '%s' of "\
-                           "instance '%s'."
+                           "instance '%s'"
 PKISPAWN_BEGIN_MESSAGE_2 = "BEGIN spawning subsystem '%s' of "\
                            "instance '%s' . . ."
 PKISPAWN_END_MESSAGE_2 = "END spawning subsystem '%s' of "\
-                         "instance '%s'."
+                         "instance '%s'"
 
 
-# PKI Deployment "Scriptlet" Messages
-INSTANCE_DESTROY_1 = "    depopulating '%s'"
-INSTANCE_RESPAWN_1 = "    repopulating '%s'"
-INSTANCE_SPAWN_1 = "    populating '%s'"
-INSTANCE_SPAWN_MKDIR_1 = "        mkdir '%s'"
-SECURITY_DATABASES_DESTROY_1 = "    removing '%s'"
-SECURITY_DATABASES_RESPAWN_1 = "    regenerating '%s'"
-SECURITY_DATABASES_SPAWN_1 = "    generating '%s'"
+# PKI Deployment "Helper" Messages
+PKIHELPER_APACHE_INSTANCES_2 = "instance '%s' contains '%d' Apache PKI subsystems"
+PKIHELPER_CHMOD_2 = "chmod %o %s"
+PKIHELPER_CHOWN_3 = "chown %s:%s %s"
+PKIHELPER_CHOWN_H_3 = "chown -h %s:%s %s"
+PKIHELPER_CP_P_2 = "cp -p %s %s"
+PKIHELPER_CP_RP_2 = "cp -rp %s %s"
+PKIHELPER_DANGLING_SYMLINK_2 = "Dangling symlink '%s'-->'%s'"
+PKIHELPER_DIRECTORY_IS_EMPTY_1 = "directory '%s' is empty"
+PKIHELPER_DIRECTORY_IS_NOT_EMPTY_1 = "directory '%s' is NOT empty"
+PKIHELPER_GID_2 = "GID of '%s' is %s"
+PKIHELPER_GROUP_1 = "retrieving GID for '%s' . . ."
+PKIHELPER_IS_A_DIRECTORY_1 = "'%s' is a directory"
+PKIHELPER_IS_A_FILE_1 = "'%s' is a file"
+PKIHELPER_IS_A_SYMLINK_1 = "'%s' is a symlink"
+PKIHELPER_JAR_XF_C_2 = "jar -xf %s -C %s"
+PKIHELPER_LINK_S_2 = "ln -s %s %s"
+PKIHELPER_MKDIR_1 = "mkdir -p %s"
+PKIHELPER_MODIFY_DIR_1 = "modifying '%s'"
+PKIHELPER_MODIFY_FILE_1 = "modifying '%s'"
+PKIHELPER_MODIFY_SYMLINK_1 = "modifying '%s'"
+PKIHELPER_PKI_SUBSYSTEM_INSTANCES_2 = "instance '%s' contains '%d' PKI subsystems"
+PKIHELPER_RM_F_1 = "rm -f %s"
+PKIHELPER_RM_RF_1 = "rm -rf %s"
+PKIHELPER_RMDIR_1 = "rmdir %s"
+PKIHELPER_SET_MODE_1 = "setting ownerships, permissions, and acls on '%s'"
+PKIHELPER_TOMCAT_INSTANCES_2 = "instance '%s' contains '%d' Tomcat PKI subsystems"
+PKIHELPER_TOUCH_1 = "touch %s"
+PKIHELPER_UID_2 = "UID of '%s' is %s"
+PKIHELPER_USER_1 = "retrieving UID for '%s' . . ."
+
 
+# PKI Deployment "Scriptlet" Messages
+CONFIGURATION_DESTROY_1 = "unconfiguring '%s'"
+CONFIGURATION_RESPAWN_1 = "reconfiguring '%s'"
+CONFIGURATION_SPAWN_1 = "configuring '%s'"
+FINALIZATION_DESTROY_1 = "finalizing '%s'"
+FINALIZATION_RESPAWN_1 = "finalizing '%s'"
+FINALIZATION_SPAWN_1 = "finalizing '%s'"
+INITIALIZATION_DESTROY_1 = "initializing '%s'"
+INITIALIZATION_RESPAWN_1 = "initializing '%s'"
+INITIALIZATION_SPAWN_1 = "initializing '%s'"
+INSTANCE_DESTROY_1 = "depopulating '%s'"
+INSTANCE_RESPAWN_1 = "repopulating '%s'"
+INSTANCE_SPAWN_1 = "populating '%s'"
+RESIDUAL_DESTROY_1 = "depopulating '%s'"
+RESIDUAL_RESPAWN_1 = "repopulating '%s'"
+RESIDUAL_SPAWN_1 = "populating '%s'"
+SECURITY_DATABASES_DESTROY_1 = "removing '%s'"
+SECURITY_DATABASES_RESPAWN_1 = "regenerating '%s'"
+SECURITY_DATABASES_SPAWN_1 = "generating '%s'"
+SLOT_ASSIGNMENT_DESTROY_1 = "unassigning slots for '%s'"
+SLOT_ASSIGNMENT_RESPAWN_1 = "reassigning slots for '%s'"
+SLOT_ASSIGNMENT_SPAWN_1 = "assigning slots for '%s'"
+SUBSYSTEM_DESTROY_1 = "depopulating '%s'"
+SUBSYSTEM_RESPAWN_1 = "repopulating '%s'"
+SUBSYSTEM_SPAWN_1 = "populating '%s'"
+WAR_EXPLOSION_DESTROY_1 = "removing '%s'"
+WAR_EXPLOSION_RESPAWN_1 = "redeploying '%s'"
+WAR_EXPLOSION_SPAWN_1 = "deploying '%s'"
+WEBSERVER_DESTROY_1 = "depopulating '%s'"
+WEBSERVER_RESPAWN_1 = "repopulating '%s'"
+WEBSERVER_SPAWN_1 = "populating '%s'"
diff --git a/base/deploy/src/scriptlets/pkiparser.py b/base/deploy/src/scriptlets/pkiparser.py
new file mode 100644
index 0000000..bff4059
--- /dev/null
+++ b/base/deploy/src/scriptlets/pkiparser.py
@@ -0,0 +1,362 @@
+#!/usr/bin/python -t
+# Authors:
+#     Matthew Harmsen <mharmsen@redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2012 Red Hat, Inc.
+# All rights reserved.
+#
+
+# System Imports
+import ConfigParser
+import argparse
+import logging
+import os
+
+
+# PKI Deployment Imports
+import pkiconfig as config
+import pkimessages as log
+
+
+# PKI Deployment Helper Functions
+def process_command_line_arguments(argv):
+    "Read and process command-line options"
+    description = None
+    if os.path.basename(argv[0]) == 'pkispawn':
+        description = 'PKI Instance Installation and Configuration'
+    elif os.path.basename(argv[0]) == 'pkidestroy':
+        description = 'PKI Instance Removal'
+    parser = argparse.ArgumentParser(
+                 description=description,
+                 add_help=False,
+                 formatter_class=argparse.RawDescriptionHelpFormatter,
+                 epilog=log.PKI_VERBOSITY)
+    mandatory = parser.add_argument_group('mandatory arguments')
+    mandatory.add_argument('-s',
+                           dest='pki_subsystem', action='store',
+                           nargs=1, choices=config.PKI_SUBSYSTEMS,
+                           required=True, metavar='<subsystem>',
+                           help='where <subsystem> is '
+                                'CA, KRA, OCSP, RA, TKS, or TPS')
+    optional = parser.add_argument_group('optional arguments')
+    optional.add_argument('--dry_run',
+                          dest='pki_dry_run_flag', action='store_true',
+                          help='do not actually perform any actions')
+    optional.add_argument('-f',
+                          dest='pkideployment_cfg', action='store',
+                          nargs=1, metavar='<file>',
+                          help='overrides default configuration filename')
+    optional.add_argument('-h', '--help',
+                          dest='help', action='help',
+                          help='show this help message and exit')
+    optional.add_argument('-p',
+                          dest='pki_root_prefix', action='store',
+                          nargs=1, metavar='<prefix>',
+                          help='directory prefix to specify local directory')
+    if os.path.basename(argv[0]) == 'pkispawn':
+        optional.add_argument('-u',
+                              dest='pki_update_flag', action='store_true',
+                              help='update instance of specified subsystem')
+    optional.add_argument('-v',
+                          dest='pki_verbosity', action='count',
+                          help='display verbose information (details below)')
+    custom = parser.add_argument_group('custom arguments '
+                                       '(OVERRIDES configuration file values)')
+    custom.add_argument('-i',
+                        dest='pki_instance_name', action='store',
+                        nargs=1, metavar='<instance>',
+                        help='PKI instance name (MUST specify REQUIRED ports)')
+    custom.add_argument('--http_port',
+                        dest='pki_http_port', action='store',
+                        nargs=1, metavar='<port>',
+                        help='HTTP port (CA, KRA, OCSP, RA, TKS, TPS)')
+    custom.add_argument('--https_port',
+                        dest='pki_https_port', action='store',
+                        nargs=1, metavar='<port>',
+                        help='HTTPS port (CA, KRA, OCSP, RA, TKS, TPS)')
+    custom.add_argument('--ajp_port',
+                        dest='pki_ajp_port', action='store',
+                        nargs=1, metavar='<port>',
+                        help='AJP port (CA, KRA, OCSP, TKS)')
+    args = parser.parse_args()
+
+    config.pki_subsystem = str(args.pki_subsystem).strip('[\']')
+    if args.pki_dry_run_flag:
+        config.pki_dry_run_flag = args.pki_dry_run_flag
+    if not args.pki_root_prefix is None:
+        config.pki_root_prefix = str(args.pki_root_prefix).strip('[\']')
+    if config.pki_root_prefix is None or\
+       len(config.pki_root_prefix) == 0:
+        config.pki_root_prefix = ""
+    elif not os.path.exists(config.pki_root_prefix) or\
+         not os.path.isdir(config.pki_root_prefix):
+        print "ERROR:  " +\
+              log.PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1 %\
+              config.pki_root_prefix
+        print
+        parser.print_help()
+        parser.exit(-1);
+    if os.path.basename(argv[0]) == 'pkispawn':
+        if args.pki_update_flag:
+            config.pki_update_flag = args.pki_update_flag
+    if args.pki_verbosity == 1:
+        config.pki_console_log_level = logging.INFO
+        config.pki_log_level = logging.INFO
+    elif args.pki_verbosity == 2:
+        config.pki_console_log_level = logging.INFO
+        config.pki_log_level = logging.DEBUG
+    elif args.pki_verbosity == 3:
+        config.pki_console_log_level = logging.DEBUG
+        config.pki_log_level = logging.DEBUG
+    elif args.pki_verbosity > 3:
+        print "ERROR:  " + log.PKI_VERBOSITY_LEVELS_MESSAGE
+        print
+        parser.print_help()
+        parser.exit(-1);
+    if not args.pki_instance_name is None:
+        config.pki_instance_name = str(args.pki_instance_name).strip('[\']')
+    if not args.pki_http_port is None:
+        config.pki_http_port = str(args.pki_http_port).strip('[\']')
+    if not args.pki_https_port is None:
+        config.pki_https_port = str(args.pki_https_port).strip('[\']')
+    if not args.pki_ajp_port is None:
+        if config.pki_subsystem in config.PKI_TOMCAT_SUBSYSTEMS:
+            config.pki_ajp_port = str(args.pki_ajp_port).strip('[\']')
+        else:
+            print "ERROR:  " +\
+                  log.PKI_CUSTOM_TOMCAT_AJP_PORT_1 %\
+                  config.pki_subsystem
+            print
+            parser.print_help()
+            parser.exit(-1);
+    if not args.pki_instance_name is None or\
+       not args.pki_http_port is None or\
+       not args.pki_https_port is None or\
+       not args.pki_ajp_port is None:
+        if config.pki_subsystem in config.PKI_APACHE_SUBSYSTEMS:
+            if args.pki_instance_name is None or\
+               args.pki_http_port is None or\
+               args.pki_https_port is None:
+                print "ERROR:  " + log.PKI_CUSTOM_APACHE_INSTANCE_1 %\
+                      config.pki_subsystem
+                print
+                parser.print_help()
+                parser.exit(-1);
+        elif config.pki_subsystem in config.PKI_TOMCAT_SUBSYSTEMS:
+            if args.pki_instance_name is None or\
+               args.pki_http_port is None or\
+               args.pki_https_port is None or\
+               args.pki_ajp_port is None:
+                print "ERROR:  " + log.PKI_CUSTOM_TOMCAT_INSTANCE_1 %\
+                      config.pki_subsystem
+                print
+                parser.print_help()
+                parser.exit(-1);
+    if not args.pkideployment_cfg is None:
+        config.pkideployment_cfg = str(args.pkideployment_cfg).strip('[\']')
+    elif os.path.basename(argv[0]) == 'pkidestroy':
+        # NOTE:  When performing 'pkidestroy', a configuration file must be
+        #        explicitly specified if it does not use the default location
+        #        and/or default configuration file name.
+        config.pkideployment_cfg = config.pki_root_prefix +\
+            config.PKI_DEPLOYMENT_REGISTRY_ROOT + "/" +\
+            config.PKI_DEPLOYMENT_DEFAULT_INSTANCE_NAME + "/" +\
+            config.pki_subsystem.lower() +"/" +\
+            config.PKI_DEPLOYMENT_DEFAULT_CONFIGURATION_FILE
+    if not os.path.exists(config.pkideployment_cfg) or\
+       not os.path.isfile(config.pkideployment_cfg):
+        print "ERROR:  " +\
+              log.PKI_FILE_MISSING_OR_NOT_A_FILE_1 %\
+              config.pkideployment_cfg
+        print
+        parser.print_help()
+        parser.exit(-1);
+    return
+
+
+def read_pki_configuration_file():
+    "Read configuration file sections into dictionaries"
+    rv = 0
+    try:
+        parser = ConfigParser.ConfigParser()
+        parser.read(config.pkideployment_cfg)
+        config.pki_common_dict = dict(parser._sections['Common'])
+        if config.pki_subsystem == "CA":
+            config.pki_web_server_dict = dict(parser._sections['Tomcat'])
+            config.pki_subsystem_dict = dict(parser._sections['CA'])
+        elif config.pki_subsystem == "KRA":
+            config.pki_web_server_dict = dict(parser._sections['Tomcat'])
+            config.pki_subsystem_dict = dict(parser._sections['KRA'])
+        elif config.pki_subsystem == "OCSP":
+            config.pki_web_server_dict = dict(parser._sections['Tomcat'])
+            config.pki_subsystem_dict = dict(parser._sections['OCSP'])
+        elif config.pki_subsystem == "RA":
+            config.pki_web_server_dict = dict(parser._sections['Apache'])
+            config.pki_subsystem_dict = dict(parser._sections['RA'])
+        elif config.pki_subsystem == "TKS":
+            config.pki_web_server_dict = dict(parser._sections['Tomcat'])
+            config.pki_subsystem_dict = dict(parser._sections['TKS'])
+        elif config.pki_subsystem == "TPS":
+            config.pki_web_server_dict = dict(parser._sections['Apache'])
+            config.pki_subsystem_dict = dict(parser._sections['TPS'])
+        # Insert empty record into dictionaries for "pretty print" statements
+        config.pki_common_dict[0] = None
+        config.pki_web_server_dict[0] = None
+        config.pki_subsystem_dict[0] = None
+    except ConfigParser.ParsingError, err:
+        rv = err
+    return rv
+
+
+def compose_pki_master_dictionary():
+    "Create a single master PKI dictionary from the sectional dictionaries"
+    config.pki_master_dict = dict()
+    # 'pkispawn'/'pkirespawn'/'pkidestroy' name/value pairs
+    config.pki_master_dict['pki_timestamp'] = config.pki_timestamp
+    # Configuration file name/value pairs
+    config.pki_master_dict.update(config.pki_common_dict)
+    config.pki_master_dict.update(config.pki_web_server_dict)
+    config.pki_master_dict.update(config.pki_subsystem_dict)
+    config.pki_master_dict.update(__name__="PKI Master Dictionary")
+    config.pki_master_dict['pki_source_conf'] =\
+        config.pki_master_dict['pki_source_root'] + "/" +\
+        config.pki_master_dict['pki_subsystem'].lower() + "/" + "conf"
+    if config.pki_master_dict['pki_subsystem'] in config.PKI_TOMCAT_SUBSYSTEMS:
+        config.pki_master_dict['pki_war'] =\
+            config.pki_master_dict['pki_source_root'] + "/" +\
+            config.pki_master_dict['pki_subsystem'].lower() + "/" +\
+            "war" + "/" + config.pki_master_dict['pki_war_name']
+        config.pki_master_dict['pki_tomcat_bin_path'] =\
+            config.pki_master_dict['pki_tomcat_root'] + "/" + "bin"
+        config.pki_master_dict['pki_tomcat_lib_path'] =\
+            config.pki_master_dict['pki_tomcat_root'] + "/" + "lib"
+        if config.pki_master_dict['pki_subsystem'] == "CA":
+            config.pki_master_dict['pki_source_emails'] =\
+                config.pki_master_dict['pki_source_root'] + "/" +\
+                "ca" + "/" + "emails"
+            config.pki_master_dict['pki_source_profiles'] =\
+                config.pki_master_dict['pki_source_root'] + "/" +\
+                "ca" + "/" + "profiles"
+    # Instance layout base name/value pairs
+    config.pki_master_dict['pki_root_prefix'] = config.pki_root_prefix
+    config.pki_master_dict['pki_path'] =\
+        config.pki_master_dict['pki_root_prefix'] +\
+        config.pki_master_dict['pki_instance_root']
+    config.pki_master_dict['pki_instance_path'] =\
+        config.pki_master_dict['pki_path'] + "/" +\
+        config.pki_master_dict['pki_instance_name']
+    config.pki_master_dict['pki_instance_database_link'] =\
+        config.pki_master_dict['pki_instance_path'] + "/" + "alias"
+    # Instance layout log name/value pairs
+    config.pki_master_dict['pki_log_path'] =\
+        config.pki_master_dict['pki_root_prefix'] +\
+        config.pki_master_dict['pki_instance_log_root']
+    config.pki_master_dict['pki_instance_log_path'] =\
+        config.pki_master_dict['pki_log_path'] + "/" +\
+        config.pki_master_dict['pki_instance_name']
+    # Instance layout configuration name/value pairs
+    config.pki_master_dict['pki_configuration_path'] =\
+        config.pki_master_dict['pki_root_prefix'] +\
+        config.pki_master_dict['pki_instance_configuration_root']
+    config.pki_master_dict['pki_instance_configuration_path'] =\
+        config.pki_master_dict['pki_configuration_path'] + "/" +\
+        config.pki_master_dict['pki_instance_name']
+    # Instance layout registry name/value pairs
+    config.pki_master_dict['pki_registry_path'] =\
+        config.pki_master_dict['pki_root_prefix'] +\
+        config.PKI_DEPLOYMENT_REGISTRY_ROOT
+    config.pki_master_dict['pki_instance_registry_path'] =\
+        config.pki_master_dict['pki_registry_path'] + "/" +\
+        config.pki_master_dict['pki_instance_name']
+    # Instance-based webserver Apache base name/value pairs
+    if config.pki_master_dict['pki_subsystem'] in config.PKI_APACHE_SUBSYSTEMS:
+        config.pki_master_dict['pki_apache_path'] =\
+            config.pki_master_dict['pki_instance_path'] + "/apache"
+    # Instance-based webserver Tomcat base name/value pairs
+    if config.pki_master_dict['pki_subsystem'] in config.PKI_TOMCAT_SUBSYSTEMS:
+        config.pki_master_dict['pki_tomcat_path'] =\
+            config.pki_master_dict['pki_instance_path'] + "/" + "tomcat"
+        config.pki_master_dict['pki_tomcat_bin_link'] =\
+            config.pki_master_dict['pki_tomcat_path'] + "/" + "bin"
+        config.pki_master_dict['pki_common_path'] =\
+            config.pki_master_dict['pki_tomcat_path'] + "/" + "common"
+        config.pki_master_dict['pki_common_lib_path'] =\
+            config.pki_master_dict['pki_common_path'] + "/" + "lib"
+        config.pki_master_dict['pki_conf_path'] =\
+            config.pki_master_dict['pki_tomcat_path'] + "/" + "conf"
+        config.pki_master_dict['pki_tomcat_lib_link'] =\
+            config.pki_master_dict['pki_tomcat_path'] + "/" + "lib"
+        config.pki_master_dict['pki_tomcat_logs_link'] =\
+            config.pki_master_dict['pki_tomcat_path'] + "/" + "logs"
+        config.pki_master_dict['pki_webapps_path'] =\
+            config.pki_master_dict['pki_tomcat_path'] + "/" + "webapps"
+        config.pki_master_dict['pki_webapps_root_path'] =\
+            config.pki_master_dict['pki_webapps_path'] + "/" + "ROOT"
+        config.pki_master_dict['pki_webapps_root_webinf_path'] =\
+            config.pki_master_dict['pki_webapps_root_path'] + "/" + "WEB-INF"
+        config.pki_master_dict['pki_webapps_webinf_path'] =\
+            config.pki_master_dict['pki_webapps_path'] + "/" + "WEB-INF"
+        config.pki_master_dict['pki_webapps_webinf_classes_path'] =\
+            config.pki_master_dict['pki_webapps_webinf_path'] + "/" + "classes"
+        config.pki_master_dict['pki_webapps_webinf_lib_path'] =\
+            config.pki_master_dict['pki_webapps_webinf_path'] + "/" + "lib"
+        config.pki_master_dict['pki_webapps_subsystem_path'] =\
+            config.pki_master_dict['pki_webapps_path'] + "/" +\
+            config.pki_master_dict['pki_subsystem'].lower()
+        config.pki_master_dict['pki_webapps_subsystem_webinf_classes_link'] =\
+            config.pki_master_dict['pki_webapps_subsystem_path'] + "/" +\
+            "WEB-INF" + "/" + "classes"
+        config.pki_master_dict['pki_webapps_subsystem_webinf_lib_link'] =\
+            config.pki_master_dict['pki_webapps_subsystem_path'] + "/" +\
+            "WEB-INF" + "/" + "lib"
+    # Instance-based webserver Apache/Tomcat configuration name/value pairs
+    config.pki_master_dict['pki_database_path'] =\
+        config.pki_master_dict['pki_instance_configuration_path'] + "/" +\
+        "alias"
+    # Instance-based subsystem base name/value pairs
+    config.pki_master_dict['pki_subsystem_path'] =\
+        config.pki_master_dict['pki_instance_path'] + "/" +\
+        config.pki_master_dict['pki_subsystem'].lower()
+    config.pki_master_dict['pki_subsystem_database_link'] =\
+        config.pki_master_dict['pki_subsystem_path'] + "/" + "alias"
+    config.pki_master_dict['pki_subsystem_configuration_link'] =\
+        config.pki_master_dict['pki_subsystem_path'] + "/" + "conf"
+    config.pki_master_dict['pki_subsystem_logs_link'] =\
+        config.pki_master_dict['pki_subsystem_path'] + "/" + "logs"
+    if config.pki_master_dict['pki_subsystem'] in config.PKI_TOMCAT_SUBSYSTEMS:
+        if config.pki_master_dict['pki_subsystem'] == "CA":
+            config.pki_master_dict['pki_subsystem_emails_path'] =\
+                config.pki_master_dict['pki_subsystem_path'] + "/" + "emails"
+            config.pki_master_dict['pki_subsystem_profiles_path'] =\
+                config.pki_master_dict['pki_subsystem_path'] + "/" + "profiles"
+        config.pki_master_dict['pki_subsystem_webapps_link'] =\
+            config.pki_master_dict['pki_subsystem_path'] + "/" + "webapps"
+    # Instance-based subsystem log name/value pairs
+    config.pki_master_dict['pki_subsystem_log_path'] =\
+        config.pki_master_dict['pki_instance_log_path'] + "/" +\
+        config.pki_master_dict['pki_subsystem'].lower()
+    config.pki_master_dict['pki_subsystem_signed_audit_log_path'] =\
+        config.pki_master_dict['pki_subsystem_log_path'] + "/" +\
+        "signedAudit"
+    # Instance-based subsystem configuration name/value pairs
+    config.pki_master_dict['pki_subsystem_configuration_path'] =\
+        config.pki_master_dict['pki_instance_configuration_path'] + "/" +\
+        config.pki_master_dict['pki_subsystem'].lower()
+    # Instance-based subsystem registry name/value pairs
+    config.pki_master_dict['pki_subsystem_registry_path'] =\
+        config.pki_master_dict['pki_instance_registry_path'] + "/" +\
+        config.pki_master_dict['pki_subsystem'].lower()
+    return
diff --git a/base/deploy/src/scriptlets/pkiscriptlet.py b/base/deploy/src/scriptlets/pkiscriptlet.py
index 5befd99..6f29e2c 100644
--- a/base/deploy/src/scriptlets/pkiscriptlet.py
+++ b/base/deploy/src/scriptlets/pkiscriptlet.py
@@ -15,7 +15,7 @@
 # with this program; if not, write to the Free Software Foundation, Inc.,
 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 #
-# Copyright (C) 2011 Red Hat, Inc.
+# Copyright (C) 2012 Red Hat, Inc.
 # All rights reserved.
 #
 
@@ -44,4 +44,3 @@ class AbstractBasePkiScriptlet(object):
         """Retrieve data from the specified PKI dictionary and
            use it to destroy an existing PKI instance."""
         return
-
diff --git a/base/deploy/src/scriptlets/security_databases.py b/base/deploy/src/scriptlets/security_databases.py
index af47cbd..093e5ec 100644
--- a/base/deploy/src/scriptlets/security_databases.py
+++ b/base/deploy/src/scriptlets/security_databases.py
@@ -15,15 +15,13 @@
 # with this program; if not, write to the Free Software Foundation, Inc.,
 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 #
-# Copyright (C) 2011 Red Hat, Inc.
+# Copyright (C) 2012 Red Hat, Inc.
 # All rights reserved.
 #
 
-# System Imports
-import os
-
 # PKI Deployment Imports
 import pkiconfig as config
+from pkiconfig import pki_master_dict as master
 import pkimessages as log
 import pkiscriptlet
 
@@ -31,48 +29,24 @@ import pkiscriptlet
 # PKI Deployment Security Database Classes
 class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
     rv = 0
-    pki_path = config.pki_root_prefix +\
-               config.pki_master_dict['pki_instance_root']
-    pki_instance_path = pki_path + "/" +\
-                        config.pki_master_dict['pki_instance_name']
-    pki_subsystem_path = pki_instance_path + "/" +\
-                         config.pki_master_dict['pki_subsystem'].lower()
-    pki_database_path = pki_subsystem_path + "/" +\
-                        config.PKI_SECURITY_DATABASE_DIR
 
     def spawn(self):
-        if not os.path.exists(self.pki_database_path):
-            config.pki_log.info(log.SECURITY_DATABASES_SPAWN_1, __name__)
-        elif not os.path.isdir(self.pki_database_path):
-            config.pki_log.error(
-                log.PKI_DIRECTORY_ALREADY_EXISTS_NOT_A_DIRECTORY_1,
-                self.pki_database_path)
-            self.rv = -1
-        else:
-            config.pki_log.error(log.PKI_DIRECTORY_ALREADY_EXISTS_1,
-                                 self.pki_database_path)
-            self.rv = -1
+        config.pki_log.info(log.SECURITY_DATABASES_SPAWN_1, __name__,
+                            extra=config.PKI_INDENTATION_LEVEL_1)
+        config.pki_log.info("NOT YET IMPLEMENTED",
+                            extra=config.PKI_INDENTATION_LEVEL_2)
         return self.rv
 
     def respawn(self):
-        if not os.path.exists(self.pki_database_path) or\
-           not os.path.isdir(self.pki_database_path):
-            config.pki_log.error(
-                log.PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1,
-                self.pki_database_path)
-            self.rv = -1
-        else:
-            config.pki_log.info(log.SECURITY_DATABASES_RESPAWN_1, __name__)
+        config.pki_log.info(log.SECURITY_DATABASES_RESPAWN_1, __name__,
+                            extra=config.PKI_INDENTATION_LEVEL_1)
+        config.pki_log.info("NOT YET IMPLEMENTED",
+                            extra=config.PKI_INDENTATION_LEVEL_2)
         return self.rv
 
     def destroy(self):
-        if not os.path.exists(self.pki_database_path) or\
-           not os.path.isdir(self.pki_database_path):
-            config.pki_log.error(
-                log.PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1,
-                self.pki_database_path)
-            self.rv = -1
-        else:
-            config.pki_log.info(log.SECURITY_DATABASES_DESTROY_1, __name__)
+        config.pki_log.info(log.SECURITY_DATABASES_DESTROY_1, __name__,
+                            extra=config.PKI_INDENTATION_LEVEL_1)
+        config.pki_log.info("NOT YET IMPLEMENTED",
+                            extra=config.PKI_INDENTATION_LEVEL_2)
         return self.rv
-
diff --git a/base/deploy/src/scriptlets/slot_assignment.py b/base/deploy/src/scriptlets/slot_assignment.py
new file mode 100644
index 0000000..6b23ea6
--- /dev/null
+++ b/base/deploy/src/scriptlets/slot_assignment.py
@@ -0,0 +1,52 @@
+#!/usr/bin/python -t
+# Authors:
+#     Matthew Harmsen <mharmsen@redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2012 Red Hat, Inc.
+# All rights reserved.
+#
+
+# PKI Deployment Imports
+import pkiconfig as config
+from pkiconfig import pki_master_dict as master
+import pkimessages as log
+import pkiscriptlet
+
+
+# PKI Deployment Instance Population Classes
+class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
+    rv = 0
+
+    def spawn(self):
+        config.pki_log.info(log.SLOT_ASSIGNMENT_SPAWN_1, __name__,
+                            extra=config.PKI_INDENTATION_LEVEL_1)
+        config.pki_log.info("NOT YET IMPLEMENTED",
+                            extra=config.PKI_INDENTATION_LEVEL_2)
+        return self.rv
+
+    def respawn(self):
+        config.pki_log.info(log.SLOT_ASSIGNMENT_RESPAWN_1, __name__,
+                            extra=config.PKI_INDENTATION_LEVEL_1)
+        config.pki_log.info("NOT YET IMPLEMENTED",
+                            extra=config.PKI_INDENTATION_LEVEL_2)
+        return self.rv
+
+    def destroy(self):
+        config.pki_log.info(log.SLOT_ASSIGNMENT_DESTROY_1, __name__,
+                            extra=config.PKI_INDENTATION_LEVEL_1)
+        config.pki_log.info("NOT YET IMPLEMENTED",
+                            extra=config.PKI_INDENTATION_LEVEL_2)
+        return self.rv
diff --git a/base/deploy/src/scriptlets/subsystem_layout.py b/base/deploy/src/scriptlets/subsystem_layout.py
new file mode 100644
index 0000000..b71fe39
--- /dev/null
+++ b/base/deploy/src/scriptlets/subsystem_layout.py
@@ -0,0 +1,107 @@
+#!/usr/bin/python -t
+# Authors:
+#     Matthew Harmsen <mharmsen@redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2012 Red Hat, Inc.
+# All rights reserved.
+#
+
+# PKI Deployment Imports
+import pkiconfig as config
+from pkiconfig import pki_master_dict as master
+import pkihelper as util
+import pkimessages as log
+import pkiscriptlet
+
+
+# PKI Deployment Instance Population Classes
+class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
+    rv = 0
+
+    def spawn(self):
+        config.pki_log.info(log.SUBSYSTEM_SPAWN_1, __name__,
+                            extra=config.PKI_INDENTATION_LEVEL_1)
+        # establish instance-based subsystem base
+        util.directory.create(master['pki_subsystem_path'])
+        if master['pki_subsystem'] == "CA":
+            util.directory.copy(master['pki_source_emails'],
+                                master['pki_subsystem_emails_path'])
+            util.directory.copy(master['pki_source_profiles'],
+                                master['pki_subsystem_profiles_path'])
+        # establish instance-based subsystem logs
+        util.directory.create(master['pki_subsystem_log_path'])
+        if master['pki_subsystem'] in config.PKI_SIGNED_AUDIT_SUBSYSTEMS:
+            util.directory.create(master['pki_subsystem_signed_audit_log_path'])
+        # establish instance-based subsystem configuration
+        util.directory.copy(master['pki_source_conf'],
+                            master['pki_subsystem_configuration_path'])
+        # establish instance-based subsystem registry
+        util.directory.create(master['pki_subsystem_registry_path'])
+        # establish convenience symbolic links
+        util.symlink.create(master['pki_database_path'],
+                            master['pki_subsystem_database_link'])
+        util.symlink.create(master['pki_subsystem_configuration_path'],
+                            master['pki_subsystem_configuration_link'])
+        util.symlink.create(master['pki_subsystem_log_path'],
+                            master['pki_subsystem_logs_link'])
+        util.symlink.create(master['pki_webapps_path'],
+                            master['pki_subsystem_webapps_link'])
+        return self.rv
+
+    def respawn(self):
+        config.pki_log.info(log.SUBSYSTEM_RESPAWN_1, __name__,
+                            extra=config.PKI_INDENTATION_LEVEL_1)
+        # update instance-based subsystem base
+        util.directory.modify(master['pki_subsystem_path'])
+        if master['pki_subsystem'] == "CA":
+            util.directory.copy(master['pki_source_emails'],
+                                master['pki_subsystem_emails_path'],
+                                overwrite_flag=True)
+            util.directory.copy(master['pki_source_profiles'],
+                                master['pki_subsystem_profiles_path'],
+                                overwrite_flag=True)
+        # update instance-based subsystem logs
+        util.directory.modify(master['pki_subsystem_log_path'])
+        if master['pki_subsystem'] in config.PKI_SIGNED_AUDIT_SUBSYSTEMS:
+            util.directory.modify(master['pki_subsystem_signed_audit_log_path'])
+        # update instance-based subsystem configuration
+        util.directory.copy(master['pki_source_conf'],
+                            master['pki_subsystem_configuration_path'],
+                            overwrite_flag=True)
+        # update instance-based subsystem registry
+        util.directory.modify(master['pki_subsystem_registry_path'])
+        # update convenience symbolic links
+        util.symlink.modify(master['pki_subsystem_database_link'])
+        util.symlink.modify(master['pki_subsystem_configuration_link'])
+        util.symlink.modify(master['pki_subsystem_logs_link'])
+        util.symlink.modify(master['pki_subsystem_webapps_link'])
+        return self.rv
+
+    def destroy(self):
+        config.pki_log.info(log.SUBSYSTEM_DESTROY_1, __name__,
+                            extra=config.PKI_INDENTATION_LEVEL_1)
+        # remove instance-based subsystem base
+        if master['pki_subsystem'] == "CA":
+            util.directory.delete(master['pki_subsystem_profiles_path'])
+            util.directory.delete(master['pki_subsystem_emails_path'])
+        util.directory.delete(master['pki_subsystem_path'])
+        # remove instance-based subsystem logs
+        util.directory.delete(master['pki_subsystem_log_path'])
+        # remove instance-based subsystem configuration
+        util.directory.delete(master['pki_subsystem_configuration_path'])
+        # remove instance-based subsystem registry
+        util.directory.delete(master['pki_subsystem_registry_path'])
+        return self.rv
diff --git a/base/deploy/src/scriptlets/war_explosion.py b/base/deploy/src/scriptlets/war_explosion.py
new file mode 100644
index 0000000..4f235d8
--- /dev/null
+++ b/base/deploy/src/scriptlets/war_explosion.py
@@ -0,0 +1,69 @@
+#!/usr/bin/python -t
+# Authors:
+#     Matthew Harmsen <mharmsen@redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2012 Red Hat, Inc.
+# All rights reserved.
+#
+
+# PKI Deployment Imports
+import pkiconfig as config
+from pkiconfig import pki_master_dict as master
+import pkihelper as util
+import pkimessages as log
+import pkiscriptlet
+
+
+# PKI Deployment Instance Population Classes
+class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
+    rv = 0
+
+    def spawn(self):
+        if master['pki_subsystem'] in config.PKI_TOMCAT_SUBSYSTEMS:
+            config.pki_log.info(log.WAR_EXPLOSION_SPAWN_1, __name__,
+                                extra=config.PKI_INDENTATION_LEVEL_1)
+            # deploy war file
+            util.directory.create(master['pki_webapps_subsystem_path'])
+            util.war.explode(master['pki_war'],
+                             master['pki_webapps_subsystem_path'])
+            # establish convenience symbolic links
+            util.symlink.create(master['pki_webapps_webinf_classes_path'],
+                master['pki_webapps_subsystem_webinf_classes_link'])
+            util.symlink.create(master['pki_webapps_webinf_lib_path'],
+                master['pki_webapps_subsystem_webinf_lib_link'])
+            # set ownerships, permissions, and acls
+            util.directory.set_mode(master['pki_webapps_subsystem_path'])
+        return self.rv
+
+    def respawn(self):
+        if master['pki_subsystem'] in config.PKI_TOMCAT_SUBSYSTEMS:
+            config.pki_log.info(log.WAR_EXPLOSION_RESPAWN_1, __name__,
+                                extra=config.PKI_INDENTATION_LEVEL_1)
+            # redeploy war file
+            util.directory.modify(master['pki_webapps_subsystem_path'])
+            util.war.explode(master['pki_war'],
+                             master['pki_webapps_subsystem_path'])
+            # update ownerships, permissions, and acls
+            # NOTE:  This includes existing convenience symbolic links
+            util.directory.set_mode(master['pki_webapps_subsystem_path'])
+        return self.rv
+
+    def destroy(self):
+        if master['pki_subsystem'] in config.PKI_TOMCAT_SUBSYSTEMS:
+            config.pki_log.info(log.WAR_EXPLOSION_DESTROY_1, __name__,
+                                extra=config.PKI_INDENTATION_LEVEL_1)
+            util.directory.delete(master['pki_webapps_subsystem_path'])
+        return self.rv
diff --git a/base/deploy/src/scriptlets/webserver_layout.py b/base/deploy/src/scriptlets/webserver_layout.py
new file mode 100644
index 0000000..9b3deb7
--- /dev/null
+++ b/base/deploy/src/scriptlets/webserver_layout.py
@@ -0,0 +1,119 @@
+#!/usr/bin/python -t
+# Authors:
+#     Matthew Harmsen <mharmsen@redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2012 Red Hat, Inc.
+# All rights reserved.
+#
+
+# PKI Deployment Imports
+import pkiconfig as config
+from pkiconfig import pki_master_dict as master
+import pkihelper as util
+import pkimessages as log
+import pkiscriptlet
+
+
+# PKI Deployment Instance Population Classes
+class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
+    rv = 0
+
+    def spawn(self):
+        config.pki_log.info(log.WEBSERVER_SPAWN_1, __name__,
+                            extra=config.PKI_INDENTATION_LEVEL_1)
+        # establish instance-based webserver base
+        if master['pki_subsystem'] in config.PKI_APACHE_SUBSYSTEMS:
+            util.directory.create(master['pki_apache_path'])
+        elif master['pki_subsystem'] in config.PKI_TOMCAT_SUBSYSTEMS:
+            util.directory.create(master['pki_tomcat_path'])
+            util.directory.create(master['pki_common_path'])
+            util.directory.create(master['pki_common_lib_path'])
+            util.directory.create(master['pki_conf_path'])
+            util.directory.create(master['pki_webapps_path'])
+            util.directory.create(master['pki_webapps_root_path'])
+            util.directory.create(master['pki_webapps_root_webinf_path'])
+            util.directory.create(master['pki_webapps_webinf_path'])
+            util.directory.create(master['pki_webapps_webinf_classes_path'])
+            util.directory.create(master['pki_webapps_webinf_lib_path'])
+        # establish instance-based webserver configuration
+        util.directory.create(master['pki_database_path'])
+        # establish convenience symbolic links
+        util.symlink.create(master['pki_database_path'],
+                            master['pki_instance_database_link'])
+        util.symlink.create(master['pki_tomcat_bin_path'],
+                            master['pki_tomcat_bin_link'])
+        util.symlink.create(master['pki_tomcat_lib_path'],
+                            master['pki_tomcat_lib_link'])
+        util.symlink.create(master['pki_instance_log_path'],
+                            master['pki_tomcat_logs_link'])
+        return self.rv
+
+    def respawn(self):
+        config.pki_log.info(log.WEBSERVER_RESPAWN_1, __name__,
+                            extra=config.PKI_INDENTATION_LEVEL_1)
+        # update instance-based webserver base
+        if master['pki_subsystem'] in config.PKI_APACHE_SUBSYSTEMS:
+            util.directory.modify(master['pki_apache_path'])
+        elif master['pki_subsystem'] in config.PKI_TOMCAT_SUBSYSTEMS:
+            util.directory.modify(master['pki_tomcat_path'])
+            util.directory.modify(master['pki_common_path'])
+            util.directory.modify(master['pki_common_lib_path'])
+            util.directory.modify(master['pki_conf_path'])
+            util.directory.modify(master['pki_webapps_path'])
+            util.directory.modify(master['pki_webapps_root_path'])
+            util.directory.modify(master['pki_webapps_root_webinf_path'])
+            util.directory.modify(master['pki_webapps_webinf_path'])
+            util.directory.modify(master['pki_webapps_webinf_classes_path'])
+            util.directory.modify(master['pki_webapps_webinf_lib_path'])
+        # update instance-based webserver configuration
+        util.directory.modify(master['pki_database_path'])
+        # update convenience symbolic links
+        util.symlink.modify(master['pki_instance_database_link'])
+        util.symlink.modify(master['pki_tomcat_bin_link'])
+        util.symlink.modify(master['pki_tomcat_lib_link'])
+        util.symlink.modify(master['pki_tomcat_logs_link'])
+        return self.rv
+
+    def destroy(self):
+        config.pki_log.info(log.WEBSERVER_DESTROY_1, __name__,
+                            extra=config.PKI_INDENTATION_LEVEL_1)
+        # remove instance-based webserver base
+        if not config.pki_dry_run_flag and\
+           master['pki_subsystem'] in config.PKI_APACHE_SUBSYSTEMS and\
+           util.instance.apache_instances(master['pki_instance_path']) == 0:
+            util.directory.delete(master['pki_apache_path'])
+        elif master['pki_subsystem'] in config.PKI_APACHE_SUBSYSTEMS and\
+             util.instance.apache_instances(master['pki_instance_path']) == 1:
+            # always display correct information (even during dry_run)
+            util.directory.delete(master['pki_apache_path'])
+        if not config.pki_dry_run_flag and\
+           master['pki_subsystem'] in config.PKI_TOMCAT_SUBSYSTEMS and\
+           util.instance.tomcat_instances(master['pki_instance_path']) == 0:
+            util.directory.delete(master['pki_tomcat_path'])
+        elif master['pki_subsystem'] in config.PKI_TOMCAT_SUBSYSTEMS and\
+             util.instance.tomcat_instances(master['pki_instance_path']) == 1:
+            # always display correct information (even during dry_run)
+            util.directory.delete(master['pki_tomcat_path'])
+        # remove instance-based webserver configuration
+        if not config.pki_dry_run_flag and\
+           util.instance.pki_subsystem_instances(\
+               master['pki_instance_path']) == 0:
+            util.directory.delete(master['pki_database_path'])
+        elif util.instance.pki_subsystem_instances(\
+            master['pki_instance_path']) == 1:
+            # always display correct information (even during dry_run)
+            util.directory.delete(master['pki_database_path'])
+        return self.rv
diff --git a/scripts/build_dogtag_pki b/scripts/build_dogtag_pki
index f2c171f..6f44dd7 100755
--- a/scripts/build_dogtag_pki
+++ b/scripts/build_dogtag_pki
@@ -127,6 +127,7 @@ PKI_TPS_THEME=${PKI_FLAVOR}-pki-tps-theme${RPM_EXT}
 PKI_CONSOLE_THEME=${PKI_FLAVOR}-pki-console-theme${RPM_EXT}
 
 # Establish PKI core package names
+PKI_DEPLOY=pki-deploy${RPM_EXT}
 PKI_SETUP=pki-setup${RPM_EXT}
 PKI_SYMKEY=pki-symkey${RPM_EXT}
 PKI_NATIVE_TOOLS=pki-native-tools${RPM_EXT}
@@ -175,7 +176,7 @@ cd ${PKI_CORE_PACKAGES_DIR}/${RPM_DIR}
 mkdir -p ${PKI_CORE_PACKAGES_DIR}/${RPM_DIR}/${COMBINED}
 cp -p ${NOARCH}/*.rpm ${PKI_ARCH}/*.rpm ${PKI_CORE_PACKAGES_DIR}/${RPM_DIR}/${COMBINED}
 cd ${PKI_CORE_PACKAGES_DIR}/${RPM_DIR}/${COMBINED}
-${PKI_SUDO} ${YUM_EXE} ${YUM_EXE_OPTIONS} ${PKI_SETUP} ${PKI_SYMKEY} ${PKI_NATIVE_TOOLS} ${PKI_UTIL} ${PKI_UTIL_JAVADOC} ${PKI_JAVA_TOOLS} ${PKI_JAVA_TOOLS_JAVADOC} ${PKI_COMMON} ${PKI_COMMON_JAVADOC} ${PKI_SELINUX} ${PKI_CA} ${PKI_SILENT}
+${PKI_SUDO} ${YUM_EXE} ${YUM_EXE_OPTIONS} ${PKI_DEPLOY} ${PKI_SETUP} ${PKI_SYMKEY} ${PKI_NATIVE_TOOLS} ${PKI_UTIL} ${PKI_UTIL_JAVADOC} ${PKI_JAVA_TOOLS} ${PKI_JAVA_TOOLS_JAVADOC} ${PKI_COMMON} ${PKI_COMMON_JAVADOC} ${PKI_SELINUX} ${PKI_CA} ${PKI_KRA} ${PKI_OCSP} ${PKI_TKS} ${PKI_SILENT}
 
 # Compose and install 'pki-ra' packages
 cd ${PKI_PWD}
diff --git a/specs/pki-core.spec b/specs/pki-core.spec
index 837557d..a38b904 100644
--- a/specs/pki-core.spec
+++ b/specs/pki-core.spec
@@ -63,6 +63,12 @@ BuildRequires:    jss >= 4.2.6-17
 BuildRequires:    tomcatjss >= 2.0.0
 %endif
 %endif
+# Add the following build-time requirements to support the "pki-deploy" package
+BuildRequires:    pki-common-theme
+BuildRequires:    pki-ca-theme
+BuildRequires:    pki-kra-theme
+BuildRequires:    pki-ocsp-theme
+BuildRequires:    pki-tks-theme
 
 Source0:          http://pki.fedoraproject.org/pki/sources/%{name}/%{name}-%{version}%{?prerel}.tar.gz
 
@@ -761,8 +767,8 @@ echo "D /var/run/pki/tks 0755 root root -"  >> %{buildroot}%{_sysconfdir}/tmpfil
 %{__rm} %{buildroot}%{_initrddir}/pki-ocspd
 %{__rm} %{buildroot}%{_initrddir}/pki-tksd
 # Create symlink to the pki-jndi-realm jar
-mkdir -p %{buildroot}%{_javadir}/tomcat6
-ln -s -f %{_javadir}/pki/pki-jndi-realm.jar %{buildroot}%{_javadir}/tomcat6/pki-jndi-realm.jar
+%{__mkdir_p} %{buildroot}%{_javadir}/tomcat6
+%{__ln_s} -f %{_javadir}/pki/pki-jndi-realm.jar %{buildroot}%{_javadir}/tomcat6/pki-jndi-realm.jar
 %else
 %{__rm} %{buildroot}%{_bindir}/pkicontrol
 %{__rm} -rf %{buildroot}%{_sysconfdir}/systemd/system/pki-cad.target.wants
@@ -786,6 +792,21 @@ if [ -d /etc/sysconfig/pki/%i ]; then                                        \
 fi                                                                           \
 )
 
+# Create PKI subsystem "war" files
+for subsystem in ca kra ocsp tks; do
+    echo "Constructing '${subsystem}.war' . . ."
+    %{__mkdir_p} %{buildroot}%{_datadir}/pki/${subsystem}/war
+    %{__cp} -r %{_datadir}/pki/common-ui/admin %{buildroot}%{_datadir}/pki/${subsystem}/war
+    %{__cp} -r %{_datadir}/pki/common-ui/css %{buildroot}%{_datadir}/pki/${subsystem}/war
+    %{__cp} -r %{_datadir}/pki/common-ui/img %{buildroot}%{_datadir}/pki/${subsystem}/war
+    %{__cp} -r %{_datadir}/pki/${subsystem}-ui/webapps/${subsystem}/* %{buildroot}%{_datadir}/pki/${subsystem}/war
+    %{__cp} -r %{buildroot}%{_datadir}/pki/${subsystem}/webapps/${subsystem}/WEB-INF %{buildroot}%{_datadir}/pki/${subsystem}/war
+    cd %{buildroot}%{_datadir}/pki/${subsystem}/war
+    jar -cvMf ../${subsystem}.war *
+    %{__rm} -rf %{buildroot}%{_datadir}/pki/${subsystem}/war/*
+    %{__mv} ../${subsystem}.war %{buildroot}%{_datadir}/pki/${subsystem}/war
+done
+
 %pre -n pki-selinux
 %saveFileContext targeted
 
@@ -1216,8 +1237,9 @@ fi
 %{_datadir}/pki/ca/emails/
 %dir %{_datadir}/pki/ca/profiles
 %{_datadir}/pki/ca/profiles/ca/
-%{_datadir}/pki/ca/webapps/
 %{_datadir}/pki/ca/setup/
+%{_datadir}/pki/ca/war/
+%{_datadir}/pki/ca/webapps/
 %dir %{_localstatedir}/lock/pki/ca
 %dir %{_localstatedir}/run/pki/ca
 %if 0%{?fedora} >= 15
@@ -1245,6 +1267,7 @@ fi
 %dir %{_datadir}/pki/kra
 %{_datadir}/pki/kra/conf/
 %{_datadir}/pki/kra/setup/
+%{_datadir}/pki/kra/war/
 %{_datadir}/pki/kra/webapps/
 %dir %{_localstatedir}/lock/pki/kra
 %dir %{_localstatedir}/run/pki/kra
@@ -1273,6 +1296,7 @@ fi
 %dir %{_datadir}/pki/ocsp
 %{_datadir}/pki/ocsp/conf/
 %{_datadir}/pki/ocsp/setup/
+%{_datadir}/pki/ocsp/war/
 %{_datadir}/pki/ocsp/webapps/
 %dir %{_localstatedir}/lock/pki/ocsp
 %dir %{_localstatedir}/run/pki/ocsp
@@ -1301,6 +1325,7 @@ fi
 %dir %{_datadir}/pki/tks
 %{_datadir}/pki/tks/conf/
 %{_datadir}/pki/tks/setup/
+%{_datadir}/pki/tks/war/
 %{_datadir}/pki/tks/webapps/
 %dir %{_localstatedir}/lock/pki/tks
 %dir %{_localstatedir}/run/pki/tks
-- 
1.7.7.6

