package org.gcube.smartgears.handler.resourceregistry.resourcemanager; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.lang.management.ManagementFactory; import java.lang.management.OperatingSystemMXBean; import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.file.FileStore; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.management.AttributeNotFoundException; import javax.management.InstanceNotFoundException; import javax.management.MBeanException; import javax.management.MBeanServer; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import javax.management.ReflectionException; import org.gcube.informationsystem.model.impl.properties.HeaderImpl; import org.gcube.informationsystem.model.reference.entities.Facet; import org.gcube.informationsystem.model.reference.entities.Resource; import org.gcube.informationsystem.model.reference.properties.Header; import org.gcube.informationsystem.model.reference.relations.ConsistsOf; import org.gcube.informationsystem.resourceregistry.api.contexts.ContextCache; import org.gcube.informationsystem.resourceregistry.api.exceptions.AvailableInAnotherContextException; import org.gcube.informationsystem.resourceregistry.api.exceptions.NotFoundException; import org.gcube.informationsystem.resourceregistry.api.exceptions.ResourceRegistryException; import org.gcube.informationsystem.resourceregistry.api.exceptions.context.ContextNotFoundException; import org.gcube.informationsystem.resourceregistry.api.exceptions.entity.resource.ResourceNotFoundException; import org.gcube.informationsystem.resourceregistry.client.ResourceRegistryClient; import org.gcube.informationsystem.resourceregistry.client.ResourceRegistryClientFactory; import org.gcube.informationsystem.resourceregistry.publisher.ResourceRegistryPublisher; import org.gcube.informationsystem.resourceregistry.publisher.ResourceRegistryPublisherFactory; import org.gcube.resourcemanagement.model.impl.entities.facets.CPUFacetImpl; import org.gcube.resourcemanagement.model.impl.entities.facets.EventFacetImpl; import org.gcube.resourcemanagement.model.impl.entities.facets.LocationFacetImpl; import org.gcube.resourcemanagement.model.impl.entities.facets.MemoryFacetImpl; import org.gcube.resourcemanagement.model.impl.entities.facets.NetworkingFacetImpl; import org.gcube.resourcemanagement.model.impl.entities.facets.SimplePropertyFacetImpl; import org.gcube.resourcemanagement.model.impl.entities.facets.SoftwareFacetImpl; import org.gcube.resourcemanagement.model.impl.entities.facets.StateFacetImpl; import org.gcube.resourcemanagement.model.impl.entities.resources.HostingNodeImpl; import org.gcube.resourcemanagement.model.impl.relations.consistsof.HasPersistentMemoryImpl; import org.gcube.resourcemanagement.model.impl.relations.consistsof.HasVolatileMemoryImpl; import org.gcube.resourcemanagement.model.impl.relations.consistsof.IsIdentifiedByImpl; import org.gcube.resourcemanagement.model.reference.entities.facets.CPUFacet; import org.gcube.resourcemanagement.model.reference.entities.facets.EventFacet; import org.gcube.resourcemanagement.model.reference.entities.facets.LocationFacet; import org.gcube.resourcemanagement.model.reference.entities.facets.MemoryFacet; import org.gcube.resourcemanagement.model.reference.entities.facets.MemoryFacet.MemoryUnit; import org.gcube.resourcemanagement.model.reference.entities.facets.NetworkingFacet; import org.gcube.resourcemanagement.model.reference.entities.facets.SimplePropertyFacet; import org.gcube.resourcemanagement.model.reference.entities.facets.SoftwareFacet; import org.gcube.resourcemanagement.model.reference.entities.facets.StateFacet; import org.gcube.resourcemanagement.model.reference.entities.resources.HostingNode; import org.gcube.resourcemanagement.model.reference.relations.consistsof.HasPersistentMemory; import org.gcube.resourcemanagement.model.reference.relations.consistsof.HasVolatileMemory; import org.gcube.resourcemanagement.model.reference.relations.consistsof.IsIdentifiedBy; import org.gcube.smartgears.configuration.container.ContainerConfiguration; import org.gcube.smartgears.configuration.container.Site; import org.gcube.smartgears.configuration.library.SmartGearsConfiguration; import org.gcube.smartgears.context.container.ContainerContext; import org.gcube.smartgears.handler.resourceregistry.ContextUtility; import org.gcube.smartgears.lifecycle.container.ContainerState; import org.gcube.smartgears.provider.ProviderFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class HostingNodeManager { private static Logger logger = LoggerFactory.getLogger(HostingNodeManager.class); public static final String MEMORY_TYPE = "memoryType"; public static final String MEMORY_TYPE_RAM = "RAM"; public static final String MEMORY_TYPE_JVM = "JVM"; public static final String JVM_MAX_MEMORY = "jvmMaxMemory"; public static final String MESSAGE = "message"; private ContainerContext containerContext; private ResourceRegistryPublisher resourceRegistryPublisher; private HostingNode hostingNode; public HostingNodeManager(ContainerContext containerContext) { this.containerContext = containerContext; this.resourceRegistryPublisher = ResourceRegistryPublisherFactory.create(); } public HostingNode getHostingNode() { return hostingNode; } public void addToContext() throws ResourceNotFoundException, ContextNotFoundException, ResourceRegistryException { try { resourceRegistryPublisher.addResourceToCurrentContext(hostingNode, false); logger.info("{} successfully added to current context ({})", hostingNode, ContextUtility.getCurrentContextName()); } catch (Exception e) { logger.error("Unable to add {} to current context ({})", hostingNode, ContextUtility.getCurrentContextName(), e); } } public void removeFromContext() throws ResourceNotFoundException, ContextNotFoundException, ResourceRegistryException { try { resourceRegistryPublisher.removeResourceFromCurrentContext(hostingNode, false); logger.info("{} successfully removed from current context ({})", hostingNode, ContextUtility.getCurrentContextName()); } catch (Exception e) { logger.error("Unable to remove {} from current context ({})", hostingNode, ContextUtility.getCurrentContextName(), e); } } public void removeFromContext(UUID contextUUID) throws ResourceNotFoundException, ContextNotFoundException, ResourceRegistryException { String contextFullName = ContextCache.getInstance().getContextFullNameByUUID(contextUUID); try { resourceRegistryPublisher.removeResourceFromContext(hostingNode, contextUUID, false); logger.info("{} successfully removed from context ({})", hostingNode, contextFullName); } catch (Exception e) { logger.error("Unable to remove {} from current context ({})", hostingNode, contextFullName, e); } } public HostingNode updateFacets() throws ResourceRegistryException { logger.debug("Updating {}", HostingNode.NAME); StateFacet stateFacet = null; MemoryFacet ramFacet = null; MemoryFacet jvmMemoryFacet = null; MemoryFacet disk = null; List> consistsOfToRemove = new ArrayList<>(); List> consistsOfList = hostingNode.getConsistsOf(); for (ConsistsOf c : consistsOfList) { if (c.getTarget() instanceof StateFacet) { stateFacet = (StateFacet) c.getTarget(); stateFacet = getStateFacet(stateFacet); continue; } if (c instanceof HasVolatileMemory) { String memoryType = (String) c.getAdditionalProperty(MEMORY_TYPE); if (memoryType.compareTo(MEMORY_TYPE_RAM) == 0) { ramFacet = (MemoryFacet) c.getTarget(); ramFacet = getRamInfo(ramFacet); continue; } if (memoryType.compareTo(MEMORY_TYPE_JVM) == 0) { jvmMemoryFacet = (MemoryFacet) c.getTarget(); jvmMemoryFacet = getJVMMemoryInfo(jvmMemoryFacet); continue; } } if (c instanceof HasPersistentMemory) { disk = (MemoryFacet) c.getTarget(); disk = getDiskSpace(disk); continue; } consistsOfToRemove.add(c); } // Resource Update has effect only on specified facet. // Removing the one that have not to be changed consistsOfList.removeAll(consistsOfToRemove); try { hostingNode = resourceRegistryPublisher.updateResource(hostingNode); } catch (NotFoundException e) { /* Update failed trying to recreate it */ // ReAdding the removed relations to recreate all consistsOfList.addAll(consistsOfToRemove); hostingNode = resourceRegistryPublisher.createResource(hostingNode); } catch (AvailableInAnotherContextException e) { addToContext(); hostingNode = resourceRegistryPublisher.updateResource(hostingNode); } catch (ResourceRegistryException e) { logger.error("error trying to publish hosting node", e); } return hostingNode; } public HostingNode updateStatus() throws ResourceRegistryException { ContainerState containerState = containerContext.lifecycle().state(); logger.debug("Setting {} {}", HostingNode.NAME, containerState.remoteForm().toLowerCase()); EventFacet eventFacet = null; StateFacet stateFacet = null; List> consistsOfToRemove = new ArrayList<>(); List> consistsOfList = hostingNode.getConsistsOf(); for (ConsistsOf c : consistsOfList) { if (c.getTarget() instanceof StateFacet) { stateFacet = (StateFacet) c.getTarget(); stateFacet = getStateFacet(stateFacet); continue; } if(c.getTarget() instanceof EventFacet) { eventFacet = (EventFacet) c.getTarget(); String value = eventFacet.getEvent(); if(value.compareTo(containerState.remoteForm().toLowerCase())==0) { // This facet must be updated (the date must be updated) so it must not be removed from udpate eventFacet.setDate(Calendar.getInstance().getTime()); continue; }else { // This is not the event facet to be updated // Setting the variable to null so it will be created if the event does not already exists. eventFacet = null; } } consistsOfToRemove.add(c); } // Resource Update has effect only on specified facet. // Removing the one that have not to be changed consistsOfList.removeAll(consistsOfToRemove); if(eventFacet == null) { eventFacet = getEventFacet(); hostingNode.addFacet(eventFacet); } try { hostingNode = resourceRegistryPublisher.updateResource(hostingNode); } catch (NotFoundException e) { /* Update failed trying to recreate it */ // ReAdding the removed relations to recreate all consistsOfList.addAll(consistsOfToRemove); hostingNode = resourceRegistryPublisher.createResource(hostingNode); } catch (AvailableInAnotherContextException e) { addToContext(); hostingNode = resourceRegistryPublisher.updateResource(hostingNode); } catch (ResourceRegistryException e) { logger.error("error trying to publish hosting node", e); } return hostingNode; } private HostingNode instantiateHostingNode() { logger.info("Creating {}", HostingNode.NAME); ContainerConfiguration containerConfiguration = containerContext.configuration(); String id = containerContext.id(); UUID uuid = UUID.fromString(id); HostingNode hostingNode = new HostingNodeImpl(); Header header = new HeaderImpl(uuid); hostingNode.setHeader(header); NetworkingFacet networkingFacet = new NetworkingFacetImpl(); try { networkingFacet.setIPAddress(InetAddress.getLocalHost().getHostAddress()); } catch (UnknownHostException e) { logger.warn("unable to detect the IP address of the host"); } String hostname = containerConfiguration.hostname(); networkingFacet.setHostName(hostname); networkingFacet.setDomainName(getDomain(hostname)); networkingFacet.setAdditionalProperty("Port", containerConfiguration.port()); IsIdentifiedBy isIdentifiedBy = new IsIdentifiedByImpl<>(hostingNode, networkingFacet, null); hostingNode.addFacet(isIdentifiedBy); List cpuFacets = getCPUFacets(); for (CPUFacet cpuFacet : cpuFacets) { hostingNode.addFacet(cpuFacet); } SoftwareFacet osSoftwareFacet = new SoftwareFacetImpl(); OperatingSystemMXBean mxbean = ManagementFactory.getOperatingSystemMXBean(); osSoftwareFacet.setGroup(mxbean.getName()); // softwareFacet.setGroup(System.getProperty("os.name")); osSoftwareFacet.setName(mxbean.getArch()); // softwareFacet.setName(System.getProperty("os.arch")); osSoftwareFacet.setVersion(mxbean.getVersion()); // softwareFacet.setName(System.getProperty("os.version")); hostingNode.addFacet(osSoftwareFacet); SmartGearsConfiguration config = ProviderFactory.provider().smartgearsConfiguration(); SoftwareFacet smartgearsSoftwareFacet = new SoftwareFacetImpl(); smartgearsSoftwareFacet.setGroup("gCube"); smartgearsSoftwareFacet.setName("SmartGears"); smartgearsSoftwareFacet.setVersion(config.version()); hostingNode.addFacet(smartgearsSoftwareFacet); SoftwareFacet smartgearsDistributionSoftwareFacet = new SoftwareFacetImpl(); smartgearsDistributionSoftwareFacet.setGroup("gCube"); smartgearsDistributionSoftwareFacet.setName("SmartGearsDistribution"); String smartgearsDistributionVersion = containerConfiguration.properties().get("SmartGearsDistribution"); smartgearsDistributionSoftwareFacet.setVersion(smartgearsDistributionVersion); smartgearsDistributionSoftwareFacet.setAdditionalProperty("publication-frequency", String.valueOf(containerConfiguration.publicationFrequency())); hostingNode.addFacet(smartgearsDistributionSoftwareFacet); SoftwareFacet javaSoftwareFacet = new SoftwareFacetImpl(); javaSoftwareFacet.setGroup(System.getProperty("java.vendor")); javaSoftwareFacet.setName("Java"); javaSoftwareFacet.setVersion(System.getProperty("java.version")); javaSoftwareFacet.setAdditionalProperty("java.vendor.url", System.getProperty("java.vendor.url")); javaSoftwareFacet.setAdditionalProperty("java.specification.version", System.getProperty("java.specification.version")); hostingNode.addFacet(javaSoftwareFacet); SimplePropertyFacet simplePropertyFacet = addEnvironmentVariables(containerConfiguration); hostingNode.addFacet(simplePropertyFacet); StateFacet stateFacet = getStateFacet(null); hostingNode.addFacet(stateFacet); EventFacet eventFacet = getEventFacet(); hostingNode.addFacet(eventFacet); MemoryFacet ramFacet = getRamInfo(null); HasVolatileMemory hasVolatileRAMMemory = new HasVolatileMemoryImpl( hostingNode, ramFacet, null); hasVolatileRAMMemory.setAdditionalProperty(MEMORY_TYPE, MEMORY_TYPE_RAM); hostingNode.addFacet(hasVolatileRAMMemory); MemoryFacet jvmMemoryFacet = getJVMMemoryInfo(null); HasVolatileMemory hasVolatileJVMMemory = new HasVolatileMemoryImpl( hostingNode, jvmMemoryFacet, null); hasVolatileJVMMemory.setAdditionalProperty(MEMORY_TYPE, MEMORY_TYPE_JVM); hostingNode.addFacet(hasVolatileJVMMemory); MemoryFacet diskFacet = getDiskSpace(null); HasPersistentMemory hasPersistentMemory = new HasPersistentMemoryImpl( hostingNode, diskFacet, null); hostingNode.addFacet(hasPersistentMemory); LocationFacet locationFacet = new LocationFacetImpl(); Site site = containerConfiguration.site(); locationFacet.setCountry(site.country()); locationFacet.setLocation(site.location()); locationFacet.setLatitude(site.latitude()); locationFacet.setLongitude(site.longitude()); hostingNode.addFacet(locationFacet); logger.info("hostingNode instanciated"); return hostingNode; } public HostingNode createHostingNode() throws ResourceRegistryException { ResourceRegistryClient resourceRegistryClient = ResourceRegistryClientFactory.create(); UUID uuid = UUID.fromString(containerContext.id()); try { hostingNode = resourceRegistryClient.getInstance(HostingNode.class, uuid); hostingNode = updateFacets(); } catch (NotFoundException e) { hostingNode = instantiateHostingNode(); hostingNode = resourceRegistryPublisher.createResource(hostingNode); } catch (AvailableInAnotherContextException e) { addToContext(); hostingNode = resourceRegistryClient.getInstance(HostingNode.class, uuid); } return hostingNode; } private StateFacet getStateFacet(StateFacet stateFacet) { if (stateFacet == null) { stateFacet = new StateFacetImpl(); } String state = containerContext.lifecycle().state().remoteForm().toLowerCase(); stateFacet.setValue(state); return stateFacet; } private EventFacet getEventFacet() { EventFacet eventFacet = new EventFacetImpl(); String state = containerContext.lifecycle().state().remoteForm().toLowerCase(); eventFacet.setDate(Calendar.getInstance().getTime()); eventFacet.setEvent(state); return eventFacet; } private MemoryFacet getDiskSpace(MemoryFacet memoryFacet) { if (memoryFacet == null) { memoryFacet = new MemoryFacetImpl(); } long free = 0; long total = 0; try { FileStore fileStore = Files .getFileStore(Paths.get(containerContext.configuration().persistence().location())); free = fileStore.getUsableSpace() / 1048576; // 1048576 = 1024*1024 // user to convert // bytes in MByte total = fileStore.getTotalSpace() / 1048576; // 1048576 = 1024*1024 // user to convert // bytes in MByte } catch (IOException ioe) { logger.warn("Unable to detect disk space information", ioe); memoryFacet.setAdditionalProperty(MESSAGE, "Unable to detect disk space information."); } memoryFacet.setUnit(MemoryUnit.MB); memoryFacet.setSize(total); memoryFacet.setUsed(total - free); return memoryFacet; } private static final long BYTE_TO_MB = 1024 * 1024; private MemoryFacet getRamInfo(MemoryFacet memoryFacet) { if (memoryFacet == null) { memoryFacet = new MemoryFacetImpl(); } /* * OperatingSystemMXBean mxbean = ManagementFactory.getOperatingSystemMXBean(); * com.sun.management.OperatingSystemMXBean sunmxbean = * (com.sun.management.OperatingSystemMXBean) mxbean; long freeMemory = * sunmxbean.getFreePhysicalMemorySize() / 1048576; // in MB long totalMemory = * sunmxbean.getTotalPhysicalMemorySize() / 1048576; // in MB */ MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); long freeMemory; try { freeMemory = Long.parseLong(mBeanServer .getAttribute(new ObjectName("java.lang", "type", "OperatingSystem"), "FreePhysicalMemorySize") .toString()) / BYTE_TO_MB; } catch (NumberFormatException | InstanceNotFoundException | AttributeNotFoundException | MalformedObjectNameException | ReflectionException | MBeanException e) { logger.warn( "Unable to get free memory from Operating System. Going to get JVM Memory. Better than nothing"); long allocatedMemory = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()); freeMemory = Runtime.getRuntime().maxMemory() - allocatedMemory; } long totalMemory; try { totalMemory = Long.parseLong(mBeanServer .getAttribute(new ObjectName("java.lang", "type", "OperatingSystem"), "TotalPhysicalMemorySize") .toString()) / BYTE_TO_MB; } catch (NumberFormatException | InstanceNotFoundException | AttributeNotFoundException | MalformedObjectNameException | ReflectionException | MBeanException e) { logger.warn("Unable to total memory from Operating System. Going to get JVM Memory. Better than nothing"); totalMemory = Runtime.getRuntime().maxMemory(); } memoryFacet.setUnit(MemoryUnit.MB); memoryFacet.setSize(totalMemory); memoryFacet.setUsed(totalMemory - freeMemory); return memoryFacet; } private MemoryFacet getJVMMemoryInfo(MemoryFacet memoryFacet) { if (memoryFacet == null) { memoryFacet = new MemoryFacetImpl(); } /* 1048576 = 1024*1024 used to convert bytes in MByte */ long jvmFreeMemory = Runtime.getRuntime().freeMemory() / 1048576; long jvmTotalMemory = Runtime.getRuntime().totalMemory() / 1048576; long jvmMaxMemory = Runtime.getRuntime().maxMemory() / 1048576; memoryFacet.setUnit(MemoryUnit.MB); memoryFacet.setSize(jvmTotalMemory); memoryFacet.setUsed(jvmTotalMemory - jvmFreeMemory); memoryFacet.setAdditionalProperty(JVM_MAX_MEMORY, jvmMaxMemory); return memoryFacet; } private static String sanitizeKey(String key) { return key.trim().replace(" ", "_"); } private SimplePropertyFacet addEnvironmentVariables(ContainerConfiguration containerConfiguration) { Map map = new HashMap(); map.putAll(containerConfiguration.properties()); map.putAll(System.getenv()); SimplePropertyFacet simplePropertyFacet = new SimplePropertyFacetImpl(); simplePropertyFacet.setName("ENVIRONMENT_VARIABLES"); simplePropertyFacet.setValue(""); for (Map.Entry entry : map.entrySet()) { String varname = entry.getKey(); if ((varname.compareToIgnoreCase("CLASSPATH") == 0) || (varname.compareToIgnoreCase("PATH") == 0) || (varname.contains("SSH")) || (varname.contains("MAIL")) || (varname.compareToIgnoreCase("LS_COLORS") == 0)) { continue; } simplePropertyFacet.setAdditionalProperty(sanitizeKey(entry.getKey()), entry.getValue()); } return simplePropertyFacet; } private static String getDomain(String hostname) { try { Pattern pattern = Pattern.compile("([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})"); Matcher regexMatcher = pattern.matcher(hostname); if (regexMatcher.matches()) { // it's an IP address, nothing to trim return hostname; } return hostname.substring(hostname.indexOf(".") + 1); } catch (Exception e) { logger.warn("Error while getting domain from hostname"); return hostname; } } public static final String CPU_PROCESSOR = "processor"; public static final String CPU_VENDOR_ID = "vendor_id"; public static final String CPU_MODEL_NAME = "model name"; public static final String CPU_CPU_MHZ = "cpu MHz"; public static final String CPU_MODEL_T = "model\t"; public static final String CPU_MODEL_B = "model\b"; public static final String CPU_MODEL_NUMBER = "modelNumber"; public static List getCPUFacets() { List cpuFacets = new ArrayList<>(); File file = new File("/proc/cpuinfo"); if (!file.exists()) { logger.warn("cannot acquire CPU info (no /proc/cpuinfo)"); return cpuFacets; } BufferedReader input = null; try { input = new BufferedReader(new FileReader(file)); String line = null; CPUFacet cpuFacet = null; while ((line = input.readLine()) != null) { if ((line.startsWith(CPU_PROCESSOR))) { // add the current // processor to the map cpuFacet = new CPUFacetImpl(); cpuFacets.add(cpuFacet); } try { if (line.contains(CPU_VENDOR_ID)) { cpuFacet.setVendor(line.split(":")[1].trim()); continue; } } catch (Exception e) { continue; } try { if (line.contains(CPU_MODEL_NAME)) { cpuFacet.setModel(line.split(":")[1].trim()); continue; } } catch (Exception e) { continue; } try { if (line.contains(CPU_CPU_MHZ)) { cpuFacet.setClockSpeed(line.split(":")[1].trim()); continue; } } catch (Exception e) { continue; } try { if ((line.contains(CPU_MODEL_T)) || (line.contains(CPU_MODEL_B))) { cpuFacet.setAdditionalProperty(CPU_MODEL_NUMBER, line.split(":")[1].trim()); continue; } } catch (Exception e) { continue; } try { String[] nameValue = line.split(":"); cpuFacet.setAdditionalProperty(sanitizeKey(nameValue[0]), line.split(":")[1].trim()); } catch (Exception e) { } } } catch (Exception e) { logger.warn("unable to acquire CPU info", e); } finally { if (input != null) { try { input.close(); } catch (IOException e) { logger.warn("unable to close stream", e); } } } return cpuFacets; } public Set getContextsUUID() throws Exception { return resourceRegistryPublisher.getResourceContexts(hostingNode); } }