7124655 Profile template expansion should happen at installation time
authorDave Miner <dave.miner@oracle.com>
Mon, 09 Jan 2012 15:48:45 -0500
changeset 1571 660c559c60f8
parent 1570 0ef86dd0d8a0
child 1572 30f73c6cdf05
7124655 Profile template expansion should happen at installation time
usr/src/cmd/ai-webserver/AI_database.py
usr/src/cmd/ai-webserver/cgi_get_manifest.py
usr/src/cmd/ai-webserver/common_profile.py
usr/src/cmd/ai-webserver/create_profile.py
usr/src/cmd/ai-webserver/data_files.py
usr/src/cmd/ai-webserver/test/test_ai_database.py
usr/src/cmd/ai-webserver/test/test_create_profile.py
--- a/usr/src/cmd/ai-webserver/AI_database.py	Mon Jan 09 11:16:30 2012 -0800
+++ b/usr/src/cmd/ai-webserver/AI_database.py	Mon Jan 09 15:48:45 2012 -0500
@@ -19,7 +19,7 @@
 #
 # CDDL HEADER END
 #
-# Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
 '''
 
 AI Database Routines
@@ -709,7 +709,7 @@
     return query_str
 
 
-def formatValue(key, value):
+def formatValue(key, value, units=True):
     ''' Format and stringify database values.
 
     Args:
@@ -722,6 +722,7 @@
             - All other criteria types are stringified only.
 
       value: The raw database value to format and stringify.
+      units: Optional argument to control inclusion of units in output value
 
     Returns: a nicely-readable string representing the value. If value is none
              returns none
@@ -742,7 +743,7 @@
               str(int(svalue[3:6])) + "." + \
               str(int(svalue[6:9])) + "." + \
               str(int(svalue[9:12]))
-    elif key == "mem" and value:
+    elif units and key == "mem" and value:
         ret = str(value) + " MB"
     else:
         ret = str(value)
--- a/usr/src/cmd/ai-webserver/cgi_get_manifest.py	Mon Jan 09 11:16:30 2012 -0800
+++ b/usr/src/cmd/ai-webserver/cgi_get_manifest.py	Mon Jan 09 15:48:45 2012 -0500
@@ -19,7 +19,7 @@
 #
 # CDDL HEADER END
 #
-# Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
 #
 '''
 cgi_get_manifest retrieves the manifest based upon certain criteria
@@ -269,6 +269,12 @@
         except (ValueError, NameError, TypeError, KeyError):
             criteria = dict()
 
+    # Generate templating dictionary from criteria
+    template_dict = dict()
+    for crit in criteria:
+        template_dict["AI_" + crit.upper()] = \
+                AIdb.formatValue(crit, criteria[crit], units=False)
+            
     # find the appropriate manifest
     try:
         manifest = AIdb.findManifest(criteria, aisql)
@@ -437,8 +443,8 @@
                         raw_profile = pfp.read()
                     # do any template variable replacement {{AI_xxx}}
                     tmpl_profile = sc.perform_templating(raw_profile,
-                                                         validate_only=False)
-                    # precautionary validation or profile, logging only
+                                                         template_dict)
+                    # precautionary validation of profile, logging only
                     sc.validate_profile_string(tmpl_profile, image_dir,
                                                dtd_validation=True,
                                                warn_if_dtd_missing=True)
--- a/usr/src/cmd/ai-webserver/common_profile.py	Mon Jan 09 11:16:30 2012 -0800
+++ b/usr/src/cmd/ai-webserver/common_profile.py	Mon Jan 09 15:48:45 2012 -0500
@@ -18,7 +18,7 @@
 #
 # CDDL HEADER END
 #
-# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
 '''
 Contains routines and definitions for any script involving profiles
 '''
@@ -44,19 +44,20 @@
 WEBSERVD_UID = pwd.getpwnam('webservd').pw_uid
 WEBSERVD_GID = grp.getgrnam('webservd').gr_gid
 
-TEMPLATE_VARIABLES = [
-            'AI_ARCH',
-            'AI_CID',
-            'AI_CPU',
-            'AI_HOSTNAME',
-            'AI_IPV4',
-            'AI_MAC',
-            'AI_MEM',
-            'AI_NETWORK',
-            'AI_PLATFORM',
-            'AI_SERVICE',
-            'AI_ZONENAME'
-            ]
+# The values in this dictionary are merely valid dummy values used to ensure
+# that templated SMF properties can be validated by installadm operations.
+TEMPLATE_VARIABLES = {
+    'AI_ARCH': 'i86pc',
+    'AI_CPU': 'i386',
+    'AI_HOSTNAME': 'solaris',
+    'AI_IPV4': '10.10.10.10',
+    'AI_MAC': '01:01:01:01:01:01',
+    'AI_MEM': '2048',
+    'AI_NETWORK': '10.0.0.0',
+    'AI_PLATFORM': 'i86pc',
+    'AI_SERVICE': 'default-i386',
+    'AI_ZONENAME': 'testzone'
+    }
 
 
 class AICriteriaTemplate(Template):
@@ -75,65 +76,25 @@
     '''
 
 
