Example #1
0
def check_published_manifest(service, dbn, manifest_name):
    """
    Used for checking that a manifest is already published in the
    install service specified.  Checks to make sure manifest
    exists in the install service's DB, and that the manifest also
    exists in the install service's published files area.
    Args:
          service - service object for service
          dbn - dbn object of install service to check against.
          manifest_name - name of manifest to check.
    Postconditions: None
    Returns: True if manifest exists in install service
             False if manifest does not exist.
    """

    # Check if manifest exists in the service's criteria DB.
    if AIdb.sanitizeSQL(manifest_name) not in AIdb.getManNames(dbn.getQueue()):
        print(
            _("Error: install service does not contain the specified "
              "manifest: %s") % manifest_name)
        return False

    # Check if manifest file exists in the service's published area.
    published_path = os.path.join(service.manifest_dir, manifest_name)

    if not os.path.exists(published_path):
        print(
            _("Error: manifest missing from published area: %s") %
            published_path)
        return False

    return True
def check_published_manifest(service, dbn, manifest_name):
    """
    Used for checking that a manifest is already published in the
    install service specified.  Checks to make sure manifest
    exists in the install service's DB, and that the manifest also
    exists in the install service's published files area.
    Args:
          service - service object for service
          dbn - dbn object of install service to check against.
          manifest_name - name of manifest to check.
    Postconditions: None
    Returns: True if manifest exists in install service
             False if manifest does not exist.
    """

    # Check if manifest exists in the service's criteria DB.
    if AIdb.sanitizeSQL(manifest_name) not in AIdb.getManNames(dbn.getQueue()):
        print(_("Error: install service does not contain the specified "
                "manifest: %s") % manifest_name)
        return False

    # Check if manifest file exists in the service's published area.
    published_path = os.path.join(service.manifest_dir, manifest_name)

    if not os.path.exists(published_path):
        print(_("Error: manifest missing from published area: %s") %
                published_path)
        return False

    return True
Example #3
0
def delete_manifest_from_db(db, manifest_instance, service_name, data_loc):
    """
    Remove manifest from DB
    """
    instance = manifest_instance[1]
    # check to see that the manifest is found in the database (as entered)
    if manifest_instance[0] not in AIdb.getManNames(db.getQueue()):
        # since all manifest names have to have .xml appended try adding that
        if manifest_instance[0] + '.xml' in AIdb.getManNames(db.getQueue()):
            man_name = manifest_instance[0] + '.xml'
        else:
            raise SystemExit(_("Error:\tManifest %s not found in database!" %
                             manifest_instance[0]))
    else:
        man_name = manifest_instance[0]

    service = AIService(service_name)
    # Do not delete if this manifest is set up as the default.
    if man_name == service.get_default_manifest():
        raise ValueError(_("Error:\tCannot delete default manifest %s.") %
                         man_name)

    # if we do not have an instance remove the entire manifest
    if instance is None:
        # remove manifest from database
        query = AIdb.DBrequest("DELETE FROM manifests WHERE name = '%s'" %
                               AIdb.sanitizeSQL(man_name), commit=True)
        db.getQueue().put(query)
        query.waitAns()
        # run getResponse to handle and errors
        query.getResponse()

        # clean up file on file system
        try:
            os.remove(os.path.join(service.manifest_dir, man_name))
        except OSError:
            print >> sys.stderr, _("Warning:\tUnable to find file %s for " +
                                   "removal!") % man_name

    # we are removing a specific instance
    else:
        # check that the instance number is within bounds for that manifest
        # (0..numInstances)
        if instance > AIdb.numInstances(man_name, db.getQueue()) or \
            instance < 0:
            raise SystemExit(_("Error:\tManifest %(name)s has %(num)i "
                               "instances" % {'name': man_name, 'num':
                               AIdb.numInstances(man_name, db.getQueue())}))

        # remove instance from database
        query = ("DELETE FROM manifests WHERE name = '%s' AND "
                "instance = '%i'") % (AIdb.sanitizeSQL(man_name), instance)
        query = AIdb.DBrequest(query, commit=True)
        db.getQueue().put(query)
        query.waitAns()
        # run getResponse to handle and errors
        query.getResponse()

        # We may need to reshuffle manifests to prevent gaps in instance
        # numbering as the DB routines expect instances to be contiguous and
        # increasing. We may have removed an instance with instances numbered
        # above thus leaving a gap.

        # get the number of instances with a larger instance
        for num in range(instance, AIdb.numInstances(man_name,
                                                     db.getQueue()) + 1):
            # now decrement the instance number
            query = ("UPDATE manifests SET instance = '%i' WHERE "
                    "name = '%s' ") % (num - 1, AIdb.sanitizeSQL(man_name))
            query += "AND instance = '%i'" % num
            query = AIdb.DBrequest(query, commit=True)
            db.getQueue().put(query)
            query.waitAns()
            # run getResponse to handle and errors
            query.getResponse()

        # remove file if manifest is no longer in database
        if man_name not in AIdb.getManNames(db.getQueue()):
            try:
                os.remove(os.path.join(service.manifest_dir, man_name))
            except OSError:
                print >> sys.stderr, _("Warning: Unable to find file %s for " +
                                       "removal!") % man_name
