""" PublisherHandler This service has been built to provide the RSS web views with all the information they need. NO OTHER COMPONENT THAN Web controllers should make use of it. """ from __future__ import absolute_import from __future__ import division from __future__ import print_function __RCSID__ = "e8f1fa8a8 (2022-06-23 09:39:04 +0300) Andrii Lytovchenko " # pylint: disable=no-self-use import six from datetime import datetime, timedelta # DIRAC from DIRAC import S_OK, gConfig, S_ERROR from DIRAC.Core.DISET.RequestHandler import RequestHandler from DIRAC.Core.Utilities.SiteSEMapping import getSEHosts, getStorageElementsHosts from DIRAC.ConfigurationSystem.Client.Helpers.Resources import getSites, getSiteCEMapping from DIRAC.DataManagementSystem.Utilities.DMSHelpers import DMSHelpers from DIRAC.ResourceStatusSystem.Client.ResourceStatusClient import ResourceStatusClient from DIRAC.ResourceStatusSystem.Utilities import Utils ResourceManagementClient = getattr( Utils.voimport("DIRAC.ResourceStatusSystem.Client.ResourceManagementClient"), "ResourceManagementClient" ) class PublisherHandler(RequestHandler): """ RPCServer used to deliver data to the web portal. """ def __init__(self, *args, **kwargs): """ Constructor """ super(PublisherHandler, self).__init__(*args, **kwargs) # ResourceStatusClient ....................................................... @classmethod def initializeHandler(cls, serviceInfoDict): """Initialization of client objects""" cls.rsClient = ResourceStatusClient() cls.rmClient = ResourceManagementClient() return S_OK() types_getSites = [] def export_getSites(self): """ Returns list of all sites considered by RSS :return: S_OK( [ sites ] ) | S_ERROR """ return getSites() types_getSitesResources = [(six.string_types, list, type(None))] def export_getSitesResources(self, siteNames): """ Returns dictionary with SEs and CEs for the given site(s). If siteNames is None, all sites are taken into account. :return: S_OK( { site1 : { ces : [ ces ], 'ses' : [ ses ] },... } ) | S_ERROR """ if siteNames is None: res = getSites() if not res["OK"]: self.log.error("Error getting sites", res["Message"]) return res siteNames = res["Value"] if isinstance(siteNames, six.string_types): siteNames = [siteNames] sitesRes = {} for siteName in siteNames: result = getSiteCEMapping() if not result["OK"]: self.log.error("Error getting sites/CEs mapping", result["Message"]) return result res = {} res["ces"] = result["Value"][siteName] # Convert StorageElements to host names result = DMSHelpers().getSiteSEMapping() if not result["OK"]: self.log.error("Error getting sites/SEs mapping", result["Message"]) sitesRes[siteName] = res continue ses = result["Value"][1].get(siteName, []) result = getStorageElementsHosts(ses) if not result["OK"]: self.log.error("Error getting storage element hosts", result["Message"]) return result # Remove duplicates res["ses"] = list(set(result["Value"])) sitesRes[siteName] = res return S_OK(sitesRes) types_getElementStatuses = [ six.string_types, (six.string_types, list, type(None)), (six.string_types, list, type(None)), (six.string_types, list, type(None)), (six.string_types, list, type(None)), (six.string_types, list, type(None)), ] @classmethod def export_getElementStatuses(cls, element, name, elementType, statusType, status, tokenOwner): """ Returns element statuses from the ResourceStatusDB """ return cls.rsClient.selectStatusElement( element, "Status", name=name, elementType=elementType, statusType=statusType, status=status, tokenOwner=tokenOwner, ) types_getElementHistory = [ six.string_types, (six.string_types, list, type(None)), (six.string_types, list, type(None)), (six.string_types, list, type(None)), ] @classmethod def export_getElementHistory(cls, element, name, elementType, statusType): """ Returns element history from ResourceStatusDB """ columns = ["Status", "DateEffective", "Reason"] return cls.rsClient.selectStatusElement( element, "History", name=name, elementType=elementType, statusType=statusType, meta={"columns": columns} ) types_getElementPolicies = [ six.string_types, (six.string_types, list, type(None)), (six.string_types, list, type(None)), ] @classmethod def export_getElementPolicies(cls, element, name, statusType): """ Returns policies for a given element """ columns = ["Status", "PolicyName", "DateEffective", "LastCheckTime", "Reason"] return cls.rmClient.selectPolicyResult( element=element, name=name, statusType=statusType, meta={"columns": columns} ) types_getNodeStatuses = [] @classmethod def export_getNodeStatuses(cls): return cls.rsClient.selectStatusElement("Node", "Status") types_getTree = [six.string_types, six.string_types] def export_getTree(self, elementType, elementName): """ Given an element type and name, finds its parent site and returns all descendants of that site. """ result = self.getSite(elementType, elementName) if not result["OK"]: return result site = result["Value"] siteStatus = self.rsClient.selectStatusElement( "Site", "Status", name=site, meta={"columns": ["StatusType", "Status"]} ) if not siteStatus["OK"]: return siteStatus tree = {site: {"statusTypes": dict(siteStatus["Value"])}} result = getSiteCEMapping() if not result["OK"]: return result ces = result["Value"][site] cesStatus = self.rsClient.selectStatusElement( "Resource", "Status", name=ces, meta={"columns": ["Name", "StatusType", "Status"]} ) if not cesStatus["OK"]: return cesStatus res = DMSHelpers().getSiteSEMapping() if not res["OK"]: self.log.error("Could not get site to SE mapping", res["Message"]) return S_OK() ses = res["Value"][1].get(site, []) sesStatus = self.rsClient.selectStatusElement( "Resource", "Status", name=list(ses), meta={"columns": ["Name", "StatusType", "Status"]} ) if not sesStatus["OK"]: return sesStatus def feedTree(elementsList): elements = {} for elementTuple in elementsList["Value"]: name, statusType, status = elementTuple if name not in elements: elements[name] = {} elements[name][statusType] = status return elements tree[site]["ces"] = feedTree(cesStatus) tree[site]["ses"] = feedTree(sesStatus) return S_OK(tree) types_setToken = [six.string_types] * 7 def export_setToken(self, element, name, statusType, token, elementType, username, lastCheckTime): lastCheckTime = datetime.strptime(lastCheckTime, "%Y-%m-%d %H:%M:%S") credentials = self.getRemoteCredentials() self.log.info(credentials) elementInDB = self.rsClient.selectStatusElement( element, "Status", name=name, statusType=statusType, elementType=elementType, lastCheckTime=lastCheckTime ) if not elementInDB["OK"]: return elementInDB elif not elementInDB["Value"]: return S_ERROR("Your selection has been modified. Please refresh.") if token == "Acquire": tokenOwner = username tokenExpiration = datetime.utcnow() + timedelta(days=1) elif token == "Release": tokenOwner = "rs_svc" tokenExpiration = datetime.max else: return S_ERROR("%s is unknown token action" % token) reason = "Token %sd by %s ( web )" % (token, username) newStatus = self.rsClient.addOrModifyStatusElement( element, "Status", name=name, statusType=statusType, elementType=elementType, reason=reason, tokenOwner=tokenOwner, tokenExpiration=tokenExpiration, ) if not newStatus["OK"]: return newStatus return S_OK(reason) def getSite(self, elementType, elementName): """ Given an element name, return its site """ if elementType == "StorageElement": elementType = "SE" if elementType == "ComputingElement": elementType = "CE" result = gConfig.getSections("Resources/Sites") if not result["OK"]: return result domainNames = result["Value"] for domainName in domainNames: sites = gConfig.getSections("Resources/Sites/%s" % domainName) if not sites["OK"]: continue for site in sites["Value"]: elements = gConfig.getValue("Resources/Sites/%s/%s/%s" % (domainName, site, elementType), "") if elementName in elements: return S_OK(site) return S_ERROR("No site") # ResourceManagementClient ................................................... types_getDowntimes = [six.string_types, six.string_types, six.string_types] @classmethod def export_getDowntimes(cls, element, elementType, name): if elementType == "StorageElement": res = getSEHosts(name) if not res["OK"]: return res names = res["Value"] else: names = name return cls.rmClient.selectDowntimeCache( element=element, name=names, meta={"columns": ["StartDate", "EndDate", "Link", "Description", "Severity"]} ) types_getCachedDowntimes = [ (six.string_types, type(None), list), (six.string_types, type(None), list), (six.string_types, type(None), list), (six.string_types, type(None), list), ] def export_getCachedDowntimes(self, element, elementType, name, severity): if elementType == "StorageElement": res = getSEHosts(name) if not res["OK"]: return res names = res["Value"] else: names = name columns = ["Element", "Name", "StartDate", "EndDate", "Severity", "Description", "Link"] res = self.rmClient.selectDowntimeCache( element=element, name=names, severity=severity, meta={"columns": columns} ) if not res["OK"]: self.log.error("Error selecting downtime cache", res["Message"]) return res result = S_OK(res["Value"]) result["Columns"] = columns return result types_setStatus = [six.string_types] * 7 def export_setStatus(self, element, name, statusType, status, elementType, username, lastCheckTime): if not lastCheckTime: lastCheckTime = None else: lastCheckTime = datetime.strptime(lastCheckTime, "%Y-%m-%d %H:%M:%S") credentials = self.getRemoteCredentials() self.log.info(credentials) elementInDB = self.rsClient.selectStatusElement( element, "Status", name=name, statusType=statusType, # status = status, elementType=elementType, lastCheckTime=lastCheckTime, ) if not elementInDB["OK"]: self.log.error("Error selecting status elements", elementInDB["Message"]) return elementInDB elif not elementInDB["Value"]: return S_ERROR("Your selection has been modified. Please refresh.") reason = "Status %s forced by %s ( web )" % (status, username) tokenExpiration = datetime.utcnow() + timedelta(days=1) newStatus = self.rsClient.addOrModifyStatusElement( element, "Status", name=name, statusType=statusType, status=status, elementType=elementType, reason=reason, tokenOwner=username, tokenExpiration=tokenExpiration, ) if not newStatus["OK"]: self.log.error("Error setting status", newStatus["Message"]) return newStatus return S_OK(reason)