-def perform_templating(profile_str, validate_only=True):
-    ''' Given profile string, do all template substitutions for any criteria
-    found in the process environment
+def perform_templating(profile_str, template_dict=dict(TEMPLATE_VARIABLES)):
+    ''' Given profile string, do all template substitutions using either a
+    provided dictionary or the default dictionary.
     Args:
-        profile_str - profile in a string
-        validate_only - boolean, set to True if doing validation only -
-            for cases where the client criteria is not available
-            If validate_only, do not generate exceptions for criteria missing
-            from environment
+        profile_str - profile as a string
+        template_dict - Optional dictionary to use for templating
     Returns:
         profile string with any templating substitution performed
     Exceptions:
-        KeyError when template variable missing when it is
-            absolutely needed; e.g., install time, installadm
-            for static profile
-        ValueError when environment variable format is invalid
+        KeyError when template variable missing
     '''
-    template_dict = dict()
-    # look for all supported criteria in process environment
-    for replacement_tag in TEMPLATE_VARIABLES:
-        if replacement_tag in os.environ:
-            envval = os.environ[replacement_tag]
-            if replacement_tag == 'AI_MAC':
-                try:
-                    macval = verifyXML.checkMAC(envval).upper()
-                except ValueError:
-                    print >> sys.stderr, _(
-                            "Warning: MAC address in enviroment is invalid "
-                            "and will be ignored.")
-                    continue
-            elif replacement_tag == 'AI_NETWORK' or \
-                    replacement_tag == 'AI_IPV4':
-                try:
-                    verifyXML.checkIPv4(envval)
-                except ValueError:
-                    print >> sys.stderr, _(
-                            "Warning: IP or network address in enviroment is "
-                            "invalid and will be ignored.")
-                    continue
-            # load template dictionary with criteria found in environment
-            if replacement_tag == 'AI_MAC':
-                template_dict[replacement_tag] = envval.upper()
-                # if the MAC address is defined,
-                # and the client ID not already defined in the environment,
-                # then derive client ID from the MAC address
-                if 'AI_CID' not in os.environ or os.environ['AI_CID'] is None:
-                    cid = "01%s" % macval
-                    template_dict['AI_CID'] = cid.upper()
-            else:
-                template_dict[replacement_tag] = envval
     # instantiate our template object derived from string Template class
     tmpl = AICriteriaTemplate(profile_str)
-    if validate_only:
-        # do not insist on all criteria being present in environment
-        profile_out = tmpl.safe_substitute(template_dict)
-    else:
-        # if template variable not in dict - exception KeyError
-        profile_out = tmpl.substitute(template_dict)
-    return profile_out # profile string with substitutions
+    # Force any MAC value to all uppercase
+    if 'AI_MAC' in template_dict:
+        template_dict['AI_MAC'] = template_dict['AI_MAC'].upper()
+    # if template variable not in dict - exception KeyError
+    profile_out = tmpl.substitute(template_dict)
+    return profile_out  # profile string with substitutions
 
 
 def sql_values_from_criteria(criteria, queue, table, gbl=False):
@@ -148,9 +109,9 @@
         gbl - if True, global
     Returns: a tuple for SQLite clauses respectively: WHERE, INTO, VALUES
     '''
-    where = list() # for WHERE clause
-    intol = list() # for INTO clause
-    vals = list() # for VALUES clause
+    where = list()  # for WHERE clause
+    intol = list()  # for INTO clause
+    vals = list()  # for VALUES clause
     for crit in AIdb.getCriteria(queue, table, onlyUsed=False, strip=True):
 
         # Determine if this crit is a range criteria or not.
@@ -275,7 +236,7 @@
         dtd_validation=False
         )
     root = etree.parse(StringIO(profile_str), parser)
-    if not resolve_entities: # just check basic XML, no inclusions
+    if not resolve_entities:  # just check basic XML, no inclusions
         return profile_str
     # validate against DTD if provided
     if dtd_validation and \
@@ -343,7 +304,7 @@
             # Inf.)
             if man_criterion[0] != "unbounded" and \
                 man_criterion[1] != "unbounded" and \
-                man_criterion[0] > man_criterion[1]: # Check min > max
+                man_criterion[0] > man_criterion[1]:  # Check min > max
                 raise SystemExit(_(
                     "Error:\tCriteria %s is not a valid range (MIN > MAX) ")
                     % crit)
@@ -364,7 +325,7 @@
                     raise SystemExit(_(
                         "Error:\tCriteria %s is not a valid hexadecimal value")
                                 % crit)
-            else: # this is a decimal value
+            else:  # this is a decimal value
                 try:
                     man_criterion = [long(str(man_criterion[0]).upper()),
                                      long(str(man_criterion[1]).upper())]
--- a/usr/src/cmd/ai-webserver/create_profile.py	Mon Jan 09 11:16:30 2012 -0800
+++ b/usr/src/cmd/ai-webserver/create_profile.py	Mon Jan 09 15:48:45 2012 -0500
@@ -19,7 +19,7 @@
 #
 # CDDL HEADER END
 #
-# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
 """
 AI create-profile
 """
@@ -258,63 +258,15 @@
             has_errors = True
             continue
 
-        # define all criteria in local environment for imminent validation
-        for crit in AIdb.getCriteria(queue, table=AIdb.PROFILES_TABLE,
-                onlyUsed=False, strip=True):
-            if crit not in criteria_dict:
-                continue
-            val = criteria[crit]
-            if not val:
-                continue
-
-            # Determine if this crit is a range criteria or not.
-            is_range_crit = AIdb.isRangeCriteria(queue, crit,
-                table=AIdb.PROFILES_TABLE)
-
-            if is_range_crit:
-                # Range criteria must be specified as a single value to be
-                # supported for templating.
-                if val[0] != val[1]:
-                    continue
-
-                # MAC specified in criteria - also set client-ID in environment
-                if crit == 'mac':
-                    val = val[0]
-                    os.environ["AI_MAC"] = \
-                        "%x:%x:%x:%x:%x:%x" % (
-                                int(val[0:2], 16),
-                                int(val[2:4], 16),
-                                int(val[4:6], 16),
-                                int(val[6:8], 16),
-                                int(val[8:10], 16),
-                                int(val[10:12], 16))
-                    os.environ["AI_CID"] = "01" + str(val).upper()
-                # IP or NETWORK specified in criteria
-                elif crit == 'network' or crit == 'ipv4':
-                    val = val[0]
-                    os.environ["AI_" + crit.upper()] = \
-                        "%d.%d.%d.%d" % (
-                                int(val[0:3]),
-                                int(val[3:6]),
-                                int(val[6:9]),
-                                int(val[9:12]))
-                else:
-                    os.environ["AI_" + crit.upper()] = val[0]
-            else:
-                # Value criteria must be specified as a single value to be
-                # supported for templating.
-                if len(val) == 1:
-                    os.environ["AI_" + crit.upper()] = val[0]
-
         # validates the profile and report errors if found
