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
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
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>'
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)
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()
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>'