Example #4
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>'
Example #5
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)
Example #6
0
    def get_manifest_names(linst):
        """
        Iterate through the services from smf.AISCF() retrieving
        all the stored manifest names.

        Args
            inst = smf.AISCF()

        Returns
            a dictionary of service manifests within a list:

                {
                    servicename1:[ manifest1, manifest2, ...],
                    ...
                }

            the width of the longest service name

        Raises
            None
        """
        width = 0
        sdict = {}
        lservices = linst.services.keys()
        lservices.sort()
        for akey in lservices:
            serv = smf.AIservice(linst, akey)
            # ensure that the current service has the keys we need.
            # if not then continue with the next service.
            if not (has_key(serv, 'service_name') and
                    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)

            sname = serv['service_name']
            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()
                    for name in AIdb.getManNames(maisql.getQueue()):
                        width = max(len(sname), width)
                        if sdict.has_key(sname):
                            slist = sdict[sname]
                            slist.extend([name])
                            sdict[sname] = slist
                        else:
                            sdict[sname] = [name]
                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\n') % \
                                os.path.basename(sys.argv[0]))
                sys.exit(1)
def insert_SQL(files):
    """
    Ensures all data is properly sanitized and formatted, then inserts it into
    the database
    Args: None
    Returns: None
    """
    query = "INSERT INTO manifests VALUES("

    # add the manifest name to the query string
    query += "'" + AIdb.sanitizeSQL(files.manifest_name) + "',"
    # check to see if manifest name is already in database (affects instance
    # number)
    if AIdb.sanitizeSQL(files.manifest_name) in \
        AIdb.getManNames(files.database.getQueue()):
            # database already has this manifest name get the number of
            # instances
        instance = AIdb.numInstances(AIdb.sanitizeSQL(files.manifest_name),
                                     files.database.getQueue())
    # this a new manifest
    else:
        instance = 0

    # actually add the instance to the query string
    query += str(instance) + ","

    # we need to fill in the criteria or NULLs for each criteria the database
    # supports (so iterate over each criteria)
    for crit in AIdb.getCriteria(files.database.getQueue(),
                                 onlyUsed=False, strip=False):
        # for range values trigger on the MAX criteria (skip the MIN's
        # arbitrary as we handle rows in one pass)
        if crit.startswith('MIN'):
            continue

        # get the values from the manifest
        values = files.criteria[crit.replace('MAX', '', 1)]

        # If the critera manifest didn't specify this criteria, fill in NULLs
        if values is None:
            # use the criteria name to determine if this is a range
            if crit.startswith('MAX'):
                query += "NULL,NULL,"
            # this is a single value
            else:
                query += "NULL,"

        # Else if this is a value criteria (not a range), insert the value
        # as a space-separated list of values which will account for the case
        # where a list of values have been given.
        elif not crit.startswith('MAX'):
            # Join the values of the list with a space separator.
            query += "'" + AIdb.sanitizeSQL(" ".join(values)) + "',"
        # else values is a range
        else:
            for value in values:
                # translate "unbounded" to a database NULL
                if value == "unbounded":
                    query += "NULL,"
                # we need to deal with mac addresses specially being
                # hexadecimal
                elif crit.endswith("mac"):
                    # need to insert with hex operand x'<val>'
                    # use an upper case string for hex values
                    query += "x'" + AIdb.sanitizeSQL(str(value).upper()) + \
                             "',"
                else:
                    query += AIdb.sanitizeSQL(str(value).upper()) + ","

    # strip trailing comma and close parentheses
    query = query[:-1] + ")"

    # update the database
    query = AIdb.DBrequest(query, commit=True)
    files.database.getQueue().put(query)
    query.waitAns()
    # in case there's an error call the response function (which will print the
    # error)
    query.getResponse()
