def retrieve(self, fileId, fileVersion=-1, pars=[], hdrs={}, targetFile=None, processing=None, processingPars=None): """ Request file `fileId` from the NG/AMS Server, store it locally in `targetFile`, and return the result of the operation as an ngamsStatus object. If `targetFile` is a directory, the name of the retrieved file is appended to the directory name. If `file_version` is given then that specific of the version will be retrieved. If `processing` and `processingPars` are given, they are passed down as the processing plug-in name and parameters to be applied to the retrieved data *on the server side*, respectively. """ pars = list(pars) pars.append(("file_id", fileId)) if fileVersion != -1: pars.append(("file_version", str(fileVersion))) if processing: pars.append(("processing", processing)) if processingPars: pars.append(("processingPars", processingPars)) targetFile = targetFile or '.' resp, host, port = self._get('RETRIEVE', pars, hdrs) host_id = "%s:%d" % (host, port) with contextlib.closing(resp): if resp.status != NGAMS_HTTP_SUCCESS: return ngamsStatus.to_status(resp, host_id, 'RETRIEVE') # If the target path is a directory, take the filename # of the incoming data as the filename fname = targetFile if os.path.isdir(fname): cdisp = resp.getheader('Content-Disposition') parts = ngamsLib.parseHttpHdr(cdisp) if 'filename' not in parts: msg = "Missing or invalid Content-Disposition header in HTTP response" raise Exception(msg) fname = os.path.join(fname, os.path.basename(parts['filename'])) # Dump the data into the target file readf = functools.partial(resp.read, 65536) with open(fname, 'wb') as f: for buf in iter(readf, ''): f.write(buf) return ngamsStatus.dummy_success_stat(host_id)
def _checkFileAccess(srvObj, reqPropsObj, httpRef, fileId, fileVersion=-1, diskId=""): """ Check if a file is accessible either on the local host or on a remotely located host. srvObj: Instance of the server object (ngamsServer). reqPropsObj: Request Property object to keep track of actions done during the request handling (ngamsReqProps). httpRef: Reference to the HTTP request handler object (ngamsHttpRequestHandler). fileId: File ID (string). fileVersion: File Version (integer). diskId: Disk ID (string). Returns: Returns message indicating if the file is available (string). """ T = TRACE() logger.debug("Checking for access to file with ID: %s", fileId) # Check if the file is located on this host, or if the request should be # forwarded (if this server should act as proxy). location, fileHost, ipAddress, filePortNo, mountPoint, filename, fileId,\ fileVersion, mimeType =\ ngamsFileUtils.locateArchiveFile(srvObj, fileId, fileVersion, diskId) if (location != NGAMS_HOST_LOCAL): # Go and get it! host, port = srvObj.get_remote_server_endpoint(fileHost) pars = (('file_access', fileId), ('file_version', fileVersion), ('disk_id', diskId)) resp = ngamsHttpUtils.httpGet(host, port, 'STATUS', pars, timeout=60) with contextlib.closing(resp): return ngamsStatus.to_status(resp, fileHost, 'STATUS').getMessage() else: # First check if this system allows for Retrieve Requests. if (not srvObj.getCfg().getAllowRetrieveReq()): msg = genLog("NGAMS_INFO_SERVICE_UNAVAIL", ["File Retrieval"]) else: fileHost = "%s:%d" % (getHostName(), filePortNo) msg = genLog("NGAMS_INFO_FILE_AVAIL", [fileId + "/Version: " + str(fileVersion), fileHost]) return msg
def get_status(self, cmd, pars=[], hdrs=[]): """ Sends a GET command to the first available NGAS server, receives the reply and returns it as a ngamsStatus object. """ resp, host, port = self._get(cmd, pars, hdrs) host_id = "%s:%d" % (host, port) # If the reply is a ngamsStatus document read it and return it return ngamsStatus.to_status(resp, host_id, cmd)
def status(self, pars=[], output=None): """ Request a general status from the NG/AMS Server associated to the object. Returns: NG/AMS Status object (ngamsStatus). """ if 'file_list' in [p[0] for p in pars]: resp, host, port = self._get(NGAMS_STATUS_CMD, pars=pars) if resp.status != NGAMS_HTTP_SUCCESS: return ngamsStatus.to_status(resp, '%s:%d' % (host, port), 'STATUS') output = output or 'file_list.xml.gz' with open(output, 'wb') as fout, contextlib.closing(resp): shutil.copyfileobj(resp, fout) return ngamsStatus.dummy_success_stat("%s:%d" % (host, port)) return self.get_status(NGAMS_STATUS_CMD, pars=pars)
def _create_remote_subscriptions(srvObj, stop_evt): """ Creates subscriptions in remote servers so they push their data into ours. """ subscriptions_in_cfg = srvObj.cfg.getSubscriptionsDic() subscriptions = [v for _, v in subscriptions_in_cfg.items()] subscriptions_created = 0 while True: # Done with all of them if not subscriptions: break # Iterate over copy, since we modify the original inside the loop for subscrObj in list(subscriptions): if (not srvObj.getThreadRunPermission()): logger.info("Terminating Subscriber thread ...") return our_host, our_port = srvObj.get_self_endpoint() subs_host, subs_port = subscrObj.getHostId(), subscrObj.getPortNo() # Not subscribing to ourselves if srvObj.is_it_us(subs_host, subs_port): logger.warning( "Skipping subscription to %s:%d because that's us", subs_host, subs_port) return # Create the URL that needs to be set on the remote end so we # get subscribed to it. # TODO: include reverse proxy information when we add support # TODO: hardcoded http will need to be changed when we add support # for https url = 'http://%s:%d/%s' % (our_host, our_port, subscrObj.getUrl() or 'QARCHIVE') logger.info("Creating subscription to %s:%d with url=%s", subs_host, subs_port, url) pars = [["subscr_id", url], ["priority", subscrObj.getPriority()], ["url", url], ["start_date", toiso8601(local=True)]] if subscrObj.getFilterPi(): pars.append(["filter_plug_in", subscrObj.getFilterPi()]) if subscrObj.getFilterPiPars(): pars.append(["plug_in_pars", subscrObj.getFilterPiPars()]) try: # Issue SUBSCRIBE command resp = ngamsHttpUtils.httpGet(subs_host, subs_port, cmd=NGAMS_SUBSCRIBE_CMD, pars=pars) with contextlib.closing(resp): stat = ngamsStatus.to_status(resp, subscrObj.getHostId(), NGAMS_SUBSCRIBE_CMD) if stat.getStatus() != NGAMS_SUCCESS: msg = "Unsuccessful NGAS XML response. Status: %s, message: %s. Will try again later" logger.warning(msg, stat.getStatus(), stat.getMessage()) continue subscriptions_created += 1 logger.info("Successfully subscribed to %s:%d with url=%s", subs_host, subs_port, subscrObj.getUrl()) # Remember to unsubscribe when going Offline. srvObj.getSubscrStatusList().append(subscrObj) subscriptions.remove(subscrObj) except: logger.exception( "Error while adding subscription, will try later") if stop_evt.wait(10): return if subscriptions_created: logger.info("Successfully established %d Subscription(s)", subscriptions_created) else: logger.info("No Subscriptions established")
def handleOffline(srvObj, reqPropsObj=None): """ Carry out the actions to put the system in Offline state (Standby). srvObj: Reference to NG/AMS server class object (ngamsServer). reqPropsObj: NG/AMS request properties object (ngamsReqProps). stopJanitorThr: Setting this to 0, the function will not try to stop the Janitor Thread. The reason for this is that the Janitor Thread may bring the server to Offline (integer/0|1). Returns: Void. """ # Stop/delete Janitor Thread + Data Check Thread + inform other # possible threads to stop execution (if running). srvObj.stopJanitorThread() srvObj.stopDataCheckThread() ngamsSubscriptionThread.stopSubscriptionThread(srvObj) srvObj.stopUserServiceThread() srvObj.stopMirControlThread() srvObj.stopCacheControlThread() srvObj.setThreadRunPermission(0) if srvObj.getCfg().getSubscrEnable(): srvObj.remote_subscription_creation_evt.set() logger.debug("Prepare NG/AMS for Offline State ...") # Unsubscribe possible subscriptions. This is tried only once. if (srvObj.getCfg().getAutoUnsubscribe()): for subscrObj in srvObj.getSubscrStatusList(): subs_host, subs_port = subscrObj.getHostId(), subscrObj.getPortNo() try: resp = ngamsHttpUtils.httpGet( subs_host, subs_port, NGAMS_UNSUBSCRIBE_CMD, [["url", subscrObj.getId()]]) with contextlib.closing(resp): stat = ngamsStatus.to_status( resp, "%s:%d" % (subs_host, subs_port), NGAMS_UNSUBSCRIBE_CMD) if stat.getStatus() != NGAMS_SUCCESS: logger.error("Error when unsubscribing from %s:%d: %s", subs_host, subs_port, stat.getMessage()) except Exception: msg = "Problem occurred while cancelling subscription " +\ "(host/port): %s/%d. Subscriber ID: %s" logger.exception(msg, subscrObj.getHostId(), subscrObj.getPortNo(), subscrObj.getId()) srvObj.resetSubscrStatusList() # Check if there are files located in the Staging Areas of the # Disks. In case yes, move these to the Back-Log Area to have them # treated at a later stage. checkStagingAreas(srvObj) # Dump disk info on all disks, invoke the Offline Plug-In to prepare the # disks for offline, and mark the disks as unmounted in the DB. ngamsDiskUtils.dumpDiskInfoAllDisks(srvObj.getHostId(), srvObj.getDb(), srvObj.getCfg()) plugIn = srvObj.getCfg().getOfflinePlugIn() if (srvObj.getCfg().getSimulation() == 0): logger.info("Invoking System Offline Plug-In: %s(srvObj, reqPropsObj)", plugIn) plugInMethod = loadPlugInEntryPoint(plugIn) plugInRes = plugInMethod(srvObj, reqPropsObj) else: pass ngamsDiskUtils.markDisksAsUnmountedInDb(srvObj.getHostId(), srvObj.getDb(), srvObj.getCfg()) # Send out possible Retained Email Notification Messages. flushMsg = "NOTE: Distribution of retained Email Notification Messages " +\ "forced at Offline" ngamsNotification.checkNotifRetBuf(srvObj.getHostId(), srvObj.getCfg(), 1, flushMsg) logger.info("NG/AMS prepared for Offline State")
def _create_remote_subscriptions(srvObj, stop_evt): """ Creates subscriptions in remote servers so they push their data into ours. """ subscriptions_in_cfg = srvObj.cfg.getSubscriptionsDic() subscriptions = [v for _, v in subscriptions_in_cfg.items()] subscriptions_created = 0 def mark_as_active(s): # Removes s from list of subscriptions pending creation, and # ensures the corresponding UNSUBSCRIBE command will be called # at server shutdown srvObj.getSubscrStatusList().append(s) subscriptions.remove(s) while True: # Done with all of them if not subscriptions: break # Iterate over copy, since we modify the original inside the loop for subscrObj in list(subscriptions): if stop_evt.is_set(): logger.info("Terminating Subscriber thread ...") return our_host, our_port = srvObj.get_self_endpoint() subs_host, subs_port = subscrObj.getHostId(), subscrObj.getPortNo() # Not subscribing to ourselves if srvObj.is_it_us(subs_host, subs_port): logger.warning("Skipping subscription to %s:%d because that's us", subs_host, subs_port) continue # Because properly supporting the "Command" configuration mechanism # still requires some more work, we prefer the "SubscriberUrl" # attribute as the main source of URL information. # We still support "Command", but with the following caveats: # * TODO: include reverse proxy information when we add support # * TODO: hardcoded http will need to be changed when we add support # for https # * TODO: fails with IpAddress == '0.0.0.0' url = subscrObj.getUrl() if not url: url = 'http://%s:%d/%s' % (our_host, our_port, subscrObj.command or 'QARCHIVE') logger.info("Creating subscription to %s:%d with url=%s", subs_host, subs_port, url) pars = [["subscr_id", subscrObj.getId()], ["priority", subscrObj.getPriority()], ["url", url], ["start_date", toiso8601(local=True)]] if subscrObj.getFilterPi(): pars.append(["filter_plug_in", subscrObj.getFilterPi()]) if subscrObj.getFilterPiPars(): pars.append(["plug_in_pars", subscrObj.getFilterPiPars()]) try: # Issue SUBSCRIBE command resp = ngamsHttpUtils.httpGet(subs_host, subs_port, cmd=NGAMS_SUBSCRIBE_CMD, pars=pars) with contextlib.closing(resp): stat = ngamsStatus.to_status(resp, subscrObj.getHostId(), NGAMS_SUBSCRIBE_CMD) if stat.getStatus() != NGAMS_SUCCESS: if stat.http_status == 409: short_msg = "Different subscription with ID '%s' already exists, giving up" long_msg = ( "NGAS attempted to create an automatic subscription " "with ID=%s to obtain data from %s:%d, but the remote server " "already has a subscription registered with the same ID, " "but different details.\n\n" "Instead of retrying to create this subscription over and over, " "this server will give up now. To fix this either remove " "the remote subscription, or change the ID of the subscription to be created " "in the local server configuration." ) logger.error(short_msg, subscrObj.getId()) ngamsNotification.notify( srvObj.host_id, srvObj.cfg, NGAMS_NOTIF_ERROR, "Automatic subscription cannot be created", long_msg % (subscrObj.getId(), subs_host, subs_port), force=True) mark_as_active(subscrObj) continue else: msg = "Unsuccessful NGAS XML response. Status: %s, message: %s. Will try again later" logger.warning(msg, stat.getStatus(), stat.getMessage()) continue subscriptions_created += 1 logger.info("Successfully subscribed to %s:%d with url=%s", subs_host, subs_port, subscrObj.getUrl()) mark_as_active(subscrObj) except: logger.exception("Error while adding subscription, will try later") if stop_evt.wait(10): return if subscriptions_created: logger.info("Successfully established %d Subscription(s)", subscriptions_created) else: logger.info("No Subscriptions established")