usr/src/cmd/ai-webserver/set_criteria.py
changeset 862 e9f31f2f2f2d
child 992 b19185e9da83
equal deleted inserted replaced
861:ccd399d2c6f7 862:e9f31f2f2f2d
       
     1 #!/usr/bin/python2.6
       
     2 #
       
     3 # CDDL HEADER START
       
     4 #
       
     5 # The contents of this file are subject to the terms of the
       
     6 # Common Development and Distribution License (the "License").
       
     7 # You may not use this file except in compliance with the License.
       
     8 #
       
     9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
       
    10 # or http://www.opensolaris.org/os/licensing.
       
    11 # See the License for the specific language governing permissions
       
    12 # and limitations under the License.
       
    13 #
       
    14 # When distributing Covered Code, include this CDDL HEADER in each
       
    15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
       
    16 # If applicable, add the following below this CDDL HEADER, with the
       
    17 # fields enclosed by brackets "[]" replaced with your own identifying
       
    18 # information: Portions Copyright [yyyy] [name of copyright owner]
       
    19 #
       
    20 # CDDL HEADER END
       
    21 #
       
    22 # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
       
    23 
       
    24 """
       
    25 AI set-criteria
       
    26 """
       
    27 
       
    28 import gettext
       
    29 import os.path
       
    30 import sys
       
    31 import lxml.etree
       
    32 from optparse import OptionParser
       
    33 
       
    34 import publish_manifest as pub_man
       
    35 import osol_install.auto_install.AI_database as AIdb
       
    36 import osol_install.libaiscf as smf
       
    37 
       
    38 def parse_options(cmd_options=None):
       
    39     """
       
    40     Parse and validate options
       
    41     Args: Optional cmd_options, used for unit testing. Otherwise, cmd line
       
    42           options handled by OptionParser
       
    43     Returns: the DataFiles object populated and initialized
       
    44     Raises: The DataFiles initialization of manifest(s) A/I, SC, SMF looks for
       
    45             many error conditions and, when caught, are flagged to the user
       
    46             via raising SystemExit exceptions.
       
    47     """
       
    48 
       
    49     usage = _("usage: %prog -n service_name -m AI_manifest_name"
       
    50               " [-c|-a <criteria=value|range> ...] | -C criteria_file")
       
    51 
       
    52     parser = OptionParser(usage=usage, prog="set-criteria")
       
    53     parser.add_option("-a", dest="criteria_a", action="append",
       
    54                       default=[], help=_("Specify criteria to append: "
       
    55                       "<-a criteria=value|range> ..."))
       
    56     parser.add_option("-c", dest="criteria_c", action="append",
       
    57                       default=[], help=_("Specify criteria: "
       
    58                       "<-c criteria=value|range> ..."))
       
    59     parser.add_option("-C",  dest="criteria_file",
       
    60                       default=None, help=_("Specify name of criteria "
       
    61                       "XML file."))
       
    62     parser.add_option("-m",  dest="manifest_name",
       
    63                       default=None, help=_("Specify name of manifest "
       
    64                       "to set criteria for."))
       
    65     parser.add_option("-n",  dest="service_name",
       
    66                       default=None, help=_("Specify name of install "
       
    67                       "service."))
       
    68 
       
    69     # Get the parsed options using parse_args().  We know we don't have
       
    70     # args, so check to make sure there are none.
       
    71     options, args = parser.parse_args(cmd_options)
       
    72     if len(args):
       
    73         parser.error(_("Unexpected arguments: %s" % args))
       
    74 
       
    75     # Check that we have the install service's name and
       
    76     # an AI manifest name
       
    77     if options.service_name is None or options.manifest_name is None:
       
    78         parser.error(_("Missing one or more required options."))
       
    79 
       
    80     # check that we aren't mixing -a, -c, and -C
       
    81     if (options.criteria_a and options.criteria_c) or \
       
    82         (options.criteria_a and options.criteria_file) or \
       
    83         (options.criteria_c and options.criteria_file):
       
    84         parser.error(_("Options used are mutually exclusive."))
       
    85 
       
    86     return options
       
    87 
       
    88 def check_published_manifest(service_dir, db, manifest_name):
       
    89     """
       
    90     Used for checking that a manifest is already published in the
       
    91     install service specified.  Checks to make sure manifest
       
    92     exists in the install service's DB, and that the manifest also
       
    93     exists in the install service's published files area.
       
    94     Args:
       
    95           service_dir - config directory of install service to check.
       
    96           db - db object of install service to check against.
       
    97           manifest_name - name of manifest to check.
       
    98     Postconditions: None
       
    99     Returns: True if manifest exists in install service
       
   100              False if manifest does not exist.
       
   101     """
       
   102 
       
   103     # Check if manifest exists in the service's criteria DB.
       
   104     if AIdb.sanitizeSQL(manifest_name) not in AIdb.getManNames(db.getQueue()):
       
   105         print(_("Error: install service does not contain the specified "
       
   106                 "manifest: %s") % manifest_name)
       
   107         return False
       
   108 
       
   109     # Check if manifest file exists in the service's published area.
       
   110     published_path = os.path.join(service_dir, "AI_data", manifest_name)
       
   111 
       
   112     if not os.path.exists(published_path):
       
   113         print(_("Error: manifest missing from published area: %s") %
       
   114                 published_path)
       
   115         return False
       
   116 
       
   117     return True
       
   118 
       
   119 def format_value(crit, value):
       
   120     """
       
   121     Format's a value (for use with set_criteria()) based on its
       
   122     criteria type.
       
   123     Args: crit - the criteria name.
       
   124           value - the value to format.
       
   125     Returns:
       
   126           Formatted value for (used by set_criteria()) to use in
       
   127           a string to query the install service's DB.
       
   128     """
       
   129     # For the value "unbounded", we store this as "NULL" in the DB.
       
   130     if value == "unbounded":
       
   131         return "NULL"
       
   132     else:
       
   133         formatted_val = "'" + AIdb.sanitizeSQL(str(value).upper()) + "'"
       
   134 
       
   135         # If its the "mac" criteria, must add a special hex operand
       
   136         if crit == "mac":
       
   137             return "x" + formatted_val
       
   138 
       
   139         return formatted_val
       
   140 
       
   141 def set_criteria(criteria, manifest_name, db, append=False):
       
   142     """
       
   143     Set a manifest's record in the criteria database with the
       
   144     criteria provided.
       
   145     If append is True -- append ones that aren't already set for
       
   146     the manifest, and replace ones that are.
       
   147     if append is False -- completely remove all criteria already
       
   148     set for the manifest, and use only the criteria specified.
       
   149     """
       
   150 
       
   151     # Build a list of criteria nvpairs to update
       
   152     nvpairs = list()
       
   153 
       
   154     # we need to fill in the criteria or NULLs for each criteria the database
       
   155     # supports (so iterate over each criteria)
       
   156     for crit in AIdb.getCriteria(db.getQueue(), onlyUsed=False, strip=True):
       
   157 
       
   158         # Get the value from the manifest
       
   159         values = criteria[crit]
       
   160 
       
   161         # the critera manifest didn't specify this criteria
       
   162         if values is None:
       
   163             # If we not appending criteria, then we must write in NULLs
       
   164             # for this criteria since we're removing all criteria not
       
   165             # specified.
       
   166             if not append:
       
   167                 # if the criteria we're processing is a range criteria, fill in
       
   168                 # NULL for two columns, MINcrit and MAXcrit
       
   169                 if AIdb.isRangeCriteria(db.getQueue(), crit):
       
   170                     nvpairs.append("MIN" + crit + "=NULL")
       
   171                     nvpairs.append("MAX" + crit + "=NULL")
       
   172                 # this is a single value
       
   173                 else:
       
   174                     nvpairs.append(crit + "=NULL")
       
   175 
       
   176         # this is a single criteria (not a range)
       
   177         elif isinstance(values, basestring):
       
   178             # translate "unbounded" to a database NULL
       
   179             if values == "unbounded":
       
   180                 nvstr = crit + "=NULL"
       
   181             else:
       
   182                 # use lower case for text strings
       
   183                 nvstr = crit + "='" + AIdb.sanitizeSQL(str(values).lower()) \
       
   184                         + "'"
       
   185             nvpairs.append(nvstr)
       
   186 
       
   187         # Else the values are a list this is a range criteria
       
   188         else:
       
   189             # Set the MIN column for this range criteria
       
   190             nvpairs.append("MIN" + crit + "=" + format_value(crit, values[0]))
       
   191 
       
   192             # Set the MAX column for this range criteria
       
   193             nvpairs.append("MAX" + crit + "=" + format_value(crit, values[1]))
       
   194 
       
   195     query = "UPDATE manifests SET " + ",".join(nvpairs) + \
       
   196             " WHERE name='" + manifest_name + "'"
       
   197 
       
   198     # update the DB
       
   199     query = AIdb.DBrequest(query, commit=True)
       
   200     db.getQueue().put(query)
       
   201     query.waitAns()
       
   202     # in case there's an error call the response function (which
       
   203     # will print the error)
       
   204     query.getResponse()
       
   205 
       
   206 
       
   207 if __name__ == '__main__':
       
   208     gettext.install("ai", "/usr/lib/locale")
       
   209 
       
   210     options = parse_options()
       
   211 
       
   212     # Get the SMF service object for the install service specified.
       
   213     try:
       
   214         svc = smf.AIservice(smf.AISCF(FMRI="system/install/server"),
       
   215                             options.service_name)
       
   216     except KeyError:
       
   217         raise SystemExit(_("Error: Failed to find service %s") %
       
   218                          options.service_name)
       
   219 
       
   220     # Get the install service's data directory and database path
       
   221     try:
       
   222         port = svc['txt_record'].rsplit(':')[-1]
       
   223     except KeyError:
       
   224         raise SystemExit(_("SMF data for service %s is corrupt.\n") %
       
   225                          options.service_name)
       
   226     service_dir = os.path.abspath("/var/ai/" + port)
       
   227     database = os.path.join(service_dir, "AI.db")
       
   228 
       
   229     # Check that the service directory and database exist
       
   230     if not (os.path.isdir(service_dir) and os.path.exists(database)):
       
   231         raise SystemExit("Error: Invalid AI service directory: %s" %
       
   232                          service_dir)
       
   233 
       
   234     # Open the database
       
   235     db = AIdb.DB(database, commit=True)
       
   236 
       
   237     # Check to make sure that the manifest whose criteria we're
       
   238     # updating exists in the install service.
       
   239     if not check_published_manifest(service_dir, db, options.manifest_name):
       
   240         raise SystemExit(1)
       
   241 
       
   242     # Process and validate criteria from -a, -c, or -C, and store
       
   243     # store the criteria in a Criteria object.
       
   244     try:
       
   245         if options.criteria_file:
       
   246             root = pub_man.verifyCriteria(pub_man.DataFiles.criteriaSchema,
       
   247                                           options.criteria_file, db)
       
   248         elif options.criteria_a:
       
   249             criteria_dict = pub_man.criteria_to_dict(options.criteria_a)
       
   250             root = pub_man.verifyCriteriaDict(pub_man.DataFiles.criteriaSchema,
       
   251                                               criteria_dict, db)
       
   252         elif options.criteria_c:
       
   253             criteria_dict = pub_man.criteria_to_dict(options.criteria_c)
       
   254             root = pub_man.verifyCriteriaDict(pub_man.DataFiles.criteriaSchema,
       
   255                                               criteria_dict, db)
       
   256     except (AssertionError, IOError, ValueError) as err:
       
   257         raise SystemExit(err)
       
   258     except (lxml.etree.LxmlError) as err:
       
   259         raise SystemExit(_("Error:\tmanifest error: %s") % err)
       
   260 
       
   261     # Instantiate a Criteria object with the XML DOM of the criteria.
       
   262     criteria = pub_man.Criteria(root)
       
   263 
       
   264     # Ensure the criteria we're adding/setting for this manifest doesn't
       
   265     # cause a criteria collision in the DB.
       
   266     colliding_criteria = pub_man.find_colliding_criteria(criteria, db,
       
   267                              exclude_manifests=[options.manifest_name])
       
   268     # If we're appending criteria pass the manifest name
       
   269     if options.criteria_a:
       
   270         pub_man.find_colliding_manifests(criteria, db, colliding_criteria,
       
   271                                          append_manifest=options.manifest_name)
       
   272     else:
       
   273         pub_man.find_colliding_manifests(criteria, db, colliding_criteria,
       
   274                                          append_manifest=None)
       
   275 
       
   276     # Update the criteria for this manifest.
       
   277     if options.criteria_a:
       
   278         set_criteria(criteria, options.manifest_name, db, append=True)
       
   279     else:
       
   280         set_criteria(criteria, options.manifest_name, db, append=False)
       
   281