Exemplo n.º 1
0
def list_manifests(service):
    '''Replies to the client with criteria list for a service.
       The output should be similar to installadm list.

    Args
        service - the name of the service being listed

    Returns
        None

    Raises
        None
    '''
    print 'Content-Type: text/html'  # HTML is following
    print  # blank line, end of headers
    print '<html>'
    print '<head>'
    sys.stdout.write('<title>%s %s</title>' %
                     (_('Manifest list for'), service))
    print '</head><body>'

    port = 0
    try:
        smf.AISCF(FMRI="system/install/server")
    except KeyError:
        # report the internal error to error_log and requesting client
        sys.stderr.write(
            _("error:The system does not have the "
              "system/install/server SMF service."))
        sys.stdout.write(
            _("error:The system does not have the "
              "system/install/server SMF service."))
        return
    services = config.get_all_service_names()
    if not services:
        # report the error to the requesting client only
        sys.stdout.write(_('error:no services on this server.\n'))
        return

    found = False
    if config.is_service(service):
        service_ctrl = AIService(service)
        found = True

        # assume new service setup
        path = service_ctrl.database_path
        if os.path.exists(path):
            try:
                aisql = AIdb.DB(path)
                aisql.verifyDBStructure()
            except StandardError as err:
                # report the internal error to error_log and
                # requesting client
                sys.stderr.write(
                    _('error:AI database access '
                      'error\n%s\n') % err)
                sys.stdout.write(
                    _('error:AI database access '
                      'error\n%s\n') % err)
                return

            # generate the list of criteria for the criteria table header
            criteria_header = E.TR()
            for crit in AIdb.getCriteria(aisql.getQueue(), strip=False):
                criteria_header.append(E.TH(crit))

            # generate the manifest rows for the criteria table body
            names = AIdb.getManNames(aisql.getQueue())
            table_body = E.TR()
            allcrit = AIdb.getCriteria(aisql.getQueue(), strip=False)
            colspan = str(max(len(list(allcrit)), 1))
            for manifest in names:

                # iterate through each manifest (and instance)
                for instance in range(
                        0, AIdb.numInstances(manifest, aisql.getQueue())):

                    table_body.append(E.TR())
                    # print the manifest name only once (from instance 0)
                    if instance == 0:
                        href = '../' + service + '/' + manifest
                        row = str(AIdb.numInstances(manifest,
                                                    aisql.getQueue()))
                        table_body.append(
                            E.TD(E.A(manifest, href=href, rowspan=row)))
                    else:
                        table_body.append(E.TD())

                    crit_pairs = AIdb.getManifestCriteria(manifest,
                                                          instance,
                                                          aisql.getQueue(),
                                                          onlyUsed=True,
                                                          humanOutput=True)

                    # crit_pairs is an SQLite3 row object which doesn't
                    # support iteritems(), etc.
                    for crit in crit_pairs.keys():
                        formatted_val = AIdb.formatValue(
                            crit, crit_pairs[crit])
                        # if we do not get back a valid value ensure a
                        # hyphen is printed (prevents "" from printing)
                        if formatted_val and crit_pairs[crit]:
                            table_body.append(
                                E.TD(formatted_val, align="center"))
                        else:
                            table_body.append(
                                E.TD(lxml.etree.Entity("nbsp"),
                                     align="center"))

            # print the default manifest at the end of the table,
            # which has the same colspan as the Criteria List label
            else:
                href = '../' + service + '/default.xml'
                table_body.append(
                    E.TR(
                        E.TD(E.A("Default", href=href)),
                        E.TD(lxml.etree.Entity("nbsp"),
                             colspan=colspan,
                             align="center")))
            web_page = E.HTML(
                E.HEAD(E.TITLE(_("OmniOS Automated "
                                 "Installation Webserver"))),
                E.BODY(
                    E.H1(
                        _("Welcome to the OmniOS "
                          "Automated Installation webserver!")),
                    E.P(
                        _("Service '%s' has the following "
                          "manifests available, served to clients "
                          "matching required criteria.") % service),
                    E.TABLE(E.TR(E.TH(_("Manifest"), rowspan="2"),
                                 E.TH(_("Criteria List"), colspan=colspan)),
                            criteria_header,
                            table_body,
                            border="1",
                            align="center"),
                ))
            print lxml.etree.tostring(web_page, pretty_print=True)

    # service is not found, provide available services on host
    if not found:
        sys.stdout.write(_('Service <i>%s</i> not found.  ') % service)
        sys.stdout.write(_('Available services are:<p><ol><i>'))
        host = socket.gethostname()
        for service_name in config.get_all_service_names():
            # assume new service setup
            port = config.get_service_port(service_name)
            sys.stdout.write(
                '<a href="http://%s:%d/cgi-bin/'
                'cgi_get_manifest.py?version=%s&service=%s">%s</a><br>\n' %
                (host, port, VERSION, service_name, service_name))
        sys.stdout.write('</i></ol>%s' % _('Please select a service '
                                           'from the above list.'))

    print '</body></html>'
