diff --git a/.classpath b/.classpath index 570eb83..2e55f9e 100644 --- a/.classpath +++ b/.classpath @@ -1,9 +1,6 @@ - - - diff --git a/INSTALL b/INSTALL deleted file mode 100644 index e9dac05..0000000 --- a/INSTALL +++ /dev/null @@ -1,11 +0,0 @@ - -to compile: - ant garService - ant buildStubs - -to deploy: - gcore-deploy-service org.gcube.vremanagement.resourcemanager.gar - cp org.gcube.vremanagement.resourcemanager.stubs.jar - - - \ No newline at end of file diff --git a/config/build.properties b/config/build.properties new file mode 100755 index 0000000..2af5eef --- /dev/null +++ b/config/build.properties @@ -0,0 +1,9 @@ +package = org.gcube.vremanagement.resourcemanager +lib.dir = Dependencies/ResourceManager +wsdl.1 = ResourceManager +package.1 = resourcemanager +package.2 = common +namespace.1=http://gcube-system.org/namespaces/vremanagement/resourcemanager +namespace.2=http://gcube-system.org/common/vremanagement/types + + diff --git a/config/deploy-jndi-config.xml b/config/deploy-jndi-config.xml new file mode 100644 index 0000000..7263b18 --- /dev/null +++ b/config/deploy-jndi-config.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + factory + org.globus.wsrf.jndi.BeanFactory + + + resourceClass + org.gcube.vremanagement.resourcemanager.impl.state.InstanceState + + + + + + + + diff --git a/config/deploy-server.wsdd b/config/deploy-server.wsdd new file mode 100644 index 0000000..05f1ff1 --- /dev/null +++ b/config/deploy-server.wsdd @@ -0,0 +1,17 @@ + + + + + share/schema/org.gcube.vremanagement.resourcemanager/ResourceManager_service.wsdl + + + + + + + + + diff --git a/config/profile.xml b/config/profile.xml new file mode 100644 index 0000000..e2ae515 --- /dev/null +++ b/config/profile.xml @@ -0,0 +1,50 @@ + + + + Service + + ResourceManager creates and manages a VRE context + VREManagement + ResourceManager + 1.0.0 + +
+ ResourceManager creates and manages Scopes within a gCube infrastructure + ResourceManager-service + 1.2.0-SNAPSHOT + + + + org.gcube.vremanagement.resourcemanager.gar + + gcube/vremanagement/ResourceManager + + + +
+ + Stubs for ResourceManager: provide facilities to interact with a ResourceManager instance + ResourceManager-stubs + 1.2.0-SNAPSHOT + + + + library + + org.gcube.vremanagement.resourcemanager.stubs.jar + + + + Test-suite for ResourceManager: provide sample usages of interaction with a ResourceManager instance + ResourceManager-test-suite + 1.2.0-SNAPSHOT + + + application + + lib/org.gcube.vremanagement.resourcemanager.testsuite.jar + + +
+
+
\ No newline at end of file diff --git a/config/reports/readme.txt b/config/reports/readme.txt new file mode 100755 index 0000000..de7adbc --- /dev/null +++ b/config/reports/readme.txt @@ -0,0 +1 @@ +This folder contains the resource reports. diff --git a/config/security_descriptor.xml b/config/security_descriptor.xml new file mode 100644 index 0000000..92fe123 --- /dev/null +++ b/config/security_descriptor.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/distro/INSTALL b/distro/INSTALL new file mode 100644 index 0000000..2cbeb98 --- /dev/null +++ b/distro/INSTALL @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/LICENSE b/distro/LICENSE similarity index 100% rename from LICENSE rename to distro/LICENSE diff --git a/MAINTAINERS b/distro/MAINTAINERS similarity index 100% rename from MAINTAINERS rename to distro/MAINTAINERS diff --git a/README b/distro/README similarity index 100% rename from README rename to distro/README diff --git a/changelog.xml b/distro/changelog.xml similarity index 100% rename from changelog.xml rename to distro/changelog.xml diff --git a/distro/descriptor.xml b/distro/descriptor.xml new file mode 100644 index 0000000..4d4ab09 --- /dev/null +++ b/distro/descriptor.xml @@ -0,0 +1,70 @@ + + servicearchive + + tar.gz + + / + + + ${distroDirectory} + / + true + + README + LICENSE + INSTALL + MAINTAINERS + changelog.xml + + 755 + true + + + + + + *:resource-manager-stubs + + false + false + resource-manager-stubs + + + + *:resource-manager-test-suite + + false + false + resource-manager-test-suite + + + + + ${configDirectory}/profile.xml + + true + + + target/${build.finalName}.gar + ${project.artifactId} + + + ${distroDirectory}/svnpath.txt + ${project.artifactId} + true + + + ${distroDirectory}/svnpath.txt + resource-manager-stubs + true + + + ${distroDirectory}/svnpath.txt + resource-manager-test-suite + true + + + \ No newline at end of file diff --git a/distro/svnpath.txt b/distro/svnpath.txt new file mode 100644 index 0000000..f416f9d --- /dev/null +++ b/distro/svnpath.txt @@ -0,0 +1 @@ +${scm.url} diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..7ce8a2c --- /dev/null +++ b/pom.xml @@ -0,0 +1,77 @@ + + + 4.0.0 + + maven-parent + org.gcube.tools + 1.0.0 + + + + org.gcube.resourcemanagement + resource-manager + 1.2.0-SNAPSHOT + pom + + Resource Manager Reactor + + + + resource-manager-stubs + resource-manager-service + resource-manager-test-suite + + + + ${project.basedir}/../wsdl + ${project.basedir}/../target/generated-sources/wsdl + ${project.basedir}/../config + ${project.basedir}/../distro + http://gcube-system.org/namespaces/vremanagement/resourcemanager + org.gcube.vremanagement.resourcemanager + + + + + + + + org.gcube.core + gcf + [1.4.0-SNAPSHOT,1.5.0-SNAPSHOT) + provided + + + junit + junit + 4.8.2 + test + + + + + + + + + + + + org.gcube.tools + maven-service-plugin + 1.0.0 + + deployer + ${basepackage} + ${wsdlDirectory} + ${wsdlOutputDirectory} + ${configDirectory} + + + + + + + \ No newline at end of file diff --git a/resource-manager-service/org.gcube.tools-my-container-tar.gz-distro-1.0.0.marker b/resource-manager-service/org.gcube.tools-my-container-tar.gz-distro-1.0.0.marker new file mode 100644 index 0000000..e69de29 diff --git a/resource-manager-service/pom.xml b/resource-manager-service/pom.xml new file mode 100644 index 0000000..92d2da2 --- /dev/null +++ b/resource-manager-service/pom.xml @@ -0,0 +1,209 @@ + + 4.0.0 + + org.gcube.resourcemanagement + resource-manager + 1.2.0-SNAPSHOT + .. + + + resource-manager-service + Resource Manager Service + A service for managing subsets of gCube resources in a given scope + + + scm:svn:http://svn.d4science.research-infrastructures.eu/gcube/trunk/vre-management/ResourceManager + scm:svn:https://svn.d4science.research-infrastructures.eu/gcube/trunk/vre-management/ResourceManager + http://svn.d4science.research-infrastructures.eu/gcube/trunk/vre-management/ResourceManager + + + + + + + org.gcube.core + gcf + provided + + + + org.gcube.resourcemanagement + resource-manager-stubs + ${project.version} + + + + org.gcube.resourcemanagement + softwaregateway-stubs + [1.0.0-SNAPSHOT,1.1.0-SNAPSHOT) + provided + + + + org.gcube.vremanagement + ghnmanager-stubs + [1.5.0-SNAPSHOT,1.6.0-SNAPSHOT) + provided + + + + com.thoughtworks.xstream + xstream + 1.3.1 + provided + + + + org.gcube.vremanagement + resourcebroker-serialization + [1.2.0-SNAPSHOT,1.3.0-SNAPSHOT) + + + + org.gcube.vremanagement + resourcebroker-stubs + [1.2.0-SNAPSHOT,1.3.0-SNAPSHOT) + + + + org.gcube.resourcemanagement + deployer-stubs + [2.3.0-SNAPSHOT,2.4.0-SNAPSHOT) + provided + + + + + + org.gcube.tools + my-container + 1.0.0 + test + + + + junit + junit + 4.8.2 + test + + + + + + + + local-deploy + + + + org.gcube.tools + maven-service-plugin + + + package + + local-deploy + + + + + + + + + + + + + + + + + org.gcube.tools + maven-service-plugin + + + make-gar + + gar-gen + + + + + + + + maven-dependency-plugin + 2.3 + + + install-my-container + generate-test-resources + + + + org.gcube.tools + my-container + 1.0.0 + tar.gz + distro + false + ${project.basedir} + + + ${project.basedir} + + + unpack + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 2.2 + + + ${distroDirectory}/descriptor.xml + + + + + servicearchive + install + + single + + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 1.7 + + + add-source + process-sources + + add-source + + + + ${basedir}/src/main/java + + + + + + + + + \ No newline at end of file diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/ResourceManager.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/ResourceManager.java new file mode 100644 index 0000000..88fc908 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/ResourceManager.java @@ -0,0 +1,327 @@ +package org.gcube.vremanagement.resourcemanager.impl; + +import java.util.HashSet; +import java.util.Set; + +import org.apache.axis.components.uuid.UUIDGenFactory; +import org.gcube.common.core.contexts.GCUBEServiceContext; +import org.gcube.common.core.faults.GCUBEFault; +import org.gcube.common.core.porttypes.GCUBEPortType; +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.common.core.scope.GCUBEScopeNotSupportedException; +import org.gcube.common.core.scope.GCUBEScope.MalformedScopeExpressionException; +import org.gcube.common.core.scope.GCUBEScopeManager.IllegalScopeException; +import org.gcube.common.core.utils.logging.GCUBELog; +import org.gcube.vremanagement.resourcemanager.impl.contexts.ServiceContext; +import org.gcube.vremanagement.resourcemanager.impl.contexts.StatefulPortTypeContext; +import org.gcube.vremanagement.resourcemanager.impl.deployment.DeployerReport; +import org.gcube.vremanagement.resourcemanager.impl.deployment.VirtualNodeManager; +import org.gcube.vremanagement.resourcemanager.impl.deployment.DeployerReport.DeployedRunningInstance; +import org.gcube.vremanagement.resourcemanager.stubs.resourcemanager.AddResourcesParameters; +import org.gcube.vremanagement.resourcemanager.stubs.resourcemanager.CreateScopeParameters; +import org.gcube.vremanagement.resourcemanager.stubs.resourcemanager.DisposeScopeParameters; +import org.gcube.vremanagement.resourcemanager.stubs.resourcemanager.InvalidOptionsFaultType; +import org.gcube.vremanagement.resourcemanager.stubs.resourcemanager.InvalidScopeFaultType; +import org.gcube.vremanagement.resourcemanager.stubs.resourcemanager.NoSuchReportFaultType; +import org.gcube.vremanagement.resourcemanager.stubs.resourcemanager.OptionsParameters; +import org.gcube.vremanagement.resourcemanager.stubs.resourcemanager.RemoveResourcesParameters; +import org.gcube.vremanagement.resourcemanager.stubs.resourcemanager.ResourcesCreationFaultType; +import org.gcube.vremanagement.resourcemanager.stubs.resourcemanager.ResourcesRemovalFaultType; +import org.gcube.vremanagement.resourcemanager.stubs.resourcemanager.ScopeOption; +import org.gcube.vremanagement.resourcemanager.stubs.resourcemanager.SendReportParameters; +import org.gcube.vremanagement.resourcemanager.impl.operators.AddResourcesOperator; +import org.gcube.vremanagement.resourcemanager.impl.operators.DisposeScopeOperator; +import org.gcube.vremanagement.resourcemanager.impl.operators.OperatorConfig; +import org.gcube.vremanagement.resourcemanager.impl.operators.RemoveResourcesOperator; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResource; +import org.gcube.vremanagement.resourcemanager.impl.state.Session; +import org.gcube.vremanagement.resourcemanager.impl.state.PublishedScopeResource; +import org.gcube.vremanagement.resourcemanager.impl.state.InstanceState; +import org.gcube.vremanagement.resourcemanager.impl.state.PublishedScopeResource.UnknownScopeOptionException; +import org.gcube.vremanagement.resourcemanager.impl.state.Session.OPERATION; +import org.globus.wsrf.NoSuchResourceException; +import org.globus.wsrf.ResourceException; + + +/** + * ResourceManager port-type implementation + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class ResourceManager extends GCUBEPortType { + + /** Singleton resource name*/ + public static final String SINGLETON_RESOURCE_KEY = "VREManagerState"; + + /** + * Object logger. + */ + protected final GCUBELog logger = new GCUBELog(this, ServiceContext.getContext()); + + @Override + protected GCUBEServiceContext getServiceContext() { + return ServiceContext.getContext(); + } + /** + * Adds a new group of {@link ScopedResource}s to the managed Scope + * + * @param resourcesList the resources to join + * @return the ID assigned to the operation, it can be used to retrieve the resource session by invoking the {@link ResourceManager#getReport(String)} operation + * @throws GCUBEFault if the operation fails + */ + public synchronized String addResources(AddResourcesParameters resourceList) throws ResourcesCreationFaultType, GCUBEFault { + logger.debug("AddResources operation invoked in scope " + ServiceContext.getContext().getScope().getName()); + try { + //checks the input scope + GCUBEScope targetScope = this.validateOperationScope(resourceList.getTargetScope()); + Session report = new Session(UUIDGenFactory.getUUIDGen().nextUUID(),OPERATION.AddResources, targetScope); + this.getResource().addSession(report); + new AddResourcesOperator(new OperatorConfig(report, this.getResource().getResourceList(), targetScope),resourceList).run(); + //resource.publish(); + //returns the session ID, it can be used to invoke the getReport operation + return report.getId(); + } catch (IllegalScopeException ise){ + logger.error("The target scope (" + resourceList.getTargetScope() + ") is not valid or null or not joined to this instance", ise); + throw ServiceContext.getContext().getDefaultException("The target scope (" + resourceList.getTargetScope() + ") is not valid or null or not joined to this instance", ise).toFault(); + } catch (Exception e) { + logger.error("Unable to manage the input given resources(s) within the scope: " + e.getMessage(), e); + throw ServiceContext.getContext().getDefaultException("Unable to manage the input given resources(s) within the scope: " + e.getMessage(), e).toFault(); + } + } + + /** + * Removes a group of {@link ScopedResource}s from the managed Scope + * + * @param resourcesList the resources to remove from the PublishedScopeResource + * @throws GCUBEFault if the operation fails + */ + public synchronized String removeResources(RemoveResourcesParameters resourceList) throws ResourcesRemovalFaultType, InvalidScopeFaultType { + + try { + GCUBEScope targetScope = this.validateOperationScope(resourceList.getTargetScope()); + Session report = new Session(UUIDGenFactory.getUUIDGen().nextUUID(), OPERATION.RemoveResources, targetScope); + this.getResource().addSession(report); + new RemoveResourcesOperator(new OperatorConfig(report, this.getResource().getResourceList(), targetScope),resourceList).run(); + return report.getId(); + } catch (IllegalScopeException ise){ + logger.error("The target scope (" + resourceList.getTargetScope() + ") is not valid or null or not joined to this instance", ise); + throw new InvalidScopeFaultType(); + } catch (Exception e) { + logger.error("Unable to manage the input given resources(s) within the scope: " + e.getMessage(), e); + //throw ServiceContext.getContext().getDefaultException("Unable to manage the input given resources(s) within the scope: " + e.getMessage(), e).toFault(); + throw new ResourcesRemovalFaultType(); + } + } + + /** + * Disposes the managed Scope + * @param params + * @return + * @throws GCUBEFault + */ + public synchronized String disposeScope(DisposeScopeParameters params) throws InvalidScopeFaultType, GCUBEFault { + logger.info("Dispose Scope invoked... the entire scope is going to be thrown away!!"); + try { + GCUBEScope.getScope(params.getName()).getServiceMap(); + } catch (MalformedScopeExpressionException e) { + logger.error("Invalid scope expression " + params.getName()); + throw new InvalidScopeFaultType(); + } catch (GCUBEScopeNotSupportedException e) { + logger.error("Scope not supported " + params.getName()); + throw new InvalidScopeFaultType(); + } + try { + Session report = new Session(UUIDGenFactory.getUUIDGen().nextUUID(), OPERATION.Dispose,GCUBEScope.getScope(params.getName())); + this.getResource().addSession(report); + new DisposeScopeOperator(new OperatorConfig(report, this.getResource().getResourceList(), GCUBEScope.getScope(params.getName()))).run(); + this.getResource().getPublishedScopeResource().dismiss(); + return report.getId(); + } catch (NoSuchResourceException e) { + logger.error("No resource found for this scope", e); + throw ServiceContext.getContext().getDefaultException("No resource found for this scope", e).toFault(); + } catch (Exception e) { + logger.error("Unable to dispose the scope: " + e.getMessage(), e); + throw ServiceContext.getContext().getDefaultException("Unable to dispose the scope: " + e.getMessage(), e).toFault(); + } + + } + + + public synchronized String createScope(CreateScopeParameters params) + throws InvalidScopeFaultType, InvalidOptionsFaultType, ResourcesCreationFaultType, GCUBEFault { + String name = params.getName(); + logger.info("Creating the new Scope " + name); + try { + GCUBEScope.getScope(name).getServiceMap(); + } catch (MalformedScopeExpressionException e) { + throw new InvalidScopeFaultType(); + } catch (GCUBEScopeNotSupportedException e) { + //the service map for this scope does not exist + params.getServiceMap(); + } + //String map = params.getServiceMap(); + this.changeScopeOptions(params.getOptionsParameters()); + return this.addResources(params.getAddResourcesParameters()); + + } + + /** + * Changes some options on the scope + * + * @param options the new options to change + * @throws GCUBEFault if any of the input options is not valid + */ + public void changeScopeOptions(OptionsParameters options) throws InvalidOptionsFaultType, InvalidOptionsFaultType, GCUBEFault { + PublishedScopeResource scoperesource = null; + try { + scoperesource = this.getResource().getPublishedScopeResource(); + } catch (NoSuchResourceException e) { + logger.error("No resource found for this scope", e); + throw ServiceContext.getContext().getDefaultException("No resource found for this scope", e).toFault(); + } catch (Exception e) { + logger.error("Change Scope Options fault: ", e); + throw ServiceContext.getContext().getDefaultException("Change Scope Options fault: ", e).toFault(); + } + //add the new options + for (ScopeOption option : options.getScopeOptionList()) { + if (option == null) continue; + logger.trace("ScopeOption received: " + option.getName() +"="+ option.getValue()); + try { + scoperesource.setOption(option.getName().trim(), option.getValue().trim()); + } catch (UnknownScopeOptionException e) { + logger.warn("Unknown option: " + option.getName()); + throw new InvalidOptionsFaultType(); + } catch (Exception e) { + logger.warn("Unable to read option: " + option.getName()); + throw new InvalidOptionsFaultType(); + } + } + //and publish the scope resource + try { + scoperesource.publish(); + } catch (Exception e) { + logger.error("Unable to publish the ScopeResouce", e); + throw ServiceContext.getContext().getDefaultException("Unable to publish the ScopeResouce", e).toFault(); + } + } + + /** + * Receives a deployment session. It is called by the Deployer services on the GHNs contacted within the {@link #addResources(AddResourcesParameters)} + * operation + * + * @param session the resource session + * @throws GCUBEFault if the session does not have a valid serialization + */ + public void sendReport(SendReportParameters reportMessage) throws GCUBEFault { + logger.info("Received session for session " + reportMessage.getCallbackID()); + logger.trace("Report content: \n" + reportMessage.getReport()); + try { + Session session = this.getResource().getSession(reportMessage.getCallbackID()); + DeployerReport dreport = new DeployerReport(reportMessage.getReport()); + session.addGHNReport(dreport); + session.save(); + PublishedScopeResource resource = this.getResource().getPublishedScopeResource(); + logger.debug("Status session is: " + dreport.getStatus()); + if (dreport.getStatus().compareToIgnoreCase("CLOSED") == 0) { + logger.trace("Setting the gHN " + dreport.getGHNID() + " as non working"); + //if the session is closed, declare the node as "non working" node + VirtualNodeManager.getNode(dreport.getGHNID(), this.getResource().getManagedScope()).isNotWorking(); + logger.trace("Parsing running instances (if any)..."); + Set resources = new HashSet(); + for (DeployedRunningInstance instance : dreport.getInstances()) { + if (instance.isAlive()) { + logger.trace("Adding instance " + instance.getRIID() + " to PublishedScopeResource"); + resource.addResource(instance.getInstance()); + resources.add((ScopedResource)instance.getInstance()); + } else { + logger.warn("Instance " + instance.getRIID() + " not found on the IS"); + } + } + session.addDeployedInstances(dreport.getInstances()); + //add the newly generated RIs to the Scope State + this.getResource().getResourceList().addResources(resources); + } + resource.publish(); + session.save(); +// //send feedback to the broker if needed +// if ((session.isReportClosed() +// && ((session.getOperation()==OPERATION.AddResources) +// || (session.getOperation()==OPERATION.UpdateResources) +// || (session.getOperation()==OPERATION.Create)))) { +// this.updateBroker(session); +// } + + } catch (NoSuchResourceException e) { + logger.error("Unable to find ResourceManager resource", e); + throw ServiceContext.getContext().getDefaultException("Unable to find ResourceManager resource", e).toFault(); + } catch (ResourceException e) { + logger.error("Unable to find ResourceManager resource", e); + throw ServiceContext.getContext().getDefaultException("Unable to find ResourceManager resource", e).toFault(); + } catch (Exception e) { + throw ServiceContext.getContext().getDefaultException("Unable to parse or save the Deployer Report", e).toFault(); + } + } + + /** + * Gets a Resource Report + * + * @param ID the session identifier + * @return the string serialization of the session + * @throws GNoSuchReportFaultTypeCUBEFault if the session is not found or does not have a valid serialization + */ + public String getReport(String ID) throws NoSuchReportFaultType { + try { + return this.getResource().getSession(ID).toXML(); + } catch (Exception e) { + logger.error("Unable to retrieve the Resource Report for ID " + ID,e); + throw new NoSuchReportFaultType(); + } + + } + +// /** +// * Sends the feedback on the deployment activity to the Broker +// * @param services the Resource Report +// * @param reports +// */ +// private void updateBroker(Session session) { +// if (session.getServices().size()>0) { +// try { +// logger.info("Sending feedback to the Broker"); +// BrokerConnector.getBroker(this.getResource().getManagedScope()).sendFeedback(session); +// } catch (Exception e) { +// logger.error("Can't send feedback to the Broker", e); +// } +// } +// } + + /** + * Validates the input scope of each PublishedScopeResource operation + * + * @param inScope the scope to check + * @return the accepted scope + * @throws IllegalScopeException if the given scope has not been accepted + */ + + private GCUBEScope validateOperationScope(String inScope) throws IllegalScopeException { + if ((inScope == null) || (inScope.compareToIgnoreCase("") == 0)) + return ServiceContext.getContext().getScope(); // use the instance scope as default + else if (GCUBEScope.getScope(inScope.trim()).isEnclosedIn(ServiceContext.getContext().getScope())) + return GCUBEScope.getScope(inScope.trim()); + else + throw new IllegalScopeException(); + } + + /** + * Gets the singleton resource + * + * @return the {@link InstanceState} + * @throws NoSuchResourceException + * @throws ResourceException + */ + private InstanceState getResource() throws NoSuchResourceException, ResourceException{ + return (InstanceState) StatefulPortTypeContext.getContext().getWSHome().find(StatefulPortTypeContext.getContext().makeKey(SINGLETON_RESOURCE_KEY)); + } + +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/Broker.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/Broker.java new file mode 100644 index 0000000..75b7667 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/Broker.java @@ -0,0 +1,43 @@ +package org.gcube.vremanagement.resourcemanager.impl.brokerage; + + +import java.util.Set; + +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedDeployedService; +import org.gcube.vremanagement.resourcemanager.impl.state.Session; + +/** + * + * Models the expected behavior of a Broker. + * A Broker is an entity that can create Deployment Plans for a set of {@link ScopedDeployedService}s to be deployed + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public interface Broker { + + /** + * Initializes the Broker instance + * @param scope the scope where the broker will act + * @throws Exception if the initialization fails (depends on the broker's nature) + */ + public void initialize(GCUBEScope scope) throws Exception; + + /** + * Creates a deployment plan for the given services + * @param session the current session + * @param services the services to deploy + * @param suggestedGHNs the (eventually) GHNs suggested for the deployment, if specified, only these nodes will be used by the planner + * @throws Exception if the preparation of the plan fails + */ + public void makePlan(Session session, Set services, String[] suggestedGHNs) throws Exception; + + /** + * Sends feedback to the broker about the execution of the plan for the given session + * @param session the current session + * @throws Exception if the broker is not able to manage the feedback + */ + public void sendFeedback(Session session) throws Exception; + +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/BrokerConnector.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/BrokerConnector.java new file mode 100644 index 0000000..36369e5 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/BrokerConnector.java @@ -0,0 +1,60 @@ +package org.gcube.vremanagement.resourcemanager.impl.brokerage; + +import java.lang.reflect.Constructor; + +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.common.core.utils.logging.GCUBELog; +import org.gcube.vremanagement.resourcemanager.impl.contexts.ServiceContext; + +/** + * Locates and initializes the Broker to use for services' deployment. A Broker is a component capable to create a plan + * for allocating gCube packages to gHNs + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class BrokerConnector { + + private static final String DEFAULT_BROKER = "org.gcube.vremanagement.resourcemanager.impl.brokerage.InternalBroker"; + + private static final GCUBELog logger = new GCUBELog(BrokerConnector.class); + + /** + * Returns the broker to use in the given scope + * @param scope + * @return + * @throws Exception + */ + public static Broker getBroker(GCUBEScope scope) throws Exception { + Broker broker = null; + try { + broker = load((String) ServiceContext.getContext().getProperty("resourceBrokerClass", true)); + } catch (Exception e) { + logger.error("Unable to load the configured broker", e); + try { + logger.info("Trying to load the internal broker"); + broker = load(DEFAULT_BROKER); + } catch (Exception e1) { + logger.error("Unable to load the internal broker", e1); + throw new Exception("unable to find a broker to use"); + } + } + if (broker != null) broker.initialize(scope); + return broker; + } + + /** + * Loads a broker instance + * @param className the full name of the Class implementing the broker to load + * @return the broker instance + * @throws Exception + */ + private static Broker load(String className) throws Exception { + if (className==null) return null; + Class clazz = (Class) Class.forName(className); + if (!Broker.class.isAssignableFrom(clazz)) + throw new Exception(className+" does not implement "+Broker.class.getName()); + Constructor constructor = clazz.getConstructor(new Class[] {}); + return (Broker) constructor.newInstance(new Object[]{}); + } +} \ No newline at end of file diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/InternalBroker.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/InternalBroker.java new file mode 100644 index 0000000..160cd3e --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/InternalBroker.java @@ -0,0 +1,96 @@ +package org.gcube.vremanagement.resourcemanager.impl.brokerage; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Set; + +import org.apache.axis.types.URI.MalformedURIException; +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.common.core.utils.logging.GCUBELog; +import org.gcube.vremanagement.resourcemanager.impl.deployment.VirtualNode; +import org.gcube.vremanagement.resourcemanager.impl.deployment.VirtualNodeManager; +import org.gcube.vremanagement.resourcemanager.impl.deployment.VirtualNode.NoGHNFoundException; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedDeployedService; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResource.STATUS; +import org.gcube.vremanagement.resourcemanager.impl.state.Session; + +/** + * + * Trivial default Broker that assigns the target gHNs to services with a round robin policy + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class InternalBroker implements Broker { + + private final GCUBELog logger=new GCUBELog(this); + + private GCUBEScope scope; + + private String[] suggestedGHNs = new String[0]; + + //used to apply a round robin assignment of the input GHNs (if any) + private int nodewalker = 0; + + public InternalBroker() {} + + /** + * {@inheritDoc} + */ + public void initialize(GCUBEScope scope) throws Exception {this.scope = scope;} + + /** + * {@inheritDoc} + */ + public void makePlan(Session session, Set services, String[] suggestedGHNs) throws Exception { + this.suggestedGHNs = suggestedGHNs; + for (ScopedDeployedService service : services) { + try { + this.assignTargetGHN(service); + } catch (NoGHNFoundException e1) { + logger.error(service.getSourceService() + "Unable to find a suitable target gHN"); + service.setStatus(STATUS.LOST); + service.setErrorMessage("Unable to find a suitable target gHN"); + throw new IOException(service.getSourceService() + " - unable to find a suitable target gHN for this service"); + } + } + + } + + /** Selects a target node for the deployment + * + * @param service the service to deploy + * @throws MalformedURIException + * @throws NoGHNFoundException if the gHN is not found + */ + private void assignTargetGHN(ScopedDeployedService service) throws org.gcube.vremanagement.resourcemanager.impl.deployment.VirtualNode.NoGHNFoundException { + if ( (service.getSourceService().getGHN() != null) && (service.getSourceService().getGHN().compareToIgnoreCase("") != 0)) { + //a specific GHN has been requested + logger.info("A specific GHN (" + service.getSourceService().getGHN() + ") has been requested by the caller for " + service); + VirtualNode node = VirtualNodeManager.getNode(service.getSourceService().getGHN(),this.scope); + node.setWorkingScope(this.scope); + node.isNotWorking(); + service.setTargetGHN(node); + } else if (this.suggestedGHNs.length > 0) { + //a set of target GHNs has been requested, assign one of them with a round robin + logger.info("A set of target GHNs " + Arrays.toString(this.suggestedGHNs)+" has been requested by the caller for " + service); + VirtualNode node = VirtualNodeManager.getNode(this.suggestedGHNs[this.nodewalker++ % this.suggestedGHNs.length],this.scope); + node.setWorkingScope(this.scope); + node.isNotWorking(); + service.setTargetGHN(node); + } else { + logger.info("no GHN has been specified as explicit target"); + //no GHN has been specified as explicit target + throw new NoGHNFoundException("No GHN was assigned to the service from the caller"); + } + + } + + /** + * {@inheritDoc} + */ + public void sendFeedback(Session session) throws Exception { + /* feedback is not managed by this broker*/ + } + +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/ServiceBroker.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/ServiceBroker.java new file mode 100644 index 0000000..505b837 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/ServiceBroker.java @@ -0,0 +1,117 @@ +package org.gcube.vremanagement.resourcemanager.impl.brokerage; + + +import java.util.List; +import java.util.Set; + +import org.apache.axis.message.addressing.EndpointReferenceType; +import org.gcube.common.core.contexts.GCUBERemotePortTypeContext; +import org.gcube.common.core.contexts.GHNContext; +import org.gcube.common.core.informationsystem.client.AtomicCondition; +import org.gcube.common.core.informationsystem.client.ISClient; +import org.gcube.common.core.informationsystem.client.queries.GCUBERIQuery; +import org.gcube.common.core.resources.GCUBERunningInstance; +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.common.core.utils.logging.GCUBELog; +import org.gcube.vremanagement.resourcebroker.stubs.ResourceBrokerPortType; +import org.gcube.vremanagement.resourcebroker.stubs.service.ResourceBrokerServiceAddressingLocator; +import org.gcube.vremanagement.resourcemanager.impl.brokerage.servicebroker.Feedback; +import org.gcube.vremanagement.resourcemanager.impl.brokerage.servicebroker.PlanParser; +import org.gcube.vremanagement.resourcemanager.impl.brokerage.servicebroker.PlanRequest; +import org.gcube.vremanagement.resourcemanager.impl.contexts.ServiceContext; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedDeployedService; +import org.gcube.vremanagement.resourcemanager.impl.state.Session; + +/** + * + * Broker that exploits the gCube Resource Broker service + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public final class ServiceBroker implements Broker { + + protected final GCUBELog logger = new GCUBELog(this); + + private GCUBEScope scope; + + private List instances = null; + + private static final int TIMEOUT = 600000; //in milliseconds + + public ServiceBroker() {} + + /** + * {@inheritDoc} + */ + public void initialize(GCUBEScope scope) throws Exception { + this.scope = scope; + logger.info("Initializing the ServiceBroker.."); + this.instances = this.queryForBrokerInstances(); + if (this.instances.size() == 0) + throw new Exception("unable to find an instance of the Resource Broker in scope"); + logger.info("Broker instances found: #" + this.instances.size()); + } + + public void makePlan(Session session, Set services, String[] suggestedGHNs) throws Exception { + String request = PlanRequest.create(services, suggestedGHNs, scope);; + logger.debug("Request for the Broker \n" + request); + String plan = null; + for (GCUBERunningInstance instance : this.instances) { + EndpointReferenceType endpoint = instance.getAccessPoint().getEndpoint("gcube/vremanagement/ResourceBroker"); + logger.debug("Querying broker instance at " + endpoint.getAddress()); + try { + plan = this.getBrokerPT(endpoint).getPlan(request); + logger.debug("Plan received from the broker \n" + plan); + break; + } catch (Exception e) { + logger.error("Unable to contact the Resource Broker instance located at " + endpoint.getAddress(), e); + continue; + } + } + PlanParser.parse(plan, services, scope); + session.setDeploymentPlan(plan); + } + + /** + * {@inheritDoc} + */ + public void sendFeedback(Session session) throws Exception { + String feedback = Feedback.create(session); + logger.debug("Feedback for the Broker \n" + feedback); + for (GCUBERunningInstance instance : this.instances) { + EndpointReferenceType endpoint = instance.getAccessPoint().getEndpoint("gcube/vremanagement/ResourceBroker"); + logger.debug("Sending broker feeback to " + endpoint.getAddress()); + try { + this.getBrokerPT(endpoint).handleFeedback(feedback); + break; + } catch (Exception e) { + logger.error("Unable to contact the Resource Broker instance located at " + endpoint.getAddress(), e); + continue; + } + } + } + + + /** + * Looks for all the Resource Broker instances in Scope + * @return the list of RIs found + * @throws Exception if the query to the IS fails + */ + private List queryForBrokerInstances() throws Exception { + //looks for all the Broker instances + ISClient client = GHNContext.getImplementation(ISClient.class); + GCUBERIQuery lookupQuery = client.getQuery(GCUBERIQuery.class); + lookupQuery.addAtomicConditions(new AtomicCondition("//ServiceName","ResourceBroker")); + lookupQuery.addAtomicConditions(new AtomicCondition("//ServiceClass","VREManagement")); + return client.execute(lookupQuery, this.scope); + + } + + private ResourceBrokerPortType getBrokerPT(EndpointReferenceType endpoint) throws Exception { + ResourceBrokerPortType pt = new ResourceBrokerServiceAddressingLocator().getResourceBrokerPortTypePort(endpoint); + pt = GCUBERemotePortTypeContext.getProxy(pt, this.scope, TIMEOUT, ServiceContext.getContext()); + return pt; + } + +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/servicebroker/Feedback.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/servicebroker/Feedback.java new file mode 100644 index 0000000..2c8aa00 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/servicebroker/Feedback.java @@ -0,0 +1,63 @@ +package org.gcube.vremanagement.resourcemanager.impl.brokerage.servicebroker; + +import java.io.IOException; +import java.util.Set; + +import org.gcube.vremanagement.resourcebroker.utils.serialization.parser.xstream.XStreamTransformer; +import org.gcube.vremanagement.resourcebroker.utils.serialization.types.PackageGroup; +import org.gcube.vremanagement.resourcebroker.utils.serialization.types.PlanResponse; +import org.gcube.vremanagement.resourcebroker.utils.serialization.types.feedback.DeployNode; +import org.gcube.vremanagement.resourcebroker.utils.serialization.types.feedback.FeedbackStatus; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedDeployedService; +import org.gcube.vremanagement.resourcemanager.impl.state.Session; + +/** + * + * Feedback for deployment plans. It uses the serialization API provided by the Broker Service + * to create the feedback information. + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class Feedback { + + /** + * Creates the feedback information for the Broker Service + * @param session the current session + * @return the XML representation of the feedback information + */ + @SuppressWarnings("deprecation") + public static String create(Session session) throws IOException { + Set services = session.getServices(); + XStreamTransformer transformer = new XStreamTransformer(); + PlanResponse resp = transformer.getResponseFromXML(session.getDeploymentPlan(), false); + org.gcube.vremanagement.resourcebroker.utils.serialization.types.feedback.Feedback fb = + new org.gcube.vremanagement.resourcebroker.utils.serialization.types.feedback.Feedback(); + fb.setPlanID(resp.getKey()); + fb.setScope(resp.getScope()); + for (PackageGroup group : resp.getPackageGroups()) { + ScopedDeployedService service = null; + for (ScopedDeployedService s : services) { + if (s.getId().compareToIgnoreCase(group.getServiceName()) == 0) { + service = s; + break; + } + } + //set the status of each package elemnt + for (org.gcube.vremanagement.resourcebroker.utils.serialization.types.PackageElem p : group.getPackages()) { + if (service == null) + p.setStatus(FeedbackStatus.FAILED); + else if (service.isSuccess()) + p.setStatus(FeedbackStatus.SUCCESS); + else //this has to be improved by using the PARTIAL state? + p.setStatus(FeedbackStatus.FAILED); + } + + DeployNode pgToAdd = new DeployNode(group); + fb.addDeployNode(pgToAdd); + } + return transformer.toXML(fb); + } + + +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/servicebroker/PlanParser.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/servicebroker/PlanParser.java new file mode 100644 index 0000000..27f95ed --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/servicebroker/PlanParser.java @@ -0,0 +1,71 @@ +package org.gcube.vremanagement.resourcemanager.impl.brokerage.servicebroker; + +import java.io.IOException; +import java.util.Set; + +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.common.core.utils.logging.GCUBELog; +import org.gcube.vremanagement.resourcebroker.utils.serialization.parser.xstream.XStreamTransformer; +import org.gcube.vremanagement.resourcebroker.utils.serialization.types.PackageGroup; +import org.gcube.vremanagement.resourcebroker.utils.serialization.types.PlanResponse; +import org.gcube.vremanagement.resourcemanager.impl.deployment.VirtualNode; +import org.gcube.vremanagement.resourcemanager.impl.deployment.VirtualNodeManager; +import org.gcube.vremanagement.resourcemanager.impl.deployment.VirtualNode.NoGHNFoundException; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedDeployedService; + +/** + * + * Parser for deployment plans. It uses the Serialization API provided by the Broker Service + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public final class PlanParser { + + private static final GCUBELog logger = new GCUBELog(PlanParser.class); + + + /** + * Parses the current plan and assigns gHNs to services + * @param plan the plan returned by the Broker + * @param services the services to deploy + * @param scope the current scope + * @throws IOException + */ + public static void parse(String plan, Set services, GCUBEScope scope) throws IOException { + XStreamTransformer transformer = new XStreamTransformer(); + PlanResponse resp = transformer.getResponseFromXML(plan, false); + if (resp.getStatus().getStatus().compareToIgnoreCase("FAILED")==0) { + logger.error("The creation of the Deployment Plan failed. Broker says: " + resp.getStatus().getMsg()); + throw new IOException ("Broker says: " + resp.getStatus().getMsg()); + } else if (resp.getStatus().getStatus().compareToIgnoreCase("SUCCESS")==0) { + logger.info("The creation of the Deployment Plan was successful"); + } + //assing ghns to services + for (PackageGroup group: resp.getPackageGroups()) { + String serviceID = group.getServiceName(); + String ghnID = group.getGHN(); + if (ghnID == null) { + logger.error("no gHN was assigned to service " + serviceID); + throw new IOException("no gHN was assigned to service " + serviceID); + } + for (ScopedDeployedService service : services) { + if (service.getId().compareToIgnoreCase(serviceID) == 0) { + logger.info("Assigning gHN " + ghnID + " to " +service); + VirtualNode node = null; + try { + node = VirtualNodeManager.getNode(ghnID,scope); + } catch (NoGHNFoundException e) { + logger.error("unable to find gHN " + ghnID + " returned by the Broker"); + throw new IOException("unable to find gHN " + ghnID + " returned by the Broker"); + } + node.setWorkingScope(scope); + node.isNotWorking(); + service.setTargetGHN(node); + break; + } + } + } + + } +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/servicebroker/PlanRequest.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/servicebroker/PlanRequest.java new file mode 100644 index 0000000..c8b2d40 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/servicebroker/PlanRequest.java @@ -0,0 +1,51 @@ +package org.gcube.vremanagement.resourcemanager.impl.brokerage.servicebroker; + +import java.io.IOException; +import java.util.Set; + +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.vremanagement.resourcemanager.impl.deployment.resources.Dependency; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedDeployedService; + +import org.gcube.vremanagement.resourcebroker.utils.serialization.parser.xstream.XStreamTransformer; +import org.gcube.vremanagement.resourcebroker.utils.serialization.types.PackageElem; +import org.gcube.vremanagement.resourcebroker.utils.serialization.types.PackageGroup; + +/** + * + * Creates a plan request with the Serialization API provided by the Broker Service + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class PlanRequest { + + /** + * Creates the request plan for the Broker Service + * @param services the services to deploy + * @param suggestedGHNs the GHNs suggested for the deployment + * @param scope the scope of the request + * @thrown {@link IOException} if the request cannot be created + * @return the XML representation of the request + * + */ + public static String create(Set services, String[] suggestedGHNs, GCUBEScope scope) throws IOException { + org.gcube.vremanagement.resourcebroker.utils.serialization.types.PlanRequest planReq = new org.gcube.vremanagement.resourcebroker.utils.serialization.types.PlanRequest(scope.toString()); + + for (ScopedDeployedService service : services) { + PackageGroup group = planReq.createPackageGroup( service.getId()); + for (Dependency dep : service.getLastResolvedDependencies()) { + group.addPackage(new PackageElem(false, dep.getService().getClazz(), dep.getService().getName(), + dep.getService().getVersion(), dep.getName(), dep.getVersion())); + if (service.getSourceService().getGHN() != null) + group.setGHN(service.getSourceService().getGHN()); + } + } + if (suggestedGHNs != null) { + for (String ghn : suggestedGHNs) + planReq.getGHNList().addGHN(ghn); + } + XStreamTransformer transformer = new XStreamTransformer(); + return transformer.toXML(planReq); + } +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/servicebroker/kxml/KFeedback.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/servicebroker/kxml/KFeedback.java new file mode 100644 index 0000000..e83ddfc --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/servicebroker/kxml/KFeedback.java @@ -0,0 +1,80 @@ +package org.gcube.vremanagement.resourcemanager.impl.brokerage.servicebroker.kxml; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.Map; +import java.util.Set; + +import org.gcube.common.core.utils.logging.GCUBELog; +import org.gcube.vremanagement.resourcemanager.impl.deployment.DeployerReport; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedDeployedService; +import org.gcube.vremanagement.resourcemanager.impl.state.Session; +import org.kxml2.io.KXmlParser; +import org.kxml2.io.KXmlSerializer; +import org.xmlpull.v1.XmlPullParserException; + +/** + * + * Feedback for deployment plans. It uses the KXML library to create the feedback information. + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class KFeedback { + + protected static final GCUBELog logger = new GCUBELog(KFeedback.class); + + /** + * Creates the feedback information for the Broker Service + * @param session the current session + * @return the XML representation of the feedback information + * @throws IOException if the feedback information cannot be created + */ + public static String create(Session session) throws IOException { + + Map reports = session.getAllGHNReports(); + Set services = session.getServices(); + KXmlParser parser = new KXmlParser(); + try { + parser.setInput(new BufferedReader(new StringReader(session.getDeploymentPlan()))); + } catch (XmlPullParserException e) { + logger.error("Unable to parse the Deployment Plan", e); + throw new IOException ("Unable to parse the Deployment Plan"); + } + StringWriter feedbackRequest = new StringWriter(); + KXmlSerializer serializer = new KXmlSerializer(); + serializer.setOutput(feedbackRequest); + serializer.startDocument("UTF-8", true); + serializer.setPrefix("", "http://gcube-system.org/namespaces/resourcebroker/broker/xsd/deployFeedback"); + serializer.startTag(null,"KFeedback").attribute(null, "planID", getPlanID(parser));; + serializer.startTag(null,"Scope").text(session.getScope().toString()).endTag(null,"Scope"); + for (String ghnid : reports.keySet()) { + serializer.startTag(null,"DeployNode"); + //TODO: to complete + serializer.startTag(null,"GHN").text(ghnid).endTag(null, "GHN"); + serializer.endTag(null,"DeployNode"); + } + serializer.endTag(null,"KFeedback"); + return feedbackRequest.toString(); + } + + private static String getPlanID(KXmlParser parser) throws IOException { + String id = ""; + loop: while (true) { + try { + switch (parser.next()) { + case KXmlParser.START_TAG: + if (parser.getName().equals("PlanResponse")) id = parser.getAttributeValue(null, "deployID"); + break loop; + case KXmlParser.END_DOCUMENT: break loop; + } + } catch (Exception e) { + logger.error("Unable to parse the Deployment Plan", e); + throw new IOException ("The creation of the Deployment Plan failed. " + e.getMessage()); + } + } + return id; + } +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/servicebroker/kxml/KPlanParser.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/servicebroker/kxml/KPlanParser.java new file mode 100644 index 0000000..4764195 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/servicebroker/kxml/KPlanParser.java @@ -0,0 +1,117 @@ +package org.gcube.vremanagement.resourcemanager.impl.brokerage.servicebroker.kxml; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.Set; + +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.common.core.utils.logging.GCUBELog; +import org.gcube.vremanagement.resourcemanager.impl.deployment.VirtualNode; +import org.gcube.vremanagement.resourcemanager.impl.deployment.VirtualNodeManager; +import org.gcube.vremanagement.resourcemanager.impl.deployment.VirtualNode.NoGHNFoundException; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedDeployedService; +import org.kxml2.io.KXmlParser; +import org.xmlpull.v1.XmlPullParserException; + +/** + * + * Parser for deployment plans. It uses the KXML library to parse the plan. + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public final class KPlanParser { + + private static final GCUBELog logger = new GCUBELog(KPlanParser.class); + + /** + * Parses the current plan and assigns gHNs to services + * @param plan the plan returned by the Broker + * @param services the services to deploy + * @param scope the current scope + * @throws IOException + */ + public static void parse(String plan, Set services, GCUBEScope scope) throws IOException { + KXmlParser parser = new KXmlParser(); + try { + parser.setInput(new BufferedReader(new StringReader(plan))); + } catch (XmlPullParserException e) { + logger.error("Unable to parse the Deployment Plan", e); + throw new IOException ("Unable to parse the Deployment Plan"); + } + loop: while (true) { + try { + switch (parser.next()) { + case KXmlParser.START_TAG: + if (parser.getName().equals("Status")) parseStatus(parser); + else if (parser.getName().equals("PackageGroup")) parsePackageGroup(services, parser, scope); + break; + case KXmlParser.END_DOCUMENT: break loop; + } + } catch (Exception e) { + logger.error("Unable to parse the Deployment Plan", e); + throw new IOException ("The creation of the Deployment Plan failed. " + e.getMessage()); + } + } + + } + + private static void parseStatus(KXmlParser parser) throws IOException { + String value = parser.getAttributeValue(null, "value"); + String errorMsg = ""; + if (value.compareToIgnoreCase("FAILED")==0) { + loop: while (true) { + try { + switch (parser.next()) { + case KXmlParser.START_TAG: + if (parser.getName().equals("ErrorMsg")) errorMsg = parser.nextText(); + break; + case KXmlParser.END_TAG: if (parser.getName().equals("Status")) break loop; + } + } catch (Exception e) { + logger.error("Unable to parse the Deployment Plan at Status element", e); + throw new IOException ("Failed to parse at Status element"); + } + } + logger.error("The creation of the Deployment Plan failed. Broker says: " + errorMsg); + throw new IOException ("Broker says: " + errorMsg); + } else if (value.compareToIgnoreCase("SUCCESS")==0) { + logger.info("The creation of the Deployment Plan was successful"); + + } + + } + + private static void parsePackageGroup(Set services, KXmlParser parser, GCUBEScope scope) throws IOException, NoGHNFoundException { + String serviceID = parser.getAttributeValue(null, "service"); + String ghnID = null; + loop: while (true) { + try { + switch (parser.next()) { + case KXmlParser.START_TAG: + if (parser.getName().equals("GHN")) ghnID = parser.nextText(); + break; + case KXmlParser.END_TAG: if (parser.getName().equals("PackageGroup")) break loop; + } + } catch (Exception e) { + logger.error("Unable to parse the Deployment Plan at PackageGroup element", e); + throw new IOException ("Failed to parse at PackageGroup element"); + } + } + if (ghnID == null) { + logger.error("a gHN was not assigned to service " + serviceID); + throw new NoGHNFoundException("a gHN was not assigned to service " + serviceID); + } + for (ScopedDeployedService service : services) { + if (service.getId().compareToIgnoreCase(serviceID) == 0) { + logger.info("Assigning gHN " + ghnID + " to " +service); + VirtualNode node = VirtualNodeManager.getNode(ghnID,scope); + node.setWorkingScope(scope); + service.setTargetGHN(node); + break; + } + } + } + +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/servicebroker/kxml/KPlanRequest.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/servicebroker/kxml/KPlanRequest.java new file mode 100644 index 0000000..5fd3576 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/brokerage/servicebroker/kxml/KPlanRequest.java @@ -0,0 +1,110 @@ +package org.gcube.vremanagement.resourcemanager.impl.brokerage.servicebroker.kxml; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.HashSet; +import java.util.Set; + +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.vremanagement.resourcemanager.impl.deployment.resources.Dependency; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedDeployedService; +import org.kxml2.io.KXmlSerializer; + +/** + * + * Creates a plan request with the KXML library to create the deployment plan request. + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class KPlanRequest { + + /** + * Creates the request plan for the Broker Service + * + * @param services + * the services to deploy + * @param suggestedGHNs + * the GHNs suggested for the deployment + * @param scope + * the scope of the request + * @thrown {@link IOException} if the request cannot be created + * @return the XML representation of the request + * + */ + public static String create(Set services, + String[] suggestedGHNs, GCUBEScope scope) throws IOException { + StringWriter request = new StringWriter(); + KXmlSerializer serializer = new KXmlSerializer(); + serializer.setOutput(request); + serializer.startDocument("UTF-8", true); + serializer.setPrefix("","http://gcube-system.org/namespaces/resourcebroker/broker/xsd/deployRequest"); + serializer.startTag(null, "PlanRequest"); + serializer.startTag(null, "Scope").text(scope.toString()).endTag(null,"Scope"); + // for each service, prepare a section like this: + // + // + // PkgServiceClass + // PkgServiceName + // PkgServiceVersion + // PkgPackageName + // PkgPackageVersion + // + // + // PkgServiceClass2 + // PkgServiceName2 + // PkgServiceVersion2 + // PkgPackageName2 + // PkgPackageVersion2 + // + // d5a5af20-ac50-11de-a928-ab32081f9f00 + // + for (ScopedDeployedService service : services) { + serializer.startTag(null, "PackageGroup").attribute(null, + "service", service.getId()); + for (Dependency dep : service.getLastResolvedDependencies()) { + serializer.startTag(null, "Package").attribute(null, "reuse", + "false"); + serializer.startTag(null, "ServiceClass").text( + dep.getService().getClazz()).endTag(null, + "ServiceClass"); + serializer.startTag(null, "ServiceName").text( + dep.getService().getName()).endTag(null, "ServiceName"); + serializer.startTag(null, "ServiceVersion").text( + dep.getService().getVersion()).endTag(null, + "ServiceVersion"); + serializer.startTag(null, "PackageName").text(dep.getName()) + .endTag(null, "PackageName"); + serializer.startTag(null, "PackageVersion").text( + dep.getVersion()).endTag(null, "PackageVersion"); + serializer.endTag(null, "Package"); + } + if (service.getSourceService().getGHN() != null) + serializer.startTag(null, "GHN").text(service.getSourceService().getGHN()).endTag(null, "GHN"); + serializer.endTag(null, "PackageGroup"); + } + + // GHNLIST + if (suggestedGHNs != null) { + serializer.startTag(null, "GHNList"); + for (String ghn : suggestedGHNs) + serializer.startTag(null, "GHN").text(ghn).endTag(null, "GHN"); + serializer.endTag(null, "GHNList"); + } + + serializer.endTag(null, "PlanRequest"); + + return request.toString(); + } + + public static void main(String[] args) { + try { + System.out.println(create(new HashSet(), + new String[] { "ID" }, GCUBEScope.getScope("/gcube"))); + } catch (IOException e) { + e.printStackTrace(); + } + + } + +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/contexts/ServiceContext.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/contexts/ServiceContext.java new file mode 100644 index 0000000..ba4b961 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/contexts/ServiceContext.java @@ -0,0 +1,116 @@ +package org.gcube.vremanagement.resourcemanager.impl.contexts; + +import java.io.File; + +import org.gcube.common.core.contexts.GCUBEServiceContext; +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.common.core.utils.handlers.GCUBEHandler; +import org.gcube.common.core.utils.handlers.GCUBEScheduledHandler; +import org.gcube.vremanagement.resourcemanager.impl.ResourceManager; + + +/** + * ResourceManager service context implementation + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class ServiceContext extends GCUBEServiceContext { + + static ServiceContext cache = new ServiceContext(); + + + /** + * Gets the current service context + * @return the service context + */ + public static ServiceContext getContext() { + return cache; + } + + + @Override protected String getJNDIName() { + return "gcube/vremanagement/ResourceManager/service"; + } + + /** + * {@inheritDoc} + * + */ + @SuppressWarnings("unchecked") + protected void onReady() throws Exception { + super.onReady(); + // no more than one scope is allowed! + if (ServiceContext.getContext().getInstance().getScopes().values().size() != 1) { + logger.fatal("ResourceManager is misconfigured, cannot start the service in multiple scopes"); + throw new IllegalServiceScopeException("ResourceManager has been configured to join more than one scope, while it can work only in ONE single scope"); + } + //creates the stateful resource for the service with a short delay + HStateScheduler stateScheduler = new HStateScheduler(10, GCUBEScheduledHandler.Mode.LAZY); + stateScheduler.setScheduled(new HState()); + stateScheduler.run(); + + } + + /** + * Creates/updates the Deployer stateful resource + * + * @author Manuele Simi (ISTI-CNR) + * + */ + @SuppressWarnings("unchecked") + protected class HStateScheduler extends GCUBEScheduledHandler { + + public HStateScheduler(long interval, Mode mode) { + super(interval, mode); + } + @Override + protected boolean repeat(Exception exception, int exceptionCount) { + if (exception!=null) { + logger.warn("Failed to create the vre manager resource (attempt "+exceptionCount+" out of 20)",exception); + if (exceptionCount >= 20) { + logger.error("Max attempts reached, no more chance to register the ResourceManager resource, the service startup failed"); + ServiceContext.getContext().setStatus(GCUBEServiceContext.Status.FAILED); + return false; + } else + return true; + } else { + return false; + } + } + + } + + /** Tries to create the resource in all the instance scopes*/ + protected class HState extends GCUBEHandler { + + public HState() {} + + public void run() throws Exception { + + GCUBEScope scope = ServiceContext.getContext().getInstance().getScopes().values().iterator().next(); + logger.trace("Adding ResourceManager resource to " + scope); + ServiceContext.getContext().setScope(scope); + StatefulPortTypeContext.getContext().getWSHome().create(StatefulPortTypeContext.getContext().makeKey(ResourceManager.SINGLETON_RESOURCE_KEY)); + } + + } + + /** + * Given a relative path in the /etc folder of the service, returns its absolute path + * + * @param path a relative path to a configuration file or folder + * @return the absolute path of the file + */ + public String getConfigurationFileAbsolutePath(String relativepath) { + File file = super.getFile(relativepath, false); + //eventually creates the directory tree if needed + if (!file.exists() || file.isDirectory()) {file.mkdirs();} + return file.getAbsolutePath(); + + } + /** IllegalServiceScopeException exception */ + public static class IllegalServiceScopeException extends Exception{ + private static final long serialVersionUID = 1L; + public IllegalServiceScopeException(String message) {super(message);}}; +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/contexts/StatefulPortTypeContext.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/contexts/StatefulPortTypeContext.java new file mode 100644 index 0000000..59d04d5 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/contexts/StatefulPortTypeContext.java @@ -0,0 +1,40 @@ +package org.gcube.vremanagement.resourcemanager.impl.contexts; + +import org.gcube.common.core.contexts.GCUBEServiceContext; +import org.gcube.common.core.contexts.GCUBEStatefulPortTypeContext; + +/** + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class StatefulPortTypeContext extends GCUBEStatefulPortTypeContext { + + static StatefulPortTypeContext cache = new StatefulPortTypeContext(); + + @Override + public String getJNDIName() { + return "gcube/vremanagement/ResourceManager"; + } + + @Override + public String getNamespace() { + return "http://gcube-system.org/namespaces/vremanagement/vremanager"; + } + + @Override + public GCUBEServiceContext getServiceContext() { + return ServiceContext.getContext(); + } + + /** + * + * @return the stateful context + */ + public static GCUBEStatefulPortTypeContext getContext() { + return cache; + } + + +} + diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/deployment/DeployerReport.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/deployment/DeployerReport.java new file mode 100644 index 0000000..80a8f2d --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/deployment/DeployerReport.java @@ -0,0 +1,383 @@ +package org.gcube.vremanagement.resourcemanager.impl.deployment; + +import java.io.BufferedReader; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.gcube.common.core.resources.GCUBERunningInstance; +import org.gcube.common.core.utils.logging.GCUBELog; +import org.gcube.vremanagement.resourcemanager.impl.contexts.ServiceContext; +import org.gcube.vremanagement.resourcemanager.impl.deployment.resources.DeployedDependency; +import org.gcube.vremanagement.resourcemanager.impl.deployment.resources.Service; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResourceFactory; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedRunningInstance; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResource.STATUS; +import org.gcube.vremanagement.resourcemanager.impl.state.ProfileDate; +import org.kxml2.io.KXmlParser; +import org.xmlpull.v1.XmlPullParserException; + +/** + * Parser for the report sent by the Deployer Service + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class DeployerReport { + + protected GCUBELog logger = new GCUBELog(this); + + private String report = ""; + + private String status = ""; + + private String id = ""; + + private String host = ""; + + private String type = ""; + + private Date lastUpdate; + + private List dependencies = new ArrayList(); + + private Set instances = new HashSet(); + + /** + * + * @param report the string representation of the report as sent by a Deployer Service + * + */ + public DeployerReport(String report) throws Exception { + this.report = report; + try { + this.parse(); + } catch (XmlPullParserException e) { + throw new Exception("invalid Deployer Report"); + } + } + /** + * Gets the GHN ID + * @return the id + * + * @throws Exception if it is impossible to parse the Deployer Report or the GHNID element is not found + */ + public String getGHNID() { + //look for GHNID element + return this.id; + + } + + public String toString() { + return this.report; + } + + /** + * @return the type + */ + public String getType() { + return type; + } + /** + * @return the lastUpdate + */ + public Date getLastUpdate() { + return lastUpdate; + } + /** + * @return the dependencies + */ + public List getDependencies() { + return dependencies; + } + + /** + * + * @return the hostname from which the report was sent + */ + public String getHost() { + return this.host; + } + + private void parse() throws Exception { + KXmlParser parser = new KXmlParser(); + parser.setInput(new BufferedReader(new StringReader(report))); + loop: while (true) { + try { + switch (parser.next()) { + case KXmlParser.START_TAG: + if (parser.getName().equals("GHNID")) this.id = parser.nextText(); + else if (parser.getName().equals("Type")) this.type = parser.nextText(); + else if (parser.getName().equals("LastUpdate")) this.lastUpdate = ProfileDate.fromXMLDateAndTime( parser.nextText()); + else if (parser.getName().equals("Status")) this.status = parser.nextText(); + else if (parser.getName().equals("Packages")) this.parsePackages(parser); + else if (parser.getName().equals("NewInstances")) this.parseInstances(parser); + break; + case KXmlParser.END_DOCUMENT: break loop; + } + } catch (Exception e) { + logger.error("Unable to parse the Deployer Report",e); + throw new Exception ("Unable to parse the Deployer Report",e); + } + } + } + + private void parseInstances(KXmlParser parser) throws Exception { + + loop: while (true) { + switch (parser.next()) { + case KXmlParser.START_TAG: + if (parser.getName().equals("NewInstance")) { + DeployedRunningInstance instance = new DeployedRunningInstance(); + innerloop: while (true) { + switch (parser.next()) { + case KXmlParser.START_TAG: + if (parser.getName().equals("ID")) instance.setRIId(parser.nextText()); + if (parser.getName().equals("ServiceClass")) instance.setServiceClass(parser.nextText()); + if (parser.getName().equals("ServiceName")) instance.setServiceName(parser.nextText()); + if (parser.getName().equals("ServiceVersion")) instance.setServiceVersion(parser.nextText()); + if (parser.getName().equals("PackageVersion")) instance.setPackageVersion(parser.nextText()); + if (parser.getName().equals("PackageName")) instance.setPackageName(parser.nextText()); + break ; + case KXmlParser.END_TAG: if (parser.getName().equals("NewInstance")){ + try { + ScopedRunningInstance ri = (ScopedRunningInstance) ScopedResourceFactory.newResource(instance.getRIID(), GCUBERunningInstance.TYPE, ServiceContext.getContext().getScope()); + ri.setHostedON(this.host); + ri.setJointTime(Calendar.getInstance().getTime()); + ri.setStatus(STATUS.PUBLISHED); + instance.setInstance(ri); + instance.isAlive = true; + instance.setMessage("An instance of this service has been correctly activated on " + this.host); + } catch (Exception e) { + logger.error("An instance of this service has been activated but it didn't start correctly on " + this.host + ". The expected instance identifier was " + instance.getRIID(), e); + instance.setMessage("An instance of this service has been activated but it didn't start correctly on " + this.host + ". The expected instance identifier was " + instance.getRIID()); + instance.isAlive = false; + } finally { + this.instances.add(instance); + } + break innerloop; + } + break; + case KXmlParser.END_DOCUMENT: throw new Exception ("Parsing failed at NewInstance"); + } + } + } + break; + case KXmlParser.END_TAG: if (parser.getName().equals("NewInstances")) break loop; + break; + case KXmlParser.END_DOCUMENT: throw new Exception ("Parsing failed at NewInstances"); + } + } + + } + + private void parsePackages(KXmlParser parser) throws Exception { + loop: while (true) { + try { + switch (parser.next()) { + case KXmlParser.START_TAG: + if (parser.getName().equals("Package")) { + DeployedDependency dd = new DeployedDependency(); + dd.setService(new Service()); + innerloop: while (true) { + switch (parser.next()) { + case KXmlParser.START_TAG: + if (parser.getName().equals("ServiceClass")) {dd.getService().setClazz(parser.nextText());} + else if (parser.getName().equals("ServiceName")) {dd.getService().setName(parser.nextText());} + else if (parser.getName().equals("ServiceVersion")) {dd.getService().setVersion(parser.nextText());} + else if (parser.getName().equals("PackageName")) {dd.setName(parser.nextText());} + else if (parser.getName().equals("PackageVersion")) {dd.setVersion(parser.nextText());} + else if (parser.getName().equals("Status")) {dd.setStatus(parser.nextText());} + else if (parser.getName().equals("Host")) {dd.setHost(parser.nextText()); this.host = dd.getHost();} + else if (parser.getName().equals("Message")) {dd.setMessage(parser.nextText());} + else parser.nextText(); + break; + case KXmlParser.END_TAG: if (parser.getName().equals("Package")){ this.dependencies.add(dd); break innerloop;} + break; + case KXmlParser.END_DOCUMENT: throw new Exception ("Parsing failed at Package"); + } + } + } + break; + case KXmlParser.END_TAG: if (parser.getName().equals("Packages")) break loop; + break; + case KXmlParser.END_DOCUMENT: throw new Exception ("Parsing failed at Packages"); + } + } catch (Exception e) { + throw new Exception ("Unable to parse the Deployer Report"); + } + } + } + /** + * @return the status + */ + public String getStatus() { + return this.status; + } + /** + * @return the instances + */ + public Set getInstances() { + return this.instances; + } + + public final class DeployedRunningInstance { + private ScopedRunningInstance instance; + private String riid; + private String serviceClass; + private String serviceName; + private String serviceVersion; + private String packageName; + private String packageVersion; + private boolean isAlive; + private String message; + /** + * @return the instance + */ + public ScopedRunningInstance getInstance() { + return instance; + } + /** + * @param instance the instance to set + */ + public void setInstance(ScopedRunningInstance instance) { + this.instance = instance; + } + /** + * @return the serviceClass + */ + public String getServiceClass() { + return serviceClass; + } + /** + * @param serviceClass the serviceClass to set + */ + public void setServiceClass(String serviceClass) { + this.serviceClass = serviceClass; + } + /** + * @return the serviceName + */ + public String getServiceName() { + return serviceName; + } + /** + * @param serviceName the serviceName to set + */ + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + /** + * @return the serviceVersion + */ + public String getServiceVersion() { + return serviceVersion; + } + /** + * @param serviceVersion the serviceVersion to set + */ + public void setServiceVersion(String serviceVersion) { + this.serviceVersion = serviceVersion; + } + /** + * @return the packageName + */ + public String getPackageName() { + return packageName; + } + /** + * @param packageName the packageName to set + */ + public void setPackageName(String packageName) { + this.packageName = packageName; + } + /** + * @return the packageVersion + */ + public String getPackageVersion() { + return packageVersion; + } + /** + * @param packageVersion the packageVersion to set + */ + public void setPackageVersion(String packageVersion) { + this.packageVersion = packageVersion; + } + /** + * @return the riid + */ + public String getRIID() { + return riid; + } + /** + * @param riid the riid to set + */ + public void setRIId(String riid) { + this.riid = riid; + } + /** + * @return the isAlive + */ + public boolean isAlive() { + return isAlive; + } + /** + * @param isAlive the isAlive to set + */ + public void setAlive(boolean isAlive) { + this.isAlive = isAlive; + } + /** + * @return the message + */ + public String getMessage() { + return message; + } + /** + * @param message the message to set + */ + public void setMessage(String message) { + this.message = message; + } + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + getOuterType().hashCode(); + result = prime * result + ((riid == null) ? 0 : riid.hashCode()); + return result; + } + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + DeployedRunningInstance other = (DeployedRunningInstance) obj; + if (!getOuterType().equals(other.getOuterType())) + return false; + if (riid == null) { + if (other.riid != null) + return false; + } else if (!riid.equals(other.riid)) + return false; + return true; + } + private DeployerReport getOuterType() { + return DeployerReport.this; + } + } + +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/deployment/SoftwareGatewayRequest.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/deployment/SoftwareGatewayRequest.java new file mode 100644 index 0000000..b7eacd8 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/deployment/SoftwareGatewayRequest.java @@ -0,0 +1,261 @@ +package org.gcube.vremanagement.resourcemanager.impl.deployment; + +import java.io.BufferedReader; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.axis.message.addressing.EndpointReferenceType; +import org.gcube.common.core.contexts.GCUBERemotePortTypeContext; +import org.gcube.common.core.contexts.GHNContext; +import org.gcube.common.core.informationsystem.client.AtomicCondition; +import org.gcube.common.core.informationsystem.client.ISClient; +import org.gcube.common.core.informationsystem.client.queries.GCUBERIQuery; +import org.gcube.common.core.resources.GCUBERunningInstance; +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.common.core.utils.logging.GCUBELog; +import org.gcube.vremanagement.resourcemanager.impl.contexts.ServiceContext; +import org.gcube.vremanagement.resourcemanager.impl.deployment.resources.Dependency; +import org.gcube.vremanagement.resourcemanager.impl.deployment.resources.Service; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedDeployedService; +import org.gcube.vremanagement.softwaregateway.stubs.AccessPortType; +import org.gcube.vremanagement.softwaregateway.stubs.DependenciesCoordinates; +import org.gcube.vremanagement.softwaregateway.stubs.service.AccessServiceAddressingLocator; +import org.kxml2.io.KXmlParser; + +/** + * Request for the Software Repository service to solve the service2package + * relationships + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public final class SoftwareGatewayRequest { + + private Map requests = new HashMap(); + + protected static final String RESOLVED_DEPS_KEY = "listResolvedDeps"; + + protected static final String MISSING_DEPS_KEY = "listMissingDeps"; + /** Target port-type name. */ + protected static final String REPOSITORY_ENDPOINT = "gcube/vremanagement/softwaregateway/Access"; + /** Target service name. */ + protected static final String REPOSITORY_NAME = "SoftwareGateway"; + /** Target service class. */ + protected static final String REPOSITORY_CLASS = "VREManagement"; + /** Timeout for contacting the target service. */ + protected static final int TIMEOUT = 600000; //in milliseconds + /** Object logger */ + protected final GCUBELog logger = new GCUBELog(this); + + + + /** + * Adds a new service to the request + * + * @param service + */ + public void addService(ScopedDeployedService service) { + DependenciesCoordinates message = new DependenciesCoordinates(); + message.setServiceClass(service.getSourceService().getClazz()); + message.setServiceName(service.getSourceService().getName()); + message.setServiceVersion(service.getSourceService().getVersion()); + requests.put(service, message); + } + + /** + * Sends the request to the appropriate SR + * + * @throws Exception + */ + public List send() throws Exception { + List service2package = new ArrayList(); + Map> eprs = this.findInstances(); + for (ScopedDeployedService service : requests.keySet()) { + DependenciesCoordinates packageCoordinates = requests.get(service); + logger.trace("Checking deps for " + packageCoordinates.getServiceName()); + //look for service deps in one of the instances found + inner: for (GCUBEScope scope : eprs.keySet()) { + for (EndpointReferenceType epr : eprs.get(scope)) { + try { + logger.trace("Trying Service Repository located at "+ epr); + AccessServiceAddressingLocator locator = new AccessServiceAddressingLocator(); + AccessPortType pt = locator.getAccessPortTypePort(epr); + String report = GCUBERemotePortTypeContext.getProxy(pt, scope, TIMEOUT, ServiceContext.getContext()).getDependencies(packageCoordinates); + service2package.add(parseDependenciesReport(service, report)); + break inner;//no need for looping on the other instances, we just need that one satisfies the request + } catch (Exception e) { + logger.warn("Unable to check deps for " + packageCoordinates.getServiceName() + " from Service Repository in " + scope + ": "+ e.getMessage(), e); + } + } + // if we are here, no SR instance was able to resolve the dependencies + //ResolvedService s = new ResolvedService(service); + service.setErrorMessage("Unable to check deps for this service in any of the Service Repository instance in scope"); + service2package.add(service); + } + } + return service2package; + } + + /** + * Parses the dependencies report + * @param report + * @return + */ + private ScopedDeployedService parseDependenciesReport(ScopedDeployedService service, String report) throws Exception { + //ResolvedService depsMap = new ResolvedService(service); + KXmlParser parser = new KXmlParser(); + parser.setInput(new BufferedReader(new StringReader(report))); + logger.trace("Parsing: " + report); + try { + loop: while (true) { + switch (parser.next()) { + case KXmlParser.START_TAG: + if (parser.getName().equals("ResolvedDependencies")) this.parseResolvedDependencies(parser, service); + if (parser.getName().equals("MissingDependencies")) this.parseMissingDependencies(parser, service); + break; + case KXmlParser.END_DOCUMENT: + break loop; + } + } + } catch (Exception e) { + logger.error("Unable to parse the deployment report returned by the Software Repository", e); + } + return service; + } + + /** + * Parses the service section of a dependency + * + * @param parser the XML parser + * @param s the service to populate + * @throws Exception + */ + private void parseService(KXmlParser parser, Service s) throws Exception { + inloop: while (true) { + switch (parser.next()) { + case KXmlParser.START_TAG: + if (parser.getName().equals("Class")) + s.setClazz(parser.nextText()); + if (parser.getName().equals("Name")) + s.setName(parser.nextText()); + if (parser.getName().equals("Version")) + s.setVersion(parser.nextText()); + break; + case KXmlParser.END_TAG: + if (parser.getName().equals("Service")) + break inloop; + break; + case KXmlParser.END_DOCUMENT: + throw new Exception("Parsing failed at Service"); + } + } + } + + /** + * Parses a generic dependency section (both missing and resolved) + * + * @param parser the XML parser + * @return the dependency + * @throws Exception + */ + private Dependency parseDependency(KXmlParser parser) throws Exception { + Dependency p = new Dependency(); + loop: while (true) { + switch (parser.next()) { + case KXmlParser.START_TAG: + if (parser.getName().equals("Service")) parseService(parser, p.getService()); + if (parser.getName().equals("Package")) p.setName(parser.nextText()); + if (parser.getName().equals("Version")) p.setVersion(parser.nextText()); + break; + case KXmlParser.END_TAG: + if ((parser.getName().equals("Dependency")) || (parser.getName().equals("MissingDependency"))) break loop; + break; + case KXmlParser.END_DOCUMENT: + throw new Exception("Parsing failed at Dependency"); + } + } + return p; + } + + private void parseResolvedDependencies(KXmlParser parser, ScopedDeployedService service) throws Exception { + logger.trace("Parsing resolved dependencies..."); + List resolved = new ArrayList(); + loop: while (true) { + switch (parser.next()) { + case KXmlParser.START_TAG: + if (parser.getName().equals("Dependency")) { + logger.trace("Dependency found"); + resolved.add(this.parseDependency(parser)); + } break; + case KXmlParser.END_TAG: + if (parser.getName().equals("ResolvedDependencies")) break loop; + break; + case KXmlParser.END_DOCUMENT: + throw new Exception("Parsing failed at ResolvedDependencies"); + } + } + logger.trace("Number of deps found: " + resolved.size()); + service.setResolvedDependencies(resolved); + } + + private void parseMissingDependencies(KXmlParser parser, ScopedDeployedService service) throws Exception { + logger.trace("Parsing missing dependencies..."); + List missing = new ArrayList(); + loop: while (true) { + switch (parser.next()) { + case KXmlParser.START_TAG: + if (parser.getName().equals("MissingDependency")) { + logger.trace("Dependency found"); + missing.add(this.parseDependency(parser)); + } break; + case KXmlParser.END_TAG: + if (parser.getName().equals("MissingDependencies")) break loop; + break; + case KXmlParser.END_DOCUMENT: + throw new Exception("Parsing failed at MissingDependencies"); + } + } + logger.trace("Number of deps found: " + missing.size()); + service.setMissingDependencies(missing); + } + + /** + * Finds instances of the Software Repository service in the current scope(s) + * @return the list of scopes and the EPR of the available SR + * @throws Exception + */ + protected Map> findInstances()throws Exception { + Map> scopeMap = new HashMap>(); + logger.debug("Looking for SR instances..."); + ISClient client = GHNContext.getImplementation(ISClient.class); + GCUBERIQuery lookupQuery = client.getQuery(GCUBERIQuery.class); + lookupQuery.addAtomicConditions(new AtomicCondition("//ServiceName",REPOSITORY_NAME)); + lookupQuery.addAtomicConditions(new AtomicCondition("//ServiceClass",REPOSITORY_CLASS)); + for (GCUBEScope scope : ServiceContext.getContext().getInstance().getScopes().values()) { + /* + * if (scope.isInfrastructure()) //TODO: deploy only in a PublishedScopeResource + * continue; + */ + logger.debug("Quering in scope " + scope); + List list = client.execute(lookupQuery, scope); + logger.debug("Found N." + list.size() + " instances"); + for (GCUBERunningInstance instance : list) { + logger.trace("Found instance " + instance.toString()); + EndpointReferenceType epr = instance.getAccessPoint().getEndpoint(REPOSITORY_ENDPOINT); + logger.trace("Found EPR " + epr.toString()); + if (!scopeMap.keySet().contains(scope)) { + Set l = new HashSet(); + scopeMap.put(scope, l); + } + scopeMap.get(scope).add(epr); + } + } + return scopeMap; + } + +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/deployment/VirtualNode.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/deployment/VirtualNode.java new file mode 100644 index 0000000..5cf1cc3 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/deployment/VirtualNode.java @@ -0,0 +1,303 @@ +package org.gcube.vremanagement.resourcemanager.impl.deployment; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.apache.axis.message.addressing.EndpointReferenceType; +import org.gcube.common.core.contexts.GCUBERemotePortTypeContext; +import org.gcube.common.core.contexts.GHNContext; +import org.gcube.common.core.informationsystem.client.AtomicCondition; +import org.gcube.common.core.informationsystem.client.ISClient; +import org.gcube.common.core.informationsystem.client.queries.GCUBEGHNQuery; +import org.gcube.common.core.informationsystem.client.queries.GCUBERIQuery; +import org.gcube.common.core.resources.GCUBERunningInstance; +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.common.core.utils.logging.GCUBELog; +import org.gcube.common.vremanagement.deployer.stubs.common.PackageInfo; +import org.gcube.common.vremanagement.deployer.stubs.deployer.DeployParameters; +import org.gcube.common.vremanagement.deployer.stubs.deployer.DeployerPortType; +import org.gcube.common.vremanagement.deployer.stubs.deployer.UndeployParameters; +import org.gcube.common.vremanagement.deployer.stubs.deployer.service.DeployerServiceAddressingLocator; +import org.gcube.vremanagement.resourcemanager.impl.contexts.ServiceContext; +import org.gcube.vremanagement.resourcemanager.impl.contexts.StatefulPortTypeContext; + +/** + * A target node for deployment and undeployment operations + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class VirtualNode { + + /** Object logger */ + protected final GCUBELog logger=new GCUBELog(this); + + /**the callback identifier to send to the Deployer*/ + private String callbackID; + + private String ghnID = ""; + + private GCUBEScope scope = null; + + private boolean isWorking = false; + + private Set packagesToAdd = new HashSet(); + + private Set packagesToRemove = new HashSet(); + + private long lastActivity; + + //here only for backward compatibility (deserialization will fail) + private String name = ""; + + //if we do not receive a deployment session from the node after one hour, we assume that the node is not working anymore + private static final long MAX_ACTIVITY = 3600 * 1000; //1h in milliseconds + + protected VirtualNode(String ID, GCUBEScope scope) throws NoGHNFoundException { + this.ghnID = ID; + this.scope = scope; + } + + public void setCallbackID(String id) { + this.callbackID = id; + } + + public String detectName() throws NoGHNFoundException { + logger.debug("Detecting name for " + this.ghnID); + if (GHNContext.getContext().getGHNID().compareToIgnoreCase(this.ghnID) == 0) + throw new NoGHNFoundException ("cannot deploy on the same GHN (ghnID=" + this.ghnID + ") where the Resource Manager is deployed"); + try { + ISClient client = GHNContext.getImplementation(ISClient.class); + GCUBEGHNQuery query = client.getQuery(GCUBEGHNQuery.class); + query.addAtomicConditions(new AtomicCondition("/ID/text()", this.ghnID)); + return client.execute(query, this.scope).get(0).getNodeDescription().getName(); + } catch (Exception e) { + logger.error("unable to find the target GHN (ghnID=" + this.ghnID + ")", e); + throw new NoGHNFoundException ("unable to find the target GHN (ghnID=" + this.ghnID + ")"); + } + } + + + /** + * Sets the packages to remove in the next {@link #deploy()} invocation + * + * @param packages the list of packagesT to add + */ + public synchronized void setPackagesToAdd(Set packages) { + //check if there is a node with all the packagesToAdd already scheduled to be deployed + //if so, the service will be already activated there, therefore there is nothing to do + if (this.hasPackages(packages)) + return; + this.packagesToAdd.addAll(packages); + } + + /** + * Gets the packagesToAdd available on this node + * + * @return the packagesToAdd + */ + protected Set getPackages() { + return this.packagesToAdd; + } + + /** + * Checks if a list of packagesToAdd is already on this node + * + * @param packagesToAdd the list of packagesToAdd to check + * @return true if all the packagesToAdd are already on this node, false otherwise + */ + protected boolean hasPackages(Set packages) { + + if (packages.size() == 0) + return false; + if (this.packagesToAdd.size() == 0) + return false; + input: for (PackageInfo inputPackage : packages) { + for (PackageInfo nodePackage : this.packagesToAdd) { + if ( (inputPackage.getServiceClass().compareToIgnoreCase(nodePackage.getServiceClass())==0) && + (inputPackage.getServiceName().compareToIgnoreCase(nodePackage.getServiceName())==0) && + (inputPackage.getServiceVersion().compareToIgnoreCase(nodePackage.getServiceVersion())==0) && + (inputPackage.getName().compareToIgnoreCase(nodePackage.getName())==0) && + (inputPackage.getVersion().compareToIgnoreCase(nodePackage.getVersion())==0)) + continue input; + + } + return false; //if we arrive here, no matching package has been found + } + return true; + } + + + /** + * Gets the node ghnID + * + * @return the node ghnID + */ + public String getID() { + return this.ghnID; + } + + /** + * Sends a request to the physical node to deploy the active list of packages to add + * + * @throws Exception if the deployment fails + */ + public synchronized void deploy(GCUBEScope scope) throws Exception { + if (packagesToAdd.size() == 0) + return; + + if (isWorking && ((System.currentTimeMillis() -this.lastActivity) < MAX_ACTIVITY)) { + logger.warn("Can't deploy on " + this +", the gHN is already working"); + return; + } + + EndpointReferenceType callbackEPR = ServiceContext.getContext().getInstance().getAccessPoint().getEndpoint(StatefulPortTypeContext.getContext().getJNDIName()); + DeployParameters param = new DeployParameters(); + param.set_package(this.packagesToAdd.toArray(new PackageInfo[0])); + param.setTargetScope(new String[] {scope.toString()}); + param.setCallbackID(callbackID); + param.setEndpointReference(callbackEPR); + EndpointReferenceType nodeEPR = this.loadDeployer(this.ghnID); + logger.trace("Deploying on " + nodeEPR.toString()); + DeployerPortType pt = GCUBERemotePortTypeContext.getProxy(new DeployerServiceAddressingLocator().getDeployerPortTypePort(nodeEPR), + scope, ServiceContext.getContext()); + pt.deploy(param); + this.packagesToAdd.clear(); + isWorking = true;//this will prevent further requests to the ghn until the deployment session is received back + this.lastActivity = System.currentTimeMillis(); + } + + /** + * Undeploys the packages from the node + * @param scope the scope to use for the undeployment + * + * @throws Exception if the operation fails + */ + public synchronized void undeploy(GCUBEScope scope) throws Exception { + if (this.packagesToRemove.size() == 0) + return; + + if (isWorking && (this.lastActivity - System.currentTimeMillis() < MAX_ACTIVITY)) { + logger.warn("Can't undeploy from " + this +", the gHN is already working"); + return; + } + + EndpointReferenceType callbackEPR = ServiceContext.getContext().getInstance().getAccessPoint().getEndpoint(StatefulPortTypeContext.getContext().getJNDIName()); + UndeployParameters params = new UndeployParameters(); + params.set_package(this.packagesToRemove.toArray(new PackageInfo[0])); + params.setTargetScope(new String[] {scope.toString()}); + params.setCallbackID(callbackID); + params.setEndpointReference(callbackEPR); + EndpointReferenceType nodeEPR = this.loadDeployer(this.ghnID); + logger.trace("Undeploying from " + nodeEPR.toString() + " in scope " + scope); + DeployerPortType pt = GCUBERemotePortTypeContext.getProxy(new DeployerServiceAddressingLocator().getDeployerPortTypePort(nodeEPR), + scope, ServiceContext.getContext()); + pt.undeploy(params); + this.packagesToRemove.clear(); + isWorking = true;//this will prevent further requests to the ghn until the deployment session is received back + this.lastActivity = System.currentTimeMillis(); + } + + /** NoGHNFoundException exception */ + public static class NoGHNFoundException extends Exception { + private static final long serialVersionUID = 1L; + public NoGHNFoundException(String message) {super(message);} + } + /** + * Marks the node as not working node + * (usually called when a closed {@link DeployerReport} is received from the node) + */ + public void isNotWorking() { + this.isWorking = false; + + } + + /** + * Sets the packages to remove in the nect {@link #undeploy()} invocation + * @param packages the packages to remove from the node + */ + public synchronized void setPackagesToRemove(Set packages) { + this.packagesToRemove.addAll(packages); + } + + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "VirtualNode [ghnID=" + ghnID + ", scope="+ scope + "]"; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((ghnID == null) ? 0 : ghnID.hashCode()); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + VirtualNode other = (VirtualNode) obj; + if (ghnID == null) { + if (other.ghnID != null) + return false; + } else if (!ghnID.equals(other.ghnID)) + return false; + return true; + } + + /** + * Assigns a working scope to the node + * @param scope the scope to set + */ + public void setWorkingScope(GCUBEScope scope) { + this.scope = scope; + } + + /** + * Gets the packages scheduled for the next deployment on this node + * @return the set of packages + */ + public Set getScheduledPackages() { + return this.packagesToAdd; + } + + /** + * Gets the current scope used on this node + * @return the current scope + */ + public GCUBEScope getWorkingScope() { + return this.scope; + } + + + /** + * Looks for the Deployer's endpoint to contact + * @param id the identifier of the gHN + * @return the endpoint reference of Deployer's portType to contact + * @throws Exception if the search fails + */ + private EndpointReferenceType loadDeployer(String id) throws Exception { + ISClient client = GHNContext.getImplementation(ISClient.class); + GCUBERIQuery riquery = client.getQuery(GCUBERIQuery.class); + riquery.addAtomicConditions(new AtomicCondition("//GHN/@UniqueID", id), + new AtomicCondition("//ServiceClass", "VREManagement"), + new AtomicCondition("//ServiceName", "Deployer")); + List results = client.execute(riquery,ServiceContext.getContext().getScope()); + return results.get(0).getAccessPoint().getEndpoint("gcube/common/vremanagement/Deployer"); + } +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/deployment/VirtualNodeManager.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/deployment/VirtualNodeManager.java new file mode 100644 index 0000000..f07ca9f --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/deployment/VirtualNodeManager.java @@ -0,0 +1,61 @@ +package org.gcube.vremanagement.resourcemanager.impl.deployment; + + +import java.util.HashMap; +import java.util.Map; + +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.vremanagement.resourcemanager.impl.deployment.VirtualNode.NoGHNFoundException; + +/** + * + * Manager for {@link VirtualNode}s + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class VirtualNodeManager { + + /** maps ghn ID to the related virtual node*/ + private static Map nodes; + + static { //at startup time, all the nodes are not working + if (nodes != null) { + for (VirtualNode node : nodes.values()) { + node.isNotWorking(); + } + + } + } + + /** + * Gets or builds the VirtualNode + * @param ID the node identifier + * @param scope the deployment scope + * @return the Virtual Node + * @throws NoGHNFoundException if the node does not exist + */ + public static VirtualNode getNode(String ID, GCUBEScope scope) throws NoGHNFoundException { + checkNodes(); + if (!nodes.containsKey(ID)) { + nodes.put(ID,new VirtualNode(ID, scope)); + } + return nodes.get(ID); + } + + /** + * Assigns the initial set of nodes to the manager. + * @param nodes the set of nodes + */ + public static void setNodes(Map nodes) { + VirtualNodeManager.nodes = nodes; + } + + /** + * Checks the set of node, must be called before any usage of nodes to check if it has been initialized + */ + private static void checkNodes(){ + if (nodes == null) + nodes = new HashMap(); + } +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/deployment/resources/Dependency.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/deployment/resources/Dependency.java new file mode 100644 index 0000000..8dced83 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/deployment/resources/Dependency.java @@ -0,0 +1,105 @@ +package org.gcube.vremanagement.resourcemanager.impl.deployment.resources; + + +public class Dependency { + + protected Service service = new Service(); + + protected String name; + + protected String version; + + /** + * @return the service + */ + public Service getService() { + return service; + } + + /** + * @param service the service to set + */ + public void setService(Service service) { + this.service = service; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the version + */ + public String getVersion() { + return version; + } + + /** + * @param version the version to set + */ + public void setVersion(String version) { + this.version = version; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((service == null) ? 0 : service.hashCode()); + result = prime * result + ((version == null) ? 0 : version.hashCode()); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + final Dependency other = (Dependency) obj; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (service == null) { + if (other.service != null) + return false; + } else if (!service.equals(other.service)) + return false; + if (version == null) { + if (other.version != null) + return false; + } else if (!version.equals(other.version)) + return false; + return true; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "Dependency [name=" + name + + ", version=" + version + + ", service=" + service + "]"; + } + +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/deployment/resources/DeployedDependency.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/deployment/resources/DeployedDependency.java new file mode 100644 index 0000000..2ec7e83 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/deployment/resources/DeployedDependency.java @@ -0,0 +1,58 @@ +package org.gcube.vremanagement.resourcemanager.impl.deployment.resources; + +/** + * A package deployed by a Deployer service + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class DeployedDependency extends Dependency { + + protected String host = ""; + + protected String status = ""; + + protected String message = ""; + + /** + * @return the host + */ + public String getHost() { + return host; + } + + /** + * @param host the host to set + */ + public void setHost(String host) { + this.host = host; + } + + /** + * @return the status + */ + public String getStatus() { + return status; + } + + /** + * @param status the status to set + */ + public void setStatus(String status) { + this.status = status; + } + + /** + * @return the message + */ + public String getMessage() { + return message; + } + + /** + * @param message the message to set + */ + public void setMessage(String message) { + this.message = message; + } +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/deployment/resources/Service.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/deployment/resources/Service.java new file mode 100644 index 0000000..f710f7e --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/deployment/resources/Service.java @@ -0,0 +1,151 @@ +package org.gcube.vremanagement.resourcemanager.impl.deployment.resources; + +import org.gcube.vremanagement.resourcemanager.stubs.resourcemanager.ServiceItem; + +/** + * A GCUBE service + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class Service { + + protected String clazz; + + protected String name; + + protected String version; + + /** the gHN on which the servie is expected to be deployed */ + protected String GHN = null; + + + public Service(String serviceClass, String serviceName, String serviceVersion) { + this.setClazz(serviceClass); + this.setName(serviceName); + this.setVersion(serviceVersion); + } + + public Service() {} + + public static Service fromServiceItem(ServiceItem item) { + Service s = new Service(); + s.setClazz(item.getServiceClass()); + s.setName(item.getServiceName()); + s.setVersion(item.getServiceVersion()); + if ((item.getGHN() != null) && (item.getGHN().compareToIgnoreCase("")!=0)) + s.setGHN(item.getGHN()); + return s; + } + + /** + * @return the clazz + */ + public String getClazz() { + return clazz; + } + + /** + * @param clazz the class to set + */ + public void setClazz(String clazz) { + this.clazz = clazz; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the version + */ + public String getVersion() { + return version; + } + + /** + * @param version the version to set + */ + public void setVersion(String version) { + this.version = version; + } + + /** + * @return the gHNID + */ + public String getGHN() { + return GHN; + } + + /** + * @param ghn the gHN to set + */ + public void setGHN(String ghn) { + GHN = ghn; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((clazz == null) ? 0 : clazz.hashCode()); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((version == null) ? 0 : version.hashCode()); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + final Service other = (Service) obj; + if (clazz == null) { + if (other.clazz != null) + return false; + } else if (!clazz.equals(other.clazz)) + return false; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (version == null) { + if (other.version != null) + return false; + } else if (!version.equals(other.version)) + return false; + return true; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "Service [class=" + clazz + + ", name=" + name + + ", version=" + version + "]"; + } + + +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/operators/AddResourcesOperator.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/operators/AddResourcesOperator.java new file mode 100644 index 0000000..3ea67ba --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/operators/AddResourcesOperator.java @@ -0,0 +1,69 @@ +package org.gcube.vremanagement.resourcemanager.impl.operators; + +import org.gcube.common.core.utils.logging.GCUBELog; +import org.gcube.vremanagement.resourcemanager.impl.contexts.ServiceContext; +import org.gcube.vremanagement.resourcemanager.stubs.resourcemanager.AddResourcesParameters; +import org.gcube.vremanagement.resourcemanager.stubs.resourcemanager.ResourceList; +import org.gcube.vremanagement.resourcemanager.stubs.resourcemanager.ServiceList; + +/** + * A Resources Operator that coordinates the adding of resources to the scope + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class AddResourcesOperator extends Operator { + + protected final GCUBELog logger = new GCUBELog(this, ServiceContext.getContext()); + + private AddResourcesParameters resourceList; + + private OperatorConfig configuration; + + /** + * Creates a new operator to manage the input resource list + * + * @param scopeState + * @param target + * @param operationID + */ + public AddResourcesOperator(OperatorConfig configuration, AddResourcesParameters resourceList){ + this.resourceList = resourceList; + this.configuration = configuration; + } + + /** + * {@inheritDoc} + */ + public void exec() throws Exception { + // deploy the services, if any + ServiceList services = resourceList.getServices(); + if ((services == null) || (services.getService() == null) || (services.getService().length == 0)) { + logger.warn("The list of services to deploy is empty"); + } else { + try { + new DeployServiceOperator(configuration, services, ACTION.ADD).run(); + } catch (Exception e) { + logger.error("Unable to activate the deployment of the given service(s)", e); + throw new Exception("Unable to activate the deployment of the given service(s)", e); + } + } + + //add the resources to the PublishedScopeResource, if any + ResourceList resources = resourceList.getResources(); + if ((resources == null) || (resources.getResource() == null) || (resources.getResource().length == 0)) + logger.warn("The list of resource to add is empty"); + else { + try { + new ScopedResourceManagerOperator(configuration, resources, ACTION.ADD).run(); + }catch (Exception e) { + logger.error("Unable to manage the given resource(s)", e); + throw new Exception("Unable to manage the given resource(s)", e); + } + } + //save the session + this.configuration.session.save(); + + } + +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/operators/DeployServiceOperator.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/operators/DeployServiceOperator.java new file mode 100644 index 0000000..f5f0e95 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/operators/DeployServiceOperator.java @@ -0,0 +1,169 @@ +package org.gcube.vremanagement.resourcemanager.impl.operators; + + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.gcube.common.core.contexts.GHNContext; +import org.gcube.common.core.informationsystem.client.ISClient; +import org.gcube.common.core.informationsystem.client.queries.GCUBEServiceQuery; +import org.gcube.common.core.resources.GCUBEHostingNode; +import org.gcube.common.core.resources.GCUBEService; +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.common.core.scope.GCUBEScope.Type; +import org.gcube.common.core.utils.logging.GCUBELog; +import org.gcube.vremanagement.resourcemanager.stubs.resourcemanager.ServiceItem; +import org.gcube.vremanagement.resourcemanager.stubs.resourcemanager.ServiceList; +import org.gcube.vremanagement.resourcemanager.impl.brokerage.BrokerConnector; +import org.gcube.vremanagement.resourcemanager.impl.deployment.resources.Service; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedDeployedService; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResource; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResourceFactory; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResource.STATUS; +import org.gcube.vremanagement.resourcemanager.impl.state.Session; +import org.gcube.vremanagement.resourcemanager.impl.state.Session.OPERATION; + +/** + * A Deployer Operator that contacts the Deployer Services on the target nodes asking for the package deployment + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class DeployServiceOperator extends Operator { + + /** Object logger */ + protected final GCUBELog logger=new GCUBELog(this); + + private ServiceList services; + + private OperatorConfig configuration; + + public DeployServiceOperator(OperatorConfig configuration, ServiceList services, ACTION action) { + this.services = services; + this.configuration = configuration; + this.action = action; + } + + public void exec() throws Exception { + + this.configuration.session.save(); + + Set servicesToDeploy = new HashSet(); + Set servicesToUndeploy = new HashSet(); + ISClient client = GHNContext.getImplementation(ISClient.class); + + for (ServiceItem serviceitem : services.getService()) { + String serviceID = null; + try { + //retrieve the service ID from the IS + GCUBEServiceQuery query = client.getQuery(GCUBEServiceQuery.class); + query.addGenericCondition("$result/Profile/Name/string() eq '"+serviceitem.getServiceName()+ + "' and $result/Profile/Class/string() eq '"+ serviceitem.getServiceClass() +"'"); + List profiles = client.execute(query, this.configuration.scope); + if ((profiles == null) || (profiles.size() == 0)) { + logger.error("Failed to find the service"); + continue; + } + logger.debug("Retrieved service ID " + profiles.get(0).getID() ); + serviceID = profiles.get(0).getID(); + } catch (Exception e) { + logger.error("Failed to retrieve the service ID from the IS ",e ); + continue; + } + //build the related scoped resource + ScopedDeployedService service = (ScopedDeployedService) ScopedResourceFactory.newResource(serviceID, GCUBEService.TYPE, this.configuration.scope); + service.setSourceServiceName(Service.fromServiceItem(serviceitem)); + try { + if (this.action == ACTION.ADD) { + service.findResource();//resolve the service deps + if (service.isSuccess()) + servicesToDeploy.add(service); + } else { + service.setCallbackID(this.configuration.session.getId()); + servicesToUndeploy.add(service); + } + } catch (Exception e) { + logger.warn("Failed to find the service",e); + } + this.configuration.session.addResource(service); + this.configuration.session.addService(service); //for the specific service section + } + //create the Deployment Plan with the Resource Broker + if ((this.action == ACTION.ADD) && this.allocate(servicesToDeploy)) { + Set resourcesToAdd = new HashSet(); + for (ScopedDeployedService service : servicesToDeploy) { + if (service.getStatus() != STATUS.LOST) { + service.setCallbackID(this.configuration.session.getId()); + resourcesToAdd.add(service); + if (this.configuration.scope.getType() == Type.VRE) { + //add also the target gHN to the scope + ScopedResource ghn = ScopedResourceFactory.newResource(service.getTargetGHNID(), GCUBEHostingNode.TYPE, this.configuration.scope); + resourcesToAdd.add(ghn); + this.configuration.session.addResource(ghn); + } + } + } + //add the services to the ScopeState (if any) + if(servicesToDeploy.size() > 0) { + this.configuration.scopeState.addResources(resourcesToAdd); + final GCUBEScope tscope = this.configuration.scope; + final Session tsession = this.configuration.session; + //periodically check if the feedback has to be sent to the broker + new Thread() { + @Override + public void run() { + while (true) { + try { Thread.sleep(5000); + } catch (InterruptedException e) {} + //logger.trace("ServiceOperator is checking if the session is closed"); + if ((tsession.isReportClosed() + && ((tsession.getOperation()==OPERATION.AddResources) + || (tsession.getOperation()==OPERATION.UpdateResources) + || (tsession.getOperation()==OPERATION.Create)))) { + try { + BrokerConnector.getBroker(tscope).sendFeedback(tsession); + } catch (Exception e) { + logger.error("Failed to send the feedback to the Broker", e); + } + break; + } + } + } + + }.start(); + } + + + } + //remove the services from the ScopeState (if any) + if(servicesToUndeploy.size() > 0) + this.configuration.scopeState.removeResources(servicesToUndeploy); + + this.configuration.session.save(); + } + + + /** + * Contacts the broker in order to allocate the given services + * @param servicesToDeploy the services to be deployed + * @return true if each service is correctly allocated, false otherwise + */ + private boolean allocate(Set servicesToDeploy) { + try { + if (servicesToDeploy.size() > 0) { + BrokerConnector.getBroker(this.configuration.scope).makePlan(this.configuration.session, servicesToDeploy, services.getGHN()); + this.configuration.session.reportBrokerWork(true, "The Deployment Plan was successfully created"); + return true; + } else { + logger.warn("The Broker was not contacted. After the dependency resolution phase, there is nothing to deploy"); + this.configuration.session.reportBrokerWork(false, "The Broker was not contacted. After the dependency resolution phase, there is nothing to deploy"); + return true; + } + } catch (Exception e) { + logger.error("An error occurred when interacting with the broker", e); + this.configuration.session.reportBrokerWork(false, "An error occurred when interacting with the broker " + e.getMessage()); + return false; + } + } +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/operators/DisposeScopeOperator.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/operators/DisposeScopeOperator.java new file mode 100644 index 0000000..1440734 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/operators/DisposeScopeOperator.java @@ -0,0 +1,49 @@ +package org.gcube.vremanagement.resourcemanager.impl.operators; + +import java.util.HashSet; +import java.util.Set; + +import org.gcube.common.core.utils.logging.GCUBELog; +import org.gcube.vremanagement.resourcemanager.impl.contexts.ServiceContext; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedDeployedService; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResource; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResource.STATUS; + +/** + * + * Dispose the entire scope + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class DisposeScopeOperator extends Operator { + + protected final GCUBELog logger = new GCUBELog(this, ServiceContext.getContext()); + + private OperatorConfig configuration; + + public DisposeScopeOperator(OperatorConfig configuration) { + this.configuration = configuration; + } + + @Override + public void exec() throws Exception { + //exclude from the resources to remove the Services' resources. This is because we also have the + //related Running Instances in the list, and we will eventually undeploy them (if # scopes ==1) + Set resources = new HashSet(); + for (ScopedResource resource : this.configuration.scopeState.getAllResources()) { + if ((resource.getType().compareTo(ScopedDeployedService.TYPE) != 0) + && (resource.getStatus() != STATUS.UNPUBLISHED) + && (resource.getStatus() != STATUS.LOST) + && (resource.getStatus() != STATUS.REMOVED)){ + resources.add(resource); + this.configuration.session.addResource(resource); + } + } + + configuration.scopeState.removeResources(resources); + //TODO: should we also undeploy the RM itself? + + this.configuration.session.save(); + } +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/operators/Operator.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/operators/Operator.java new file mode 100644 index 0000000..f10b3a3 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/operators/Operator.java @@ -0,0 +1,45 @@ +package org.gcube.vremanagement.resourcemanager.impl.operators; + +import org.gcube.common.core.utils.logging.GCUBELog; + +/** + * + * Base class for manager's operators + * . + * A manage operator is devoted to execute certain assigned tasks. + * The {@link #exec()} method is asynchronously called + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public abstract class Operator { + + protected final GCUBELog logger=new GCUBELog(this.getClass()); + + protected ACTION action; + + protected OperatorConfig configuration; + + public enum ACTION {ADD,REMOVE} + + public final void run() { + //new Thread() { + // public void run() { + try { + Operator.this.exec(); + } catch (Exception e) { + logger.error("The operator was unable to manage the request",e); + } + // } + //}.start(); + + } + + /** + * Executes the operator's tasks + * + * @throws Exception if any of the tasks fails + */ + public abstract void exec() throws Exception; + +} \ No newline at end of file diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/operators/OperatorConfig.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/operators/OperatorConfig.java new file mode 100644 index 0000000..341b791 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/operators/OperatorConfig.java @@ -0,0 +1,28 @@ +package org.gcube.vremanagement.resourcemanager.impl.operators; + +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.vremanagement.resourcemanager.impl.contexts.ServiceContext; +import org.gcube.vremanagement.resourcemanager.impl.state.ScopeState; +import org.gcube.vremanagement.resourcemanager.impl.state.Session; + +/** + * Basic configuration for every {@link Operator} + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class OperatorConfig { + + public final Session session; + + public final GCUBEScope scope; + + public final ScopeState scopeState; + + public OperatorConfig(Session report, ScopeState scopeState, GCUBEScope ... scope) { + this.session = report; + this.scopeState = scopeState; + this.scope = (scope!=null && scope.length >0)? scope[0] : ServiceContext.getContext().getScope(); + } + +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/operators/RemoveResourcesOperator.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/operators/RemoveResourcesOperator.java new file mode 100644 index 0000000..3558d0b --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/operators/RemoveResourcesOperator.java @@ -0,0 +1,57 @@ +package org.gcube.vremanagement.resourcemanager.impl.operators; + +import org.gcube.common.core.utils.logging.GCUBELog; +import org.gcube.vremanagement.resourcemanager.impl.contexts.ServiceContext; +import org.gcube.vremanagement.resourcemanager.stubs.resourcemanager.RemoveResourcesParameters; +import org.gcube.vremanagement.resourcemanager.stubs.resourcemanager.ResourceList; +import org.gcube.vremanagement.resourcemanager.stubs.resourcemanager.ServiceList; + +/** + * A Resources Operator that coordinates the removal of resources from the scope + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class RemoveResourcesOperator extends Operator { + + protected final GCUBELog logger = new GCUBELog(this, ServiceContext.getContext()); + + private OperatorConfig configuration; + + private RemoveResourcesParameters resourceList; + + public RemoveResourcesOperator(OperatorConfig configuration, RemoveResourcesParameters resourceList) { + this.configuration = configuration; + this.resourceList = resourceList; + } + + public void exec() throws Exception { + + // undeploy the services, if any + ServiceList services = resourceList.getServices(); + if ((services == null) || (services.getService() == null) || (services.getService().length == 0)) { + logger.warn("The list of services to undeploy is empty"); + } else { + try { + new DeployServiceOperator(configuration, services, ACTION.REMOVE).run(); + } catch (Exception e) { + logger.error("Unable to activate the undeployment of the given service(s)", e); + throw new Exception("Unable to activate the undeployment of the given service(s)", e); + } + } + + //removes the resources from the PublishedScopeResource, if any + ResourceList resources = resourceList.getResources(); + if ((resources == null) || (resources.getResource().length == 0)) + logger.warn("The list of resource to add is empty"); + else { + try { + new ScopedResourceManagerOperator(configuration, resources, ACTION.REMOVE).run(); + }catch (Exception e) { + logger.error("Unable to manage the given resource(s)", e); + throw new Exception("Unable to manage the given resource(s)", e); + } + } + } + +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/operators/ScopedResourceManagerOperator.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/operators/ScopedResourceManagerOperator.java new file mode 100644 index 0000000..4255b4f --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/operators/ScopedResourceManagerOperator.java @@ -0,0 +1,59 @@ +package org.gcube.vremanagement.resourcemanager.impl.operators; + +import java.util.HashSet; +import java.util.Set; + +import org.gcube.vremanagement.resourcemanager.stubs.resourcemanager.ResourceItem; +import org.gcube.vremanagement.resourcemanager.stubs.resourcemanager.ResourceList; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResource; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResourceFactory; + + +/** + * Add and remove list of {@link ScopedResource}s to/from a given scope + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class ScopedResourceManagerOperator extends Operator { + + + private ResourceList resources; + + public ScopedResourceManagerOperator(OperatorConfig configuration, ResourceList resources, ACTION action) { + this.configuration = configuration; + this.resources = resources; + this.action = action; + } + + public void exec() throws Exception { + + Set toadd = new HashSet(); + Set toremove = new HashSet(); + ScopedResource sresource = null; + for (ResourceItem resource : this.resources.getResource()) { + try { + sresource = ScopedResourceFactory.newResource(resource.getID(), resource.getType(), this.configuration.scope); + if (this.action == ACTION.ADD) { + logger.info("Adding resource " + sresource.getId() + " (" + sresource.getType() + ") to scope " + this.configuration.scope.toString()); + toadd.add(sresource); + } else if (this.action == ACTION.REMOVE) { + logger.info("Removing resource " + sresource.getId() + " (" + sresource.getType() + ") from scope " + this.configuration.scope.toString()); + toremove.add(sresource); + } + + //add the resource item to the session + this.configuration.session.addResource(sresource); + } catch (Exception e) { + logger.error("Unable to manage the resource",e); + } + } + if(toadd.size() > 0) + configuration.scopeState.addResources(toadd); + if(toremove.size() > 0) + configuration.scopeState.removeResources(toremove); + + this.configuration.session.save(); + } + +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/resources/ScopedAnyResource.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/resources/ScopedAnyResource.java new file mode 100644 index 0000000..4168be3 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/resources/ScopedAnyResource.java @@ -0,0 +1,129 @@ +package org.gcube.vremanagement.resourcemanager.impl.resources; + + +import java.util.List; + +import org.gcube.common.core.contexts.GHNContext; +import org.gcube.common.core.resources.GCUBECS; +import org.gcube.common.core.resources.GCUBECSInstance; +import org.gcube.common.core.resources.GCUBECollection; +import org.gcube.common.core.resources.GCUBEExternalRunningInstance; +import org.gcube.common.core.resources.GCUBEGenericResource; +import org.gcube.common.core.resources.GCUBEMCollection; +import org.gcube.common.core.resources.GCUBEResource; +import org.gcube.common.core.resources.GCUBERuntimeResource; +import org.gcube.common.core.resources.GCUBEService; +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.vremanagement.resourcemanager.impl.contexts.ServiceContext; +import org.gcube.common.core.informationsystem.client.AtomicCondition; +import org.gcube.common.core.informationsystem.client.ISClient; +import org.gcube.common.core.informationsystem.client.ISTemplateQuery; +import org.gcube.common.core.informationsystem.client.queries.GCUBECSInstanceQuery; +import org.gcube.common.core.informationsystem.client.queries.GCUBECSQuery; +import org.gcube.common.core.informationsystem.client.queries.GCUBECollectionQuery; +import org.gcube.common.core.informationsystem.client.queries.GCUBEExternalRIQuery; +import org.gcube.common.core.informationsystem.client.queries.GCUBEGenericResourceQuery; +import org.gcube.common.core.informationsystem.client.queries.GCUBEMCollectionQuery; +import org.gcube.common.core.informationsystem.client.queries.GCUBERuntimeResourceQuery; +import org.gcube.common.core.informationsystem.client.queries.GCUBEServiceQuery; +import org.gcube.common.core.informationsystem.publisher.ISPublisher; + +import com.thoughtworks.xstream.annotations.XStreamOmitField; + + +/** + * + * Models a generic scoped {@link GCUBEResource} + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public final class ScopedAnyResource extends ScopedResource { + + @XStreamOmitField + GCUBEResource profile = null; + + @XStreamOmitField + @SuppressWarnings("unchecked") + Class profileClass = null; + + protected ScopedAnyResource(String id, String type, GCUBEScope scope) { + super(id, type, scope); + } + + /** + * Gets the profile of the Scoped Resource + * @throws Exception if the resource was not found + */ + @Override + @SuppressWarnings({ "unchecked", "rawtypes"}) + protected void find() throws Exception { + try { + ISClient client = GHNContext.getImplementation(ISClient.class); + Class query = null; + if (this.type.compareToIgnoreCase(GCUBECollection.TYPE) == 0) {query = GCUBECollectionQuery.class; profileClass = GCUBECollection.class;} + else if (this.type.compareToIgnoreCase(GCUBEMCollection.TYPE) == 0) {query = GCUBEMCollectionQuery.class; profileClass = GCUBEMCollection.class;} + else if (this.type.compareToIgnoreCase(GCUBEGenericResource.TYPE) == 0) {query = GCUBEGenericResourceQuery.class; profileClass = GCUBEGenericResource.class;} + else if (this.type.compareToIgnoreCase(GCUBEExternalRunningInstance.TYPE) == 0) {query = GCUBEExternalRIQuery.class; profileClass = GCUBEExternalRunningInstance.class;} + else if (this.type.compareToIgnoreCase(GCUBECS.TYPE) == 0) {query = GCUBECSQuery.class; profileClass = GCUBECS.class;} + else if (this.type.compareToIgnoreCase(GCUBECSInstance.TYPE) == 0) {query = GCUBECSInstanceQuery.class; profileClass = GCUBECSInstance.class;} + else if (this.type.compareToIgnoreCase(GCUBEService.TYPE) == 0) {query = GCUBEServiceQuery.class; profileClass = GCUBEService.class;} + else if (this.type.compareToIgnoreCase(GCUBERuntimeResource.TYPE) == 0) {query = GCUBERuntimeResourceQuery.class; profileClass = GCUBERuntimeResource.class;} + else throw new Exception("Unknown resource type: " + this.type); + + ISTemplateQuery realquery = (ISTemplateQuery) client.getQuery(query); + realquery.addAtomicConditions(new AtomicCondition("//ID",this.id)); + List profiles = client.execute(realquery, GCUBEScope.getScope(this.scope)); + if ((profiles != null) && (profiles.size() > 0)) + this.profile = profiles.get(0); + else + // obviously, in the case of adding, the resource is not in the current scope, therefore we look upstairs (the enclosing scope) + this.profile = (GCUBEResource) client.execute(realquery, GCUBEScope.getScope(this.scope).getEnclosingScope()).get(0); + } catch (Exception e) { + throw new Exception("unable to find the target resource (ID=" + id + "). Possible cause: " + e.getMessage(), e); + } + + } + + @Override + protected void addToScope() throws ResourceNotFound, Exception { + this.findResource(); + getLogger().debug("Adding scope to resource profile"); + try { + profile.addScope(this.getScope()); + //republish the resource + ISPublisher publisher = GHNContext.getImplementation(ISPublisher.class); + publisher.updateGCUBEResource(this.profile, ServiceContext.getContext().getScope(), ServiceContext.getContext()); + //republish also in the infrastructure scope, if needed + if (ServiceContext.getContext().getScope().getType() == GCUBEScope.Type.VO) { + if (this.profile.getScopes().values().contains(ServiceContext.getContext().getScope().getEnclosingScope())) { + publisher.updateGCUBEResource(this.profile, ServiceContext.getContext().getScope().getEnclosingScope(), ServiceContext.getContext()); + } + } + } catch (Exception e) { + this.noHopeForMe("Failed to add the scope ("+ this.getScope()+") to resource " + this.getId(), e); + } + } + + @Override + protected void removeFromScope() throws ResourceNotFound, Exception { + this.findResource(); + getLogger().debug("Removing scope from resource profile"); + try { + profile.removeScope(this.getScope()); + //republish the resource + ISPublisher publisher = GHNContext.getImplementation(ISPublisher.class); + publisher.updateGCUBEResource(this.profile, ServiceContext.getContext().getScope(), ServiceContext.getContext()); + //republish also in the infrastructure scope, if needed + if (ServiceContext.getContext().getScope().getType() == GCUBEScope.Type.VO) { + if (this.profile.getScopes().values().contains(ServiceContext.getContext().getScope().getEnclosingScope())) { + publisher.updateGCUBEResource(this.profile, ServiceContext.getContext().getScope().getEnclosingScope(), ServiceContext.getContext()); + } + } + } catch (Exception e) { + this.noHopeForMe("Failed to remove the scope ("+ this.getScope()+") from resource " + this.getId(), e); + } + + } + +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/resources/ScopedDeployedService.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/resources/ScopedDeployedService.java new file mode 100644 index 0000000..08f4599 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/resources/ScopedDeployedService.java @@ -0,0 +1,397 @@ +package org.gcube.vremanagement.resourcemanager.impl.resources; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.gcube.common.core.resources.GCUBEService; +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.common.vremanagement.deployer.stubs.common.PackageInfo; +import org.gcube.vremanagement.resourcemanager.impl.deployment.SoftwareGatewayRequest; +import org.gcube.vremanagement.resourcemanager.impl.deployment.VirtualNode; +import org.gcube.vremanagement.resourcemanager.impl.deployment.resources.Dependency; +import org.gcube.vremanagement.resourcemanager.impl.deployment.resources.Service; +import org.gcube.vremanagement.resourcemanager.impl.operators.Operator.ACTION; + +/** + * + * Models a scoped {@link GCUBEService} + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public final class ScopedDeployedService extends ScopedResource { + + public static final String TYPE = GCUBEService.TYPE; + + /** Target port-type name. */ + protected static final String REPOSITORY_ENDPOINT = "gcube/vremanagement/softwarerepository/SoftwareRepository"; + + /** Target service name. */ + protected static final String REPOSITORY_NAME = "SoftwareRepository"; + + /** Target service class. */ + protected static final String REPOSITORY_CLASS = "VREManagement"; + + /** Timeout in millisecs for the channel with the Software Repository instance */ + protected static final int TIMEOUT = 600000; + + /** resolved dependencies */ + private List resolved = new ArrayList(); + + /** missing dependency */ + private List missing = new ArrayList(); + + /** the packages deployed for this service*/ + private Map > node2packages = new HashMap>(); + + /** the packages deployed for this service*/ + private Map > node2missing = new HashMap>(); + + private Service service; + + private String lastActivityOnGHN; + + //the target ghn(s) + private List virtualNodes = new ArrayList(); + + /** maps node with the scope used when deploying there */ + private Map node2scope = new HashMap(); + + /** maps RI id with the ghn id (where the instance is deployed on*/ + private Map ri2ghn = new HashMap(); + + protected ScopedDeployedService(String id, GCUBEScope scope) { + super(id, TYPE, scope); + } + + public void setSourceServiceName(Service service) { + this.service = service; + } + + public Service getSourceService() { + return this.service; + } + + /** + * Gets the list of resolved dependencies when the deployment was performed + * on a specific node + * + * @param ghnid the ID of the node + * @return the resolved dependencies + */ + public List getResolvedDependencies(String ghnid) { + if (node2packages.get(ghnid) == null) + return Collections.emptyList(); + return Collections.unmodifiableList(node2packages.get(ghnid)); + } + + /** + * Gets the list of resolved dependencies to be used in the next deployment + * + * @return the resolved dependencies + */ + public List getLastResolvedDependencies() { + return this.resolved; + } + + /** + * Sets the list of resolved dependencies to be used in the next deployment + * + * @param dependencies the resolved dependencies + */ + public void setResolvedDependencies(List dependencies) { + this.resolved = dependencies; + } + + /** + * Gets the list of missing dependencies when the deployment was performed + * on a specific node + * + * @param ghnid the ID of the node + * @return the missing dependencies + */ + public List getMissingDependencies(String ghnid) { + if (node2missing.get(ghnid) == null) + return Collections.emptyList(); + return Collections.unmodifiableList(node2missing.get(ghnid)); + } + + /** + * Gets the list of missing dependencies for the last (failed) deployment + * + * @return the missing dependencies + */ + public List getLastMissingDependencies() { + return this.missing; + } + + /** + * Sets the list of missing dependencies for the next deployment + * + * @param dependencies the missign dependencies + */ + public void setMissingDependencies(List dependencies) { + ///this.getLogger().trace("Setting missing deps " + dependencies.size()); + missing= dependencies; + } + + @Override + protected void find() throws Exception { + //looks for the service and its deps in the Software Repository + SoftwareGatewayRequest request = new SoftwareGatewayRequest(); + request.addService(this); + this.setErrorMessage(""); //empty any previous message + try { + request.send(); + if (this.resolved.size() == 0) { + this.success = false; + this.setErrorMessage("Invalid list of dependecies retrieved from the Software Repository"); + } else if (this.missing.size() > 0) {//ops, some deps are missing! + this.success = false; + this.setErrorMessage("The service cannot be deployed due to one or more missing dependencies: " + this.missing); + } else { + this.success = true; + } + } catch (Exception e) { + this.success= false; + this.setErrorMessage("Unable to resolve the list of dependencies for this service " + e.getMessage()); + getLogger().error("Unable to resolve the list of deps for " + this.service, e); + throw new Exception("Unable to resolve the list of dependencies for " + this.service, e); + } + } + + + @Override + protected void addToScope() throws ResourceNotFound, Exception { + logger.trace("This service belongs to " + this.scope); + if (!this.isSuccess()) { + this.noHopeForMe(this.getErrorMessage(),new Exception()); + } + + if ((this.lastActivityOnGHN == null) || (this.lastActivityOnGHN.compareTo("")==0)) { + this.noHopeForMe("Unable to find a suitable target gHN where to deploy the service",new Exception()); + } + this.action = ACTION.ADD; + this.setErrorMessage(""); //empty any previous message + Exception lastException = null; + //deploy the service + List nodesToRemove = new ArrayList(); + for (VirtualNode node : this.virtualNodes) { + try { + node.deploy(this.getScope()); + //store packages for future undeployment and reporting purposes + List resolvedPackages = new ArrayList(); + for (Dependency dep : this.resolved) + resolvedPackages.add(dep); + node2packages.put(node.getID(), resolvedPackages); + //store missing packages for reporting purposes + List missingPackages = new ArrayList(); + for (Dependency dep : this.missing) + missingPackages.add(dep); + if (node2missing == null) { + logger.trace("node2missing is null"); + node2missing = new HashMap>(); // this is here for backwards compatibility + } + node2missing.put(node.getID(), missingPackages); + } catch (Exception e) { + nodesToRemove.add(node); + logger.error("Failed to deploy on " + node.getID(), e); + logger.info("Virtual Node " + node.toString() + " will be removed from the queue"); + lastException=e; + } + + } + this.missing.clear(); + this.resolved.clear(); + + if (nodesToRemove.size() > 0 ) { + this.virtualNodes.removeAll(nodesToRemove); + this.noHopeForMe("Unable to contact the target gHN ("+ this.lastActivityOnGHN +") where to deploy the service",lastException); + } + + } + + + @Override + protected void removeFromScope() throws ResourceNotFound, Exception { + logger.trace("This service belongs to " + this.scope); + if (!this.isSuccess()) { + //TODO: could we undeploy static packages here? + this.noHopeForMe(this.getErrorMessage(),new Exception()); + } + + if ((this.lastActivityOnGHN == null) || (this.lastActivityOnGHN.compareTo("")==0)) { + this.noHopeForMe("Unable to find a valid target gHN where to undeploy the service", new Exception()); + } + this.setErrorMessage(""); //empty any previous message + //undeploy the service from all the virtual nodes where it was deployed (if any) + this.action = ACTION.REMOVE; + try { + for (VirtualNode node : this.virtualNodes) { + try { + if (node2scope.containsKey(node.getID())) + node.undeploy(GCUBEScope.getScope(node2scope.get(node.getID()))); + else + node.undeploy(this.getScope()); //try the service's scope + } catch (Exception e) { + this.noHopeForMe("Failed to undeploy from "+ node ,e); + } + } + this.virtualNodes.clear(); + } catch (Exception e) { + this.noHopeForMe("Unable to contact the target gHN ("+ this.lastActivityOnGHN +") where to undeploy the service, deployer says" + e.getMessage() ,e); + } + } + + /** + * Schedules the undeployment of all the instances of this service from scope + * + * @throws Exception if the operation fails + */ + public void scheduleUndeploy() { + for (VirtualNode node : this.virtualNodes) + scheduleUndeploy(node); + } + + /** + * Schedules the undeployment of an instance of this service from the node + * + * @param node the node from where to undeploy the service + * @throws Exception if the operation fails + */ + public void scheduleUndeploy(VirtualNode node) { + getLogger().info("Scheduling undeployment of service "+ this.service+ " from GHN " + this.lastActivityOnGHN); + //notify the node to operate in the scope previously used for the deployment + this.checkNode2Scope(); + if (node2scope.containsKey(node.getID())) //it has to be in, but it couldn't if the service was deployed from a previous (version < 1.1)RM instance + node.setWorkingScope(GCUBEScope.getScope(node2scope.get(node.getID()))); + //prepare the input list of packagesToAdd for that GHN + List deps = node2packages.get(node.getID()); + if (deps == null) + return; + Set packages = new HashSet(); + for (int i = 0; i < deps.size(); i++) { + PackageInfo p = new PackageInfo(); + p.setServiceName(deps.get(i).getService().getName()); + p.setServiceClass(deps.get(i).getService().getClazz()); + p.setServiceVersion(deps.get(i).getService().getVersion()); + p.setVersion(deps.get(i).getVersion()); + p.setName(deps.get(i).getName()); + getLogger().trace("Adding Package to undeployment request: " + deps.get(i)); + packages.add(p); + } + node.setPackagesToRemove(packages); + this.lastActivityOnGHN = node.getID(); + } + + /** + * Undeploys any instances of this service from the given gHN + * + * @param lastActivityOnGHN the id of the gHN from where to undeploy the service + * @throws Exception + */ + public void removeFromScope(String ghnID) throws VirtualNode.NoGHNFoundException, Exception { + logger.trace("This service belongs to " + this.scope); + this.action = ACTION.REMOVE; + for (VirtualNode node : this.virtualNodes) + if (node.getID().compareToIgnoreCase(ghnID) == 0) { + checkNode2Scope(); + if (node2scope.containsKey(ghnID)) + node.undeploy(GCUBEScope.getScope(node2scope.get(ghnID))); + else + node.undeploy(this.getScope()); //try the service's scope + this.virtualNodes.remove(node); + return; + } + this.noHopeForMe("Unable to find the gHN with id " + this.lastActivityOnGHN + + " to undeploy the instance of the " + this.service, new VirtualNode.NoGHNFoundException("")); + } + + /** + * Sets the GHN where the service will be deployed + * + * @param node the target GHN + */ + public void setTargetGHN(VirtualNode node) { + getLogger().info("Using GHN " + node.getID() + " for " + this); + + Set packages = new HashSet(); + //prepare the input list of packagesToAdd for that GHN + List deps = this.resolved; + for (int i = 0; i < deps.size(); i++) { + PackageInfo p = new PackageInfo(); + p.setServiceName(deps.get(i).getService().getName()); + p.setServiceClass(deps.get(i).getService().getClazz()); + p.setServiceVersion(deps.get(i).getService().getVersion()); + p.setVersion(deps.get(i).getVersion()); + p.setName(deps.get(i).getName()); //packageName + getLogger().trace("Adding Package to deployment request: " + deps.get(i)); + packages.add(p); + } + node.setPackagesToAdd(packages); + this.checkNode2Scope();//for backward compatibility (version <= 1.1) + node2scope.put(node.getID(), node.getWorkingScope().toString()); + this.virtualNodes.add(node); + this.lastActivityOnGHN = node.getID(); + } + + /** + * Gets the ID of the target gHN + * @return the ID of the target gHN + */ + public String getTargetGHNID() { + return lastActivityOnGHN; //this.virtualNodes.size() > 0 ? this.virtualNodes.get(0).getID() : ""; + + } + + /** + * Gets the ID of the target gHN + * @return the ID of the target gHN + */ + public String getTargetGHN() { + return this.virtualNodes.size() > 0 ? this.virtualNodes.get(0).getID() : ""; + } + + + public void setRIonGHN(String ri, String ghn) { + this.ri2ghn.put(ri, ghn); + } + + /** + * Sets the callback ID from now on for all the service's operations + * @param id the callback ID + */ + public void setCallbackID(String id) { + for (VirtualNode node : this.virtualNodes) + node.setCallbackID(id); + + } + /** + * Gets the list of {@link VirtualNode}s where the service is actually deployed within this scope + * @return the list of {@link VirtualNode}s + */ + public List getNodes() { + return this.virtualNodes; + } + + /** + * Number of times the resource is searched in the IS before to declare it lost + * @return the number of attempts to do + */ + @Override + protected int getMaxFindAttempts(){ + return 3; + } + + private void checkNode2Scope(){ + //this is for backward compatibility, if the obj is loaded from an old serialization + //node2scope could not be initialized + if (this.node2scope == null) + this.node2scope = new HashMap(); + } +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/resources/ScopedGHN.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/resources/ScopedGHN.java new file mode 100644 index 0000000..009f2ef --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/resources/ScopedGHN.java @@ -0,0 +1,129 @@ +package org.gcube.vremanagement.resourcemanager.impl.resources; + +import java.util.List; + +import org.apache.axis.message.addressing.Address; +import org.apache.axis.message.addressing.EndpointReferenceType; +import org.gcube.common.core.contexts.GCUBERemotePortTypeContext; +import org.gcube.common.core.contexts.GHNContext; +import org.gcube.common.core.informationsystem.client.AtomicCondition; +import org.gcube.common.core.informationsystem.client.ISClient; +import org.gcube.common.core.informationsystem.client.ISClient.ISUnsupportedQueryException; +import org.gcube.common.core.informationsystem.client.queries.GCUBEGHNQuery; +import org.gcube.common.core.informationsystem.client.queries.GCUBERIQuery; +import org.gcube.common.core.resources.GCUBEHostingNode; +import org.gcube.common.core.resources.GCUBERunningInstance; +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.common.vremanagement.ghnmanager.stubs.AddScopeInputParams; +import org.gcube.common.vremanagement.ghnmanager.stubs.GHNManagerPortType; +import org.gcube.common.vremanagement.ghnmanager.stubs.service.GHNManagerServiceAddressingLocator; +import org.gcube.vremanagement.resourcemanager.impl.contexts.ServiceContext; + +/** + * Models a scoped {@link GCUBEHostingNode} + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public final class ScopedGHN extends ScopedResource { + + public static final String TYPE = GCUBEHostingNode.TYPE; + + private String nodename = ""; + + private transient EndpointReferenceType ghnEpr; + + protected ScopedGHN(String id, GCUBEScope scope) { + super(id, TYPE, scope); + } + + + @Override + protected void addToScope() throws ResourceNotFound, Exception { + if (this.ghnEpr == null) + this.findResource(); + //contact the GHNManager to add the GHN to the given scope + EndpointReferenceType endpoint = new EndpointReferenceType(); + try { + endpoint.setAddress(new Address("http://"+ this.nodename +"/wsrf/services/gcube/common/vremanagement/GHNManager")); + GHNManagerServiceAddressingLocator locator = new GHNManagerServiceAddressingLocator(); + GHNManagerPortType pt = GCUBERemotePortTypeContext.getProxy(locator.getGHNManagerPortTypePort(endpoint), + ServiceContext.getContext().getScope().getEnclosingScope(), ServiceContext.getContext()); + AddScopeInputParams params = new AddScopeInputParams(); + params.setScope(this.scope.toString()); + params.setMap(""); //eventually, set here the new Service Map + pt.addScope(params); + + } catch (Exception e) { + this.noHopeForMe("Failed to add GHN to scope " + scope.toString(), e); + } + + } + + @Override + protected void find() throws Exception { + ISClient client = null; + GCUBEGHNQuery query = null; + try { + client = GHNContext.getImplementation(ISClient.class); + query = client.getQuery(GCUBEGHNQuery.class); + query.addAtomicConditions(new AtomicCondition("/ID/text()", this.id)); + } catch (Exception e) { + throw new Exception("unable to query of the target GHN (ID=" + this.id + ")", e); + } + try { + List hostingNodes = client.execute(query, ServiceContext.getContext().getScope()); + this.nodename = hostingNodes.get(0).getNodeDescription().getName(); + this.hostedOn = nodename; + this.ghnEpr = this.loadGHNmanager(hostingNodes.get(0).getID(), client, ServiceContext.getContext().getScope()); + } catch (Exception e) { + //try in the enclosing scope + try { + List hostingNodes = client.execute(query, ServiceContext.getContext().getScope().getEnclosingScope()); + this.nodename = hostingNodes.get(0).getNodeDescription().getName(); + this.hostedOn = nodename; + this.ghnEpr = this.loadGHNmanager(hostingNodes.get(0).getID(), client,ServiceContext.getContext().getScope().getEnclosingScope()); + } catch (Exception ei) { + throw new Exception("unable to find the target GHN (ID=" + this.id + ")", e); + } + } + + } + + @Override + protected void removeFromScope() throws ResourceNotFound, Exception { + if (this.ghnEpr == null) + this.findResource(); + //EndpointReferenceType endpoint = new EndpointReferenceType(); + try { + //endpoint.setAddress(new Address("http://"+ this.nodename +"/wsrf/services/gcube/common/vremanagement/GHNManager")); + GHNManagerServiceAddressingLocator locator = new GHNManagerServiceAddressingLocator(); + GHNManagerPortType pt = GCUBERemotePortTypeContext.getProxy(locator.getGHNManagerPortTypePort(this.ghnEpr), + ServiceContext.getContext().getScope().getEnclosingScope(), ServiceContext.getContext()); + pt.removeScope(this.scope.toString()); + } catch (Exception e) { + this.noHopeForMe("Failed to remove GHN from scope " + scope.toString(), e); + } + + } + + /** + * Looks for the GHN manager's endpoint to contact + * @param id the identifier of the gHN + * @param client the ISClient instance to use + * @param scope + * @return the endpoint reference of gHNManager's portType to contact + * @throws Exception if the search fails + */ + private EndpointReferenceType loadGHNmanager(String id, ISClient client, GCUBEScope scope) throws Exception { + //looks for the GHN manager's endpoint to contact + GCUBERIQuery riquery = client.getQuery(GCUBERIQuery.class); + riquery.addAtomicConditions(new AtomicCondition("//GHN/@UniqueID", id), + new AtomicCondition("//ServiceClass", "VREManagement"), + new AtomicCondition("//ServiceName", "GHNManager")); + List results = client.execute(riquery,scope); + return results.get(0).getAccessPoint().getEndpoint("gcube/common/vremanagement/GHNManager"); + } + + +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/resources/ScopedResource.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/resources/ScopedResource.java new file mode 100644 index 0000000..af1a5e9 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/resources/ScopedResource.java @@ -0,0 +1,332 @@ +package org.gcube.vremanagement.resourcemanager.impl.resources; + + +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import org.gcube.common.core.resources.GCUBEResource; +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.common.core.utils.logging.GCUBELog; +import org.gcube.vremanagement.resourcemanager.impl.operators.Operator.ACTION; +import com.thoughtworks.xstream.annotations.XStreamOmitField; + +/** + * An abstract model for a scoped {@link GCUBEResource} + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public abstract class ScopedResource { + + /** Object getLogger() */ + @XStreamOmitField + protected GCUBELog logger; + + /** the resource identifier*/ + protected String id; + + /** the resource type */ + protected String type; + + /** where the resource is hosted on, it makes sense for RI, GHN*/ + protected String hostedOn = ""; + + /** result of the last operation performed on the resource */ + protected boolean success; + + /** the error message, if any, from the last operation */ + protected String errorMessage = ""; + + protected String scope; + + /** the last action performed on the resource*/ + protected ACTION action; + + /** Last modification time stamp*/ + protected Date lastModificationTime; + + /** Last modification time stamp*/ + protected Date jointTime; + + /** the current status of the resource */ + protected STATUS status; + + /** resource's legal statuses */ + public static enum STATUS { + CREATED() {public List previous() {return Collections.emptyList();}}, + ADDREQUESTED() {public List previous() {return Collections.unmodifiableList(Arrays.asList(CREATED));}}, + ADDED() {public List previous() {return Collections.unmodifiableList(Arrays.asList(ADDREQUESTED));}}, + PUBLISHED() {public List previous() {return Collections.unmodifiableList(Arrays.asList(ADDED));}}, + REMOVEREQUESTED() {public List previous() {return Collections.unmodifiableList(Arrays.asList(ADDED, PUBLISHED));}}, + REMOVED() {public List previous() {return Collections.unmodifiableList(Arrays.asList(REMOVEREQUESTED));}}, + UNPUBLISHED () {public List previous() {return Collections.unmodifiableList(Arrays.asList(REMOVED));}}, + LOST () {public List previous() {return Collections.unmodifiableList(Arrays.asList(REMOVED,REMOVEREQUESTED,ADDED,PUBLISHED,CREATED,UNPUBLISHED));}}; + + abstract public List previous(); + } + + public ScopedResource(String id, String type, GCUBEScope scope) { + this.id = id; + this.type = type; + this.scope = scope.toString(); + this.status = STATUS.CREATED; + } + + /** + * Custom + * + * @throws ResourceNotFound if it is impossible to locate the resource + */ + protected abstract void find() throws Exception;; + + /** + * Looks for the resource in the infrastructure + * + * @throws ResourceNotFound if it is impossible to locate the resource + */ + public synchronized void findResource() throws ResourceNotFound { + this.setErrorMessage(""); //empty any previous message + int max_attempts = this.getMaxFindAttempts(); //try to find the resource 5 times + int i = 0; + while (true) { + try { + this.find(); + this.success = true; + break; + } catch (Exception e) { + logger.warn("Can't find resource "+ this + " on the IS"); + if (i++ <= max_attempts) { + logger.warn("try again in 5 secs"); + try {Thread.sleep(5000);} catch (InterruptedException e1) {} + continue; + } + else { + this.noHopeForMe("Can't find resource "+ this + " on the IS", new ResourceNotFound(e)); + break; + } + } + } + } + + public synchronized void doAction(ACTION action) throws ResourceNotFound, Exception { + this.action = action; + switch (action) { + case ADD: this.addToScope(); this.setJointTime(Calendar.getInstance().getTime()); this.success= true; break; + case REMOVE: this.removeFromScope(); this.success= true; break; + default: break; + } + this.setChanged(); + } + + /** + * Adds the resource to the scope + * @throws Exception if the operation fails + * @throws ResourceNotFound if the resource does not exist in the infrastructure + */ + protected abstract void addToScope() throws ResourceNotFound, Exception; + + + /** + * Removes the resource from the scope + * @throws Exception if the operation fails + * @throws ResourceNotFound if the resource does not exist in the infrastructure + */ + protected abstract void removeFromScope() throws ResourceNotFound, Exception; + + + /** + * @return the resource ID + */ + public String getId() { + return id; + } + + /** + * @return the resource type + */ + public String getType() { + return type; + } + + /** + * @return the scope + */ + public final GCUBEScope getScope() { + return GCUBEScope.getScope(this.scope); + } + + protected final void setScope(GCUBEScope scope) { + this.scope = scope.toString(); + } + /** + * @return the action + */ + public ACTION getAction() { + return action; + } + + /** + * @return the errorMessage + */ + public String getErrorMessage() { + return errorMessage; + } + + /** + * @param errorMessage the errorMessage to set + */ + public void setErrorMessage(String errorMessage) { + this.success = false; + this.errorMessage = errorMessage; + } + + /** + * Updates the time the resource joined the scope + * @param time the new joint time + */ + public void setJointTime(Date time) { + this.jointTime = time; + + } + + private void setChanged() { + this.setLastModificationTime(Calendar.getInstance().getTime()); + } + + /** + * @return the success + */ + public boolean isSuccess() { + return success; + } + + /** + * @return the node on which the Scope is hosted + */ + public String getHostedOn() { + return hostedOn; + } + + public void setHostedON(String hostedOn) { + this.hostedOn = hostedOn; + } + + public Date getJointTime() { + if (jointTime == null) + jointTime = Calendar.getInstance().getTime(); + return jointTime; + } + + public Date getLastModificationTime() { + if (lastModificationTime == null) + this.setChanged(); + return lastModificationTime; + } + + private void setLastModificationTime(Date lastModificationTime) { + this.lastModificationTime = lastModificationTime; + } + + /** + * @return the status + */ + public STATUS getStatus() { + return status; + } + + /** + * @param status the status to set + */ + public synchronized void setStatus(STATUS status) { + getLogger().trace(this.toString()+ ": status set to " + status); + this.status = status; + } + + /** + * Number of times the resource is searched in the IS before to declare it lost + * @return + */ + protected int getMaxFindAttempts(){ + return 5; + } + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((id == null) ? 0 : id.hashCode()); + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ScopedResource other = (ScopedResource) obj; + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) + return false; + return true; + } + + + /** + * Gives up the operation on the resource + * @param message the error message to return + * @param e the exception that generates the hopeless + * @throws E the source exception + */ + protected void noHopeForMe(String message, E e) throws E { + getLogger().error(this.toString() +": Unable to manage the resource " + message ,e); + this.setStatus(STATUS.LOST); + this.success = false; + this.setErrorMessage(message); + throw e; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "Resource [id=" + id + + ", type=" + type + + ", timestamp=" + lastModificationTime + + ", scope=" + scope + + ", status=" + status + + " hostedOn=" + hostedOn + "]"; + } + + + protected GCUBELog getLogger() { + if (this.logger == null) + logger=new GCUBELog(this); + return logger; + } + + /** Unable to find the resource in the infrastructure*/ + public class ResourceNotFound extends Exception { + public ResourceNotFound(String message) {super(message);} + + public ResourceNotFound(Exception e) { + super(e); + } + + private static final long serialVersionUID = -6111206113583291172L; + } + +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/resources/ScopedResourceFactory.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/resources/ScopedResourceFactory.java new file mode 100644 index 0000000..5dbdf7b --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/resources/ScopedResourceFactory.java @@ -0,0 +1,85 @@ +package org.gcube.vremanagement.resourcemanager.impl.resources; + +import org.gcube.common.core.resources.GCUBEHostingNode; +import org.gcube.common.core.resources.GCUBERunningInstance; +import org.gcube.common.core.resources.GCUBEService; +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.common.core.utils.logging.GCUBELog; +import org.gcube.vremanagement.resourcemanager.impl.state.ScopeState; + +public class ScopedResourceFactory { + + protected static GCUBELog logger = new GCUBELog(ScopedResourceFactory.class); + + private static ScopeState scopeState = null; + + public static void setResourceList(final ScopeState list) { + scopeState = list; + } + + /** + * Creates a new {@link ScopedResource} + * + * @param id the gCube Resource identifier + * @param type the resource type + * @param scope the {@link GCUBEScope} assigned to the resource + * @return the resource + */ + public static ScopedResource newResource(String id, String type, GCUBEScope scope) throws Exception { + + if (scopeState != null) { + if (scopeState.containsResource(id)) { + logger.trace("Taking resource " + id + " from the scope state"); + scopeState.getResource(id).setScope(scope); + return scopeState.getResource(id); + } + } + if ((id == null) || (id.compareTo("") == 0)) + throw new Exception("invalid resource id specified"); + + if ((type == null) || (type.compareTo("") == 0)) + throw new Exception("invalid resource type specified"); + + ScopedResource sresource = null; + logger.trace("Creating a new resource " + id + ""); + if (type.compareToIgnoreCase(GCUBEHostingNode.TYPE) == 0) + sresource = new ScopedGHN(id, scope); + else if (type.compareToIgnoreCase(GCUBERunningInstance.TYPE) == 0) + sresource = new ScopedRunningInstance(id, scope); + else if (type.compareToIgnoreCase(GCUBEService.TYPE) == 0) + sresource = new ScopedDeployedService(id, scope); + else + sresource = new ScopedAnyResource(id, type, scope); + + return sresource; + } + + /** + * Gets the service that originates the RI, if any + * @param scopedRunningInstance + * @return the service + */ + public static ScopedDeployedService getRelatedService( + ScopedRunningInstance scopedRunningInstance) throws ServiceNotFoundException { + for (ScopedResource resource : scopeState.getResourcesByType(ScopedDeployedService.TYPE)) { + if (resource == null) continue; + try { + ScopedDeployedService service = (ScopedDeployedService)resource; + if ((service.getSourceService().getClazz().compareToIgnoreCase(scopedRunningInstance.getSourceServiceClass())==0) + && (service.getSourceService().getName().compareToIgnoreCase(scopedRunningInstance.getSourceServiceName())==0)) { + logger.debug("Source service found for RI " + scopedRunningInstance); + return service; + } + }catch (Exception e ) { + logger.warn("An error occurred when looking for the source service for RI "+ scopedRunningInstance + ", error message: " + e.getMessage()); + } + } + logger.error("Unable to find a source service for " + scopedRunningInstance); + throw new ServiceNotFoundException(); + } + + /** Service not found exception, it occurs when a RI was not deployed by this RM instance*/ + public static class ServiceNotFoundException extends Exception { + private static final long serialVersionUID = 3904279081090871340L; + } +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/resources/ScopedRunningInstance.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/resources/ScopedRunningInstance.java new file mode 100644 index 0000000..077a03e --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/resources/ScopedRunningInstance.java @@ -0,0 +1,234 @@ +package org.gcube.vremanagement.resourcemanager.impl.resources; + +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; + +import org.apache.axis.message.addressing.EndpointReferenceType; +import org.gcube.common.core.contexts.GCUBERemotePortTypeContext; +import org.gcube.common.core.contexts.GHNContext; +import org.gcube.common.core.informationsystem.client.AtomicCondition; +import org.gcube.common.core.informationsystem.client.ISClient; +import org.gcube.common.core.informationsystem.client.QueryParameter; +import org.gcube.common.core.informationsystem.client.XMLResult; +import org.gcube.common.core.informationsystem.client.queries.GCUBEGHNQuery; +import org.gcube.common.core.informationsystem.client.queries.GCUBEGenericQuery; +import org.gcube.common.core.informationsystem.client.queries.GCUBERIQuery; +import org.gcube.common.core.resources.GCUBEHostingNode; +import org.gcube.common.core.resources.GCUBERunningInstance; +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.common.vremanagement.ghnmanager.stubs.GHNManagerPortType; +import org.gcube.common.vremanagement.ghnmanager.stubs.ScopeRIParams; +import org.gcube.common.vremanagement.ghnmanager.stubs.service.GHNManagerServiceAddressingLocator; +import org.gcube.vremanagement.resourcemanager.impl.contexts.ServiceContext; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResourceFactory.ServiceNotFoundException; + +/** + * A scoped {@link GCUBERunningInstance} + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public final class ScopedRunningInstance extends ScopedResource { + + public static final String TYPE = GCUBERunningInstance.TYPE; + + private String hostingNodeName = ""; + + /** data of the service from which the RI was generated */ + private ServiceData sourceService = new ServiceData();; + + /** ghn ID of the node hosting the RI */ + private String hostedOnID = ""; + + /** Scopes the instance belongs to */ + private List scopes; + + private transient EndpointReferenceType ghnEpr; + + protected ScopedRunningInstance(String id, GCUBEScope scope, String ... hostedOn) { + super(id, TYPE, scope); + if ((hostedOn != null) && (hostedOn.length > 0)) + this.hostedOn = hostedOn[0]; + } + + @Override + protected void find() throws Exception { + //looks for the RI to manage + try { + ISClient client = GHNContext.getImplementation(ISClient.class); + //we cannot use here the GCUBERunningInstanceQuery because it returns only ready instances, + //we need to find a RI, no matter in which state it is + GCUBEGenericQuery query = client.getQuery("GCUBEResourceQuery"); + query.addParameters(new QueryParameter("FILTER", "$result/ID/text()/string() eq \""+id+"\""), + new QueryParameter ("RESULT", "$result")); + List results = client.execute(query, ServiceContext.getContext().getScope()); + //query.addAtomicConditions(new AtomicCondition("/ID/text()", id)); + + if (results == null || results.size() == 0) { + results = client.execute(query, ServiceContext.getContext().getScope().getEnclosingScope()); + if (results == null || results.size() == 0) { + throw new Exception("unable to find target RI "+ this); + } + } + + GCUBERunningInstance profile = GHNContext.getImplementation(GCUBERunningInstance.class); + profile.load(new StringReader(results.get(0).evaluate("/").get(0))); + this.sourceService = new ServiceData(); + this.sourceService.serviceID = profile.getServiceID(); + this.sourceService.serviceClass = profile.getServiceClass(); + this.sourceService.serviceName = profile.getServiceName(); + this.hostedOnID = profile.getGHNID(); + this.scopes = new ArrayList(); + for (GCUBEScope scope : profile.getScopes().values()) + this.scopes.add(scope.getName()); + } catch (Exception e) { + throw new Exception("unable to find the target RI " + this,e); + } + + //looks for the GHN to contact + try { + ISClient client = GHNContext.getImplementation(ISClient.class); + GCUBEGHNQuery query = client.getQuery(GCUBEGHNQuery.class); + query.addAtomicConditions(new AtomicCondition("/ID/text()", this.hostedOnID)); + List hostingNode = client.execute(query, ServiceContext.getContext().getScope()); + this.hostingNodeName = hostingNode.get(0).getNodeDescription().getName(); + this.ghnEpr = this.loadGHNmanager(hostingNode.get(0).getID(), client); + this.hostedOn = hostingNodeName; + } catch (Exception e) { + this.noHopeForMe("unable to find the hosting GHN (ID=" + this.hostedOnID + ")", new ResourceNotFound(e)); + } + + } + + /** + * Adds the RI to the scope + * + * @throws Exception if it is not possible to add the resource + */ + @Override + protected void addToScope() throws ResourceNotFound, Exception { + if (this.hostingNodeName.compareToIgnoreCase("") == 0) + this.findResource(); + try { + GHNManagerServiceAddressingLocator locator = new GHNManagerServiceAddressingLocator(); + GHNManagerPortType pt = GCUBERemotePortTypeContext.getProxy(locator.getGHNManagerPortTypePort(this.ghnEpr), + ServiceContext.getContext().getScope(), ServiceContext.getContext()); + ScopeRIParams params = new ScopeRIParams(); + params.setClazz(this.sourceService.serviceClass); + params.setName(this.sourceService.serviceName); + params.setScope(this.scope); + pt.addRIToScope(params); + } catch (Exception e) { + this.setStatus(STATUS.LOST); + this.success = false; + this.setErrorMessage("Failed to add RunningInstance to scope " + scope); + getLogger().error("Failed to add RunningInstance to scope " + scope, e); + throw new Exception("Failed to add RunningInstance to scope " + scope); + } + } + + + /** + * Removes the RI from the scope + * + * @throws Exception if it is not possible to remove the resource + */ + @Override + protected void removeFromScope() throws ResourceNotFound,ServiceNotFoundException, Exception { + this.findResource(); + try { + GHNManagerServiceAddressingLocator locator = new GHNManagerServiceAddressingLocator(); + GHNManagerPortType pt = GCUBERemotePortTypeContext.getProxy(locator.getGHNManagerPortTypePort(this.ghnEpr), + ServiceContext.getContext().getScope(), ServiceContext.getContext()); + ScopeRIParams params = new ScopeRIParams(); + params.setClazz(this.sourceService.serviceClass); + params.setName(this.sourceService.serviceName); + params.setScope(this.scope); + pt.removeRIFromScope(params); + } catch (Exception e) { + this.noHopeForMe("Failed to remove RunningInstance from scope " + scope.toString() +" "+ e.getMessage(),e); + } + } + + protected String getSourceServiceClass() { + return this.sourceService.serviceClass; + } + + protected String getSourceServiceName() { + return this.sourceService.serviceName; + } + + public boolean isUndeployNeeded() throws ResourceNotFound { + try { + this.findResource(); + } catch (ResourceNotFound e) { + this.noHopeForMe("Failed to remove RunningInstance from scope " + scope.toString() +". Can't find the related service to undeploy "+ e.getMessage(),e); + } + if (this.scopes.size() == 1) { + getLogger().info(this + " joins only this scope: it's going to be undeployed"); + return true; + } + return false; + } + + /** + * @return the ID of the gHN hosting the instance + */ + public String getHostedOnID() { + return hostedOnID; + } + + public void reportFailureOnSourceService(String message, Exception e) { + try { + this.noHopeForMe(message, e); + } catch (Exception e1) { + getLogger().error(this + "An error has been reported from outside when managing the source service", e1); + } + } + + public void wasSuccessful() { + this.success = true; + } + + /** + * Number of times the resource is searched in the IS before to declare it lost + * @return the number of attempts to do + */ + @Override + protected int getMaxFindAttempts(){ + return 40; + } + + /** + * Looks for the GHN manager's endpoint to contact + * @param id the identifier of the gHN + * @param client the ISClient instance to use + * @return the endpoint reference of gHNManager's portType to contact + * @throws Exception if the search fails + */ + private EndpointReferenceType loadGHNmanager(String id, ISClient client) throws Exception { + //looks for the GHN manager's endpoint to contact + GCUBERIQuery riquery = client.getQuery(GCUBERIQuery.class); + riquery.addAtomicConditions(new AtomicCondition("//GHN/@UniqueID", id), + new AtomicCondition("//ServiceClass", "VREManagement"), + new AtomicCondition("//ServiceName", "GHNManager")); + List results = client.execute(riquery,ServiceContext.getContext().getScope()); + return results.get(0).getAccessPoint().getEndpoint("gcube/common/vremanagement/GHNManager"); + } + + + /** + * + * Groups some service data + * + * @author Manuele Simi (ISTI-CNR) + * + */ + class ServiceData { + String serviceID; + String serviceName; + String serviceClass; + } + +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/resources/types/MultiKeysMap.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/resources/types/MultiKeysMap.java new file mode 100644 index 0000000..92ff5ba --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/resources/types/MultiKeysMap.java @@ -0,0 +1,259 @@ +package org.gcube.vremanagement.resourcemanager.impl.resources.types; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + + +/** + * + * A thread-safe Map with multiple keys allowing multiple values per key + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class MultiKeysMap implements Iterable { + + private List values = Collections.synchronizedList(new LinkedList()); + + private List wrappedValues = Collections.synchronizedList(new LinkedList()); + + private Map> k1values = Collections.synchronizedMap(new HashMap>()); + + private Map> k2values = Collections.synchronizedMap(new HashMap>()); + + + /** + * {@inheritDoc} + */ + public Iterator iterator() { + return values.iterator(); + } + + /** + * Associates the specified value with the two keys + * + * @param key1 the value's primary key + * @param key2 the value's secondary key + * @param value the value + */ + public void put(K1 key1, K2 key2, V value) { + WrappedValue wrappedValue = this.new WrappedValue(value); + wrappedValue.addKey1(key1); + wrappedValue.addKey2(key2); + if (wrappedValues.contains(wrappedValue)) + wrappedValues.remove(wrappedValue); + wrappedValues.add(wrappedValue); + if (values.contains(value)) + values.remove(value); + values.add(value); + //add the value index to k1values + if (!k1values.containsKey(key1)) + k1values.put(key1, new HashSet()); + k1values.get(key1).add(value); + //add the value index to k2values + if (!k2values.containsKey(key2)) + k2values.put(key2, new HashSet()); + k2values.get(key2).add(value); + } + + /** + * removes the given values and its keys from the map + * @param value + */ + public void removeValue(V value) { + values.remove(value); + WrappedValue wvalue = wrappedValues.get(this.wrappedIndexOf(value)); + + k1values.get(wvalue.key1).remove(value); + if (k1values.get(wvalue.key1).isEmpty()) + k1values.remove(wvalue.key1); + + k2values.get(wvalue.key2).remove(value); + if (k2values.get(wvalue.key2).isEmpty()) + k2values.remove(wvalue.key2); + + wrappedValues.remove(wvalue); + + } + + /** + * Removes all the values associated to the primary key + * + * @param key the key of type K1 + */ + public void removeValuesByPrimaryKey(K1 key) { + Set valuesToRemove = k1values.get(key); + if (valuesToRemove == null) + return; + Iterator iterator = valuesToRemove.iterator(); + while (iterator.hasNext()) { + V value = iterator.next(); + values.remove(value); + if (value == null) { + continue; + } + WrappedValue wvalue = wrappedValues.get(this.wrappedIndexOf(value)); + k2values.get(wvalue.key2).remove(value); + if (k2values.get(wvalue.key2).isEmpty()) + k2values.remove(wvalue.key2); + wrappedValues.remove(wvalue); + } + + k1values.remove(key); + } + + + /** + * Removes all the values associated to the secondary key + * + * @param key the key of type K2 + */ + public void removeValuesBySecondaryKey(K2 key) { + Set valuesToRemove = k2values.get(key); + if (valuesToRemove == null) + return; + Iterator iterator = valuesToRemove.iterator(); + while (iterator.hasNext()) { + V value = iterator.next(); + values.remove(value); + if (value == null) { + return; + } + WrappedValue wvalue = wrappedValues.get(this.wrappedIndexOf(value)); + k1values.get(wvalue.key1).remove(value); + if (k1values.get(wvalue.key1).isEmpty()) + k1values.remove(wvalue.key1); + wrappedValues.remove(wvalue); + } + + k2values.remove(key); + } + + + + private int wrappedIndexOf(V value) { + int index = 0; + if (value==null) + return -1; + for (WrappedValue wvalue : wrappedValues) { + if (wvalue.equals(value)) + return index; + index++; + } + return -1; + } + + + /** + * Returns the values to which this map maps the specified primary key + * + * @param key key whose associated values are to be returned + * @return the values to which this map maps the specified primary key + */ + @SuppressWarnings("unchecked") + public Set getValuesByPrimaryKey(K1 key) { + return k1values.get(key) == null? (Set)Collections.emptySet(): k1values.get(key); + } + + /** + * Returns the values to which this map maps the specified primary key + * @param key key whose associated values are to be returned + * @return @return the values to which this map maps the specified secondary key + */ + @SuppressWarnings("unchecked") + public Set getValuesBySecondaryKey(K2 key) { + return k2values.get(key) == null? (Set)Collections.emptySet(): k2values.get(key); + } + + /** + * Returns a set view of the primary keys contained in this map + * + * @return a set view of the primary keys contained in this map + */ + @SuppressWarnings("unchecked") + public Set primaryKeySet() { + return k1values.keySet() == null? (Set)Collections.emptySet(): k1values.keySet(); + } + + /** + * Returns a set view of the secondary keys contained in this map + * + * @return a set view of the secondary keys contained in this map + */ + @SuppressWarnings("unchecked") + public Set secondaryKeySet() { + return k2values.keySet() == null? (Set)Collections.emptySet(): k2values.keySet(); + } + + /** + * Returns a collection view of the values contained in this map + * + * @return a collection view of the values contained in this map + */ + public Collection values() { + return Collections.unmodifiableCollection(values); + } + + /** + * Removes all mapping from this maps + */ + public void clean () { + values.clear(); + wrappedValues.clear(); + k1values.clear(); + k2values.clear(); + } + + /** + * + * Internal Map object + * + * It maps a value V to both its primary and secondary key + * + * @author Manuele Simi (ISTI-CNR) + * + */ + class WrappedValue { + + V value; + K1 key1; + K2 key2; + WrappedValue(V value) { this.value = value;} + void addKey1(K1 key) {this.key1=key;} + void addKey2(K2 key) {this.key2=key;} + + @Override + public int hashCode() { + return ((value == null) ? 0 : value.hashCode()); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() == obj.getClass()) { + WrappedValue other = (WrappedValue) obj; + if ((this.value == null) && (other.value == null)) { + return true; + } else if (this.value.equals(other.value)) { + return true; + } + } else { //check if obj is the wrapped object + if (this.value.equals(obj)) { + return true; + } + } + return false; + } + } + +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/InstanceState.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/InstanceState.java new file mode 100644 index 0000000..31a9c41 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/InstanceState.java @@ -0,0 +1,173 @@ +package org.gcube.vremanagement.resourcemanager.impl.state; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.gcube.common.core.contexts.GHNContext; +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.common.core.state.GCUBEWSResource; +import org.gcube.vremanagement.resourcemanager.impl.contexts.ServiceContext; +import org.gcube.vremanagement.resourcemanager.impl.deployment.VirtualNode; +import org.gcube.vremanagement.resourcemanager.impl.deployment.VirtualNodeManager; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResource; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResourceFactory; +import org.gcube.vremanagement.resourcemanager.impl.state.observers.Executor; +import org.gcube.vremanagement.resourcemanager.impl.state.observers.Publisher; +import org.gcube.vremanagement.resourcemanager.impl.state.observers.Serializer; +import org.globus.wsrf.NoSuchResourceException; + +/** + * The ResourceManager's stateful resource + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public final class InstanceState extends GCUBEWSResource { + + final protected GCUBEScope scope = ServiceContext.getContext().getInstance().getScopes().values().iterator().next(); + + protected ScopeState scopeState; + + /** session id -> session map*/ + static Map id2session = Collections.synchronizedMap(new HashMap());; + + @SuppressWarnings("unchecked") + @Override + protected void initialise(Object... params) throws Exception { + scopeState = new ScopeState(); + try { + logger.info("Initializing the instance state from the local file system..."); + Serializer.load(scopeState, scope); + //synch IS list w/ scopeState + this.getPublishedScopeResource().loadFromLocalState(scopeState); //it's already done by the Publisher observer? + } catch (IOException io) { + logger.warn("The local serialized scope state is not available"); + logger.info("Loading the instance state from the IS..."); + if (this.getPublishedScopeResource().load()) { + logger.info("Instance state harvested from the IS"); + scopeState.initialize(scope, scope.getName(), GHNContext.getContext().isSecurityEnabled()); + //synch scopeState w/ IS list + this.getPublishedScopeResource().to(scopeState); + } else { + logger.info("Empty instance state created"); + //we assume that if we are running on a secure ghn, we are in a secure Scope + scopeState.initialize(scope, scope.getName(), GHNContext.getContext().isSecurityEnabled()); + this.getPublishedScopeResource().loadFromLocalState(scopeState); + + } + scopeState.getRawScopeState().data.put("NODES", new HashMap()); + } + ScopedResourceFactory.setResourceList(scopeState); + VirtualNodeManager.setNodes((Map) scopeState.getRawScopeState().data.get("NODES")); + this.registerObservers(); + //let's notify the observers about the current scope state + new Thread() { + /* (non-Javadoc) + * @see java.lang.Thread#run() + */ + @Override + public void run() { + scopeState.notifyObservers(); + } + + }.start(); + + } + + private void registerObservers() { + //register the observers + scopeState.addObserver(new Executor()); + scopeState.addObserver(new Publisher()); + scopeState.addObserver(new Serializer()); + } + + + /** + * Gets the RP ManagedScope + * + * @return the scope managed by this instance + */ + synchronized public GCUBEScope getManagedScope () { + return this.scope; + } + + + /** + * Gets the {@link Session} + * + * @param id the session ID + * @return the session + * @throws IOException + */ + public Session getSession(String id) throws IOException { + if (! id2session.containsKey(id)) + id2session.put(id, Session.load(id)); + return id2session.get(id); + } + + /** + * Gets the string representation of a {@link Session} + * + * @param id the session ID + * @return the string representation of the session + * @throws IOException + */ + public String getSerializedSession(String id) throws IOException { + return Session.loadAsString(id); + } + + + /** + * Adds a new {@link Session} to the service's state + * + * @param session the session to add + */ + public void addSession(Session session) { + this.getResourceList().setLastSession(session); + id2session.put(session.getId(), session); + } + + /** + * Gets the {@link PublishedScopeResource} + * + * @return the {@link PublishedScopeResource} + * @throws NoSuchResourceException + */ + public PublishedScopeResource getPublishedScopeResource() throws NoSuchResourceException { + try { + return PublishedScopeResource.getResource(this.scope); + } catch (Exception e) { + logger.warn("Unable to get the PublishedScopeResource", e); + throw new NoSuchResourceException(); + } + } + + /** + * Gets the list of {@link ScopedResource}s + * @return the list of scoped resources + */ + public ScopeState getResourceList() { + return scopeState; + } + + /** + * Disposes the current scope + */ + public void dispose() { + Set allResources = new HashSet(); + for (ScopedResource resource : this.scopeState.getAllResources()) { + allResources.add(resource); + } + //remove all resources + this.scopeState.removeResources(allResources); + + //TODO: unpublish the published scope resource + + //TODO: undeploy myself? + + } +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/ProfileDate.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/ProfileDate.java new file mode 100644 index 0000000..87e4efd --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/ProfileDate.java @@ -0,0 +1,56 @@ +package org.gcube.vremanagement.resourcemanager.impl.state; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.regex.Pattern; + +/** + * Transforms from {@link Date} to a valid string representation for the xs:dateAndTime XML Schema data type and vice versa. + * It can be used to write/read a {@link Date} object to/from a profile. + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public final class ProfileDate { + + static final DateFormat dateAndTime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); + + /** + * Transforms the input {@link Date} in a valid string representation for the xs:dateAndTime XML Schema data type + * @param date the {@link Date} object to tranform + * @return the {@link String} object + */ + public static synchronized String toXMLDateAndTime(Date date) { + String formatted = dateAndTime.format(date); + StringBuilder toXS = new StringBuilder(); + toXS.append(formatted.substring(0, formatted.length()-2)); + toXS.append(":"); + toXS.append(formatted.substring(formatted.length()-2, formatted.length())); + return toXS.toString(); + + } + + /** + * Transforms the input xs:dateAndTime representation in a {@link Date} object + * @param date the string representation of xs:dateAndTime (e.g. " 2009-05-12T16:46:03+02:00 ") + * @return the {@link Date} object + * @throws ParseException if the input date is not in an valid format + */ + public static synchronized Date fromXMLDateAndTime(String date) throws ParseException { + + //the test is for backward compatibility, to read the old profiles that have no time zone in the dateAndTime fields + Pattern p = Pattern.compile("^.*T\\d{2}:\\d{2}:\\d{2}$"); //ends with 'T'HH:mm:ss + if (p.matcher(date).matches()) { + return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").parse(date); + } else { + StringBuilder toDate = new StringBuilder(); + toDate.append(date.substring(0, date.length()-3)); + toDate.append(date.substring(date.length()-2, date.length())); + return dateAndTime.parse(toDate.toString()); + } + + } + +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/PublishedScopeResource.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/PublishedScopeResource.java new file mode 100644 index 0000000..1f3bfea --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/PublishedScopeResource.java @@ -0,0 +1,622 @@ +package org.gcube.vremanagement.resourcemanager.impl.state; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; +import java.text.ParseException; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.gcube.vremanagement.resourcemanager.impl.contexts.ServiceContext; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResource; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResourceFactory; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResource.STATUS; +import org.gcube.common.core.contexts.GHNContext; +import org.gcube.common.core.informationsystem.client.AtomicCondition; +import org.gcube.common.core.informationsystem.client.ISClient; +import org.gcube.common.core.informationsystem.client.queries.GCUBEGenericResourceQuery; +import org.gcube.common.core.informationsystem.publisher.ISPublisher; +import org.gcube.common.core.resources.GCUBEGenericResource; +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.common.core.scope.GCUBEScope.Type; +import org.gcube.common.core.utils.logging.GCUBELog; +import org.kxml2.io.KXmlParser; +import org.kxml2.io.KXmlSerializer; + +/** + * The scope resource published in the IS + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class PublishedScopeResource { + + protected GCUBELog logger = new GCUBELog(this); + + private GCUBEGenericResource resource; + + private PublishedResourceList publishedResourceList; + + private GCUBEScope scope; + + private static final String NS =""; + + private String creator = "", designer = "", service = ""; + + private Date startTime = null, endTime = null; + + private boolean securityEnabled = false; + + private boolean loaded = false; + + private boolean dismissed =false; + + private static Map cache = new HashMap();; + + public void reload() throws Exception { + try { + publishedResourceList = new PublishedResourceList(); + this.load(); + //this.store();//to make sure we have locally the latest resource + } catch (Exception e) { + logger.error("Failed to reload the Scope Resource"); + throw e; + } + } + + // make it private to avoid explicit creation of VREscope objects + private PublishedScopeResource(GCUBEScope scope) throws Exception{ + this.scope = scope; + publishedResourceList = new PublishedResourceList(); + //get the service EPR from the RI profile + this.service = ServiceContext.getContext().getInstance().getAccessPoint().getRunningInstanceInterfaces().getEndpoint().get(0).getValue(); + // get the resource implementation + try { + this.resource = GHNContext.getImplementation(GCUBEGenericResource.class); + } catch (Exception e) { + throw new Exception("Unable to create resource to publish for " + scope.toString()); + } + } + + /** + * Gets the current Scope Resource + * @param scope the scope of the resource to load + * @return the PublishedScopeResource + */ + public static PublishedScopeResource getResource(GCUBEScope scope) throws Exception { + if (! cache.containsKey(scope)) + cache.put(scope, new PublishedScopeResource(scope)); + return cache.get(scope); + } + + /** + * Adds a resource to PublishedScopeResource + * + * @param resource + * @throws Exception + */ + public void addResource(ScopedResource resource) throws Exception { + this.publishedResourceList.add(resource.getId(), resource.getType(),resource.getJointTime(), resource.getHostedOn()); + resource.setStatus(STATUS.PUBLISHED); + } + + /** + * Removes a resource from the PublishedScopeResource + * + * @param resource + * @throws Exception + */ + public void removeResource(ScopedResource resource) throws Exception { + this.publishedResourceList.remove(resource.getId(), resource.getType()); + resource.setStatus(STATUS.UNPUBLISHED); + } + + /** + * Returns a string representation of the PublishedScopeResource + * @throws IOException + */ + public String toString() { + try { + this.resource.setBody(this.prepareBody()); + StringWriter ret = new StringWriter(); + this.resource.store(ret); + return ret.toString(); + } catch (Exception e) { logger.error("Invalid Scope Resource serialization",e); return "";} + + } + + /** + * @return the scope + */ + public GCUBEScope getScope() { + return scope; + } + + /** + * Returns the type of the scope. + * + * @return the type. + */ + public Type getType() { + return scope.getType(); + } + + /** + * Sets a scope option + * + * @param name + * the option name + * @param value + * the option value + * @throws Exception + * @throws ParseException + */ + public void setOption(String name, String value) + throws Exception { + if (value == null) + return; + + try { + if (name.compareToIgnoreCase("DESIGNER") == 0) { + // the designer's DN + this.designer = value; + } else if (name.compareToIgnoreCase("CREATOR") == 0) { + // the creator's DN + this.creator = value; + } else if (name.compareToIgnoreCase("ENDTIME") == 0) { + // the time at which the scope will be dismissed + this.endTime = ProfileDate.fromXMLDateAndTime(value); + } else if (name.compareToIgnoreCase("STARTTIME") == 0) { + // the time this scope was created + this.startTime = ProfileDate.fromXMLDateAndTime(value); + } else if (name.compareToIgnoreCase("DESCRIPTION") == 0) { + // the scope description + this.resource.setDescription(value); + } else if (name.compareToIgnoreCase("DISPLAYNAME") == 0) { + // the name to display (the unique name is the qualified scope) + this.resource.setName(value); + } else if (name.compareToIgnoreCase("SECURITYENABLED") == 0) { + // the name to display (the unique name is the qualified scope) + this.securityEnabled = Boolean.valueOf(value); + } else + throw new UnknownScopeOptionException(); + } catch (ParseException e) { + throw new Exception ("Unable to parse option " + name); + } + + } + + /** + * Publishes the {@link PublishedScopeResource} into the IS + */ + public synchronized void publish() throws Exception { + if (this.dismissed) return; + this.resource.setBody(this.prepareBody()); + logger.trace("Publishing Scope Resource: \n" + this.toString()); + //this.store(); + ISPublisher publisher = GHNContext.getImplementation(ISPublisher.class); + if (this.loaded) { + publisher.updateGCUBEResource(this.resource, this.scope, ServiceContext.getContext()); + } + else { + publisher.registerGCUBEResource(this.resource, this.scope, ServiceContext.getContext()); + this.loaded = true; + } + } + + /** + * Dismisses the {@link PublishedScopeResource} from the IS and the scope + */ + public synchronized void dismiss() throws Exception { + this.resource.setBody(this.prepareBody()); + logger.trace("Unpublishing Scope Resource: \n" + this.toString()); + //this.store(); + ISPublisher publisher = GHNContext.getImplementation(ISPublisher.class); + publisher.removeGCUBEResource(this.resource.getID(), this.resource.getType(), this.scope, ServiceContext.getContext()); + this.dismissed = true; + } + + private String prepareBody() throws IOException { + StringWriter body = new StringWriter(); // serialises to a temporary writer first + KXmlSerializer serializer = new KXmlSerializer(); + serializer.setOutput(body); + try { + serializer.startDocument("UTF-8", true); + serializer.startTag(NS,"Scope").text(scope.toString()).endTag(NS,"Scope"); + serializer.startTag(NS,"Service").text(this.service).endTag(NS,"Service"); + serializer.startTag(NS,"Creator").text(this.getCreator()).endTag(NS,"Creator"); + serializer.startTag(NS,"Designer").text(this.getDesigner()).endTag(NS,"Designer"); + if (this.startTime == null) + this.startTime = Calendar.getInstance().getTime(); + serializer.startTag(NS,"StartTime").text(ProfileDate.toXMLDateAndTime(this.startTime)).endTag(NS,"StartTime"); + if (this.endTime != null) + serializer.startTag(NS,"EndTime").text(ProfileDate.toXMLDateAndTime(this.endTime)).endTag(NS,"EndTime"); + serializer.startTag(NS,"SecurityEnabled").text(String.valueOf(this.securityEnabled)).endTag(NS,"SecurityEnabled"); + publishedResourceList.store(serializer); + } catch (Exception e) { + logger.error("The Scope Resource does not have a valid serialisation", e); + throw new IOException("The Scope Resource does not have a valid serialisation"); + } + finally { + body.close(); + } + return body.toString(); + + } + + /** + * @return the creator + */ + public String getCreator() { + return (creator != null) ? creator : ""; + } + + /** + * @return the designer + */ + public String getDesigner() { + return (designer != null)? designer : ""; + } + + /** + * @return the description + */ + public String getDescription() { + return (this.resource.getDescription() != null)? this.resource.getDescription() : ""; + } + + /** + * @return the display name + */ + public String getDisplayName() { + return (this.resource.getName() != null) ? this.resource.getName() : ""; + } + + /** + * @return the startTime + */ + public Date getStartTime() { + return startTime; + } + + /** + * @return the endTime + */ + public Date getEndTime() { + return endTime; + } + + /** + * @return the securityEnabled + */ + public boolean isSecurityEnabled() { + return securityEnabled; + } + + + /** + * Loads from the Body element the resource information + * @param body the Body of the generic resource + * @throws Exception if the element is not valid or well formed + */ + private void parseBody(String body) throws Exception { + KXmlParser parser = new KXmlParser(); + parser.setInput(new BufferedReader(new StringReader(body))); + loop: while (true) { + try { + switch (parser.next()) { + case KXmlParser.START_TAG: + if (parser.getName().equals("Creator")) this.creator = parser.nextText(); + else if (parser.getName().equals("Designer")) this.designer = parser.nextText(); + else if (parser.getName().equals("EndTime")) this.endTime = ProfileDate.fromXMLDateAndTime(parser.nextText()); + else if (parser.getName().equals("StartTime")) this.startTime = ProfileDate.fromXMLDateAndTime(parser.nextText()); + //else if (parser.getName().equals("DisplayName")) this.display = parser.nextText(); + else if (parser.getName().equals("SecurityEnabled")) this.securityEnabled = Boolean.valueOf(parser.nextText()); + else if (parser.getName().equals(PublishedResourceList.RESOURCES_ELEMENT)) publishedResourceList.load(parser); + else parser.nextText();//just skip the text + break; + case KXmlParser.END_DOCUMENT: break loop; + } + } catch (Exception e) { + throw new Exception ("Unable to parse the PublishedScopeResource body"); + } + } + } + + /** + * Loads the scope resource from the IS + * @param resource the resource to load + * + * @return true if the resource is successfully loaded, false otherwise + */ + protected boolean load() { + + try { + ISClient client = GHNContext.getImplementation(ISClient.class); + GCUBEGenericResourceQuery query = client.getQuery(GCUBEGenericResourceQuery.class); + query.addAtomicConditions(new AtomicCondition("//SecondaryType", determineSecondaryType()), + new AtomicCondition("//Body/Scope", scope.toString())); + //logger.trace(query.toString()); + List results = client.execute(query, scope); + if ((results != null) && (results.size() > 0)) { + this.resource = results.get(0); + this.parseBody(this.resource.getBody()); + this.loaded = true; + logger.trace("Resource loaded from the IS: \n" + this.toString()); + return true; + } else + logger.warn("Unable to load the resource for "+ this.scope.toString() + " from the IS"); + } catch (Exception e) {logger.warn("Published resource for "+ this.scope.toString()+ " does not exist on the IS yet", e);} + return false; + } + + + + /** + * Determines the secondary type of the scope resource + * + * @return the secondary type + */ + private String determineSecondaryType() { + if (this.scope.getType() == Type.VRE) + return GCUBEGenericResource.SECONDARYTYPE_VRE; + else if (this.scope.getType() == Type.VO) + return GCUBEGenericResource.SECONDARYTYPE_VO; + else + return GCUBEGenericResource.SECONDARYTYPE_INFRASTRUCTURE; + } + + + public boolean loaded() { + return this.loaded; + } + + /** + * Fills the input {@link ScopeState} with the actual content of the {@link PublishedResourceList} + * + * @param scopeState the list to fill + */ + public void to(ScopeState scopeState) { + logger.debug("To: Filling the local scope state with the published state"); + scopeState.addResources(publishedResourceList.asScopedResouces()); + scopeState.setDesigner(this.getDesigner()); + scopeState.setManager(this.getCreator()); + scopeState.setSecurity(this.isSecurityEnabled()); + scopeState.changeDescription(this.getDescription()); + scopeState.setEndTime(this.endTime); + logger.trace("Setting the scope state to " + ProfileDate.toXMLDateAndTime(this.startTime)); + scopeState.setStartTime(this.startTime); + logger.trace("Scope state set to:" + ProfileDate.toXMLDateAndTime(scopeState.getStartTime())); + scopeState.setName(this.getDisplayName()); + + } + + /** + * Fills this {@link PublishedResourceList} with the content of the input {@link ScopeState} + * + * @param scopeState the list to load + * @throws Exception if the load fails + */ + public synchronized void loadFromLocalState(ScopeState scopeState) throws Exception { + logger.debug("LoadFromLocalState: Loading the published state from the local scope state"); + //reloading is needed in order to reuse the resource ID and + //to avoid to cancel the old Generic Resource from the IS (plus reusing any other information do not overridden) + this.reload(); + publishedResourceList = new PublishedResourceList();//empty the scopeState + this.synchBasicInfo(scopeState); + for(ScopedResource resource : scopeState.getAllResources()) + this.addResource(resource); + + //initialise some resource's fields + if (this.scope.getType() == Type.VRE) + this.resource.setSecondaryType(GCUBEGenericResource.SECONDARYTYPE_VRE); + else if (this.scope.getType() == Type.VO) + this.resource.setSecondaryType("VO"); + else + this.resource.setSecondaryType("INFRASTRUCTURE"); + + this.resource.addScope(this.scope); + + // force the first serialization of Body + this.toString(); + + } + /** + * Synchronizes this {@link PublishedResourceList} with the content of the input {@link ScopeState} + * + * @param scopeState the list to synchronize with + * @throws Exception if the synchronization fails + */ + public synchronized void synchWithLocalState(ScopeState scopeState) throws Exception { + logger.debug("SynchWithLocalState: Synch the published state from the scope state"); + for(ScopedResource resource : scopeState.getAllResources()) { + switch (resource.getStatus()) { + case ADDED: this.addResource(resource); break; + case REMOVED: this.removeResource(resource); break; + case LOST: this.removeResource(resource); break; + //in the other statuses, the resource is just ignored + } + } + + this.synchBasicInfo(scopeState); + } + + private void synchBasicInfo(ScopeState scopeState) throws Exception { + logger.debug("Synchronizing basic info to publish"); + this.setOption("CREATOR",scopeState.getManager()); + this.setOption("DESIGNER",scopeState.getDesigner()); + this.setOption("DESCRIPTION",scopeState.getDescription()); + this.setOption("DISPLAYNAME",scopeState.getName()); + //here we directly assign the values, don't want to format/unformat them via setOption() + this.endTime = scopeState.getEndTime(); + this.startTime = scopeState.getStartTime(); + this.securityEnabled = scopeState.isSecurityEnabled(); + + } + + /** InvalidVREOption exception */ + public static class UnknownScopeOptionException extends Exception { + private static final long serialVersionUID = 1L; + } + + /** + * List of resources forming the {@link PublishedScopeResource} + * + * @author Manuele Simi (ISTI-CNR) + * + */ + class PublishedResourceList { + + private Set resources = Collections.synchronizedSet(new HashSet()); + + private static final String RESOURCES_ELEMENT = "ScopedRescources"; + + private static final String RESOURCE_ELEMENT = "ScopedRescource"; + + class Item { + protected String id; + protected String type; + protected String hostedOn; + protected Date timestamp; + + protected Item(String id, String value, Date timestamp, String ... hostedOn) { + this.id = id; + this.type = value; + this.timestamp = timestamp; + this.hostedOn = (hostedOn!=null && hostedOn.length>0) ? hostedOn[0] : null; + } + + protected Item(String id, String value, String ... hostedOn) { + this(id, value, null, hostedOn); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((id == null) ? 0 : id.hashCode()); + result = prime * result + + ((type == null) ? 0 : type.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + final Item other = (Item) obj; + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) + return false; + if (type == null) { + if (other.type != null) + return false; + } else if (!type.equals(other.type)) + return false; + return true; + } + + + } + + void add(String id, String type, Date time, String ... hostedOn) {resources.add(new Item(id, type, time, hostedOn));} + + void remove(String id, String type) {resources.remove(new Item(id, type)); } + + Set asScopedResouces() { + Set temp = new HashSet(); + for (Item item : resources) { + ScopedResource resource; + try { + resource = ScopedResourceFactory.newResource(item.id, item.type, scope); + resource.setJointTime(item.timestamp); + if ((item.hostedOn != null) && (item.hostedOn.compareTo("") != 0)) + resource.setHostedON(item.hostedOn); + temp.add(resource); + } catch (Exception e) { + logger.error("Can't build the resource",e); + } + + } + return temp; + + } + + int getSize() {return resources.size();} + /** + * Stores the resources in the given serializer + * @param serializer the serializer + * @throws IOException if the storage fails + */ + void store(KXmlSerializer serializer) throws IOException { + serializer.startTag(PublishedScopeResource.NS, RESOURCES_ELEMENT); + for (Item item : resources) { + serializer.startTag(PublishedScopeResource.NS, RESOURCE_ELEMENT); + serializer.startTag(PublishedScopeResource.NS, "ResourceID").text(item.id).endTag(PublishedScopeResource.NS, "ResourceID"); + serializer.startTag(PublishedScopeResource.NS, "ResourceType").text(item.type).endTag(PublishedScopeResource.NS, "ResourceType"); + if ((item.hostedOn != null) && (item.hostedOn.compareTo("") != 0)) + serializer.startTag(PublishedScopeResource.NS, "HostedOn").text(item.hostedOn).endTag(PublishedScopeResource.NS, "HostedOn"); + if (item.timestamp != null) + serializer.startTag(PublishedScopeResource.NS, "JointTime").text(ProfileDate.toXMLDateAndTime(item.timestamp)).endTag(PublishedScopeResource.NS, "JointTime"); + serializer.endTag(PublishedScopeResource.NS, RESOURCE_ELEMENT); + } + serializer.endTag(PublishedScopeResource.NS, RESOURCES_ELEMENT); + } + + /** + * Parses the Resources element + * @param parser the parser + * @throws Exception if the element is not valid or well formed + */ + void load(KXmlParser parser) throws Exception { + loop: while (true) { + try { + switch (parser.next()) { + case KXmlParser.START_TAG: + if (parser.getName().equals(RESOURCE_ELEMENT)) { + String id=null, type=null, hostedOn=null; + Date time = null; + innerloop: while (true) { + switch (parser.next()) { + case KXmlParser.START_TAG: + if (parser.getName().equals("ResourceID")) id=parser.nextText(); + else if (parser.getName().equals("ResourceType")) type=parser.nextText(); + else if (parser.getName().equals("HostedOn")) hostedOn=parser.nextText(); + else if (parser.getName().equals("JointTime")) time=ProfileDate.fromXMLDateAndTime(parser.nextText()); + else parser.nextText(); + break; + case KXmlParser.END_TAG: if (parser.getName().equals(RESOURCE_ELEMENT)){this.add(id, type, time, hostedOn);break innerloop;} + break; + case KXmlParser.END_DOCUMENT: throw new Exception ("Parsing failed at " + RESOURCE_ELEMENT); + } + } + } + break; + case KXmlParser.END_TAG: if (parser.getName().equals(RESOURCES_ELEMENT)) break loop; + break; + case KXmlParser.END_DOCUMENT: throw new Exception ("Failed to parse at " + RESOURCES_ELEMENT); + } + } catch (Exception e) { + throw new Exception ("Failed to parse at Resources"); + } + } + } + + + } + + +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/RawScopeState.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/RawScopeState.java new file mode 100644 index 0000000..2958b61 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/RawScopeState.java @@ -0,0 +1,55 @@ +package org.gcube.vremanagement.resourcemanager.impl.state; + +import java.util.HashMap; +import java.util.Map; + +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResource; +import org.gcube.vremanagement.resourcemanager.impl.resources.types.MultiKeysMap; + +/** + * + * The scope state. + * + * While {@link ScopeState} incorporates the behavior of a scope state, this separate and + * minimal class contains only the core information to serialize and nothing else. + * This way, the serialized data are separated from the state behavior, by minimizing + * the need of future changes in the state class. + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class RawScopeState { + + /** the scope this state belongs to*/ + protected String scope; + + /** the list of resources */ + protected MultiKeysMap resources; + + //Open structure for information to store. By using it, we will avoid to broke XSTREAM serialization + //when we need to add more information to the class + /** any data belonging the state worthy to be serialized*/ + protected Map data; + + protected RawScopeState () {} + + /** + * Initializes the state + * @param scope the scope this state belongs to + */ + protected void initialize(final GCUBEScope scope) { + resources = new MultiKeysMap(); + data = new HashMap(); + this.scope = scope.toString(); + } + + /** + * @return the scope this state belongs to + */ + public GCUBEScope getScope() { + return GCUBEScope.getScope(this.scope); + } + + +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/ResourceManagerHome.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/ResourceManagerHome.java new file mode 100644 index 0000000..e7a0509 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/ResourceManagerHome.java @@ -0,0 +1,21 @@ +package org.gcube.vremanagement.resourcemanager.impl.state; + +import org.gcube.common.core.contexts.GCUBEStatefulPortTypeContext; +import org.gcube.common.core.state.GCUBEWSHome; +import org.gcube.vremanagement.resourcemanager.impl.contexts.StatefulPortTypeContext; + +/** + * + * Home for stateful resource + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class ResourceManagerHome extends GCUBEWSHome { + + @Override + public GCUBEStatefulPortTypeContext getPortTypeContext() { + return StatefulPortTypeContext.getContext(); + } + +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/ScopeState.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/ScopeState.java new file mode 100644 index 0000000..3347ccd --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/ScopeState.java @@ -0,0 +1,329 @@ +package org.gcube.vremanagement.resourcemanager.impl.state; + +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Observable; +import java.util.Set; + +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.common.core.utils.logging.GCUBELog; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedDeployedService; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResource; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResource.STATUS; +import org.gcube.vremanagement.resourcemanager.impl.state.observers.Serializer; + +/** + * + * A list of scoped resources + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class ScopeState extends Observable { + + protected GCUBELog logger = new GCUBELog(this); + + /** last operation performed on the list*/ + protected OPERATION lastOperationPerformed; + + public enum OPERATION {CREATED, LOADED, TOBEMANAGED, PUBLISHED, SERIALIZED, EXECUTED}; + + private RawScopeState rawState; + + private Session report; + + protected ScopeState() {} + + protected void initialize(GCUBEScope scope, String name, boolean securityEnabled, String ... description) { + rawState = new RawScopeState(); + rawState.initialize(scope); + this.rawState.data.put("NAME", name); + this.lastOperationPerformed = OPERATION.CREATED; + if (!this.rawState.data.containsKey("STARTTIME")) + this.rawState.data.put("STARTTIME", Calendar.getInstance().getTime()); + this.rawState.data.put("ENDTIME", null); + if ((! this.rawState.data.containsKey("DESCRIPTION")) && (description.length > 0)) + this.rawState.data.put("DESCRIPTION", description[0]); + this.rawState.data.put("SECURITYENABLED",securityEnabled); + } + + /** + * Sets the scope manager identity + * @param manager the manager + */ + public synchronized void setManager(String manager) { + this.rawState.data.put("MANAGER", manager); + this.notifyObservers(); + } + + /** + * Sets the scope designer identity + * @param manager the manager + */ + public synchronized void setDesigner(String designer) { + this.rawState.data.put("DESIGNER", designer); + this.notifyObservers(); + } + + /** + * Changes the scope description + * + * @param description the description + */ + public synchronized void changeDescription (String description) { + this.rawState.data.put("DESCRIPTION", description); + this.notifyObservers(); + } + + /** + * Adds a new resources to the list + * @param newresources the resources to add + */ + public synchronized void addResources(Set newresources) { + for (ScopedResource resource : newresources) { + //if (resource.getStatus() != STATUS.PUBLISHED)//no need to manage it again + resource.setStatus(STATUS.ADDREQUESTED); + if (rawState.resources.primaryKeySet().contains(resource.getId())) + rawState.resources.removeValuesByPrimaryKey(resource.getId()); + rawState.resources.put(resource.getId(), resource.getType(), resource); + } + this.setLastOperationPerformed(OPERATION.TOBEMANAGED); + this.notifyObservers(); + } + + /** + * Gets resources of the given type + * + * @param type the type to filter + * @return the collection of resources + */ + public synchronized Set getResourcesByType(String type) { + return rawState.resources.getValuesBySecondaryKey(type); + } + + /** + * Gets the resource with the given id + * + * @param id the resource identified + * @return the resource + */ + public synchronized ScopedResource getResource(String id) { + return (ScopedResource) rawState.resources.getValuesByPrimaryKey(id).iterator().next(); + } + + public boolean containsResource(String id) { + return rawState.resources.primaryKeySet().contains(id); + } + + /** + * Removes all the resource of the given type + * + * @param type the type of resources to remove + */ + public synchronized void removeAllResourcesByType(String type) { + for (ScopedResource resource : rawState.resources.getValuesBySecondaryKey(type)) { + resource.setStatus(STATUS.REMOVEREQUESTED); + rawState.resources.put(resource.getId(), resource.getType(), resource);//TODO: is needed? + } + this.setLastOperationPerformed(OPERATION.TOBEMANAGED); + this.notifyObservers(); + rawState.resources.removeValuesBySecondaryKey(type); + } + + /** + * Removes the resources from the scope + * + * @param oldresources the resources to remove + * + */ + public synchronized void removeResources(Set oldresources) { + //resources must be in the state in order to be removed :-) + //this is to prevent service's unconscious cleanup.. + for (ScopedResource resource : oldresources) { + resource.setStatus(STATUS.REMOVEREQUESTED); + rawState.resources.put(resource.getId(), resource.getType(), resource); + if (resource.getType().compareToIgnoreCase(ScopedDeployedService.TYPE) == 0) { + ((ScopedDeployedService) resource).setCallbackID(this.getLastReport().getId()); + ((ScopedDeployedService) resource).scheduleUndeploy(); + } + } + this.setLastOperationPerformed(OPERATION.TOBEMANAGED); + this.notifyObservers(); //let the obs do their work before to remove the resources + for (ScopedResource resource : oldresources) { + logger.debug("Removing resource " + resource+ " from the scope state"); + if (resource.getStatus() == STATUS.UNPUBLISHED) { + rawState.resources.removeValuesByPrimaryKey(resource.getId()); + logger.debug("...removed"); + } else { + logger.warn("Resource " + resource + " is still PUBLISHED in the scope state, can't be removed"); + } + } + + this.notifyObservers(); //notify about the physical removal(s) + } + + /** + * Removes the resource from the state, no matter about its actual status + * @param resources + */ + public synchronized void forceResourceRemoval(Set resources) { + for (ScopedResource resource : resources) { + logger.debug("Removing resource " + resources+ " from the scope state"); + rawState.resources.removeValuesByPrimaryKey(resource.getId()); + } + + } + + /** + * Empty the list of resources + */ + protected synchronized void removeAllResources() { + for (ScopedResource resource : rawState.resources.values()) + resource.setStatus(STATUS.REMOVEREQUESTED); + this.setLastOperationPerformed(OPERATION.TOBEMANAGED); + this.notifyObservers(); + rawState.resources.clean(); + } + + /** + * Gets the resource's scope + * + * @return the scope + */ + public GCUBEScope getScope() { + return rawState.getScope(); + } + + public synchronized void notifyObservers(Object whatschanged) { + // Otherwise it won't propagate changes + setChanged(); + super.notifyObservers(whatschanged); + } + + public synchronized void notifyObservers() { + // Otherwise it won't propagate changes + setChanged(); + super.notifyObservers(); + } + + /** + * Gets all the {@link ScopedResource}s + * + * @return all the {@link ScopedResource}s + */ + public synchronized Collection getAllResources() { + return Collections.unmodifiableCollection(rawState.resources.values()); + } + + /** + * Gets the scope manager + * @return the scope manger + */ + public String getManager() { + return (String) this.rawState.data.get("MANAGER"); + } + + /** + * Gets the scope designer + * @return the scope designer + */ + public String getDesigner() { + return (String) this.rawState.data.get("DESIGNER"); + } + + public String getDescription() { + return (String) this.rawState.data.get("DESCRIPTION"); + } + + public String getName() { + + return (String) this.rawState.data.get("NAME"); + } + + + public Date getEndTime() { + return (Date) this.rawState.data.get("ENDTIME"); + } + + public Date getStartTime() { + return (Date) this.rawState.data.get("STARTTIME"); + } + + public boolean isSecurityEnabled() { + return (Boolean) this.rawState.data.get("SECURITYENABLED"); + } + + public void setEndTime(Date endTime) { + this.rawState.data.put("ENDTIME", endTime); + this.notifyObservers(); + } + + public void setStartTime(Date startTime) { + logger.trace("setStartTime: Start time " + ProfileDate.toXMLDateAndTime(startTime)); + this.rawState.data.put("STARTTIME", startTime); + this.notifyObservers(); + } + + public void setName(String name) { + this.rawState.data.put("NAME", name); + this.notifyObservers(); + } + + /** + * @return the the last operation performed on the list + */ + public OPERATION getLastOperationPerformed() { + return lastOperationPerformed; + } + + /** + * @param operation the last operation performed on the list + */ + public synchronized void setLastOperationPerformed(OPERATION operation) { + this.lastOperationPerformed = operation; + } + + + /** + * Gets the {@link RawScopeState} + * @return the raw state + */ + public RawScopeState getRawScopeState() { + return rawState; + } + + /** + * Sets the new {@link RawScopeState} + * it usually invoked at deserialization time, see {@link Serializer#load(ScopeState, GCUBEScope)} + * @param state + */ + public void setRawScopeState(RawScopeState state) { + this.rawState = state; + + } + + public void setSecurity(boolean securityEnabled) { + this.rawState.data.put("SECURITYENABLED",securityEnabled); + + } + + /** + * Gets the last active {@link Session} + * @return the session + */ + public Session getLastReport() { + return this.report; + } + + /** + * Sets the last active {@link Session} + * @param session the session + */ + public void setLastSession(Session report) { + this.report = report; + + } + +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/Session.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/Session.java new file mode 100644 index 0000000..c9e5bd8 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/Session.java @@ -0,0 +1,463 @@ +package org.gcube.vremanagement.resourcemanager.impl.state; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Serializable; +import java.io.StringWriter; +import java.util.Calendar; +import java.util.Collections; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.common.core.utils.logging.GCUBELog; +import org.gcube.vremanagement.resourcemanager.impl.contexts.ServiceContext; +import org.gcube.vremanagement.resourcemanager.impl.deployment.DeployerReport; +import org.gcube.vremanagement.resourcemanager.impl.deployment.DeployerReport.DeployedRunningInstance; +import org.gcube.vremanagement.resourcemanager.impl.deployment.resources.Dependency; +import org.gcube.vremanagement.resourcemanager.impl.deployment.resources.DeployedDependency; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedDeployedService; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResource; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResource.STATUS; +import org.kxml2.io.KXmlSerializer; + +/** + * Session for each service operation. + * It holds all the information related to the activities performed to + * satisfy the caller's request. + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class Session implements Serializable { + + protected final GCUBELog logger = new GCUBELog(this, ServiceContext.getContext()); + + final static String NS = ""; + + /** */ + private static final long serialVersionUID = 1180822699138069365L; + + private static final String reportDir = "reports"; + + /** report last update timestamp */ + private Calendar lastUpdate = new GregorianCalendar(); + + private String id = ""; + + private OPERATION operation; + + private GCUBEScope scope = null; + + private boolean brokerWasSuccessful, brokerReportAvailable = false; + + private String brokerMessage = ""; + + /** ghn id -> deployer report map*/ + private Map node2report = Collections.synchronizedMap(new HashMap()); + + /** Status of a dependency resolver request */ + public enum DEPSTATUS {SUCCESS, FAILED}; + + public enum OPERATION {Create, AddResources, UpdateResources, RemoveResources, Dispose}; + + private Set services = Collections.synchronizedSet(new HashSet()); + + private Set resources = Collections.synchronizedSet(new HashSet()); + + /** the instances deployed by the services included in this report*/ + private Set instances = new HashSet(); + + private String deploymentPlan; + + /** internally used by {@link Session#loadAsString(String)}*/ + private Session () {} + + /** + * Builds a new empty report + * @param id the session ID assigned to the operation + */ + public Session(String id, OPERATION operation, GCUBEScope ... scope) { + this.id = id; + this.operation = operation; + if ((scope == null)|| (scope[0] == null)) + this.scope = ServiceContext.getContext().getInstance().getScopes().values().iterator().next(); + else + this.scope = scope[0]; + } + + /** + * Adds a Deployer Report to the Resource Report + * @param report the string representation of the report, as sent by a Deployer service + * @throws Exception if a problem in the report parsing occurs + */ + synchronized public void addGHNReport(DeployerReport report) throws Exception { + node2report.put(report.getGHNID(), report); + this.lastUpdate = new GregorianCalendar(); + } + + synchronized public void addResource(ScopedResource resource) { + this.resources.add(resource); + this.lastUpdate = new GregorianCalendar(); + } + + synchronized public void addService(ScopedDeployedService service) { + this.services.add(service); + this.lastUpdate = new GregorianCalendar(); + } + /** + * + */ + public String toXML() throws IOException { + StringWriter report = new StringWriter(); // serialises to a temporary writer first + KXmlSerializer serializer = new KXmlSerializer(); + serializer.setOutput(report); + //serializer.setProperty(XmlSerializer, "\t"); + try { + serializer.startDocument("UTF-8", true); + serializer.startTag(NS,"ResourceReport"); + serializer.startTag(NS,"ID").text(this.id).endTag(NS, "ID"); + serializer.startTag(NS,"Status").text(this.isReportClosed()?"CLOSED" :"OPEN").endTag(NS, "Status"); + serializer.startTag(NS,"Operation").text(this.operation.toString()).endTag(NS, "Operation"); + serializer.startTag(NS,"LastUpdate").text(ProfileDate.toXMLDateAndTime(this.lastUpdate.getTime())).endTag(NS,"LastUpdate"); + serializer.startTag(NS,"TargetScope").text(this.scope.toString()).endTag(NS,"TargetScope"); + + //resources section + serializer.startTag(NS,"Resources"); + for (ScopedResource resource : resources) { + serializer.startTag(NS,"Resource"); + serializer.startTag(NS,"ID").text(resource.getId()).endTag(NS,"ID"); + serializer.startTag(NS,"Type").text(resource.getType()).endTag(NS,"Type"); + if (resource.isSuccess()) { + if (this.operation == OPERATION.AddResources) + serializer.startTag(NS,"Status").text("ADDED").endTag(NS,"Status"); + else + serializer.startTag(NS,"Status").text("REMOVED").endTag(NS,"Status"); + //serializer.startTag(NS,"ErrorDescription").text("-").endTag(NS,"ErrorDescription"); + } else { + serializer.startTag(NS,"Status").text("FAILED").endTag(NS,"Status"); + serializer.startTag(NS,"ErrorDescription").text(resource.getErrorMessage()).endTag(NS,"ErrorDescription"); + } + serializer.endTag(NS,"Resource"); + } + + serializer.endTag(NS,"Resources"); + //broker section + if (brokerReportAvailable) { + serializer.startTag(NS,"DeploymentPlanCreation"); + serializer.startTag(NS,"Status").text(this.brokerWasSuccessful?"SUCCESS":"FAILED").endTag(NS, "Status"); + serializer.startTag(NS,"Message").text(this.brokerMessage).endTag(NS, "Message"); + serializer.endTag(NS,"DeploymentPlanCreation"); + } + //services + serializer.startTag(NS,"Services"); + for (ScopedDeployedService service : this.services) { + serializer.startTag(NS,"Service"); + serializer.startTag(NS,"ID").text(service.getId()).endTag(NS, "ID"); + serializer.startTag(NS,"Class").text(service.getSourceService().getClazz()).endTag(NS, "Class"); + serializer.startTag(NS,"Name").text(service.getSourceService().getName()).endTag(NS, "Name"); + serializer.startTag(NS,"Version").text(service.getSourceService().getVersion()).endTag(NS, "Version"); + if (((this.operation == OPERATION.AddResources) ||(this.operation == OPERATION.Create)) + && (service.getStatus() != STATUS.LOST) && (this.brokerWasSuccessful)) { + if ( (service.getMissingDependencies(service.getTargetGHNID()).size() > 0) || (service.getErrorMessage().length() > 0) ) { + serializer.startTag(NS,"DependenciesResolutionStatus").text(DEPSTATUS.FAILED.name()).endTag(NS, "DependenciesResolutionStatus"); + serializer.startTag(NS,"DeployedOn").text("not deployed").endTag(NS, "DeployedOn"); + serializer.startTag(NS,"ErrorDescription").text(service.getErrorMessage()).endTag(NS, "ErrorDescription"); + }else { + serializer.startTag(NS,"DependenciesResolutionStatus").text(DEPSTATUS.SUCCESS.name()).endTag(NS, "DependenciesResolutionStatus"); + serializer.startTag(NS,"DeployedOn").text(service.getTargetGHNID()).endTag(NS, "DeployedOn"); + serializer.startTag(NS,"ErrorDescription").text("-").endTag(NS, "ErrorDescription"); + } + serializer.startTag(NS,"DependenciesResolution"); + //resolved dependencies + serializer.startTag(NS,"ResolvedDependencies"); + for (Dependency dep : service.getResolvedDependencies(service.getTargetGHNID())) { + serializer.startTag(NS,"Dependency"); + serializer.startTag(NS,"ServiceClass").text(dep.getService().getClazz()).endTag(NS, "ServiceClass"); + serializer.startTag(NS,"ServiceName").text(dep.getService().getName()).endTag(NS, "ServiceName"); + serializer.startTag(NS,"ServiceVersion").text(dep.getService().getVersion()).endTag(NS, "ServiceVersion"); + serializer.startTag(NS,"PackageName").text(dep.getName()).endTag(NS, "PackageName"); + serializer.startTag(NS,"PackageVersion").text(dep.getVersion()).endTag(NS, "PackageVersion"); + serializer.endTag(NS,"Dependency"); + } + serializer.endTag(NS,"ResolvedDependencies"); + //missing dependencies + serializer.startTag(NS,"MissingDependencies"); + for (Dependency dep : service.getMissingDependencies(service.getTargetGHNID())) { + serializer.startTag(NS,"Dependency"); + serializer.startTag(NS,"ServiceClass").text(dep.getService().getClazz()).endTag(NS, "ServiceClass"); + serializer.startTag(NS,"ServiceName").text(dep.getService().getName()).endTag(NS, "ServiceName"); + serializer.startTag(NS,"ServiceVersion").text(dep.getService().getVersion()).endTag(NS, "ServiceVersion"); + serializer.startTag(NS,"PackageName").text(dep.getName()).endTag(NS, "PackageName"); + serializer.startTag(NS,"PackageVersion").text(dep.getVersion()).endTag(NS, "PackageVersion"); + serializer.endTag(NS,"Dependency"); + } + serializer.endTag(NS,"MissingDependencies"); + serializer.endTag(NS,"DependenciesResolution"); + } + //add the deployment report if it is available + String reportID = service.getTargetGHNID(); + if (this.operation == OPERATION.AddResources) + serializer.startTag(NS,"DeploymentActivity"); + else + serializer.startTag(NS,"UndeploymentActivity"); + if (node2report.keySet().contains(reportID)) { + serializer.startTag(NS,"GHN"); + serializer.startTag(NS,"ID").text(service.getTargetGHNID()).endTag(NS, "ID"); + serializer.startTag(NS,"Host").text(node2report.get(reportID).getHost()).endTag(NS,"Host"); + serializer.startTag(NS,"LastReportReceivedOn").text(ProfileDate.toXMLDateAndTime(node2report.get(reportID).getLastUpdate())).endTag(NS,"LastReportReceivedOn"); + serializer.startTag(NS,"LastReportReceived"); + serializer.startTag(NS,"Status").text(node2report.get(reportID).getStatus()).endTag(NS, "Status"); + this.addGHNReport(serializer, node2report.get(reportID), service); + serializer.endTag(NS, "LastReportReceived"); + serializer.endTag(NS,"GHN"); + DeployedRunningInstance instance = this.getInstanceForService(service); + if ((this.operation == OPERATION.AddResources) + || (this.operation == OPERATION.Create)){ + if (instance != null) { + serializer.startTag(NS,"RelatedRunningInstance"); + if (instance.isAlive()) + serializer.startTag(NS, "ID").text(instance.getRIID()).endTag(NS, "ID"); + serializer.startTag(NS,"Status").text(instance.isAlive()? "SUCCESS" : "FAILED").endTag(NS,"Status"); + serializer.startTag(NS,"Message").text(instance.getMessage()).endTag(NS,"Message"); + serializer.endTag(NS,"RelatedRunningInstance"); + + } else if (node2report.get(reportID).getStatus().compareToIgnoreCase("CLOSED") == 0 ) { + serializer.startTag(NS,"RelatedRunningInstance"); + serializer.startTag(NS,"Status").text("FAILED").endTag(NS,"Status"); + serializer.startTag(NS,"Message").text("The Deployer service did not detect any new instance of this service on the target gHN").endTag(NS,"Message"); + serializer.endTag(NS,"RelatedRunningInstance"); + } + } + } else { + if (service.isSuccess() && (this.brokerReportAvailable)) + serializer.text("The report is still not available for this service"); + else + serializer.text("No report"); + } + if (this.operation == OPERATION.AddResources) { + serializer.endTag(NS,"DeploymentActivity"); + } + else + serializer.endTag(NS,"UndeploymentActivity"); + + serializer.endTag(NS,"Service"); + } + serializer.endTag(NS,"Services"); + serializer.endTag(NS,"ResourceReport"); + serializer.endDocument(); + } + catch (Exception e) { + logger.error("The Resource Report does not have a valid serialisation", e); + throw new IOException("The Resource Report does not have a valid serialisation "+ e.getMessage()); + } + finally { + report.close(); + } + return report.toString(); + } + + /** + * Gets the deployed instance (if any) for the given service + * @param service the service + * @return the instance or null if no instances was found + */ + private DeployedRunningInstance getInstanceForService(ScopedDeployedService service) { + for (DeployedRunningInstance instance : this.instances) { + if ( (instance.getServiceClass().compareToIgnoreCase(service.getSourceService().getClazz()) == 0) + && (instance.getServiceName().compareToIgnoreCase(service.getSourceService().getName()) == 0)) { + return instance; + } + } + return null; + } + + /** + * @return the lastUpdate + */ + public Calendar getLastUpdate() { + return lastUpdate; + } + + /** + * @return the resource report identifier + */ + public String getId() { + return id; + } + + /** + * @return the scope this report belongs to + */ + public GCUBEScope getScope() { + return scope; + } + + /** + * Saves the report on the local file system + * + * @throws IOException if the saving fails + */ + synchronized public void save() throws IOException { + FileWriter file = new FileWriter(getReportFile(this.id)); + file.write(this.toXML()); + file.close(); + } + + /** + * Loads the report from the file system + * + * @param id the report ID + * @return the report + * @throws IOException if the report does not have a valid serialization + */ + protected static Session load(String id) throws IOException { + Session report = new Session(); + // load the report from its serialization + + return report; + } + + /** + * Loads the report from the file system + * + * @param id the report ID + * @return the string representation of the report + * @throws IOException if the report does not have a valid serialization + */ + protected static String loadAsString(String id) throws IOException { + + // load the report serialization + + File f = getReportFile(id); + if (! f.exists()) + throw new IOException("Unable to find a serialized report with ID=" + id); + + BufferedReader br = new BufferedReader(new FileReader(f)); + StringBuilder report = new StringBuilder(); + String s; + while((s = br.readLine()) != null) { + report.append(s); + } + + br.close(); + return report.toString(); + } + + private static File getReportFile(String id) throws IOException { + return new File(ServiceContext.getContext().getConfigurationFileAbsolutePath(reportDir) + File.separator + id + ".xml"); + + } + + private void addGHNReport(KXmlSerializer serializer, DeployerReport report, ScopedDeployedService service) throws Exception { + serializer.startTag(NS, "Packages"); + for (DeployedDependency dep: report.getDependencies()) { + if (this.isDepOfService(dep, service, report.getGHNID())) { + serializer.startTag(NS,"Package"); + serializer.startTag(NS,"ServiceClass").text(dep.getService().getClazz()).endTag(NS, "ServiceClass"); + serializer.startTag(NS,"ServiceName").text(dep.getService().getName()).endTag(NS, "ServiceName"); + serializer.startTag(NS,"ServiceVersion").text(dep.getService().getVersion()).endTag(NS, "ServiceVersion"); + serializer.startTag(NS,"PackageName").text(dep.getName()).endTag(NS, "PackageName"); + serializer.startTag(NS,"PackageVersion").text(dep.getVersion()).endTag(NS, "PackageVersion"); + serializer.startTag(NS,"Status").text(dep.getStatus()).endTag(NS, "Status"); + serializer.startTag(NS,"Message").text(dep.getMessage()).endTag(NS, "Message"); + serializer.endTag(NS,"Package"); + } + } + serializer.endTag(NS, "Packages"); + + } + + public synchronized void reportBrokerWork(boolean wasSuccessful, String brokerMessage) { + this.brokerReportAvailable = true; + this.brokerWasSuccessful = wasSuccessful; + this.brokerMessage = brokerMessage; + + } + + /** + * Adds newly deployed instances to the report. These instances are reported to be activated by the Deployer following a deployment + * request + * + * @param instances the new instances + */ + public void addDeployedInstances(Set instances) { + if ((instances == null) || (instances.size() == 0)) + return; + this.instances.addAll(instances); + } + + /** + * Checks if a dependency belongs to a specific service + * @param dep the dependency to check + * @param service the potential owner service + * @return true if the dependency belongs the service + */ + private boolean isDepOfService(DeployedDependency dep, ScopedDeployedService service,String targetGHNID) { + for (Dependency resolvedDependency : service.getResolvedDependencies(targetGHNID)) { + if (dep.equals((Dependency)resolvedDependency)) return true; + } + return false; + } + + /** + * Checks whether the report is closed or no + * @return true if the report is closed, false otherwise + */ + public boolean isReportClosed() { + + for ( ScopedResource resource : this.resources) { + if ((resource.getStatus() == STATUS.ADDREQUESTED) || (resource.getStatus() == STATUS.REMOVEREQUESTED)) + return false;//the resource still need to be managed + } + + for (ScopedDeployedService service : this.services) { + String reportID = service.getTargetGHNID(); + if (service.isSuccess() && (this.brokerWasSuccessful)) { + //check the availability of the report and its status + if (! node2report.keySet().contains(reportID)) { + return false; + } else if (node2report.get(reportID).getStatus().compareToIgnoreCase("CLOSED") != 0){ + return false; + } + } + } + return true; + } + + public Set getServices() { + return this.services; + } + + /** + * Gets all the GHN reports received + * @return a map where the key is the GHN ID and the value is the related report + */ + public Map getAllGHNReports() { + return node2report; + } + + /** + * @return the {@link OPERATION} related to this report + */ + public OPERATION getOperation() { + return operation; + } + + public void setDeploymentPlan(String plan) { + this.deploymentPlan = plan; + } + + public String getDeploymentPlan() throws IOException { + if (this.deploymentPlan == null) + throw new IOException("No Deployment Plan is available"); + return this.deploymentPlan; + } + +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/observers/Executor.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/observers/Executor.java new file mode 100644 index 0000000..c1e4c2a --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/observers/Executor.java @@ -0,0 +1,154 @@ +package org.gcube.vremanagement.resourcemanager.impl.state.observers; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.gcube.vremanagement.resourcemanager.impl.deployment.VirtualNodeManager; +import org.gcube.vremanagement.resourcemanager.impl.deployment.VirtualNode.NoGHNFoundException; +import org.gcube.vremanagement.resourcemanager.impl.operators.Operator.ACTION; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedDeployedService; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResource; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResourceFactory; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedRunningInstance; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResource.ResourceNotFound; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResource.STATUS; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResourceFactory.ServiceNotFoundException; +import org.gcube.vremanagement.resourcemanager.impl.state.ScopeState; +import org.gcube.vremanagement.resourcemanager.impl.state.ScopeState.OPERATION; + +/** + * + * Performs management operations on {@link ScopedResource}s depending on their {@link STATUS} + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class Executor extends ScopeObserver { + + @Override + protected void scopeChanged(ScopeState scopeState) { + if (scopeState.getLastOperationPerformed() == OPERATION.EXECUTED) + return; //nothing to manage + boolean managed = false; + //RIs have to be managed first + //if a gHN is removed from a scope, the hosted RI are automatically removed too by gCore and + //this has to be avoided, otherwise the removeFromScope will not find them at removing time + Map> toRemoveFromNode = new HashMap>(); + Set toRemoveFromScope = new HashSet(); + Set toRemoveFromState = new HashSet(); + for (ScopedResource resource : scopeState.getResourcesByType(ScopedRunningInstance.TYPE)){ + switch (resource.getStatus()) { + case ADDREQUESTED: this.addResourceToScope(resource); managed = true; break; + case REMOVEREQUESTED: + try { + if (((ScopedRunningInstance)resource).isUndeployNeeded()) { + String hostedOnID = ((ScopedRunningInstance)resource).getHostedOnID(); + try { + if (! toRemoveFromNode.containsKey(hostedOnID)) + toRemoveFromNode.put(hostedOnID, new ArrayList()); + ScopedDeployedService service = ScopedResourceFactory.getRelatedService((ScopedRunningInstance)resource); + if (service.getNodes().size() > 1) { + logger.debug("Removing " + service + " from node " + resource.getHostedOn()); + //there exist other instances of the service in the scope, we are going to remove only this one + service.scheduleUndeploy(VirtualNodeManager.getNode(hostedOnID, resource.getScope())); + service.setCallbackID(scopeState.getLastReport().getId()); + scopeState.getLastReport().addResource(service); + scopeState.getLastReport().addService(service); + toRemoveFromNode.get(hostedOnID).add(service); + } else if (service.getNodes().size() == 1) { + //remove the service from the whole scope, since the instance to remove is deployed only once + logger.debug("Removing " + service + " from the scope"); + scopeState.getLastReport().addResource(service); + scopeState.getLastReport().addService(service); + toRemoveFromScope.add(service); + } if (service.getNodes().size() < 0) { + //what's that?? + ((ScopedRunningInstance)resource).reportFailureOnSourceService("Can't find any node where the instance is deployed",new Exception("Can't find any node where the instance is deployed")); + resource.setStatus(STATUS.LOST); + toRemoveFromState.add(resource); + } + ((ScopedRunningInstance)resource).wasSuccessful(); + resource.setStatus(STATUS.REMOVED); + } catch (ServiceNotFoundException e) { + ((ScopedRunningInstance)resource).reportFailureOnSourceService("Unable to find the source service. It might not be deployed by this Resource Manager instance",e); + resource.setStatus(STATUS.LOST); + toRemoveFromState.add(resource); + } catch (NoGHNFoundException e) { + ((ScopedRunningInstance)resource).reportFailureOnSourceService("Unable to find the hosting gHN, ID=" + hostedOnID,e); + resource.setStatus(STATUS.LOST); + toRemoveFromState.add(resource); + } + } else { + this.removeResourceFromScope(resource); + toRemoveFromState.add(resource); + } + } catch (ResourceNotFound e) { + resource.setStatus(STATUS.LOST); + } + managed = true;break; + } + } + //now we can manage all the rest of the resources + for (ScopedResource resource : scopeState.getAllResources()) { + switch (resource.getStatus()) { + case ADDREQUESTED: this.addResourceToScope(resource); managed = true; break; + case REMOVEREQUESTED: this.removeResourceFromScope(resource); toRemoveFromState.add(resource); managed = true;break; + } + } + //notify the others for serialization and publication duties + if (managed) { + scopeState.setLastOperationPerformed(OPERATION.EXECUTED); + scopeState.notifyObservers(); + } + //if there are services to undeploy, redo the procedure for them + for (String ghnid : toRemoveFromNode.keySet()) { + for (ScopedDeployedService service : toRemoveFromNode.get(ghnid)) { + try { + service.removeFromScope(ghnid); + } catch (Exception e) { + logger.error("Unable to undeploy " + service + " from " + ghnid, e); + } + } + } + //remove the services from the state or trigger the notifier to manage the service removed from specific nodes + if (toRemoveFromScope.size() > 0 ) + scopeState.removeResources(toRemoveFromScope); + else if (toRemoveFromNode.keySet().size() > 0) + scopeState.notifyObservers(); + + //...and finally, a physical cleanup of the state + scopeState.forceResourceRemoval(toRemoveFromState); + } + + + private void addResourceToScope(ScopedResource resource) { + try { + resource.doAction(ACTION.ADD); + resource.setStatus(STATUS.ADDED); + } catch (ResourceNotFound e) { + //the resource does not exist..it will be removed from the internal state + resource.setStatus(STATUS.REMOVED); + } catch (Exception e) { + resource.setStatus(STATUS.LOST); + } + + } + + private void removeResourceFromScope(ScopedResource resource) { + + try { + resource.doAction(ACTION.REMOVE); + resource.setStatus(STATUS.REMOVED); + } catch (ResourceNotFound e) { + //tolerate this exception... anyway it will be removed from the internal state + resource.setStatus(STATUS.REMOVED); + } catch (Exception e) { + //can't cope with this exception and don't know what to do + resource.setStatus(STATUS.LOST); + } + } +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/observers/Publisher.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/observers/Publisher.java new file mode 100644 index 0000000..87d01f8 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/observers/Publisher.java @@ -0,0 +1,42 @@ +package org.gcube.vremanagement.resourcemanager.impl.state.observers; + +import org.gcube.vremanagement.resourcemanager.impl.state.ProfileDate; +import org.gcube.vremanagement.resourcemanager.impl.state.PublishedScopeResource; +import org.gcube.vremanagement.resourcemanager.impl.state.ScopeState; +import static org.gcube.vremanagement.resourcemanager.impl.state.ScopeState.OPERATION; +/** + * + * Synchronizes the resources' list with the IS + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class Publisher extends ScopeObserver { + + + @Override + protected void scopeChanged(ScopeState scopeState) { + + if (scopeState.getLastOperationPerformed() == OPERATION.PUBLISHED) + return; //no need to republish and loop on this + + try { + PublishedScopeResource resource = PublishedScopeResource.getResource(scopeState.getScope()); + try { + logger.trace("PUBLISHER: Start time " + ProfileDate.toXMLDateAndTime(scopeState.getStartTime())); + resource.synchWithLocalState(scopeState); + resource.publish(); + scopeState.setLastOperationPerformed(OPERATION.PUBLISHED); + scopeState.notifyObservers();//force to serialize after publishing (not nice, thought) + } catch (Exception e) { + logger.fatal("Can't publish the Scope Resource in the IS"); + throw e; + } + } catch (Exception e) { + logger.fatal("An error occured in the resource's publishing", e); + } + + } + +} + diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/observers/ScopeObserver.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/observers/ScopeObserver.java new file mode 100644 index 0000000..aeeebd1 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/observers/ScopeObserver.java @@ -0,0 +1,41 @@ +package org.gcube.vremanagement.resourcemanager.impl.state.observers; + +import java.util.Observable; +import java.util.Observer; + +import org.gcube.common.core.utils.logging.GCUBELog; +import org.gcube.vremanagement.resourcemanager.impl.state.ScopeState; + +/** + * + * Base observer for {@link ScopeState} + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public abstract class ScopeObserver implements Observer { + + protected GCUBELog logger = new GCUBELog(this.getClass()); + + public void update(Observable observed, Object arg) { + + logger.trace(this.getClass().getSimpleName() + " Observer has been notified"); + ScopeState resources; + + if (ScopeState.class.isAssignableFrom(observed.getClass())) + resources = (ScopeState) observed; + else + throw new IllegalArgumentException("Can't manage the observed obj"); + + //notify subclasses + this.scopeChanged(resources); + } + + /** + * Manages the modified scope + * + * @param scopeState the scope + */ + protected abstract void scopeChanged(ScopeState scopeState); + +} diff --git a/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/observers/Serializer.java b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/observers/Serializer.java new file mode 100644 index 0000000..b39f629 --- /dev/null +++ b/resource-manager-service/src/main/java/org/gcube/vremanagement/resourcemanager/impl/state/observers/Serializer.java @@ -0,0 +1,121 @@ +package org.gcube.vremanagement.resourcemanager.impl.state.observers; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.vremanagement.resourcemanager.impl.contexts.ServiceContext; +import org.gcube.vremanagement.resourcemanager.impl.deployment.VirtualNode; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedAnyResource; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedDeployedService; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedGHN; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResource; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedRunningInstance; +import org.gcube.vremanagement.resourcemanager.impl.resources.types.MultiKeysMap; +import org.gcube.vremanagement.resourcemanager.impl.state.RawScopeState; +import org.gcube.vremanagement.resourcemanager.impl.state.ScopeState; + +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.io.xml.QNameMap; +import com.thoughtworks.xstream.io.xml.StaxDriver; +import javax.xml.namespace.QName; + +/** + * + * Serializer for {@link ScopeState} + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class Serializer extends ScopeObserver { + + public final static File persistentList = ServiceContext.getContext().getPersistentFile("ScopedResourceList.xml", true); + private final static String namespace = "http://gcube-system.org/namespaces/resourcemanager/manager/xsd/state"; + + /** + * Serializes the notified list on the file system + * @param scopeState the list to serialize + */ + @Override + protected synchronized void scopeChanged(final ScopeState scopeState) { + try { + store(scopeState); + //scopeState.setLastOperationPerformed(OPERATION.SERIALIZED); //if we record this, the others observer manage the list with no need + //we do not notify the others obs here, since the serialization does not imply any change in the list + } catch (IOException e) { + logger.fatal("Cannot serialize the resource's list", e); + } + + } + + public synchronized static void store(final ScopeState scopeState) throws IOException { + + QNameMap qmap = new QNameMap(); + qmap.registerMapping(new QName(namespace, ""), RawScopeState.class); + StaxDriver driver = new StaxDriver(qmap); + driver.setRepairingNamespace(false); + XStream stream = new XStream(driver); + prepareStream(stream); + + FileOutputStream fs = new FileOutputStream(persistentList); + stream.toXML(scopeState.getRawScopeState(), fs); + fs.close(); + } + + /** + * Loads the list of {@link ScopedResource} from the local file system + * @param the actual scope + * @return the resource list, if any + * @throws IOException if the list was not found on the file system + */ + public synchronized static void load(ScopeState scopeState, GCUBEScope scope) throws IOException { + if (! persistentList.exists()) + throw new IOException(); + + //try to load the local list of resources + QNameMap qmap = new QNameMap(); + qmap.registerMapping(new QName(namespace, ""), RawScopeState.class); + StaxDriver driver = new StaxDriver(qmap); + driver.setRepairingNamespace(false); + XStream stream = new XStream(driver); + prepareStream(stream); + + RawScopeState state = (RawScopeState) stream.fromXML(new FileInputStream((persistentList))); + + //a bit of sanity checks.... + if ((state == null) || (state.getScope() == null) || (state.getScope().getName().compareTo(scope.getName()) != 0)) + throw new IOException(); + + //inject the state + scopeState.setRawScopeState(state); + } + + private static void prepareStream(XStream stream) { + stream.processAnnotations(RawScopeState.class); + stream.alias(RawScopeState.class.getSimpleName(), RawScopeState.class); + + stream.processAnnotations(ScopedResource.class); + stream.alias(ScopedResource.class.getSimpleName(), ScopedResource.class); + + stream.alias(ScopedGHN.class.getSimpleName(), ScopedGHN.class); + stream.processAnnotations(ScopedGHN.class); + + stream.alias(ScopedRunningInstance.class.getSimpleName(), ScopedRunningInstance.class); + stream.processAnnotations(ScopedRunningInstance.class); + + stream.alias(ScopedDeployedService.class.getSimpleName(), ScopedDeployedService.class); + stream.processAnnotations(ScopedDeployedService.class); + + stream.alias(ScopedAnyResource.class.getSimpleName(), ScopedAnyResource.class); + stream.processAnnotations(ScopedAnyResource.class); + + stream.alias("ResourceList", MultiKeysMap.class); + stream.alias("ResourceData", Map.class); + stream.alias("Scope", GCUBEScope.class); + stream.alias("ScopeType", GCUBEScope.Type.class); + stream.alias("VirtualNode", VirtualNode.class); + } +} diff --git a/resource-manager-service/src/test/java/org/gcube/vremanagement/resourcemanager/impl/resources/state/ResourceListTest.java b/resource-manager-service/src/test/java/org/gcube/vremanagement/resourcemanager/impl/resources/state/ResourceListTest.java new file mode 100644 index 0000000..13f7e77 --- /dev/null +++ b/resource-manager-service/src/test/java/org/gcube/vremanagement/resourcemanager/impl/resources/state/ResourceListTest.java @@ -0,0 +1,112 @@ +package org.gcube.vremanagement.resourcemanager.impl.state; + +import java.util.HashSet; +import java.util.Set; + +import org.gcube.common.core.resources.GCUBEHostingNode; +import org.gcube.common.core.resources.GCUBERunningInstance; +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResource; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResourceFactory; + +import com.thoughtworks.xstream.XStream; + +import static org.junit.Assert.*; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + + + +/** + * + * JUnit test class for {@link ScopeState} + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class ResourceListTest { + + private static final String SCOPE = "/gcube/devsec"; + + private static final String NAME = "test-list"; + + private static final String DESCRIPTION = "This is a test for scoped resources"; + + private ScopeState list; + + @Test + public void initializeList(String name) { + list = new ScopeState(); + list.initialize(GCUBEScope.getScope(SCOPE), NAME, false, DESCRIPTION); + + } + + protected void setUp() throws Exception { + + } + + + + @Test + public void testAddResource()throws Exception { + Set resourceToAdd = new HashSet(); + ScopedResource ghn = ScopedResourceFactory.newResource("ID1", GCUBEHostingNode.TYPE, GCUBEScope.getScope(SCOPE)); + resourceToAdd.add(ghn); + list.addResources(resourceToAdd); + Assert.assertEquals(1, list.getResourcesByType(ghn.getType()).size()); + ScopedResource ghn2 = ScopedResourceFactory.newResource("ID2", GCUBEHostingNode.TYPE, GCUBEScope.getScope(SCOPE)); + resourceToAdd.add(ghn2); + list.addResources(resourceToAdd); + Assert.assertEquals(2, list.getResourcesByType(ghn2.getType()).size()); + ScopedResource ri = ScopedResourceFactory.newResource("RI1", GCUBERunningInstance.TYPE, GCUBEScope.getScope(SCOPE)); + ri.setHostedON("node1.p:8080"); + resourceToAdd.add(ri); + list.addResources(resourceToAdd); + Assert.assertEquals(3, list.getResourcesByType(ri.getType()).size()); + } + + public void testGetResourcesByType() { + fail("Not yet implemented"); + } + + @Test + public void testChangeDescription() { + list.changeDescription("This is a new description"); + } + + public void testRemoveAllResourcesByType() { + fail("Not yet implemented"); + } + + @Test + public void testRemoveResource() throws Exception { + Set resourceToRemove = new HashSet(); + ScopedResource ghn = ScopedResourceFactory.newResource("ID1", GCUBEHostingNode.TYPE, GCUBEScope.getScope(SCOPE)); + resourceToRemove.add(ghn); + list.removeResources(resourceToRemove); + } + + @Test + public void testCleanList() { + + } + + + public void testAddObserver() { + fail("Not yet implemented"); + } + + public void testDeleteObserver() { + fail("Not yet implemented"); + + } + + @Test + protected void tearDown() throws Exception { + XStream stream = new XStream(); + stream.processAnnotations(ScopeState.class); + System.out.println(stream.toXML(list)); + + } +} diff --git a/resource-manager-service/src/test/java/org/gcube/vremanagement/resourcemanager/impl/resources/types/MultiKeysMapTest.java b/resource-manager-service/src/test/java/org/gcube/vremanagement/resourcemanager/impl/resources/types/MultiKeysMapTest.java new file mode 100644 index 0000000..846d00e --- /dev/null +++ b/resource-manager-service/src/test/java/org/gcube/vremanagement/resourcemanager/impl/resources/types/MultiKeysMapTest.java @@ -0,0 +1,128 @@ +package org.gcube.vremanagement.resourcemanager.impl.resources.types; + + +import org.gcube.common.core.resources.GCUBEHostingNode; +import org.gcube.common.core.resources.GCUBERunningInstance; +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResource; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResourceFactory; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.*; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + + + +/** + * + * JUnit test for {@link MultiKeysMap} + * + * @author Manuele Simi (ISTI-CNR) + * + */ +public class MultiKeysMapTest { + + private MultiKeysMap map; + + public MultiKeysMapTest(String name) { + super(name); + this.map = new MultiKeysMap(); + } + + @Test + public void testPut() throws Exception { + System.out.println("Adding resources test"); + System.out.println("Map before the method"); + printMap(); + ScopedResource ghn = ScopedResourceFactory.newResource("ID1", GCUBEHostingNode.TYPE, GCUBEScope.getScope("/gcube")); + map.put(ghn.getId(), ghn.getType(), ghn); + Assert.assertEquals(1, map.values().size()); + ScopedResource ghn2 = ScopedResourceFactory.newResource("ID2", GCUBEHostingNode.TYPE, GCUBEScope.getScope("/gcube")); + map.put(ghn2.getId(), ghn2.getType(), ghn2); + Assert.assertEquals(2, map.values().size()); + ScopedResource ri = ScopedResourceFactory.newResource("RI1", GCUBERunningInstance.TYPE, GCUBEScope.getScope("/gcube")); + map.put(ri.getId(), ri.getType(), ri); + Assert.assertEquals(3, map.values().size()); + System.out.println("Map after the method"); + printMap(); + } + + @Test + public void testRemoveValuesByPrimaryKey() throws Exception { + testPut(); + System.out.println("testRemoveValuesByPrimaryKey"); + map.removeValuesByPrimaryKey("ID2"); + System.out.println("Map after testRemoveValuesByPrimaryKey method"); + printMap(); + } + + @Test + public void testRemoveValue() throws Exception { + ScopedResource ghn = ScopedResourceFactory.newResource("ID1", GCUBEHostingNode.TYPE, GCUBEScope.getScope("/gcube")); + map.removeValue(ghn); + } + + + @Test + public void testRemoveValuesBySecondaryKey() throws Exception { + ScopedResource ri = ScopedResourceFactory.newResource("RI1", GCUBERunningInstance.TYPE, GCUBEScope.getScope("/gcube")); + map.removeValuesBySecondaryKey(ri.getType()); + } + + + @Test + public void testGetValuesByPrimaryKey() { + fail("Not yet implemented"); + } + + @Test + public void testGetValuesBySecondaryKey() { + fail("Not yet implemented"); + } + + @Test + public void testPrimaryKeySet() { + fail("Not yet implemented"); + } + + @Test + public void testSecondaryKeySet() { + fail("Not yet implemented"); + } + + @Test + public void testValues() { + for (ScopedResource resource : map.values()) { + System.out.println("Resource Value ID = " + resource.getId()); + } + } + + @Test + public void testClean() { + map.clean(); + } + + + + @Test + public void printMap() { + System.out.println("Values by KEY1:"); + for (String key1 : map.primaryKeySet()) { + for (ScopedResource resource : map.getValuesByPrimaryKey(key1)) + System.out.println( key1 + " ->" +resource.getId()); + } + System.out.println("Values by KEY2:"); + for (String key2 : map.secondaryKeySet()) { + for (ScopedResource resource : map.getValuesBySecondaryKey(key2)) + System.out.println( key2 + " ->" +resource.getId()); + } + System.out.println("Values:"); + for (ScopedResource resource : map.values()) { + System.out.println(resource.getId()); + } + } + +} diff --git a/resource-manager-service/src/test/java/org/gcube/vremanagement/resourcemanager/impl/resources/types/MultiKeysTester.java b/resource-manager-service/src/test/java/org/gcube/vremanagement/resourcemanager/impl/resources/types/MultiKeysTester.java new file mode 100644 index 0000000..fa0b694 --- /dev/null +++ b/resource-manager-service/src/test/java/org/gcube/vremanagement/resourcemanager/impl/resources/types/MultiKeysTester.java @@ -0,0 +1,75 @@ +package org.gcube.vremanagement.resourcemanager.impl.resources.types; + +import java.util.Observable; +import java.util.Observer; + + +import org.gcube.common.core.resources.GCUBEHostingNode; +import org.gcube.common.core.resources.GCUBERunningInstance; +import org.gcube.common.core.scope.GCUBEScope; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResource; +import org.gcube.vremanagement.resourcemanager.impl.resources.ScopedResourceFactory; +import org.gcube.vremanagement.resourcemanager.impl.resources.types.MultiKeysMap; + +import static org.junit.Assert.*; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +public class MultiKeysTester { + + MultiKeysMap map; + + @Test + public void createMap() { + this.map = new MultiKeysMap(); + } + + @Test + public void addObserver() { + this.createMap(); + //map.addObserver(new CollectionWatcher()); + } + + @Test + public void populateMap() throws Exception { + ScopedResource ghn = ScopedResourceFactory.newResource("ID1", GCUBEHostingNode.TYPE,GCUBEScope.getScope("/gcube")); + map.put(ghn.getId(), ghn.getType(), ghn); + ScopedResource ghn2 = ScopedResourceFactory.newResource("ID2", GCUBEHostingNode.TYPE, GCUBEScope.getScope("/gcube")); + map.put(ghn2.getId(), ghn2.getType(), ghn2); + ScopedResource ri = ScopedResourceFactory.newResource("RI1", GCUBERunningInstance.TYPE, GCUBEScope.getScope("/gcube")); + map.put(ri.getId(), ri.getType(), ri); + printMap(map); + map.removeValuesBySecondaryKey(ri.getType()); + printMap(map); + map.removeValue(ghn); + printMap(map); + } + + @Test + public void printMap(MultiKeysMap map) { + System.out.println("Values by KEY1:"); + for (String key1 : map.primaryKeySet()) { + for (ScopedResource resource : map.getValuesByPrimaryKey(key1)) + System.out.println( key1 + " ->" +resource.getId()); + } + System.out.println("Values by KEY2:"); + for (String key2 : map.secondaryKeySet()) { + for (ScopedResource resource : map.getValuesBySecondaryKey(key2)) + System.out.println( key2 + " ->" +resource.getId()); + } + System.out.println("Values:"); + for (ScopedResource resource : map.values()) { + System.out.println(resource.getId()); + } + } + + public class CollectionWatcher implements Observer { + + public void update(Observable o, Object arg) { + System.out.println("Notified"); + + } + + } +} diff --git a/resource-manager-stubs/pom.xml b/resource-manager-stubs/pom.xml new file mode 100644 index 0000000..b9e2a57 --- /dev/null +++ b/resource-manager-stubs/pom.xml @@ -0,0 +1,108 @@ + + + 4.0.0 + + + org.gcube.resourcemanagement + resource-manager + 1.2.0-SNAPSHOT + + resource-manager-stubs + Resource Manager Stubs + + + UTF-8 + + + + + org.gcube.core + gcf + [1.4.0-SNAPSHOT,1.5.0) + provided + + + junit + junit + 4.8.2 + test + + + + + + + + + org.apache.maven.plugins + maven-resources-plugin + 2.5 + + + copy-config + + copy-resources + + process-resources + + ${project.build.outputDirectory}/META-INF + + + ${configDirectory} + + + + + + + + + org.gcube.tools + maven-service-plugin + + + + ResourceManager + stubs.resourcemanager + ${namespace} + -Nhttp://gcube-system.org/common/vremanagement/types=${basepackage}.stubs.common + + + + + + generate-stubs + + stub-gen + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 1.7 + + + add-source + process-sources + + add-source + + + + ${basedir}/target/generated-sources/stubs + + + + + + + + + + + diff --git a/resource-manager-test-suite/pom.xml b/resource-manager-test-suite/pom.xml new file mode 100644 index 0000000..26152bd --- /dev/null +++ b/resource-manager-test-suite/pom.xml @@ -0,0 +1,85 @@ + + + 4.0.0 + + + org.gcube.resourcemanagement + resource-manager + 1.2.0-SNAPSHOT + + + resource-manager-test-suite + + Resource Manager test suite + + + + UTF-8 + + + + + + + + org.gcube.core + gcf + [1.4.0-SNAPSHOT,1.5.0) + provided + + + + org.gcube.resourcemanagement + resource-manager-stubs + ${project.version} + + + + org.gcube.resourcemanagement + resource-manager-service + ${project.version} + + + + junit + junit + 4.8.2 + test + + + + + + + + + + org.apache.maven.plugins + maven-resources-plugin + 2.5 + + + copy-config + + copy-resources + + process-resources + + ${project.build.outputDirectory}/META-INF + + + ${configDirectory} + + + + + + + + + + + + diff --git a/wsdl/ResourceManager.wsdl b/wsdl/ResourceManager.wsdl new file mode 100644 index 0000000..3b09523 --- /dev/null +++ b/wsdl/ResourceManager.wsdl @@ -0,0 +1,302 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/wsdl/VREManagementTypes.wsdl b/wsdl/VREManagementTypes.wsdl new file mode 100755 index 0000000..3c91bb7 --- /dev/null +++ b/wsdl/VREManagementTypes.wsdl @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + diff --git a/wsdl/VREManagementTypes.xsd b/wsdl/VREManagementTypes.xsd new file mode 100755 index 0000000..5de0b3a --- /dev/null +++ b/wsdl/VREManagementTypes.xsd @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +