From 04356549e94dacc57fd2db85a86a052906e2d90e Mon Sep 17 00:00:00 2001
From: Matthew Harmsen <mharmsen@redhat.com>
Date: Wed, 11 Apr 2012 18:25:36 -0700
Subject: [PATCH] Created first three 'scriptlets': * 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) 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.

---
 base/deploy/CMakeLists.txt                       |   83 +++-
 base/deploy/config/pkideployment.cfg             |    3 +
 base/deploy/src/pkidestroy                       |   91 +++--
 base/deploy/src/pkispawn                         |  127 ++++--
 base/deploy/src/scriptlets/instance.py           |  105 ----
 base/deploy/src/scriptlets/instance_layout.py    |  124 +++++
 base/deploy/src/scriptlets/pkiconfig.py          |   41 ++-
 base/deploy/src/scriptlets/pkihelper.py          |  550 ++++++++++++++--------
 base/deploy/src/scriptlets/pkilogging.py         |   27 +-
 base/deploy/src/scriptlets/pkimanifest.py        |   90 ++++
 base/deploy/src/scriptlets/pkimessages.py        |   72 ++-
 base/deploy/src/scriptlets/pkiparser.py          |  223 +++++++++
 base/deploy/src/scriptlets/security_databases.py |   21 +-
 base/deploy/src/scriptlets/subsystem_layout.py   |  106 +++++
 base/deploy/src/scriptlets/webserver_layout.py   |  135 ++++++
 15 files changed, 1385 insertions(+), 413 deletions(-)
 create mode 100644 base/deploy/src/scriptlets/convenience_symlinks.py
 create mode 100644 base/deploy/src/scriptlets/explode_war.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/subsystem_layout.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..975b32e 100644
--- a/base/deploy/CMakeLists.txt
+++ b/base/deploy/CMakeLists.txt
@@ -34,13 +34,19 @@ execute_process(
 )
 install(
     FILES
-        src/scriptlets/instance.py
+        src/scriptlets/convenience_symlinks.py
+        src/scriptlets/explode_war.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/subsystem_layout.py
+        src/scriptlets/webserver_layout.py
     DESTINATION
         ${PYTHON_SITE_PACKAGES}/pki/deployment
     PERMISSIONS
@@ -84,22 +90,36 @@ install(CODE "file(MAKE_DIRECTORY \$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/d
 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/instance_layout.py\"
+    \"\$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/spawn/ca/0010_instance_layout\")"
 )
-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\")"
+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/ca/0020_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/ca/0030_subsystem_layout\")"
+)
+#install(CODE "execute_process(COMMAND
+#    ${CMAKE_COMMAND} -E create_symlink
+#    \"${PYTHON_SITE_PACKAGES}/pki/deployment/explode_war.py\"
+#    \"\$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/spawn/ca/0040_explode_war\")"
+#)
+#install(CODE "execute_process(COMMAND
+#    ${CMAKE_COMMAND} -E create_symlink
+#    \"${PYTHON_SITE_PACKAGES}/pki/deployment/convenience_symlinks.py\"
+#    \"\$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/spawn/ca/0050_convenience_symlinks\")"
+#)
+#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/0060_security_databases\")"
+#)
 
 # generate and install symbolic links for pkispawn KRA
 
@@ -113,6 +133,37 @@ install(
 
 
 # generate and install symbolic links for pkidestroy CA
+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/ca/9990_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/destroy/ca/9980_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/destroy/ca/9970_subsystem_layout\")"
+)
+#install(CODE "execute_process(COMMAND
+#    ${CMAKE_COMMAND} -E create_symlink
+#    \"${PYTHON_SITE_PACKAGES}/pki/deployment/explode_war.py\"
+#    \"\$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/destroy/ca/9960_explode_war\")"
+#)
+#install(CODE "execute_process(COMMAND
+#    ${CMAKE_COMMAND} -E create_symlink
+#    \"${PYTHON_SITE_PACKAGES}/pki/deployment/convenience_symlinks.py\"
+#    \"\$ENV{DESTDIR}${DATA_INSTALL_DIR}/deployment/destroy/ca/9950_convenience_symlinks\")"
+#)
+#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/ca/9940_security_databases\")"
+#)
+
 
 # generate and install symbolic links for pkidestroy KRA
 
diff --git a/base/deploy/config/pkideployment.cfg b/base/deploy/config/pkideployment.cfg
index db9ce54..3ac60cd 100644
--- a/base/deploy/config/pkideployment.cfg
+++ b/base/deploy/config/pkideployment.cfg
@@ -1,5 +1,8 @@
 [Common]
 pki_instance_root=/var/lib/pki
+pki_instance_configuration_root=/etc/pki
+pki_instance_log_root=/var/log/pki
+pki_instance_registry_root=/etc/sysconfig/pki
 pki_instance_name=instance
 pki_user=pkiuser
 pki_group=pkiuser
diff --git a/base/deploy/src/pkidestroy b/base/deploy/src/pkidestroy
index a762e14..f58ea5e 100755
--- a/base/deploy/src/pkidestroy
+++ b/base/deploy/src/pkidestroy
@@ -30,8 +30,9 @@ try:
     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,18 +53,27 @@ def main(argv):
     if not os.geteuid() == 0:
         sys.exit("'%s' must be run as root!" % argv[0])
 
+    # Generate a timestamp
+    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 +\
+                             config.PKIDESTROY_LOG_PATH
+        config.pki_log_name = config.PKIDESTROY_LOG_PREFIX +\
+                              config.pki_subsystem.lower() +\
+                              config.PKIDESTROY_LOG_SUFFIX + "." +\
+                              timestamp + "." +\
+                              config.PKI_DEPLOYMENT_LOG_EXTENSION
+    else:
+        config.pki_log_dir = config.PKI_DEPLOYMENT_DEFAULT_LOG_PATH
+        config.pki_log_name = config.PKI_DEPLOYMENT_DEFAULT_LOG_NAME
     rv = pkilogging.enable_pki_logger(config.pki_log_dir,
                                       config.pki_log_name,
                                       config.pki_log_level,
@@ -76,17 +86,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,26 +118,34 @@ 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.create_pki_master_dictionary()
+    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 = config.PKIDESTROY_PATH +\
                           "/" + 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()
@@ -129,7 +154,8 @@ def main(argv):
     rv = 0
     config.pki_log.info(log.PKIDESTROY_BEGIN_MESSAGE_2,
                         config.pki_subsystem,
-                        config.pki_master_dict['pki_instance_name'])
+                        config.pki_master_dict['pki_instance_name'],
+                        extra=config.PKI_INDENTATION_LEVEL_0)
     for pki_scriptlet in pki_subsystem_scriptlets:
         scriptlet = __import__(config.PKI_DEPLOYMENT_SCRIPTLETS_MODULE +\
                                '.' + pki_scriptlet[5:],
@@ -140,9 +166,12 @@ def main(argv):
             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_master_dict['pki_instance_name'],
+                        extra=config.PKI_INDENTATION_LEVEL_0)
+    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
diff --git a/base/deploy/src/pkispawn b/base/deploy/src/pkispawn
index a48a475..8e99328 100755
--- a/base/deploy/src/pkispawn
+++ b/base/deploy/src/pkispawn
@@ -30,10 +30,12 @@ try:
     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