Exemplo n.º 2
0
def get_mfest_or_profile_criteria(sname, services, dbtable):
    """
    Iterate through all the manifests or profiles for the named service (sname)
    pointed to by the SCF service.

    Args
        sname = service name
        services = config.get_all_service_props()
        dbtable = database table, distinguishing manifests from profiles
            Assumed to be one of AIdb.MANIFESTS_TABLE or AIdb.PROFILES_TABLE

    Returns
        a dictionary of the criteria for the named service within a list:

            {
                servicename1:[
                             { 'arch':arch1, 'mem':memory1, 'ipv4':ipaddress1,
                               'mac':macaddr1, 'platform':platform1,
                               'network':network1, 'cpu':cpu1, 'zonename':z1 },
                             ...
                            ]
            }

        * Note1: platform, network and cpu are currently not-implemented
                 upstream.
        * Note2: could simply use a list of dictionaries but implemented as a
                 dictionary of a list of dictionary which will allow for
                 multiple services to be listed at the same time.

        width of longest manifest or profile name

        width of longest criteria

    Raises
        None
    """
    sdict = dict()
    width = 0
    cwidth = 0
    # ensure the named service is in our service dictionary.
    lservices = services.keys()
    if sname in lservices:
        try:
            path = AIService(sname).database_path
        except VersionError as version_err:
            warn_version(version_err)
            return sdict, width, cwidth

        if os.path.exists(path):
            try:
                maisql = AIdb.DB(path)
                maisql.verifyDBStructure()
                aiqueue = maisql.getQueue()
                if dbtable == AIdb.MANIFESTS_TABLE:
                    for name in AIdb.getNames(aiqueue, dbtable):
                        sdict[name] = list()
                        instances = AIdb.numInstances(name, aiqueue)
                        for instance in range(0, instances):
                            width = max(len(name), width)
                            criteria = AIdb.getManifestCriteria(name,
                                            instance, aiqueue,
                                            humanOutput=True,
                                            onlyUsed=True)
                            if criteria:
                                tdict, twidth = get_criteria_info(criteria)
                                cwidth = max(twidth, cwidth)
                                sdict[name].append(tdict)
                elif dbtable == AIdb.PROFILES_TABLE:
                    for name in AIdb.getNames(aiqueue, dbtable):
                        sdict[name] = list()
                        criteria = AIdb.getProfileCriteria(name,
                                        aiqueue,
                                        humanOutput=True,
                                        onlyUsed=True)
                        width = max(len(name), width)
                        tdict, twidth = get_criteria_info(criteria)
                        cwidth = max(twidth, cwidth)

                        sdict[name].append(tdict)
                else:
                    raise ValueError("Invalid value for dbtable: %s" % dbtable)

            except StandardError as err:
                sys.stderr.write(_('Error: AI database access error\n%s\n')
                                   % err)
                sys.exit(1)
        else:
            sys.stderr.write(_('Error: unable to locate AI database on server '
                               'for %s\n') % sname)
            sys.exit(1)

    return sdict, width, cwidth