-        tmpl_profile = df.validate_file(profile_name, profile_file, image_dir,
+        raw_profile = df.validate_file(profile_name, profile_file, image_dir,
                                         verbose=False)
-        if not tmpl_profile:
+        if not raw_profile:
             has_errors = True
             continue
 
-        # create file from template string and report failures
-        full_profile_path = copy_profile_internally(tmpl_profile)
+        # create file from profile string and report failures
+        full_profile_path = copy_profile_internally(raw_profile)
         if not full_profile_path:
             has_errors = True
             continue
@@ -371,43 +323,14 @@
         raise SystemExit(missing_profile_error.format(
                          service=options.service_name, profile=profile_name))
 
-    crit_dict = AIdb.getProfileCriteria(profile_name, queue, humanOutput=True,
-                 onlyUsed=True)
-
-    # if there is criteria set for profile export the value in environment
-    if crit_dict:
-        for crit in crit_dict.keys():
-            is_range_crit = crit.startswith('MIN') or crit.startswith('MAX')
-            val = crit_dict[crit]
-            if crit.startswith('MAX'):
-                critname = crit[3:] 
-
-                # if the corresponding MIN value not set in db continue
-                if "MIN" + critname not in crit_dict:
-                    continue
-                min_value = crit_dict["MIN" + critname]
-                if not min_value:
-                    continue
-
-                # if the MIN value is equal to MAX, define the variable
-                # in environment
-                if val and val == min_value:
-                    fmt_value = AIdb.formatValue(critname, min_value)
-                    os.environ["AI_" + critname.upper()] = fmt_value
-                    if critname == "mac":
-                        cid = fmt_value.replace(":", "")
-                        os.environ["AI_CID"] = "01" + cid.upper()
-            if not is_range_crit and val:
-                os.environ["AI_" + crit.upper()] = val
-
     # validates the profile and report the errors if found 
-    tmpl_profile = df.validate_file(profile_name, profile_file, image_dir,
+    raw_profile = df.validate_file(profile_name, profile_file, image_dir,
                                     verbose=False)
-    if not tmpl_profile:
+    if not raw_profile:
         raise SystemExit(1)
 
-    # create file from template string and report failures
-    tmp_profile_path = copy_profile_internally(tmpl_profile)
+    # create file from string and report failures
+    tmp_profile_path = copy_profile_internally(raw_profile)
     if not tmp_profile_path:
         raise SystemExit(1) 
 
--- a/usr/src/cmd/ai-webserver/data_files.py	Mon Jan 09 11:16:30 2012 -0800
+++ b/usr/src/cmd/ai-webserver/data_files.py	Mon Jan 09 15:48:45 2012 -0500
@@ -19,7 +19,7 @@
 #
 # CDDL HEADER END
 #
-# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
 
 """
 data_files
@@ -341,7 +341,7 @@
         image_dir - path of service image, used to locate service_bundle
         verbose - boolean, True if verbosity desired, False otherwise
     
-    Return: profile in string format if it is valid, None otherwise.
+    Return: Raw profile in string format if it is valid, None otherwise.
 
     '''
     if verbose:
@@ -360,7 +360,7 @@
     tmpl_profile = None
     try:
         # do any templating
-        tmpl_profile = sc.perform_templating(raw_profile, validate_only=False)
+        tmpl_profile = sc.perform_templating(raw_profile)
         # validate
         validated_xml = sc.validate_profile_string(tmpl_profile, image_dir,
                                                    dtd_validation=True,
@@ -371,7 +371,7 @@
         value = sys.exc_info()[1]  # take value from exception
         found = False
         # check if missing variable in error is supported
-        for tmplvar in sc.TEMPLATE_VARIABLES:
+        for tmplvar in sc.TEMPLATE_VARIABLES.keys():
             if "'" + tmplvar + "'" == str(value):  # values in single quotes
                 found = True  # valid template variable, but not in env
                 break
@@ -384,7 +384,7 @@
                 _("Error: template variable %s in profile %s is not a "
                   "valid template variable.  Valid template variables:  ") \
                 % (value, profile_name) + '\n\t' + \
-                ', '.join(sc.TEMPLATE_VARIABLES)
+                ', '.join(sc.TEMPLATE_VARIABLES.keys())
         err = list()  # no supplemental message text needed for this exception
     # for all errors
     if errmsg:
@@ -398,7 +398,7 @@
         return 
     if validated_xml and verbose:
         print >> sys.stderr, " Passed"
-    return tmpl_profile
+    return raw_profile
 
 
 # The criteria class is a list object with an overloaded get_item method
--- a/usr/src/cmd/ai-webserver/test/test_ai_database.py	Mon Jan 09 11:16:30 2012 -0800
+++ b/usr/src/cmd/ai-webserver/test/test_ai_database.py	Mon Jan 09 15:48:45 2012 -0500
@@ -19,7 +19,7 @@
 #
 # CDDL HEADER END
 #
-# Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
 #
 
 '''
@@ -282,6 +282,8 @@
         fmt = AIdb.formatValue('MINmem', self.mem)
         self.assertEqual(fmt.split(' ')[0], self.mem)
         self.assertEqual(fmt.split(' ')[1], 'MB')
+        fmt = AIdb.formatValue('MINmem', self.mem, units=False)
+        self.assertEqual(fmt, self.mem)
 
     def test_other_formatValue(self):
         '''Ensure that formatValue does nothing with all other criteria'''
--- a/usr/src/cmd/ai-webserver/test/test_create_profile.py	Mon Jan 09 11:16:30 2012 -0800
+++ b/usr/src/cmd/ai-webserver/test/test_create_profile.py	Mon Jan 09 15:48:45 2012 -0500
@@ -19,7 +19,7 @@
 #
 # CDDL HEADER END
 #
-# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
 #
 
 '''
@@ -30,7 +30,7 @@
 '''
 
 import gettext
-import lxml.etree 
+import lxml.etree
 import os
 import shutil
 import tempfile
@@ -134,7 +134,7 @@
 class MockGetManifestCriteria(object):
     '''Class for mock getCriteria '''
     def __init__(self):
-        self.criteria = {"arch": "sparc", 
+        self.criteria = {"arch": "sparc",
                          "MINmem": None, "MAXmem": None, "MINipv4": None,
                          "MAXipv4": None, "MINmac": None, "MAXmac": None}
 
@@ -150,13 +150,13 @@
     def __init__(self, *args, **kwargs):
 
         if MockAIservice.KEYERROR:
-            raise KeyError() 
+            raise KeyError()
 
 
 class MockAISCF(object):
     '''Class for mock AISCF '''
     def __init__(self, *args, **kwargs):
-        pass  
+        pass
 
 
 class MockAIService(object):
@@ -173,7 +173,7 @@
 class MockAIRoot(object):
     '''Class for mock _AI_root'''
     def __init__(self, tag="auto_install", name=None):
-        # name is value of name attribute in manifest 
+        # name is value of name attribute in manifest
         if name:
             self.root = lxml.etree.Element(tag, name=name)
         else:
@@ -196,16 +196,16 @@
 
 
 class MockInstallAdmImage(object):
-    '''Class for mock InstallAdmImage class''' 
+    '''Class for mock InstallAdmImage class'''
 
     path = None
-    
+
     def __init__(self, *args, **kwargs):
         pass
 
 
 class MockEuid(object):
-    '''Class for mock geteuid() ''' 
+    '''Class for mock geteuid() '''
 
     @classmethod
     def geteuid(cls):
@@ -235,65 +235,65 @@
         '''Ensure no options caught'''
         self.assertRaises(SystemExit, create_profile.parse_options,
                 create_profile.DO_CREATE, [])
-        myargs = ["mysvc"] 
+        myargs = ["mysvc"]
         self.assertRaises(SystemExit, create_profile.parse_options,
-                create_profile.DO_CREATE, myargs) 
-        myargs = ["profile"] 
+                create_profile.DO_CREATE, myargs)
+        myargs = ["profile"]
         self.assertRaises(SystemExit, create_profile.parse_options,
-                create_profile.DO_CREATE, myargs) 
-        myargs = ["mysvc", "profile"] 
+                create_profile.DO_CREATE, myargs)
+        myargs = ["mysvc", "profile"]
         self.assertRaises(SystemExit, create_profile.parse_options,
-                create_profile.DO_CREATE, myargs) 
+                create_profile.DO_CREATE, myargs)
 
         self.assertRaises(SystemExit, create_profile.parse_options,
-            create_profile.DO_UPDATE, []) 
-        myargs = ["mysvc"] 
+            create_profile.DO_UPDATE, [])
+        myargs = ["mysvc"]
         self.assertRaises(SystemExit, create_profile.parse_options,
-                create_profile.DO_UPDATE, myargs) 
-        myargs = ["profile"] 
+                create_profile.DO_UPDATE, myargs)
+        myargs = ["profile"]
         self.assertRaises(SystemExit, create_profile.parse_options,
-                create_profile.DO_UPDATE, myargs) 
-        myargs = ["mysvc", "profile"] 
+                create_profile.DO_UPDATE, myargs)
+        myargs = ["mysvc", "profile"]
         self.assertRaises(SystemExit, create_profile.parse_options,
-                create_profile.DO_UPDATE, myargs) 
+                create_profile.DO_UPDATE, myargs)
 
     def test_parse_invalid_options(self):
         '''Ensure invalid option flagged'''
-        myargs = ["-n", "mysvc", "-p", "profile", "-u"] 
+        myargs = ["-n", "mysvc", "-p", "profile", "-u"]
         self.assertRaises(SystemExit, create_profile.parse_options,
-                create_profile.DO_CREATE, myargs) 
-        myargs = ["-n", "mysvc", "-p", "profile", "-a"] 
+                create_profile.DO_CREATE, myargs)
+        myargs = ["-n", "mysvc", "-p", "profile", "-a"]
         self.assertRaises(SystemExit, create_profile.parse_options,
-                create_profile.DO_CREATE, myargs) 
+                create_profile.DO_CREATE, myargs)
 
-        myargs = ["-n", "mysvc", "-p", "profile", "-u"] 
+        myargs = ["-n", "mysvc", "-p", "profile", "-u"]
         self.assertRaises(SystemExit, create_profile.parse_options,
-                create_profile.DO_UPDATE, myargs) 
-        myargs = ["-n", "mysvc", "-p", "profile", "-a"] 
+                create_profile.DO_UPDATE, myargs)
+        myargs = ["-n", "mysvc", "-p", "profile", "-a"]
         self.assertRaises(SystemExit, create_profile.parse_options,
-                create_profile.DO_UPDATE, myargs) 
+                create_profile.DO_UPDATE, myargs)
 
     def test_parse_options_novalue(self):
         '''Ensure options with missing value caught'''
-        myargs = ["-n", "mysvc", "-p", "profile", "-c"] 
+        myargs = ["-n", "mysvc", "-p", "profile", "-c"]
         self.assertRaises(SystemExit, create_profile.parse_options,
-                create_profile.DO_CREATE, myargs) 
-        myargs = ["-n", "-f", "profile"] 
+                create_profile.DO_CREATE, myargs)
+        myargs = ["-n", "-f", "profile"]
         self.assertRaises(SystemExit, create_profile.parse_options,
-                create_profile.DO_CREATE, myargs) 
-        myargs = ["-n", "mysvc", "-p"] 
+                create_profile.DO_CREATE, myargs)
+        myargs = ["-n", "mysvc", "-p"]
         self.assertRaises(SystemExit, create_profile.parse_options,
-                create_profile.DO_CREATE, myargs) 
+                create_profile.DO_CREATE, myargs)
 
-        myargs = ["-n", "mysvc", "-p", "profile", "-f"] 
+        myargs = ["-n", "mysvc", "-p", "profile", "-f"]
         self.assertRaises(SystemExit, create_profile.parse_options,
-                create_profile.DO_UPDATE, myargs) 
-        myargs = ["-n", "-f", "profile"] 
+                create_profile.DO_UPDATE, myargs)
+        myargs = ["-n", "-f", "profile"]
         self.assertRaises(SystemExit, create_profile.parse_options,
-                create_profile.DO_UPDATE, myargs) 
-        myargs = ["-n", "mysvc", "-p"] 
+                create_profile.DO_UPDATE, myargs)
+        myargs = ["-n", "mysvc", "-p"]
         self.assertRaises(SystemExit, create_profile.parse_options,
-                create_profile.DO_UPDATE, myargs) 
+                create_profile.DO_UPDATE, myargs)
 
 
 class CriteriaToDict(unittest.TestCase):
@@ -377,40 +377,29 @@
 
     def test_parse_multi_options(self):
         '''Ensure multiple profiles processed'''
-        myargs = ["-n", "mysvc", "-f", "profile", "-f", "profile2"] 
+        myargs = ["-n", "mysvc", "-f", "profile", "-f", "profile2"]
         options = create_profile.parse_options(create_profile.DO_CREATE,
                 myargs)
         self.assertEquals(options.profile_file, ["profile", "profile2"])
 
     def test_perform_templating(self):
         '''Test SC profile templating'''
-        # preserve our environment
-        saveenv = {}
-        for replacement_tag in com.TEMPLATE_VARIABLES:
-            if replacement_tag in os.environ:
-                saveenv[replacement_tag] = os.environ[replacement_tag]
-        # load environment variables for translation
-        os.environ['AI_ARCH'] = 'sparc'
-        os.environ['AI_MAC'] = '0a:0:0:0:0:0'
-        os.environ['AI_ZONENAME'] = 'myzone'
+        # load template variables for translation
+        template_dict = {'AI_ARCH': 'sparc', 'AI_MAC': '0a:0:0:0:0:0'}
         # provide template to test
-        tmpl_str = "{{AI_ARCH}} {{AI_MAC}} {{AI_ZONENAME}} {{AI_CID}}" 
+        tmpl_str = "{{AI_ARCH}} {{AI_MAC}}"
         # do the templating
-        profile = com.perform_templating(tmpl_str, False)
+        profile = com.perform_templating(tmpl_str, template_dict)
         # check for expected results
         self.assertNotEquals(profile.find('sparc'), -1)
         self.assertNotEquals(profile.find('0A:0:0:0:0:0'), -1)  # to upper
-        self.assertNotEquals(profile.find('010A0000000000'), -1)  # client ID
+        # simulate situation in which criteria are missing
+        self.assertRaises(KeyError, com.perform_templating,
+                          tmpl_str + " {{FOO_BAR}}")
+        # test zone case where only criterion is zone name
+        zone_dict = {'AI_ZONENAME': 'myzone'}
+        profile = com.perform_templating("{{AI_ZONENAME}}", zone_dict)
         self.assertNotEquals(profile.find('myzone'), -1)
-        # simulate situation in which criteria are missing
-        del os.environ['AI_ARCH']
-        self.assertRaises(KeyError, com.perform_templating, tmpl_str, False)
-        # restore our environment
-        for replacement_tag in com.TEMPLATE_VARIABLES:
-            if replacement_tag in saveenv:
-                os.environ[replacement_tag] = saveenv[replacement_tag]
-            elif replacement_tag in os.environ:
-                del os.environ[replacement_tag]
 
 
 class DoUpdateProfile(unittest.TestCase):
@@ -448,7 +437,7 @@
         self.old_file = old_prof.name
         old_prof.writelines(orig_prof)
         old_prof.close()
-        
+
         #create dummy db and populate it with temporary profile
         dbfile = tempfile.NamedTemporaryFile(dir=self.tmp_dir, delete=False)
         self.dbpath = dbfile.name
@@ -459,7 +448,7 @@
                     "platform TEXT, MINnetwork INTEGER, MAXnetwork INTEGER,"
                     "MINmem INTEGER, MAXmem INTEGER, zonename TEXT)")
 
-        dbcon.execute("CREATE TABLE manifests (name TEXT, instance INTEGER, " 
+        dbcon.execute("CREATE TABLE manifests (name TEXT, instance INTEGER, "
                     "arch TEXT, MINmac INTEGER, MAXmac INTEGER,"
                     "MINipv4 INTEGER, MAXipv4 INTEGER, cpu TEXT,"
                     "platform TEXT, MINnetwork INTEGER, MAXnetwork INTEGER,"
@@ -473,7 +462,7 @@
         dbcon.close()
 
         # initialize service and dbpath
-        self.service_AIService = svc.AIService 
+        self.service_AIService = svc.AIService
         create_profile.AIService = MockAIService
         MockAIService.database_path = self.dbpath
         MockAIService.image = MockInstallAdmImage()
@@ -488,7 +477,7 @@
         # to perform chown on profile file
         self.sc_uid = com.WEBSERVD_UID
         self.sc_gid = com.WEBSERVD_GID
-        com.WEBSERVD_UID = getpwnam(os.environ.get("USER"))[2] 
+        com.WEBSERVD_UID = getpwnam(os.environ.get("USER"))[2]
         com.WEBSERVD_GID = getpwnam(os.environ.get("USER"))[3]
 
         self.os_geteuid = os.geteuid
@@ -499,7 +488,7 @@
 
         com.WEBSERVD_UID = self.sc_uid
         com.WEBSERVD_GID = self.sc_gid
-        com.INTERNAL_PROFILE_DIRECTORY = self.sc_profile_dir 
+        com.INTERNAL_PROFILE_DIRECTORY = self.sc_profile_dir
         config.is_service = self.config_is_service
         svc.AIService = self.service_AIService
         os.geteuid = self.os_geteuid