Example #8
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 insert_SQL(files):
    """
    Ensures all data is properly sanitized and formatted, then inserts it into
    the database
    Args: None
    Returns: None
    """
    query = "INSERT INTO manifests VALUES("

    # add the manifest name to the query string
    query += "'" + AIdb.sanitizeSQL(files.manifest_name) + "',"
    # check to see if manifest name is already in database (affects instance
    # number)
    if AIdb.sanitizeSQL(files.manifest_name) in \
        AIdb.getManNames(files.database.getQueue()):
        # database already has this manifest name get the number of
        # instances
        instance = AIdb.numInstances(AIdb.sanitizeSQL(files.manifest_name),
                                     files.database.getQueue())
    # this a new manifest
    else:
        instance = 0

    # actually add the instance to the query string
    query += str(instance) + ","

    # we need to fill in the criteria or NULLs for each criteria the database
    # supports (so iterate over each criteria)
    for crit in AIdb.getCriteria(files.database.getQueue(),
                                 onlyUsed=False,
                                 strip=False):
        # for range values trigger on the MAX criteria (skip the MIN's
        # arbitrary as we handle rows in one pass)
        if crit.startswith('MIN'):
            continue

        # get the values from the manifest
        values = files.criteria[crit.replace('MAX', '', 1)]

        # If the critera manifest didn't specify this criteria, fill in NULLs
        if values is None:
            # use the criteria name to determine if this is a range
            if crit.startswith('MAX'):
                query += "NULL,NULL,"
            # this is a single value
            else:
                query += "NULL,"

        # Else if this is a value criteria (not a range), insert the value
        # as a space-separated list of values which will account for the case
        # where a list of values have been given.
        elif not crit.startswith('MAX'):
            # Join the values of the list with a space separator.
            query += "'" + AIdb.sanitizeSQL(" ".join(values)) + "',"
        # else values is a range
        else:
            for value in values:
                # translate "unbounded" to a database NULL
                if value == "unbounded":
                    query += "NULL,"
                # we need to deal with mac addresses specially being
                # hexadecimal
                elif crit.endswith("mac"):
                    # need to insert with hex operand x'<val>'
                    # use an upper case string for hex values
                    query += "x'" + AIdb.sanitizeSQL(str(value).upper()) + \
                             "',"
                else:
                    query += AIdb.sanitizeSQL(str(value).upper()) + ","

    # strip trailing comma and close parentheses
    query = query[:-1] + ")"

    # update the database
    query = AIdb.DBrequest(query, commit=True)
    files.database.getQueue().put(query)
    query.waitAns()
    # in case there's an error call the response function (which will print the
    # error)
    query.getResponse()
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>'