Exemplo n.º 3
0
    def get_service_manifests(sname, linst):
        """
        Iterate through all the manifests for the named service (sname)
        pointed to by the SCF service. 

        Args
            sname = service name
            inst = smf.AISCF()

        Returns
            a dictionary of the criteria for the named service within a list:

                {
                    servicename1:[
                                 { 'arch':arch1, 'mem':memory1, 'ipv4':ipaddress1,
                                   'mac':macaddr1, 'platform':platform1, 'network':network1
                                   'cpu':cpu1 },
                                 ...
                                ]
                }
            
            * Note1: platform, network and cpu are currently not-implemented upstream.
            * Note2: could simply use a list of dictionaries but implemented as a 
                     dictionary of a list of dictionary which will allow for multiple 
                     services to be listed at the same time.

            width of longest manifest name

	    width of longest criteria

        Raises
            None
        """
        sdict = {}
        width = 0
        cwidth = 0
        # ensure the named service is in our service dictionary.
        lservices = linst.services.keys()
        if sname in lservices:
            serv = smf.AIservice(linst, sname)
            if not has_key(serv, 'txt_record'):
                sys.stderr.write(_('%s: error: SMF service key '
                                   'property does not exist\n') % \
                                os.path.basename(sys.argv[0]))
                sys.exit(1)

            port = serv['txt_record'].split(':')[-1]
            path = os.path.join('/var/ai', str(port), 'AI.db')
            if os.path.exists(path):
                try:
                    maisql = AIdb.DB(path)
                    maisql.verifyDBStructure()
                    aiqueue = maisql.getQueue()
                    for name in AIdb.getManNames(aiqueue):
                        sdict[name] = []
                        instances = AIdb.numInstances(name, aiqueue)
                        for instance in range(0, instances):
                            criteria = AIdb.getManifestCriteria(name, 
                                            instance, aiqueue, 
                                            humanOutput = True, 
                                            onlyUsed = True)
    
                            width = max(len(name), width)
                            tdict, twidth = get_criteria_info(criteria)
                            cwidth = max(twidth, cwidth)

                            sdict[name].extend([tdict])

                except Exception, err:
                    sys.stderr.write(_('%s: error: AI database access '
                                       'error\n%s\n') % \
                                (os.path.basename(sys.argv[0]), err))
                    sys.exit(1)
            else: 
                sys.stderr.write(_('%s: error: unable to locate '
                                   'AI database on server for %s\n') % \
                                (os.path.basename(sys.argv[0]), sname))
                sys.exit(1)
Exemplo n.º 4
0
def find_colliding_manifests(criteria, db, collisions, append_manifest=None):
    """
    For each manifest/instance pair in collisions check that the manifest
    criteria diverge (i.e. are not exactly the same) and that the ranges do not
    collide for ranges.
    Raises if: a range collides, or if the manifest has the same criteria as a
    manifest already in the database (SystemExit raised)
    Returns: Nothing
    Args: criteria - Criteria object holding the criteria that is to be
                     added/set for a manifest.
          db - AI_database object for the install service.
          collisions - a dictionary with collisions, as produced by
                       find_colliding_criteria()
          append_manifest - name of manifest we're appending criteria to.
                            This arg is passed in when we're calling this
                            function to find criteria collisions for an
                            already published manifest that we're appending
                            criteria to.
    """

    # If we're appending criteria to an already published manifest, get a
    # dictionary of the criteria that's already published for that manifest.
    if append_manifest is not None:
        published_criteria = AIdb.getManifestCriteria(append_manifest,
                                                      0,
                                                      db.getQueue(),
                                                      humanOutput=True,
                                                      onlyUsed=False)

    # check every manifest in collisions to see if manifest collides (either
    # identical criteria, or overlaping ranges)
    for man_inst in collisions:
        # get all criteria from this manifest/instance pair
        db_criteria = AIdb.getManifestCriteria(man_inst[0],
                                               man_inst[1],
                                               db.getQueue(),
                                               humanOutput=True,
                                               onlyUsed=False)

        # iterate over every criteria in the database
        for crit in AIdb.getCriteria(db.getQueue(),
                                     onlyUsed=False,
                                     strip=False):

            # Get the criteria name (i.e. no MIN or MAX)
            crit_name = crit.replace('MIN', '', 1).replace('MAX', '', 1)
            # Set man_criterion to the key of the DB criteria or None
            man_criterion = criteria[crit_name]

            if man_criterion and crit.startswith('MIN'):
                man_criterion = man_criterion[0]
            elif man_criterion and crit.startswith('MAX'):
                man_criterion = man_criterion[1]

            # If man_criterion is still None, and if we're appending criteria
            # to an already published manifest, look for criteria in the
            # published set of criteria for the manifest we're appending to
            # as well, because existing criteria might cause a collision,
            # which we need to compare for.
            if man_criterion is None and append_manifest is not None:
                man_criterion = published_criteria[str(crit)]
                # replace database NULL's with Python None
                if man_criterion == '':
                    man_criterion = None

            # set the database criteria
            if db_criteria[str(crit)] == '':
                # replace database NULL's with a Python None
                db_criterion = None
            else:
                db_criterion = db_criteria[str(crit)]

            # Replace unbounded's in the criteria (i.e. 0/+inf)
            # with a Python None.
            if isinstance(man_criterion, basestring) and \
               man_criterion == "unbounded":
                man_criterion = None

            # check to determine if this is a range collision by using
            # collisions and if not are the manifests divergent

            if ((crit.startswith('MIN')
                 and collisions[man_inst].find(crit + ",") != -1)
                    or (crit.startswith('MAX')
                        and collisions[man_inst].find(crit + ",") != -1)):
                if str(db_criterion).lower() != str(man_criterion).lower():
                    raise SystemExit(
                        _("Error:\tManifest has a range "
                          "collision with manifest:%s/%i"
                          "\n\tin criteria: %s!") %
                        (man_inst[0], man_inst[1], crit.replace(
                            'MIN', '', 1).replace('MAX', '', 1)))

            # Either the range did not collide or this is not a range
            # criteria.  (If the value of this criteria in the db does
            # not equal the value of this criteria for the set of criteria
            # to check, we can break out knowing we diverge for this
            # manifest/instance)
            elif not db_criterion and not man_criterion:
                # Neither the value for this criteria in the db nor
                # the value for for this criteria in the given set of
                # criteria to check are populated.  Loop around to
                # check the next criteria.
                continue
            elif not db_criterion or not man_criterion:
                # One of the two are not populated, we can break knowing
                # they're different.
                break
            else:
                # Both are populated.  If none of values in the list for
                # this criteria to be added are equal to any of the values
                # in the list for this criteria from the db, there will be
                # no collision.  We can break out.
                if not [value for value in man_criterion if \
                    AIdb.is_in_list(crit, value, str(db_criterion), None)]:
                    break

        # end of for loop and we never broke out (collision)
        else:
            raise SystemExit(
                _("Error:\tManifest has same criteria as " +
                  "manifest: %s/%i!") % (man_inst[0], man_inst[1]))
