def __getXmlrpcServer(self): """ get an xmlrpc server object WARNING: if CFG.USE_SSL is off, we are sending info in the clear. """ log_debug(3) # build the URL url = CFG.RHN_PARENT or '' url = string.split(parseUrl(url)[1], ':')[0] if CFG.USE_SSL: url = 'https://' + url + '/XMLRPC' else: url = 'http://' + url + '/XMLRPC' log_debug(3, 'server url: %s' % url) if CFG.HTTP_PROXY: serverObj = rpclib.Server(url, proxy=CFG.HTTP_PROXY, username=CFG.HTTP_PROXY_USERNAME, password=CFG.HTTP_PROXY_PASSWORD) else: serverObj = rpclib.Server(url) if CFG.USE_SSL and CFG.CA_CHAIN: if not os.access(CFG.CA_CHAIN, os.R_OK): log_error('ERROR: missing or cannot access (for ca_chain): %s' % CFG.CA_CHAIN) raise rhnFault(1000, _("RHN Proxy error (file access issues). " "Please contact your system administrator. " "Please refer to RHN Proxy logs.")) serverObj.add_trusted_cert(CFG.CA_CHAIN) serverObj.add_header('X-RHN-Client-Version', 2) return serverObj
def getPackagePath(self, pkgFilename): """ OVERLOADS getPackagePath in common/rhnRepository. Returns complete path to an RPM file. """ log_debug(3, pkgFilename) mappingName = "package_mapping:%s:" % self.channelName pickledMapping = self._cacheObj(mappingName, self.channelVersion, self.__channelPackageMapping, ()) mapping = cPickle.loads(pickledMapping) # If the file name has parameters, it's a different kind of package. # Determine the architecture requested so we can construct an # appropriate filename. if type(pkgFilename) == types.ListType: arch = pkgFilename[3] if isSolarisArch(arch): pkgFilename = "%s-%s-%s.%s.pkg" % (pkgFilename[0], pkgFilename[1], pkgFilename[2], pkgFilename[3]) if not mapping.has_key(pkgFilename): log_error("Package not in mapping: %s" % pkgFilename) raise rhnFault(17, _("Invalid RPM package requested: %s") % pkgFilename) filePath = "%s/%s" % (CFG.PKG_DIR, mapping[pkgFilename]) log_debug(4, "File path", filePath) if not os.access(filePath, os.R_OK): log_debug(4, "Package not found locally: %s" % pkgFilename) raise NotLocalError(filePath, pkgFilename) return filePath
def token_server_groups(server_id, tokens_obj): assert(isinstance(tokens_obj, ActivationTokens)) h = rhnSQL.prepare(_query_token_server_groups) server_groups = {} for token in tokens_obj.tokens: token_id = token['token_id'] h.execute(token_id=token_id) while 1: row = h.fetchone_dict() if not row: break server_group_id = row['server_group_id'] server_groups[server_group_id] = row # Now try to subscribe server to group ret = [] for server_group_id, sg in server_groups.items(): log_debug(4, "token server group", sg) try: join_server_group(server_id, server_group_id) except rhnSQL.SQLError, e: log_error("Failed to add server to group", server_id, server_group_id, sg["name"]) raise rhnFault(80, _("Failed to add server to group %s") % sg["name"]) else: ret.append("Subscribed to server group '%s'" % sg["name"])
def _handleServerResponse(self, status): """ This method can be overridden by subclasses who want to handle server responses in their own way. By default, we will wrap all the headers up and send them back to the client with an error status. This method should return apache.OK if everything went according to plan. """ if (status != apache.HTTP_OK) and (status != apache.HTTP_PARTIAL_CONTENT): # Non 200 response; have to treat it differently log_debug(2, "Forwarding status %s" % status) # Copy the incoming headers to err_headers_out headers = self.responseContext.getHeaders() if headers is not None: for k in headers.keys(): rhnLib.setHeaderValue(self.req.err_headers_out, k, self._get_header(k)) else: log_error('WARNING? - no incoming headers found!') # And that's that return status if (status == apache.HTTP_PARTIAL_CONTENT): return apache.HTTP_PARTIAL_CONTENT else: # apache.HTTP_OK becomes apache.OK. return apache.OK
def guest_registered(self, host_sid, guest_sid): host_system_slots = server_lib.check_entitlement(host_sid) host_system_slots = host_system_slots.keys() try: host_system_slots.remove("virtualization_host") except ValueError: pass try: host_system_slots.remove("virtualization_host_platform") except ValueError: pass guest_system_slots = server_lib.check_entitlement(guest_sid) guest_system_slots = guest_system_slots.keys() for entitlement in host_system_slots: if entitlement not in guest_system_slots: try: procedure.rhn_entitlements.entitle_server(guest_sid, entitlement) except rhnSQL.SQLError, e: log_error("Error adding entitlement %s: %s" % (entitlement, str(e))) # rhnSQL.rollback() return
def _transformKickstartRequestForBroker(self, req): # Get the checksum for the requested resource from the satellite. (status, checksum) = self._querySatelliteForChecksum(req) if status != apache.OK or not checksum: return status # If we got this far, we have the checksum. Create a new URI based on # the checksum. newURI = self._generateCacheableKickstartURI(req.uri, checksum) if not newURI: # Couldn't create a cacheable URI, log an error and revert to # BZ 158236 behavior. log_error('Could not create cacheable ks URI from "%s"' % req.uri) return apache.OK # Now we must embed the old URI into a header in the original request # so that the SSL Redirect has it available if the resource has not # been cached yet. We will also embed a header that holds the new URI, # so that the content handler can use it later. log_debug(3, "Generated new kickstart URI: %s" % newURI) req.headers_in.add(HEADER_ACTUAL_URI, req.uri) req.headers_in.add(HEADER_EFFECTIVE_URI, newURI) return apache.OK
def entitle(self, server_id, history, virt_type = None): """ Entitle a server according to the entitlements we have configured. """ log_debug(3, self.entitlements) entitle_server = rhnSQL.Procedure("rhn_entitlements.entitle_server") # TODO: entitle_server calls can_entitle_server, so we're doing this # twice for each successful call. Is it necessary for external error # handling or can we ditch it? can_entitle_server = rhnSQL.Function( "rhn_entitlements.can_entitle_server", rhnSQL.types.NUMBER()) can_ent = None history["entitlement"] = "" # Do a quick check to see if both virt entitlements are present. (i.e. # activation keys stacked together) If so, give preference to the more # powerful virtualization platform and remove the regular virt # entitlement from the list. found_virt = False found_virt_platform = False for entitlement in self.entitlements: if entitlement[0] == VIRT_ENT_LABEL: found_virt = True elif entitlement[0] == VIRT_PLATFORM_ENT_LABEL: found_virt_platform = True for entitlement in self.entitlements: if virt_type is not None and entitlement[0] in \ (VIRT_ENT_LABEL, VIRT_PLATFORM_ENT_LABEL): continue # If both virt entitlements are present, skip the least powerful: if found_virt and found_virt_platform and entitlement[0] == VIRT_ENT_LABEL: log_debug(1, "Virtualization and Virtualization Platform " + "entitlements both present.") log_debug(1, "Skipping Virtualization.") continue try: can_ent = can_entitle_server(server_id, entitlement[0]) except rhnSQL.SQLSchemaError, e: can_ent = 0 try: # bugzilla #160077, skip attempting to entitle if we cant if can_ent: entitle_server(server_id, entitlement[0]) except rhnSQL.SQLSchemaError, e: log_error("Token failed to entitle server", server_id, self.get_names(), entitlement[0], e.errmsg) if e.errno == 20220: #ORA-20220: (servergroup_max_members) - Server group membership #cannot exceed maximum membership raise rhnFault(91, _("Registration failed: RHN Software Management service entitlements exhausted")) #No idea what error may be here... raise rhnFault(90, e.errmsg)
def reload(self, server, reload_all = 0): log_debug(4, server, "reload_all = %d" % reload_all) if not self.server.load(int(server)): log_error("Could not find server record for reload", server) raise rhnFault(29, "Could not find server record in the database") self.cert = None # it is lame that we have to do this h = rhnSQL.prepare(""" select label from rhnServerArch where id = :archid """) h.execute(archid = self.server["server_arch_id"]) data = h.fetchone_dict() if not data: raise rhnException("Found server with invalid numeric " "architecture reference", self.server.data) self.archname = data['label'] # we don't know this one anymore (well, we could look for, but # why would we do that?) self.user = None # XXX: Fix me if reload_all: if not self.reload_packages_byid(self.server["id"]) == 0: return -1 if not self.reload_hardware_byid(self.server["id"]) == 0: return -1 return 0
def process(self): log_debug(3) # nice thing that req has a read() method, so it makes it look just # like an fd try: fd = self.input.decode(self.req) except IOError: # client timed out return apache.HTTP_BAD_REQUEST # Read the data from the request _body = fd.read() fd.close() # In this case, we talk to a client (maybe through a proxy) # make sure we have something to decode if _body is None or len(_body) == 0: return apache.HTTP_BAD_REQUEST # Decode the request; avoid logging crappy responses try: params, method = self.decode(_body) except rpclib.ResponseError: log_error("Got bad XML-RPC blob of len = %d" % len(_body)) return apache.HTTP_BAD_REQUEST else: if params is None: params = () # make the actual function call and return the result return self.call_function(method, params)
def auth_system(self, system_id): """ System authentication. We override the standard function because we need to check additionally if this system_id is entitled for proxy functionality. """ log_debug(3) server = rhnHandler.auth_system(self, system_id) # if it did not blow up, we have a valid server. Check proxy # entitlement. # XXX: this needs to be moved out of the rhnServer module, # possibly in here h = rhnSQL.prepare(""" select 1 from rhnProxyInfo pi where pi.server_id = :server_id """) h.execute(server_id = self.server_id) row = h.fetchone_dict() if not row: # we require entitlement for this functionality log_error("Server not entitled for Proxy", self.server_id) raise rhnFault(1002, _( 'RHN Proxy service not enabled for server profile: "%s"') % server.server["name"]) # we're fine... return server
def _init_request_processor(self, req): log_debug(3) # Override the parent class's behaviour # figure out what kind of request handler we need to instantiate if req.method == "POST": self._req_processor = apachePOST(self.clientVersion, req) return apache.OK if req.method == "GET": try: self._req_processor = apacheGET(self.clientVersion, req) except HandlerNotFoundError, e: log_error("Unable to handle GET request for server %s" % (e.args[0], )) return apache.HTTP_METHOD_NOT_ALLOWED # We want to give the request processor the ability to override # the default behaviour of calling _setSessionToken # XXX This is a but kludgy - misa 20040827 if hasattr(self._req_processor, 'init_request'): if not self._req_processor.init_request(): return apache.HTTP_METHOD_NOT_ALLOWED # Request initialized return apache.OK token = self._setSessionToken(req.headers_in) if token is None: return apache.HTTP_METHOD_NOT_ALLOWED return apache.OK
def _connectToParent(self): """ Handler part 1 Should not return an error code -- simply connects. """ scheme, host, port, self.uri = self._parse_url(self.rhnParent) self.responseContext.setConnection(self._create_connection()) if not self.uri: self.uri = '/' log_debug(3, 'Scheme:', scheme) log_debug(3, 'Host:', host) log_debug(3, 'Port:', port) log_debug(3, 'URI:', self.uri) log_debug(3, 'HTTP proxy:', self.httpProxy) log_debug(3, 'HTTP proxy username:'******'HTTP proxy password:'******'CA cert:', self.caChain) try: self.responseContext.getConnection().connect() except socket.error, e: log_error("Error opening connection", self.rhnParent, e) Traceback(mail=0) raise rhnFault(1000, _("RHN Proxy could not successfully connect its RHN parent. " "Please contact your system administrator."))
def guest_migrated(self, old_host_sid, new_host_sid, guest_sid, guest_uuid): try: procedure.rhn_entitlements.repoll_virt_guest_entitlements(new_host_sid) except rhnSQL.SQLError, e: log_error("Error adding entitlement: %s" %str(e)) # rhnSQL.rollback() return
def headerParserHandler(self, req): log_setreq(req) # init configuration options with proper component options = req.get_options() # if we are initializing out of a <Location> handler don't # freak out if not options.has_key("RHNComponentType"): # clearly nothing to do return apache.OK initCFG(options["RHNComponentType"]) initLOG(CFG.LOG_FILE, CFG.DEBUG) if req.method == 'GET': # This is the ping method return apache.OK self.servers = rhnImport.load("upload_server/handlers", interface_signature='upload_class') if not options.has_key('SERVER'): log_error("SERVER not set in the apache config files!") return apache.HTTP_INTERNAL_SERVER_ERROR server_name = options['SERVER'] if not self.servers.has_key(server_name): log_error("Unable to load server %s from available servers %s" % (server_name, self.servers)) return apache.HTTP_INTERNAL_SERVER_ERROR server_class = self.servers[server_name] self.server = server_class(req) return self._wrapper(req, "headerParserHandler")
def check_connection(self): try: c = self.prepare("select 1") c.execute() except: # try to reconnect, that one MUST WORK always log_error("DATABASE CONNECTION TO '%s' LOST" % self.database, "Exception information: %s" % sys.exc_info()[1]) self.connect() # only allow one try
def rollback(serverId, actionId, dry_run=0): log_debug(3, dry_run) # since rhnActionTransactions table is gone, this call have to fail log_error("Invalid rollback.rollback action %s for server id %s" % (actionId, serverId)) raise InvalidAction( "Invalid rollback.rollback action %s for server id %s" % (actionId, serverId))
def notify(self, indexName="server"): try: client = xmlrpclib.ServerProxy(self.addr) result = client.admin.updateIndex(indexName) except Exception, e: log_error("Failed to notify search service located at %s to update %s indexes" \ % (self.addr, indexName), e) return False
def headerParserHandler(self, req): ret = basePackageUpload.BasePackageUpload.headerParserHandler(self, req) # Optional headers maps = [['Null-Org', 'null_org'], ['Packaging', 'packaging']] for hn, sn in maps: header_name = "%s-%s" % (self.header_prefix, hn) if req.headers_in.has_key(header_name): setattr(self, sn, req.headers_in[header_name]) if ret != apache.OK: return ret if CFG.SEND_MESSAGE_TO_ALL: rhnSQL.closeDB() log_debug(1, "send_message_to_all is set") rhnFlags.set("apache-return-code", apache.HTTP_NOT_FOUND) try: outage_message = open(CFG.MESSAGE_TO_ALL).read() except IOError: log_error("Missing outage message file") outage_message = "Outage mode" raise rhnFault(20001, outage_message, explain=0) # Init the database connection rhnSQL.initDB() use_session = 0 if self.field_data.has_key('Auth-Session'): session_token = self.field_data['Auth-Session'] use_session = 1 else: encoded_auth_token = self.field_data['Auth'] if not use_session: auth_token = self.get_auth_token(encoded_auth_token) if len(auth_token) < 2: log_debug(3, auth_token) raise rhnFault(105, "Unable to autenticate") self.username, self.password = auth_token[:2] force = self.field_data['Force'] force = int(force) log_debug(1, "Username", self.username, "Force", force) if use_session: self.org_id, self.force = rhnPackageUpload.authenticate_session(session_token, force=force, null_org=self.null_org) else: # We don't push to any channels self.org_id, self.force = rhnPackageUpload.authenticate(self.username, self.password, force=force, null_org=self.null_org) nevra = [self.package_name, "", self.package_version, self.package_release, self.package_arch] return apache.OK
def load(self, dir, interface_signature='rpcClasses'): # The key we use for caching root_dir = "/usr/share/rhn" key = (dir, root_dir, interface_signature) if self._imports.has_key(key): return self._imports[key] dirname = "%s/%s" % (root_dir, dir) # We need to import things if root_dir is not None and root_dir not in sys.path: sys.path.append(root_dir) fromcomps = dir.split('/') _imports = {} # Keep track of the modules we've already tried to load, to avoid loading # them twice modules = [] # Load each module (that is not internal - i.e. doesn't start with _) for module in os.listdir(dirname): log_debug(5, "Attempting to load module %s from %s %s" % ( module, '.'.join(fromcomps), dirname)) if module[0] in ('_', '.'): # We consider it 'internal' and we don't load it log_debug(6, "Ignoring module %s" % module) continue # Importing files or directories with . in them is broken, so keep # only the first part module = module.split('.', 1)[0] if module in modules: log_debug(6, "Already tried to load Module %s" % (module, )) continue # Add it to the list, so we don't load it again modules.append(module) # We use fromclause to build the full module path fromclause = '.'.join(fromcomps + [module]) # Try to import the module try: m = __import__(fromclause, {}, {}, [module]) except ImportError, e: log_error("Error importing %s: %s" % (module, e)) log_debug(6, "Details: sys.path: %s" % (sys.path, )) continue if not hasattr(m, interface_signature): # The module does not support our API log_error("Module %s doesn't support our API" % (module, )) continue log_debug(5, "Module %s loaded" % (module, )) _imports[module] = getattr(m, interface_signature)
def solve_dependencies(server_id, deps, version): """ The unchanged version of solve_dependencies. IN: server_id := id info of the server deps := list of filenames that are needed by the caller version := version of the client OUT: Dictionary with key values being the filnames in deps and the values being a list of lists of package info. Example := {'filename1' : [['name', 'version', 'release', 'epoch'], ['name2', 'version2', 'release2', 'epoch2']]} """ # first, uniquify deps deplist = [] for dep in deps: if dep not in deplist: deplist.append(dep) # SQL statement. It is a union of 3 statements: # - Lookup by package name # - Lookup by provides # - Lookup by file name statement = "%s UNION ALL %s UNION ALL %s" % ( __packages_sql, __provides_sql, __files_sql) h = rhnSQL.prepare(statement) # prepare return value packages = {} # Iterate through the dependency problems for dep in deplist: dict = {} h.execute(server_id = server_id, dep = dep) rs = h.fetchall_dict() or [] if not rs: # test shortcut log_error("Unable to solve dependency", server_id, dep) packages[dep] = [] continue for p in rs: if p['epoch'] == None: p['epoch'] = "" entry = [] map(lambda f, e = entry, p = p: e.append(p[f]), ['name', 'version', 'release', 'epoch']) if dict.has_key(entry[0]) and dict[entry[0]][1] < p['preference']: # Already have it with a lower preference continue # The first time we see this package. dict[entry[0]] = (entry, p['preference']) packages[dep] = _avoid_compat_packages(dict) # v2 clients are done if version > 1: return packages else: return _v2packages_to_v1list(packages, deplist)
def _send_package_stream(self, package, channel): log_debug(3, package, channel) path, dummy = self.get_package_path_by_filename(package, channel) log_debug(3, "Package path", path) if not os.path.exists(path): log_error("Missing package (satellite dumper): %s" % path) raise rhnFault(3007, "Unable to retrieve package %s" % package) return self._send_stream(path)
def __check_Int_String(name, value, package = package): if type(value) not in (StringType, IntType): log_error("Field %s (%s)=`%s' in %s does not pass type checks" % ( name, type(value), str(value), str(package))) raise rhnFault(30, _("Invalid value for %s in package tuple: " "%s (%s)") % (name, value, type(value))) value = str(value) if not len(value): log_error("Field %s has an EMPTY value in %s" % (value, package)) return value
def add_hardware(self, hardware): log_debug(4, hardware) if not hardware: return -1 if type(hardware) == type({}): hardware = UserDictCase(hardware) if not isinstance(hardware, UserDictCase): log_error("argument type is not hash: %s" % hardware) raise TypeError, "This function requires a hash as an argument" # validation is important hw_class = hardware.get("class") if hw_class is None: return -1 hw_class = string.lower(hw_class) class_type = None if hw_class in ["video", "audio", "audio_hd", "usb", "other", "hd", "floppy", "mouse", "modem", "network", "cdrom", "scsi", "unspec", "scanner", "tape", "capture", "raid", "socket", "keyboard", "printer", "firewire", "ide"]: class_type = HardwareDevice elif hw_class == "cpu": class_type = CPUDevice elif hw_class == "netinfo": class_type = NetworkInformation elif hw_class == "memory": class_type = MemoryInformation elif hw_class == "dmi": class_type = DMIInformation elif hw_class == "installinfo": class_type = InstallInformation elif hw_class == "netinterfaces": class_type = NetIfaceInformation else: log_error("UNKNOWN CLASS TYPE `%s'" % hw_class) # Same trick: try-except and raise the exception so that Traceback # can send the e-mail try: raise KeyError, "Unknwon class type `%s' for hardware '%s'" % ( hw_class, hardware) except: Traceback(mail=1) return # create the new device new_dev = class_type(hardware) if self.__hardware.has_key(class_type): _l = self.__hardware[class_type] else: _l = self.__hardware[class_type] = [] _l.append(new_dev) self.__changed = 1 return 0
def search(server_id, username = None): log_debug(3, server_id, username) s = Server(None) if not s.reload(server_id) == 0: log_error("Reloading server id %d failed" % server_id) # we can't say that the server is not really found raise rhnFault(20) # now that it is reloaded, fix up the s.user field if username: s.user = rhnUser.search(username) return s
def check_package_file(rel_path, logpkg, raisepkg): if rel_path is None: log_error("Package path null for package id", logpkg) raise rhnFault(17, _("Invalid RPM package %s requested") % raisepkg) filePath = "%s/%s" % (CFG.MOUNT_POINT, rel_path) if not os.access(filePath, os.R_OK): # Package not found on the filesystem log_error("Package not found", filePath) raise rhnFault(17, _("Package not found")) return filePath
def _process_diffs(server_id, action_id, diffs): _disable_old_diffs(server_id) for file_path, diff in diffs.items(): action_config_revision_id = _lookup_action_revision_id(server_id, action_id, file_path) if action_config_revision_id is None: log_error( "Missing config file for action id %s, server id %s, path %s" % (server_id, action_id, file_path)) continue _add_result(action_config_revision_id, diff)
def certificate(self): """ convert to XML """ dump = self.attrs dump["checksum"] = self.__checksum dump["fields"] = self.__fields try: x = xmlrpclib.dumps((dump,)) except TypeError, e: log_error("Could not marshall certificate for %s" % dump) e.args = e.args + (dump,) # Carry on the information for the exception reporting raise
def __validate_checksum(self, secret): """ compute the current checksum against a secret and check it against the current checksum """ csum = self.compute_checksum(secret) if not csum == self.__checksum: # fail, current checksum does not match log_error("Checksum check failed: %s != %s" % (csum, self.__checksum), "fields = %s" % str(self.__fields), "attrs = %s" % str(self.attrs)) return 0 return 1
def _mark_dep_failures(server_id, action_id, data): if not data: log_debug(4, "Nothing to do") return failed_deps = data.get('failed_deps') if not failed_deps: log_debug(4, "No failed deps") return if not isinstance(failed_deps, ListType): # Not the right format log_error("action_extra_data.packages.remove: server %s, action %s: " "wrong type %s" % (server_id, action_id, type(failed_deps))) return inserts = {} for f in ( 'server_id', 'action_id', 'name', 'version', 'release', 'epoch', 'needs_name', 'needs_version', 'ignore_null', 'flags', 'suggested', 'sense'): inserts[f] = [] for failed_dep in failed_deps: try: pkg, needs_pkg, flags, suggested, sense = _check_dep(server_id, action_id, failed_dep) except InvalidDep: continue inserts['server_id'].append(server_id) inserts['action_id'].append(action_id) inserts['name'] .append(pkg[0]) inserts['version'].append(pkg[1]) inserts['release'].append(pkg[2]) inserts['epoch'].append(None) inserts['needs_name'].append(needs_pkg[0]) inserts['needs_version'].append(needs_pkg[1]) inserts['flags'].append(flags) inserts['suggested'].append(suggested) inserts['ignore_null'].append(1) inserts['sense'].append(sense) h = rhnSQL.prepare(_query_delete_dep_failures) rowcount = h.execute(server_id=server_id, action_id=action_id) log_debug(5, "Removed old rows", rowcount) h = rhnSQL.prepare(_query_insert_dep_failures) rowcount = h.execute_bulk(inserts) log_debug(5, "Inserted rows", rowcount)
def _get_method_params(self): # Returns the method name and params for this call # Split the request into parts array = string.split(self.req.path_info, '/') if len(array) < 4: log_error("Invalid URI for GET request", self.req.path_info) raise rhnFault(21, _("Invalid URI %s" % self.req.path_info)) self.channel, method = (array[2], array[3]) params = tuple(array[4:]) return method, params