+    from pki.deployment import pkimanifest as manifest
 except ImportError:
     print >> sys.stderr, """\
 There was a problem importing one of the required Python modules. The
@@ -55,35 +57,52 @@ def main(argv):
     # Set the umask
     os.umask(config.PKI_DEPLOYMENT_DEFAULT_UMASK)
 
+    # Generate a timestamp
+    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:
+        action = config.PKISPAWN
         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 +\
+                                 config.PKISPAWN_LOG_PATH
+            config.pki_log_name = config.PKISPAWN_LOG_PREFIX +\
+                                  config.pki_subsystem.lower() +\
+                                  config.PKISPAWN_LOG_SUFFIX + "." +\
+                                  timestamp + "." +\
+                                  config.PKI_DEPLOYMENT_LOG_EXTENSION
+        else:
+            config.pki_log_dir = config.PKI_DEPLOYMENT_DEFAULT_LOG_PATH
+            config.pki_log_name = config.PKI_DEPLOYMENT_DEFAULT_LOG_NAME
         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)
     else:
+        action = config.PKIRESPAWN
         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 +\
+                                 config.PKIRESPAWN_LOG_PATH
+            config.pki_log_name = config.PKIRESPAWN_LOG_PREFIX +\
+                                  config.pki_subsystem.lower() +\
+                                  config.PKIRESPAWN_LOG_SUFFIX + "." +\
+                                  timestamp + "." +\
+                                  config.PKI_DEPLOYMENT_LOG_EXTENSION
+        else:
+            config.pki_log_dir = config.PKI_DEPLOYMENT_DEFAULT_LOG_PATH
+            config.pki_log_name = config.PKI_DEPLOYMENT_DEFAULT_LOG_NAME
         rv = pkilogging.enable_pki_logger(config.pki_log_dir,
                                           config.pki_log_name,
                                           config.pki_log_level,
@@ -96,17 +115,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,26 +147,34 @@ 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.create_pki_master_dictionary()
+    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 = config.PKISPAWN_PATH +\
                           "/" + 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()
@@ -149,7 +183,8 @@ def main(argv):
     rv = 0
     config.pki_log.info(pki_deployment_begin,
                         config.pki_subsystem,
-                        config.pki_master_dict['pki_instance_name'])
+                        config.pki_master_dict['pki_instance_name'],
+                        extra=config.PKI_INDENTATION_LEVEL_0)
     for pki_scriptlet in pki_subsystem_scriptlets:
         scriptlet = __import__(config.PKI_DEPLOYMENT_SCRIPTLETS_MODULE +\
                                '.' + pki_scriptlet[5:],
@@ -161,11 +196,31 @@ def main(argv):
             rv = instance.respawn()
         if rv != 0:
             sys.exit(1)
+
+    # Save a timestamped copy of the installation/update manifest file
+    filename = config.pki_root_prefix +\
+               config.pki_master_dict['pki_instance_registry_root'] + "/" +\
+               config.pki_master_dict['pki_instance_name'] + "/" +\
+               config.pki_master_dict['pki_subsystem'].lower() +"/" +\
+               action + "_" + config.PKI_MANIFEST + "." +\
+               timestamp + "." + config.PKI_DEPLOYMENT_CSV_EXTENSION
+    config.pki_log.info(log.PKI_MANIFEST_MESSAGE_1, filename,
+                        extra=config.PKI_INDENTATION_LEVEL_1)
+    # for record in manifest.database:
+    #     print tuple(record)
+    if not config.pki_dry_run_flag:
+        manifest.file.register(filename)
+        manifest.file.write()
+
+    # Log final process messages
     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_master_dict['pki_instance_name'],
+                        extra=config.PKI_INDENTATION_LEVEL_0)
+    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
diff --git a/base/deploy/src/scriptlets/convenience_symlinks.py b/base/deploy/src/scriptlets/convenience_symlinks.py
new file mode 100644
index 0000000..e69de29
diff --git a/base/deploy/src/scriptlets/explode_war.py b/base/deploy/src/scriptlets/explode_war.py
new file mode 100644
index 0000000..e69de29
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..bcaf181
--- /dev/null
+++ b/base/deploy/src/scriptlets/instance_layout.py
@@ -0,0 +1,124 @@
+#!/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.
+#
+
+# PKI Deployment Imports
+import pkiconfig as config
+import pkihelper as util
+import pkimessages as log
+import pkiscriptlet
+
+
+# PKI Deployment Instance Population Classes
+class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
+    rv = 0
+
+    # instance base
+    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']
+
+    # instance logs
+    pki_log_path = config.pki_root_prefix +\
+        config.pki_master_dict['pki_instance_log_root']
+    pki_instance_log_path = pki_log_path + "/" +\
+        config.pki_master_dict['pki_instance_name']
+
+    # instance configuration
+    pki_configuration_path = config.pki_root_prefix +\
+        config.pki_master_dict['pki_instance_configuration_root']
+    pki_instance_configuration_path = pki_configuration_path + "/" +\
+        config.pki_master_dict['pki_instance_name']
+
+    # instance registry
+    pki_registry_path = config.pki_root_prefix +\
+        config.pki_master_dict['pki_instance_registry_root']
+    pki_instance_registry_path = pki_registry_path + "/" +\
+        config.pki_master_dict['pki_instance_name']
+
+    def spawn(self):
+        config.pki_log.info(log.INSTANCE_SPAWN_1, __name__,
+                            extra=config.PKI_INDENTATION_LEVEL_1)
+        # establish 'uid' and 'gid'
+        util.identity.set_uid(config.pki_master_dict['pki_user'])
+        util.identity.set_gid(config.pki_master_dict['pki_group'])
+        # establish instance base
+        util.directory.create(self.pki_path)
+        util.directory.create(self.pki_instance_path)
+        # establish instance logs
+        util.directory.create(self.pki_log_path)
+        util.directory.create(self.pki_instance_log_path)
+        # establish instance configuration
+        if self.pki_configuration_path != config.PKI_SHARED_CONFIGURATION_ROOT:
+            util.directory.create(self.pki_configuration_path)
+        util.directory.create(self.pki_instance_configuration_path)
+        # establish instance registry
+        util.directory.create(self.pki_registry_path)
+        util.directory.create(self.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)
+        # establish 'uid' and 'gid'
+        util.identity.set_uid(config.pki_master_dict['pki_user'])
+        util.identity.set_gid(config.pki_master_dict['pki_group'])
+        # update instance base
+        util.directory.modify(self.pki_path)
+        util.directory.modify(self.pki_instance_path)
+        # update instance logs
+        util.directory.modify(self.pki_log_path)
+        util.directory.modify(self.pki_instance_log_path)
+        # update instance configuration
+        if self.pki_configuration_path != config.PKI_SHARED_CONFIGURATION_ROOT:
+            util.directory.modify(self.pki_configuration_path)
+        util.directory.modify(self.pki_instance_configuration_path)
+        # update instance registry
+        util.directory.modify(self.pki_registry_path)
+        util.directory.modify(self.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(self.pki_instance_path):
+            util.directory.delete(self.pki_instance_path)
+        if util.directory.is_empty(self.pki_path):
+            util.directory.delete(self.pki_path)
+        # remove instance logs
+        if util.directory.is_empty(self.pki_instance_log_path):
+            util.directory.delete(self.pki_instance_log_path)
+        if util.directory.is_empty(self.pki_log_path):
+            util.directory.delete(self.pki_log_path)
+        # remove instance configuration
+        if util.directory.is_empty(self.pki_instance_configuration_path):
+            util.directory.delete(self.pki_instance_configuration_path)
+        if util.directory.is_empty(self.pki_configuration_path) and\
+           self.pki_configuration_path != config.PKI_SHARED_CONFIGURATION_ROOT:
+            util.directory.delete(self.pki_configuration_path)
+        # remove instance registry
+        if util.directory.is_empty(self.pki_instance_registry_path):
+            util.directory.delete(self.pki_instance_registry_path)
+        if util.directory.is_empty(self.pki_registry_path):
+            util.directory.delete(self.pki_registry_path)
+        return self.rv
+
diff --git a/base/deploy/src/scriptlets/pkiconfig.py b/base/deploy/src/scriptlets/pkiconfig.py
index 7d676c0..281c371 100644
--- a/base/deploy/src/scriptlets/pkiconfig.py
+++ b/base/deploy/src/scriptlets/pkiconfig.py
@@ -38,28 +38,63 @@ PKI_DEPLOYMENT_VERBOSITY=\
 PKI_DEPLOYMENT_DEFAULT_DIR_PERMISSIONS = 00770
 PKI_DEPLOYMENT_DEFAULT_EXE_PERMISSIONS = 00770
 PKI_DEPLOYMENT_DEFAULT_FILE_PERMISSIONS = 00660
+PKI_DEPLOYMENT_DEFAULT_SYMLINK_PERMISSIONS = 00777
 PKI_DEPLOYMENT_DEFAULT_UMASK = 00002
+PKI_DEPLOYMENT_DEFAULT_LOG_PATH = "/dev"
+PKI_DEPLOYMENT_DEFAULT_LOG_NAME = "null"
+PKI_DEPLOYMENT_CSV_EXTENSION = "csv"
+PKI_DEPLOYMENT_LOG_EXTENSION = "log"
+
+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' : '............... '}
 
 PKIDESTROY_PATH = PKI_DEPLOYMENT_PATH + "/" + "destroy"
 PKIDESTROY_LOG_PATH = "/var/log"
 PKIDESTROY_LOG_PREFIX = "pki-"
-PKIDESTROY_LOG_SUFFIX = "-destroy.log"
+PKIDESTROY_LOG_SUFFIX = "-destroy"
 PKIDESTROY_LOGGER = "pkidestroy"
 
 PKIRESPAWN_PATH = PKI_DEPLOYMENT_PATH + "/" + "spawn"
 PKIRESPAWN_LOG_PATH = "/var/log"
 PKIRESPAWN_LOG_PREFIX = "pki-"
-PKIRESPAWN_LOG_SUFFIX = "-respawn.log"
+PKIRESPAWN_LOG_SUFFIX = "-respawn"
 PKIRESPAWN_LOGGER = "pkirespawn"
+PKIRESPAWN = "respawn"
 
 PKISPAWN_PATH = PKI_DEPLOYMENT_PATH + "/" + "spawn"
 PKISPAWN_LOG_PATH = "/var/log"
 PKISPAWN_LOG_PREFIX = "pki-"
-PKISPAWN_LOG_SUFFIX = "-spawn.log"
+PKISPAWN_LOG_SUFFIX = "-spawn"
 PKISPAWN_LOGGER = "pkispawn"
+PKISPAWN = "spawn"
 
+PKI_APACHE = "apache"
+PKI_COMMON_DIR = "common"
+PKI_CONF_DIR = "conf"
+PKI_LIB_DIR = "lib"
+PKI_MANIFEST = "manifest"
 PKI_SECURITY_DATABASE_DIR = "alias"
+PKI_SHARED_CONFIGURATION_ROOT = "/etc/pki"
+PKI_SIGNED_AUDIT_DIR = "signedAudit"
+PKI_TOMCAT = "tomcat"
+PKI_TYPE_DIRECTORY = "directory"
+PKI_TYPE_FILE = "file"
+PKI_TYPE_SYMLINK = "symlink"
+PKI_WEBAPPS_DIR = "webapps"
+PKI_WEBAPPS_ROOT_DIR = PKI_WEBAPPS_DIR + "/" + "ROOT"
+PKI_WEBAPPS_ROOT_WEBINF_DIR = PKI_WEBAPPS_ROOT_DIR + "/" + "WEB-INF"
+PKI_WEBAPPS_WEBINF_DIR = PKI_WEBAPPS_DIR + "/" + "WEB-INF"
+PKI_WEBAPPS_WEBINF_CLASSES_DIR = PKI_WEBAPPS_WEBINF_DIR + "/" + "classes"
+PKI_WEBAPPS_WEBINF_LIB_DIR = PKI_WEBAPPS_WEBINF_DIR + "/" + "lib"
+PKI_WEBINF_DIR = "WEB-INF"
+PKI_WEBINF_CLASSES_DIR = PKI_WEBINF_DIR + "/" + "classes"
+PKI_WEBINF_LIB_DIR = PKI_WEBINF_DIR + "/" + "lib"
+
 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"]
 
diff --git a/base/deploy/src/scriptlets/pkihelper.py b/base/deploy/src/scriptlets/pkihelper.py
index ee2bdd2..cd49048 100644
--- a/base/deploy/src/scriptlets/pkihelper.py
+++ b/base/deploy/src/scriptlets/pkihelper.py
@@ -20,203 +20,371 @@
 #
 
 # System Imports
-import ConfigParser
-import argparse
-import logging
+import sys
 import os
+import shutil
+from grp import getgrnam
+from pwd import getpwnam
 
 
 # PKI Deployment Imports
 import pkiconfig as config
+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
-    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
+# PKI Deployment Identity Class
+class identity:
+    def get_uid(self, critical_failure=True):
+        try:
+            pki_uid = config.pki_master_dict['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 = config.pki_master_dict['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]
+            config.pki_master_dict['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]
+            config.pki_master_dict['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 = config.PKI_TYPE_DIRECTORY
+                record.user = config.pki_master_dict['pki_user']
+                record.group = config.pki_master_dict['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, 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
+                config.pki_log.info(log.PKIHELPER_MODIFY_DIR_1, name,
+                                    extra=config.PKI_INDENTATION_LEVEL_2)
+                # 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 = config.PKI_TYPE_DIRECTORY
+                record.user = config.pki_master_dict['pki_user']
+                record.group = config.pki_master_dict['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 copy(self, old_name, new_name, uid=None, gid=None,
+             perms=config.PKI_DEPLOYMENT_DEFAULT_DIR_PERMISSIONS, acls=None,
+             recursive_flag=True, overwrite_flag=True, critical_failure=True):
+        ### NOT YET IMPLEMENTED
+        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):
+        ### NOT YET IMPLEMENTED
+        return
+
+    def modify(self, name, uid=None, gid=None,
+               perms=config.PKI_DEPLOYMENT_DEFAULT_FILE_PERMISSIONS,
+               acls=None, critical_failure=True):
+        ### NOT YET IMPLEMENTED
+        return
+
+    def delete(self, name, critical_failure=True):
+        ### NOT YET IMPLEMENTED
+        return
+
+    def copy(self, old_name, new_name, uid=None, gid=None,
+             perms=config.PKI_DEPLOYMENT_DEFAULT_FILE_PERMISSIONS, acls=None,
+             overwrite_flag=True, critical_failure=True):
+        ### NOT YET IMPLEMENTED
+        return
+
+
+# PKI Deployment Symbolic Link Class
+class symlink:
+    def create(self, name, link, uid=None, gid=None,
+               perms=config.PKI_DEPLOYMENT_DEFAULT_SYMLINK_PERMISSIONS,
+               acls=None, critical_failure=True):
+        ### NOT YET IMPLEMENTED
+        return
+
+    def modify(self, name, link, uid=None, gid=None,
+               perms=config.PKI_DEPLOYMENT_DEFAULT_SYMLINK_PERMISSIONS,
+               acls=None, critical_failure=True):
+        ### NOT YET IMPLEMENTED
+        return
+
+    def delete(self, link, critical_failure=True):
+        ### NOT YET IMPLEMENTED
+        return
+
+
+# PKI Deployment Helper Class Instances
+identity = identity()
+instance = instance()
+directory = directory()
+file = file()
+symlink = symlink()
 
diff --git a/base/deploy/src/scriptlets/pkilogging.py b/base/deploy/src/scriptlets/pkilogging.py
index 776677c..cd6ad5f 100644
--- a/base/deploy/src/scriptlets/pkilogging.py
+++ b/base/deploy/src/scriptlets/pkilogging.py
@@ -31,16 +31,37 @@ 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)
+
+    # 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..00d5c53
--- /dev/null
+++ b/base/deploy/src/scriptlets/pkimanifest.py
@@ -0,0 +1,90 @@
+#!/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
+from collections import namedtuple
+import csv
+
+
+# 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:
+    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..aee364f 100644
--- a/base/deploy/src/scriptlets/pkimessages.py
+++ b/base/deploy/src/scriptlets/pkimessages.py
@@ -20,17 +20,6 @@
 #
 
 # 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,6 +36,20 @@ PKI_DICTIONARY_WEB_SERVER="\n"\
 "=====================================================\n"\
 "    DISPLAY CONTENTS OF PKI WEB SERVER DICTIONARY\n"\
 "====================================================="
+
+
+# PKI Deployment Error 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_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 "\
@@ -55,6 +58,10 @@ PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1 = "Directory '%s' is either "\
                                              "missing or is NOT a directory!"
 PKI_FILE_MISSING_OR_NOT_A_FILE_1 = "File '%s' is either missing "\
                                    "or is NOT a regular file!"
+PKI_IOERROR_1 = "IOError:  %s!"
+PKI_KEYERROR_1 = "KeyError:  %s!"
+PKI_MANIFEST_MESSAGE_1 = "generating manifest file called '%s'"
+PKI_OSERROR_1 = "OSError:  %s!"
 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 +71,46 @@ 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 "Helper" Messages
+PKIHELPER_USER_1 = "retrieving UID for '%s' . . ."
+PKIHELPER_UID_2 = "UID of '%s' is %s"
+PKIHELPER_GROUP_1 = "retrieving GID for '%s' . . ."
+PKIHELPER_GID_2 = "GID of '%s' is %s"
+PKIHELPER_MKDIR_1 = "mkdir -p %s"
+PKIHELPER_MODIFY_DIR_1 = "modifying '%s'"
+PKIHELPER_CHMOD_2 = "chmod %o %s"
+PKIHELPER_CHOWN_3 = "chown %s:%s %s"
+PKIHELPER_RMDIR_1 = "rmdir %s"
+PKIHELPER_RM_RF_1 = "rm -rf %s"
+PKIHELPER_DIRECTORY_IS_EMPTY_1 = "directory '%s' is empty"
+PKIHELPER_DIRECTORY_IS_NOT_EMPTY_1 = "directory '%s' is NOT empty"
+PKIHELPER_APACHE_INSTANCES_2 = "instance '%s' contains '%d' Apache PKI subsystems"
+PKIHELPER_PKI_SUBSYSTEM_INSTANCES_2 = "instance '%s' contains '%d' PKI subsystems"
+PKIHELPER_TOMCAT_INSTANCES_2 = "instance '%s' contains '%d' Tomcat PKI subsystems"
 
 
 # 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'"
+INSTANCE_DESTROY_1 = "depopulating '%s'"
+INSTANCE_RESPAWN_1 = "repopulating '%s'"
+INSTANCE_SPAWN_1 = "populating '%s'"
+SECURITY_DATABASES_DESTROY_1 = "removing '%s'"
+SECURITY_DATABASES_RESPAWN_1 = "regenerating '%s'"
+SECURITY_DATABASES_SPAWN_1 = "generating '%s'"
+SUBSYSTEM_DESTROY_1 = "depopulating '%s'"
+SUBSYSTEM_RESPAWN_1 = "repopulating '%s'"
+SUBSYSTEM_SPAWN_1 = "populating '%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..52c04d5
--- /dev/null
+++ b/base/deploy/src/scriptlets/pkiparser.py
@@ -0,0 +1,223 @@
+#!/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 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=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);
+    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 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
+
diff --git a/base/deploy/src/scriptlets/security_databases.py b/base/deploy/src/scriptlets/security_databases.py
index af47cbd..38a168a 100644
--- a/base/deploy/src/scriptlets/security_databases.py
+++ b/base/deploy/src/scriptlets/security_databases.py
@@ -42,15 +42,18 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
 
     def spawn(self):
         if not os.path.exists(self.pki_database_path):
-            config.pki_log.info(log.SECURITY_DATABASES_SPAWN_1, __name__)
+            config.pki_log.info(log.SECURITY_DATABASES_SPAWN_1, __name__,
+                                extra=config.PKI_INDENTATION_LEVEL_1)
         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.pki_database_path,
+                extra=config.PKI_INDENTATION_LEVEL_1)
             self.rv = -1
         else:
             config.pki_log.error(log.PKI_DIRECTORY_ALREADY_EXISTS_1,
-                                 self.pki_database_path)
+                                 self.pki_database_path,
+                                 extra=config.PKI_INDENTATION_LEVEL_1)
             self.rv = -1
         return self.rv
 
@@ -59,10 +62,12 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
            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.pki_database_path,
+                extra=config.PKI_INDENTATION_LEVEL_1)
             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)
         return self.rv
 
     def destroy(self):
@@ -70,9 +75,11 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
            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.pki_database_path,
+                extra=config.PKI_INDENTATION_LEVEL_1)
             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)
         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..c66d288
--- /dev/null
+++ b/base/deploy/src/scriptlets/subsystem_layout.py
@@ -0,0 +1,106 @@
+#!/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.
+#
+
+# PKI Deployment Imports
+import pkiconfig as config
+import pkihelper as util
+import pkimessages as log
+import pkiscriptlet
+
+
+# PKI Deployment Instance Population Classes
+class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
+    rv = 0
+
+    # instance-based subsystem base
+    pki_instance_path = config.pki_root_prefix +\
+        config.pki_master_dict['pki_instance_root'] + "/" +\
+        config.pki_master_dict['pki_instance_name']
+    pki_subsystem_path = pki_instance_path + "/" +\
+        config.pki_master_dict['pki_subsystem'].lower()
+
+    # instance-based subsystem logs
+    pki_instance_log_path = config.pki_root_prefix +\
+        config.pki_master_dict['pki_instance_log_root'] + "/" +\
+        config.pki_master_dict['pki_instance_name']
+    pki_subsystem_log_path = pki_instance_log_path + "/" +\
+        config.pki_master_dict['pki_subsystem'].lower()
+    pki_subsystem_signed_audit_log_path = pki_instance_log_path + "/" +\
+        config.pki_master_dict['pki_subsystem'].lower() + "/" +\
+        config.PKI_SIGNED_AUDIT_DIR
+
+    # instance-based subsystem configuration
+    pki_instance_configuration_path = config.pki_root_prefix +\
+        config.pki_master_dict['pki_instance_configuration_root'] + "/" +\
+        config.pki_master_dict['pki_instance_name']
+    pki_subsystem_configuration_path = pki_instance_configuration_path + "/" +\
+        config.pki_master_dict['pki_subsystem'].lower()
+
+    # instance-based subsystem registry
+    pki_instance_registry_path = config.pki_root_prefix +\
+        config.pki_master_dict['pki_instance_registry_root'] + "/" +\
+        config.pki_master_dict['pki_instance_name']
+    pki_subsystem_registry_path = pki_instance_registry_path + "/" +\
+        config.pki_master_dict['pki_subsystem'].lower()
+
+    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(self.pki_subsystem_path)
+        # establish instance-based subsystem logs
+        util.directory.create(self.pki_subsystem_log_path)
+        if config.pki_subsystem in config.PKI_SIGNED_AUDIT_SUBSYSTEMS:
+            util.directory.create(self.pki_subsystem_signed_audit_log_path)
+        # establish instance-based subsystem configuration
+        util.directory.create(self.pki_subsystem_configuration_path)
+        # establish instance-based subsystem registry
+        util.directory.create(self.pki_subsystem_registry_path)
+        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(self.pki_subsystem_path)
+        # update instance-based subsystem logs
+        util.directory.modify(self.pki_subsystem_log_path)
+        if config.pki_subsystem in config.PKI_SIGNED_AUDIT_SUBSYSTEMS:
+            util.directory.modify(self.pki_subsystem_signed_audit_log_path)
+        # update instance-based subsystem configuration
+        util.directory.modify(self.pki_subsystem_configuration_path)
+        # update instance-based subsystem registry
+        util.directory.modify(self.pki_subsystem_registry_path)
+        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
+        util.directory.delete(self.pki_subsystem_path)
+        # remove instance-based subsystem logs
+        util.directory.delete(self.pki_subsystem_log_path)
+        # remove instance-based subsystem configuration
+        util.directory.delete(self.pki_subsystem_configuration_path)
+        # remove instance-based subsystem registry
+        util.directory.delete(self.pki_subsystem_registry_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..69ab059
--- /dev/null
+++ b/base/deploy/src/scriptlets/webserver_layout.py
@@ -0,0 +1,135 @@
+#!/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.
+#
+
+# PKI Deployment Imports
+import pkiconfig as config
+import pkihelper as util
+import pkimessages as log
+import pkiscriptlet
+
+
+# PKI Deployment Instance Population Classes
+class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
+    rv = 0
+
+    # instance-based webserver base
+    pki_instance_path = config.pki_root_prefix +\
+                        config.pki_master_dict['pki_instance_root'] + "/" +\
+                        config.pki_master_dict['pki_instance_name']
+
+    # instance-based webserver Apache base
+    pki_apache_path = pki_instance_path + "/" + config.PKI_APACHE
+
+    # instance-based webserver Tomcat base
+    pki_tomcat_path = pki_instance_path + "/" + config.PKI_TOMCAT
+    pki_common_path = pki_tomcat_path + "/" + config.PKI_COMMON_DIR
+    pki_common_lib_path = pki_common_path + "/" + config.PKI_LIB_DIR
+    pki_conf_path = pki_tomcat_path + "/" + config.PKI_CONF_DIR
+    pki_webapps_path = pki_tomcat_path + "/" + config.PKI_WEBAPPS_DIR
+    pki_webapps_root_path = pki_tomcat_path + "/" +\
+                            config.PKI_WEBAPPS_ROOT_DIR
+    pki_webapps_root_webinf_path = pki_tomcat_path + "/" +\
+                                   config.PKI_WEBAPPS_ROOT_WEBINF_DIR
+    pki_webapps_webinf_path = pki_tomcat_path + "/" +\
+                              config.PKI_WEBAPPS_WEBINF_DIR
+    pki_webapps_webinf_classes_path = pki_tomcat_path + "/" +\
+                                      config.PKI_WEBAPPS_WEBINF_CLASSES_DIR
+    pki_webapps_webinf_lib_path = pki_tomcat_path + "/" +\
+                                  config.PKI_WEBAPPS_WEBINF_LIB_DIR
+
+    # instance-based webserver configuration
+    pki_instance_configuration_path = config.pki_root_prefix +\
+        config.pki_master_dict['pki_instance_configuration_root'] + "/" +\
+        config.pki_master_dict['pki_instance_name']
+    pki_alias_configuration_path = pki_instance_configuration_path + "/" +\
+                                   config.PKI_SECURITY_DATABASE_DIR
+
+    def spawn(self):
+        config.pki_log.info(log.WEBSERVER_SPAWN_1, __name__,
+                            extra=config.PKI_INDENTATION_LEVEL_1)
+        # establish instance-based webserver base
+        if config.pki_subsystem in config.PKI_APACHE_SUBSYSTEMS:
+            util.directory.create(self.pki_apache_path)
+        elif config.pki_subsystem in config.PKI_TOMCAT_SUBSYSTEMS:
+            util.directory.create(self.pki_tomcat_path)
+            util.directory.create(self.pki_common_path)
+            util.directory.create(self.pki_common_lib_path)
+            util.directory.create(self.pki_conf_path)
+            util.directory.create(self.pki_webapps_path)
+            util.directory.create(self.pki_webapps_root_path)
+            util.directory.create(self.pki_webapps_root_webinf_path)
+            util.directory.create(self.pki_webapps_webinf_path)
+            util.directory.create(self.pki_webapps_webinf_classes_path)
+            util.directory.create(self.pki_webapps_webinf_lib_path)
+        # establish instance-based webserver configuration
+        util.directory.create(self.pki_alias_configuration_path)
+        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 config.pki_subsystem in config.PKI_APACHE_SUBSYSTEMS:
+            util.directory.modify(self.pki_apache_path)
+        elif config.pki_subsystem in config.PKI_TOMCAT_SUBSYSTEMS:
+            util.directory.modify(self.pki_tomcat_path)
+            util.directory.modify(self.pki_common_path)
+            util.directory.modify(self.pki_common_lib_path)
+            util.directory.modify(self.pki_conf_path)
+            util.directory.modify(self.pki_webapps_path)
+            util.directory.modify(self.pki_webapps_root_path)
+            util.directory.modify(self.pki_webapps_root_webinf_path)
+            util.directory.modify(self.pki_webapps_webinf_path)
+            util.directory.modify(self.pki_webapps_webinf_classes_path)
+            util.directory.modify(self.pki_webapps_webinf_lib_path)
+        # update instance-based webserver configuration
+        util.directory.modify(self.pki_alias_configuration_path)
+        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\
+           config.pki_subsystem in config.PKI_APACHE_SUBSYSTEMS and\
+           util.instance.apache_instances(self.pki_instance_path) == 0:
+            util.directory.delete(self.pki_apache_path)
+        elif config.pki_subsystem in config.PKI_APACHE_SUBSYSTEMS and\
+             util.instance.apache_instances(self.pki_instance_path) == 1:
+            # always display correct information (even during dry_run)
+            util.directory.delete(self.pki_apache_path)
+        if not config.pki_dry_run_flag and\
+           config.pki_subsystem in config.PKI_TOMCAT_SUBSYSTEMS and\
+           util.instance.tomcat_instances(self.pki_instance_path) == 0:
+            util.directory.delete(self.pki_tomcat_path)
+        elif config.pki_subsystem in config.PKI_TOMCAT_SUBSYSTEMS and\
+             util.instance.tomcat_instances(self.pki_instance_path) == 1:
+            # always display correct information (even during dry_run)
+            util.directory.delete(self.pki_tomcat_path)
+        # remove instance-based webserver configuration
+        if not config.pki_dry_run_flag and\
+           util.instance.pki_subsystem_instances(self.pki_instance_path) == 0:
+            util.directory.delete(self.pki_alias_configuration_path)
+        elif util.instance.pki_subsystem_instances(self.pki_instance_path) == 1:
+            # always display correct information (even during dry_run)
+            util.directory.delete(self.pki_alias_configuration_path)
+        return self.rv
+
-- 
1.7.7.6