Exemplo n.º 5
0
def get_mfest_or_profile_criteria(sname, services, dbtable):
    """
    Iterate through all the manifests or profiles for the named service (sname)
    pointed to by the SCF service.

    Args
        sname = service name
        services = config.get_all_service_props()
        dbtable = database table, distinguishing manifests from profiles
            Assumed to be one of AIdb.MANIFESTS_TABLE or AIdb.PROFILES_TABLE

    Returns
        a dictionary of the criteria for the named service within a list:

            {
                servicename1:[
                             { 'arch':arch1, 'mem':memory1, 'ipv4':ipaddress1,
                               'mac':macaddr1, 'platform':platform1,
                               'network':network1, 'cpu':cpu1, 'zonename':z1 },
                             ...
                            ]
            }

        * Note1: platform, network and cpu are currently not-implemented
                 upstream.
        * Note2: could simply use a list of dictionaries but implemented as a
                 dictionary of a list of dictionary which will allow for
                 multiple services to be listed at the same time.

        width of longest manifest or profile name

        width of longest criteria

    Raises
        None
    """
    sdict = dict()
    width = 0
    cwidth = 0
    # ensure the named service is in our service dictionary.
    lservices = services.keys()
    if sname in lservices:
        try:
            path = AIService(sname).database_path
        except VersionError as version_err:
            warn_version(version_err)
            return sdict, width, cwidth

        if os.path.exists(path):
            try:
                maisql = AIdb.DB(path)
                maisql.verifyDBStructure()
                aiqueue = maisql.getQueue()
                if dbtable == AIdb.MANIFESTS_TABLE:
                    for name in AIdb.getNames(aiqueue, dbtable):
                        sdict[name] = list()
                        instances = AIdb.numInstances(name, aiqueue)
                        for instance in range(0, instances):
                            width = max(len(name), width)
                            criteria = AIdb.getManifestCriteria(
                                name,
                                instance,
                                aiqueue,
                                humanOutput=True,
                                onlyUsed=True)
                            if criteria:
                                tdict, twidth = get_criteria_info(criteria)
                                cwidth = max(twidth, cwidth)
                                sdict[name].append(tdict)
                elif dbtable == AIdb.PROFILES_TABLE:
                    for name in AIdb.getNames(aiqueue, dbtable):
                        sdict[name] = list()
                        criteria = AIdb.getProfileCriteria(name,
                                                           aiqueue,
                                                           humanOutput=True,
                                                           onlyUsed=True)
                        width = max(len(name), width)
                        tdict, twidth = get_criteria_info(criteria)
                        cwidth = max(twidth, cwidth)

                        sdict[name].append(tdict)
                else:
                    raise ValueError("Invalid value for dbtable: %s" % dbtable)

            except StandardError as err:
                sys.stderr.write(
                    _('Error: AI database access error\n%s\n') % err)
                sys.exit(1)
        else:
            sys.stderr.write(
                _('Error: unable to locate AI database on server '
                  'for %s\n') % sname)
            sys.exit(1)

    return sdict, width, cwidth
def find_colliding_manifests(criteria, db, collisions, append_manifest=None):
    """
    For each manifest/instance pair in collisions check that the manifest
    criteria diverge (i.e. are not exactly the same) and that the ranges do not
    collide for ranges.
    Raises if: a range collides, or if the manifest has the same criteria as a
    manifest already in the database (SystemExit raised)
    Returns: Nothing
    Args: criteria - Criteria object holding the criteria that is to be
                     added/set for a manifest.
          db - AI_database object for the install service.
          collisions - a dictionary with collisions, as produced by
                       find_colliding_criteria()
          append_manifest - name of manifest we're appending criteria to.
                            This arg is passed in when we're calling this
                            function to find criteria collisions for an
                            already published manifest that we're appending
                            criteria to.
    """

    # If we're appending criteria to an already published manifest, get a
    # dictionary of the criteria that's already published for that manifest.
    if append_manifest is not None:
        published_criteria = AIdb.getManifestCriteria(append_manifest, 0,
                                                      db.getQueue(),
                                                      humanOutput=True,
                                                      onlyUsed=False)

    # check every manifest in collisions to see if manifest collides (either
    # identical criteria, or overlaping ranges)
    for man_inst in collisions:
        # get all criteria from this manifest/instance pair
        db_criteria = AIdb.getManifestCriteria(man_inst[0],
                                               man_inst[1],
                                               db.getQueue(),
                                               humanOutput=True,
                                               onlyUsed=False)

        # iterate over every criteria in the database
        for crit in AIdb.getCriteria(db.getQueue(),
                                     onlyUsed=False, strip=False):

            # Get the criteria name (i.e. no MIN or MAX)
            crit_name = crit.replace('MIN', '', 1).replace('MAX', '', 1)
            # Set man_criterion to the key of the DB criteria or None
            man_criterion = criteria[crit_name]

            if man_criterion and crit.startswith('MIN'):
                man_criterion = man_criterion[0]
            elif man_criterion and crit.startswith('MAX'):
                man_criterion = man_criterion[1]

            # If man_criterion is still None, and if we're appending criteria
            # to an already published manifest, look for criteria in the
            # published set of criteria for the manifest we're appending to
            # as well, because existing criteria might cause a collision,
            # which we need to compare for.
            if man_criterion is None and append_manifest is not None:
                man_criterion = published_criteria[str(crit)]
                # replace database NULL's with Python None
                if man_criterion == '':
                    man_criterion = None

            # set the database criteria
            if db_criteria[str(crit)] == '':
                # replace database NULL's with a Python None
                db_criterion = None
            else:
                db_criterion = db_criteria[str(crit)]

            # Replace unbounded's in the criteria (i.e. 0/+inf)
            # with a Python None.
            if isinstance(man_criterion, basestring) and \
               man_criterion == "unbounded":
                man_criterion = None

            # check to determine if this is a range collision by using
            # collisions and if not are the manifests divergent

            if((crit.startswith('MIN') and
                collisions[man_inst].find(crit + ",") != -1) or
               (crit.startswith('MAX') and
                collisions[man_inst].find(crit + ",") != -1)
              ):
                if str(db_criterion).lower() != str(man_criterion).lower():
                    raise SystemExit(_("Error:\tManifest has a range "
                                       "collision with manifest:%s/%i"
                                       "\n\tin criteria: %s!") %
                                     (man_inst[0], man_inst[1],
                                      crit.replace('MIN', '', 1).
                                      replace('MAX', '', 1)))

            # Either the range did not collide or this is not a range
            # criteria.  (If the value of this criteria in the db does
            # not equal the value of this criteria for the set of criteria
            # to check, we can break out knowing we diverge for this
            # manifest/instance)
            elif not db_criterion and not man_criterion:
                # Neither the value for this criteria in the db nor
                # the value for for this criteria in the given set of
                # criteria to check are populated.  Loop around to
                # check the next criteria.
                continue
            elif not db_criterion or not man_criterion:
                # One of the two are not populated, we can break knowing
                # they're different.
                break
            else:
                # Both are populated.  If none of values in the list for
                # this criteria to be added are equal to any of the values
                # in the list for this criteria from the db, there will be
                # no collision.  We can break out.
                if not [value for value in man_criterion if \
                    AIdb.is_in_list(crit, value, str(db_criterion), None)]:
                    break

        # end of for loop and we never broke out (collision)
        else:
            raise SystemExit(_("Error:\tManifest has same criteria as " +
                               "manifest: %s/%i!") %
                             (man_inst[0], man_inst[1]))
Exemplo n.º 7
0
    def index(self):
        """ The server's main page """

        # generate the list of criteria for the criteria table header
        criteriaHeader = E.TR()
        for crit in AIdb.getCriteria(self.AISQL.getQueue(), strip=False):
            criteriaHeader.append(E.TH(crit))

        # generate the manifest rows for the criteria table body
        names = AIdb.getManNames(self.AISQL.getQueue())
        tableBody = E.TR()
        for manifest in names:

            # iterate through each manifest (and instance)
            for instance in range(0,
                    AIdb.numInstances(manifest, self.AISQL.getQueue())):

                tableBody.append(E.TR())
                # print the manifest name only once (key off instance 0)
                if instance == 0:
                    tableBody.append(
                        E.TD(E.A(manifest,
                                 href="/manifests/" + manifest,
                                 rowspan=str(AIdb.numInstances(manifest,
                                    self.AISQL.getQueue())))
                            )
                    )
                else:
                    tableBody.append(E.TD())
                critPairs = AIdb.getManifestCriteria(manifest, instance,
                                                     self.AISQL.getQueue(),
                                                     onlyUsed=True,
                                                     humanOutput=True)
                # critPairs is an SQLite3 row object which doesn't support
                # iteritems(), etc.
                for crit in critPairs.keys():
                    formatted_val = AIdb.formatValue(crit, critPairs[crit])
                    # if we do not get back a valid value ensure a hyphen is
                    # printed (this prevents "" from printing)
                    if formatted_val and critPairs[crit]:
                        tableBody.append(E.TD(formatted_val, align="center"))
                    else:
                        tableBody.append(E.TD(lxml.etree.Entity("nbsp"),
                                              align="center"))

        # print the default manifest at the end of the table
        else:
            tableBody.append(
                             E.TR(
                                  E.TD(
                                       E.A("Default",
                                           href="/manifests/default.xml")),
                                  E.TD(lxml.etree.Entity("nbsp"),
                                       colspan=str(max(len(list(
                                       AIdb.getCriteria(self.AISQL.getQueue(),
                                       strip=False))), 1)),
                                       align="center")
                             )
            )
        web_page = \
                E.HTML(
                       E.HEAD(
                              E.TITLE(_("%s A/I Webserver") % _DISTRIBUTION)
                       ),
                       E.BODY(
                              E.H1(_("Welcome to the %s A/I "
                                     "webserver!") % _DISTRIBUTION),
                              E.P(_("This server has the following "
                                    "manifests available, served to clients "
                                    "matching required criteria.")),
                              E.TABLE(
                                      E.TR(
                                           E.TH(_("Manifest"), rowspan="2"),
                                           E.TH(_("Criteria List"),
                                                colspan=str(max(len(list(
                                                AIdb.getCriteria(self.AISQL.\
                                                getQueue(),
                                                strip=False))), 1)))
                                      ),
                                      criteriaHeader,
                                      tableBody,
                                      border="1", align="center"
                              ),
                       )
                )
        return lxml.etree.tostring(web_page, pretty_print=True)
def list_manifests(service):
    '''Replies to the client with criteria list for a service.
       The output should be similar to installadm list.

    Args
        service - the name of the service being listed

    Returns
        None

    Raises
        None
    '''
    print 'Content-Type: text/html'     # HTML is following
    print                               # blank line, end of headers
    print '<html>'
    print '<head>'
    sys.stdout.write('<title>%s %s</title>' %
                     (_('Manifest list for'), service))
    print '</head><body>'

    port = 0
    try:
        smf.AISCF(FMRI="system/install/server")
    except KeyError:
        # report the internal error to error_log and requesting client
        sys.stderr.write(_("error:The system does not have the "
                           "system/install/server SMF service."))
        sys.stdout.write(_("error:The system does not have the "
                           "system/install/server SMF service."))
        return
    services = config.get_all_service_names()
    if not services:
        # report the error to the requesting client only
        sys.stdout.write(_('error:no services on this server.\n'))
        return

    found = False
    if config.is_service(service):
        service_ctrl = AIService(service)
        found = True

        # assume new service setup
        path = service_ctrl.database_path
        if os.path.exists(path):
            try:
                aisql = AIdb.DB(path)
                aisql.verifyDBStructure()
            except StandardError as err:
                # report the internal error to error_log and
                # requesting client
                sys.stderr.write(_('error:AI database access '
                                   'error\n%s\n') % err)
                sys.stdout.write(_('error:AI database access '
                                   'error\n%s\n') % err)
                return

            # generate the list of criteria for the criteria table header
            criteria_header = E.TR()
            for crit in AIdb.getCriteria(aisql.getQueue(), strip=False):
                criteria_header.append(E.TH(crit))

            # generate the manifest rows for the criteria table body
            names = AIdb.getManNames(aisql.getQueue())
            table_body = E.TR()
            allcrit = AIdb.getCriteria(aisql.getQueue(), strip=False)
            colspan = str(max(len(list(allcrit)), 1))
            for manifest in names:

                # iterate through each manifest (and instance)
                for instance in range(0,
                        AIdb.numInstances(manifest, aisql.getQueue())):

                    table_body.append(E.TR())
                    # print the manifest name only once (from instance 0)
                    if instance == 0:
                        href = '../' + service + '/' + manifest
                        row = str(AIdb.numInstances(manifest,
                                                    aisql.getQueue()))
                        table_body.append(E.TD(
                                   E.A(manifest, href=href, rowspan=row)))
                    else:
                        table_body.append(E.TD())

                    crit_pairs = AIdb.getManifestCriteria(manifest,
                                                          instance,
                                                          aisql.getQueue(),
                                                          onlyUsed=True,
                                                          humanOutput=True)

                    # crit_pairs is an SQLite3 row object which doesn't
                    # support iteritems(), etc.
                    for crit in crit_pairs.keys():
                        formatted_val = AIdb.formatValue(crit,
                                                         crit_pairs[crit])
                        # if we do not get back a valid value ensure a
                        # hyphen is printed (prevents "" from printing)
                        if formatted_val and crit_pairs[crit]:
                            table_body.append(E.TD(formatted_val,
                                                   align="center"))
                        else:
                            table_body.append(E.TD(lxml.etree.Entity("nbsp"),
                                                   align="center"))

            # print the default manifest at the end of the table,
            # which has the same colspan as the Criteria List label
            else:
                href = '../' + service + '/default.xml'
                table_body.append(E.TR(E.TD(E.A("Default", href=href)),
                                       E.TD(lxml.etree.Entity("nbsp"),
                                            colspan=colspan,
                                            align="center")))
            web_page = E.HTML(
                         E.HEAD(
                            E.TITLE(_("OmniOS Automated "
                                      "Installation Webserver"))
                            ),
                         E.BODY(
                            E.H1(_("Welcome to the OmniOS "
                                   "Automated Installation webserver!")),
                            E.P(_("Service '%s' has the following "
                                "manifests available, served to clients "
                                "matching required criteria.") % service),
                            E.TABLE(
                               E.TR(
                                    E.TH(_("Manifest"), rowspan="2"),
                                    E.TH(_("Criteria List"),
                                        colspan=colspan)),
                                    criteria_header,
                                    table_body,
                                    border="1", align="center"),
                          )
                     )
            print lxml.etree.tostring(web_page, pretty_print=True)

    # service is not found, provide available services on host
    if not found:
        sys.stdout.write(_('Service <i>%s</i> not found.  ') % service)
        sys.stdout.write(_('Available services are:<p><ol><i>'))
        host = socket.gethostname()
        for service_name in config.get_all_service_names():
            # assume new service setup
            port = config.get_service_port(service_name)
            sys.stdout.write('<a href="http://%s:%d/cgi-bin/'
                   'cgi_get_manifest.py?version=%s&service=%s">%s</a><br>\n' %
                   (host, port, VERSION, service_name, service_name))
        sys.stdout.write('</i></ol>%s' % _('Please select a service '
                   'from the above list.'))

    print '</body></html>'