diff --git a/org.eclipse.winery.model.adaptation/src/main/java/org/eclipse/winery/model/adaptation/placement/PlacementUtils.java b/org.eclipse.winery.model.adaptation/src/main/java/org/eclipse/winery/model/adaptation/placement/PlacementUtils.java index 94d1e13f92..9de8634185 100644 --- a/org.eclipse.winery.model.adaptation/src/main/java/org/eclipse/winery/model/adaptation/placement/PlacementUtils.java +++ b/org.eclipse.winery.model.adaptation/src/main/java/org/eclipse/winery/model/adaptation/placement/PlacementUtils.java @@ -28,11 +28,20 @@ import java.util.function.Predicate; import java.util.stream.Collectors; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; + +import javax.xml.namespace.QName; + import org.eclipse.winery.model.ids.definitions.ServiceTemplateId; +import org.eclipse.winery.model.tosca.TBoundaryDefinitions; +import org.eclipse.winery.model.tosca.TBoundaryXML; import org.eclipse.winery.model.tosca.TEntityTemplate; import org.eclipse.winery.model.tosca.TNodeTemplate; import org.eclipse.winery.model.tosca.TPolicy; +import org.eclipse.winery.model.tosca.TPropertyMapping; +import org.eclipse.winery.model.tosca.TQProvUrl; import org.eclipse.winery.model.tosca.TRelationshipTemplate; +import org.eclipse.winery.model.tosca.TSelfServiceApplicationUrl; import org.eclipse.winery.model.tosca.TServiceTemplate; import org.eclipse.winery.model.tosca.TTag; import org.eclipse.winery.model.tosca.TTopologyTemplate; @@ -104,7 +113,7 @@ public static TTopologyTemplate groupAndPlaceComponents(ServiceTemplateId servic topology = deepCopy(topologyBackup, false); assignToLocation(topology, blackList); assignToProviders(topology, blackList); - completedTopology = completeModel(serviceTemplateId, topology, blackList); + completedTopology = completeModel(serviceTemplateId, topology); } return completedTopology; } @@ -116,13 +125,10 @@ public static TTopologyTemplate groupAndPlaceComponents(ServiceTemplateId servic * * @param serviceTemplateId the ID of the ServiceTemplate to complete * @param topology the incomplete TopologyTemplate to complete - * @param blackList the black list containing the NodeTemplates with the providers that are not usable for - * them * @return the completed TopologyTemplate if completion is successful, the cleared and blacklisted TopologyTemplate * otherwise */ - private static TTopologyTemplate completeModel(ServiceTemplateId serviceTemplateId, TTopologyTemplate topology, - Map> blackList) { + public static TTopologyTemplate completeModel(ServiceTemplateId serviceTemplateId, TTopologyTemplate topology) { Splitting splitting = new Splitting(); IRepository repo = RepositoryFactory.getRepository(); @@ -637,4 +643,184 @@ private static String getNextProvider(HashMap> provi } return bestProv; } + + /** + * Completes the model based on given nodetype requirements. + * + * @param incompleteServiceTemplateId the ID of the ServiceTemplate to complete + * @param topology the incomplete TopologyTemplate to complete + * @return the completed TopologyTemplate if completion is successful, the cleared and blacklisted TopologyTemplate + * otherwise + */ + public static ServiceTemplateId completeModelBasedOnReqs(ServiceTemplateId incompleteServiceTemplateId, TTopologyTemplate topology, List blacklist, Map policies) throws Exception { + Splitting splitting = new Splitting(); + IRepository repo = RepositoryFactory.getRepository(); + + try { + ServiceTemplateId newId = null; + // create new temporary ServiceTemplate as working copy + TServiceTemplate incompleteServiceTemplate = repo.getElement(incompleteServiceTemplateId); + ServiceTemplateId placementId = new ServiceTemplateId(incompleteServiceTemplateId.getNamespace().getDecoded(), + VersionSupport.getNewComponentVersionId(incompleteServiceTemplateId, "placement"), false); + repo.forceDelete(placementId); + TServiceTemplate placementServiceTemplate = new TServiceTemplate.Builder(placementId.getXmlId().getDecoded(), topology) + .setName(placementId.getXmlId().getDecoded()) + .setTargetNamespace(incompleteServiceTemplateId.getNamespace().getDecoded()) +// .setBoundaryDefinitions(incompleteServiceTemplate.getBoundaryDefinitions()) + .setTags(incompleteServiceTemplate.getTags()) + .build(); + + // resolve open requirements until the topology is completed + while (topology != null && !splitting.getOpenRequirements(placementServiceTemplate).isEmpty()) { + // add a target label to the topology based on the provider and location assignment + assignNodesToTargetLabels(topology); + placementServiceTemplate.setTopologyTemplate(topology); + repo.setElement(placementId, placementServiceTemplate); + + // complete next level of requirements + ServiceTemplateId newServiceTemplateId = splitting.completeNextRequirement(placementId, blacklist, policies); + topology = repo.getElement(newServiceTemplateId).getTopologyTemplate(); + + // delete intermediate result to avoid cluttering + repo.forceDelete(placementId); + placementId = newServiceTemplateId; + } + + // Set selfserviceUrl for completed Templates that DO SPECIFY BOUNDARYDEFINITIONS required for selfserviceurl + TServiceTemplate completedST = repo.getElement(placementId); + + final List allowedPortNames = List.of("VMOpenPorts"); + final List allowedIPNames = List.of("VMIP"); + + TBoundaryDefinitions definitions = new TBoundaryDefinitions(); + TBoundaryDefinitions.Properties properties = new TBoundaryDefinitions.Properties(); + List propertyMappings = new ArrayList<>(); + + assert completedST.getTopologyTemplate() != null; + for (TEntityTemplate nt : completedST.getTopologyTemplate().getNodeTemplateOrRelationshipTemplate()) { + String suitableIPKey = null; + String suitablePortKey = null; + if (nt.getProperties() != null) { + LinkedHashMap kvProperties = ((TEntityTemplate.WineryKVProperties) nt.getProperties()).getKVProperties(); + for (Map.Entry entry : kvProperties.entrySet()) { + if (allowedIPNames.contains(entry.getKey())) { + suitableIPKey = entry.getKey(); + } + if (allowedPortNames.contains(entry.getKey())) { + suitablePortKey = entry.getKey(); + } + } + if (suitableIPKey != null && suitablePortKey != null) { + String targetPropertyRef = "concat('http://', " + nt.getId() + ".Properties." + suitableIPKey + ", ':', " + nt.getId() + ".Properties." + suitablePortKey + ")"; + TPropertyMapping selfserviceProperty = new TPropertyMapping("/*/*[local-name()='selfServiceApplicationUrl']", nt, targetPropertyRef); + propertyMappings.add(selfserviceProperty); + + String targetPropertyRefQProv = nt.getId() + ".Properties.QProvEndpoint"; + TPropertyMapping qprovProperty = new TPropertyMapping("/*/*[local-name()='qProvUrl']", nt, targetPropertyRefQProv); + propertyMappings.add(qprovProperty); + + TBoundaryXML boundaryXML = new TBoundaryXML(); + boundaryXML.setNamespace("http://www.eclipse.org/winery/model/selfservice"); + boundaryXML.setQProvUrl(new TQProvUrl("http://www.eclipse.org/winery/model/selfservice")); + boundaryXML.setSelfserviceApplicationUrl(new TSelfServiceApplicationUrl("http://www.eclipse.org/winery/model/selfservice")); + properties.setBoundaryXML(boundaryXML); + + properties.setPropertyMappings(propertyMappings); + + definitions.setProperties(properties); + completedST.setBoundaryDefinitions(definitions); + + //save completed ServiceTemplate using originalname + completed + newId = new ServiceTemplateId( + incompleteServiceTemplateId.getNamespace().getDecoded(), + VersionSupport.getNewComponentVersionId(incompleteServiceTemplateId, "completed"), + false); + completedST.setId(newId.getXmlId().getDecoded()); + completedST.setName(newId.getXmlId().getDecoded()); + repo.setElement(newId, completedST); + repo.forceDelete(placementId); + break; + } + } + } + // returned completed topologyId + return newId; + } catch (Exception e) { + LOGGER.error("Exception while completing topology: {}", e.getMessage()); + throw new Exception(e); + } + } + + /** + * Checks if the repository contains equivalent servicetemplates + * + * @param serviceTemplateId Id of the servicetemplate to check for equivalency + * @param topology topology of the servicetemplate to check for equivalency + * @return + */ + public static List checkServiceTemplateForEquivalency(ServiceTemplateId serviceTemplateId, TTopologyTemplate topology, Boolean includeSelf) { + List nodeTemplates = topology.getNodeTemplates(); + List relationshipTemplates = topology.getRelationshipTemplates(); + Set serviceTemplateIds = RepositoryFactory.getRepository().getAllDefinitionsChildIds(ServiceTemplateId.class); + if (!includeSelf) { + serviceTemplateIds = serviceTemplateIds.stream().filter(id -> !id.equals(serviceTemplateId)).collect(Collectors.toSet()); + } + + return serviceTemplateIds.stream() + // remove topologies that contain different nodetemplates/relationshiptemplates + .filter(id -> { + TServiceTemplate serviceTemplateFilter = RepositoryFactory.getRepository().getElement(id); + TTopologyTemplate topologyTemplateFilter = serviceTemplateFilter.getTopologyTemplate(); + assert topologyTemplateFilter != null; + + // check if number of nodetemplates matches + if (topology.getNodeTemplateOrRelationshipTemplate().size() != topologyTemplateFilter.getNodeTemplateOrRelationshipTemplate().size()) { + return false; + } + + // check if type of all nodetemplates match + List nodeTemplatesFilter = topologyTemplateFilter.getNodeTemplates(); + if (!nodeTemplates.stream().allMatch( nt -> nodeTemplatesFilter.stream().anyMatch( ntFilter -> nt.getType().equals(ntFilter.getType())))) { + return false; + } + + // check if deployment artifacts of all nodetemplates match + if (!nodeTemplates.stream().allMatch( nt -> nodeTemplatesFilter.stream().anyMatch( ntFilter -> { + // if both nodetemplates have no DAs return match + if (ntFilter.getDeploymentArtifacts() == null && nt.getDeploymentArtifacts() == null) { + return true; + } + // if one of the nodetemplates does not have any DAs but the other does, return no match + if (ntFilter.getDeploymentArtifacts() == null || nt.getDeploymentArtifacts() == null) { + return false; + } + + // check if the artifactRef of all DAs of the nodetemplate also exist in the other nodetemplate + return nt.getDeploymentArtifacts().stream().allMatch(da -> ntFilter.getDeploymentArtifacts().stream().anyMatch(daFilter -> da.getArtifactRef().equals(daFilter.getArtifactRef()))); + }))) { + return false; + } + + // check if types of all relationship templates match + List relationshipTemplatesFilter = topologyTemplateFilter.getRelationshipTemplates(); + if (!relationshipTemplates.stream().allMatch( rt -> relationshipTemplatesFilter.stream().anyMatch( rtFilter -> rt.getType().equals(rtFilter.getType())))) { + return false; + } + return true; + }) + .toList(); + } + + /** + * Checks if the a given servicetemplate is complete + * + * @param serviceTemplateId Id of the servicetemplate to check for equivalency + * @param topology topology of the servicetemplate to check for equivalency + * @return true if deployment model is complete, false if incomplete + */ + public static boolean checkIfDeploymentModelIsComplete(ServiceTemplateId serviceTemplateId, TTopologyTemplate topology) { + Splitting splitting = new Splitting(); + TServiceTemplate serviceTemplate = RepositoryFactory.getRepository().getElement(serviceTemplateId); + return splitting.getOpenRequirements(serviceTemplate).isEmpty(); + } } diff --git a/org.eclipse.winery.model/org.eclipse.winery.model.tosca.canonical/src/main/java/org/eclipse/winery/model/tosca/TBoundaryDefinitions.java b/org.eclipse.winery.model/org.eclipse.winery.model.tosca.canonical/src/main/java/org/eclipse/winery/model/tosca/TBoundaryDefinitions.java index fdb743ec7f..58ed2d92bd 100644 --- a/org.eclipse.winery.model/org.eclipse.winery.model.tosca.canonical/src/main/java/org/eclipse/winery/model/tosca/TBoundaryDefinitions.java +++ b/org.eclipse.winery.model/org.eclipse.winery.model.tosca.canonical/src/main/java/org/eclipse/winery/model/tosca/TBoundaryDefinitions.java @@ -150,7 +150,10 @@ public void accept(Visitor visitor) { @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "", propOrder = { "any", - "propertyMappings" + "propertyMappings", + "selfServiceApplicationUrl", + "qprovUrl", + "boundaryXML" }) public static class Properties implements Serializable { @@ -159,6 +162,42 @@ public static class Properties implements Serializable { @XmlElementWrapper(name = "PropertyMappings") @XmlElement(name = "PropertyMapping", required = true) protected List propertyMappings; + @XmlElement(name = "selfServiceApplicationUrl") + protected TSelfServiceApplicationUrl selfServiceApplicationUrl; + + @XmlElement(name = "qprovUrl") + protected TQProvUrl qprovUrl; + + @XmlElement(name = "ServiceTemplateProperties") + protected TBoundaryXML boundaryXML; + + public TBoundaryXML getBoundaryXML() { + return boundaryXML; + } + + public void setBoundaryXML(TBoundaryXML boundaryXML) { + this.boundaryXML = boundaryXML; + } + + + public TQProvUrl getQprovUrl() { + return qprovUrl; + } + + public void setQprovUrl(TQProvUrl qprovUrl) { + this.qprovUrl = qprovUrl; + } + + + @Nullable + public TSelfServiceApplicationUrl getSelfServiceApplicationUrl() { + return selfServiceApplicationUrl; + } + + public void setSelfServiceApplicationUrl(TSelfServiceApplicationUrl selfServiceApplicationUrl) { + this.selfServiceApplicationUrl = selfServiceApplicationUrl; + } + @Nullable public Object getAny() { diff --git a/org.eclipse.winery.model/org.eclipse.winery.model.tosca.canonical/src/main/java/org/eclipse/winery/model/tosca/TBoundaryXML.java b/org.eclipse.winery.model/org.eclipse.winery.model.tosca.canonical/src/main/java/org/eclipse/winery/model/tosca/TBoundaryXML.java new file mode 100644 index 0000000000..696665d755 --- /dev/null +++ b/org.eclipse.winery.model/org.eclipse.winery.model.tosca.canonical/src/main/java/org/eclipse/winery/model/tosca/TBoundaryXML.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + *******************************************************************************/ + +package org.eclipse.winery.model.tosca; + +import java.io.Serializable; +import java.util.Objects; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlType; + +import org.eclipse.winery.model.tosca.visitor.Visitor; + +import org.eclipse.jdt.annotation.NonNull; + +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "qProvUrl", + "selfServiceApplicationUrl" +}) +public class TBoundaryXML implements Serializable { + @XmlAttribute(name = "xmlns") + protected String namespace; + + @XmlElement(name = "qProvUrl") + protected TQProvUrl qProvUrl; + + @XmlElement(name = "selfServiceApplicationUrl") + protected TSelfServiceApplicationUrl selfServiceApplicationUrl; + + public TSelfServiceApplicationUrl getSelfserviceApplicationUrl() { + return selfServiceApplicationUrl; + } + + public void setSelfserviceApplicationUrl(TSelfServiceApplicationUrl selfServiceApplicationUrl) { + this.selfServiceApplicationUrl = selfServiceApplicationUrl; + } + + public TQProvUrl getQProvUrl() { + return qProvUrl; + } + + public void setQProvUrl(TQProvUrl qProvUrl) { + this.qProvUrl = qProvUrl; + } + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + @Deprecated // used for XML deserialization of API request content + public TBoundaryXML() { + } + + public TBoundaryXML(@NonNull TSelfServiceApplicationUrl selfServiceApplicationUrl, @NonNull TQProvUrl qProvUrl, @NonNull String namespace) { + this.qProvUrl = qProvUrl; + this.selfServiceApplicationUrl = selfServiceApplicationUrl; + this.namespace = namespace; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof TBoundaryXML)) return false; + TBoundaryXML that = (TBoundaryXML) o; + return Objects.equals(qProvUrl, that.qProvUrl); + } + + @Override + public int hashCode() { + return Objects.hash(qProvUrl); + } + + public void accept(Visitor visitor) { + visitor.visit(this); + } +} diff --git a/org.eclipse.winery.model/org.eclipse.winery.model.tosca.canonical/src/main/java/org/eclipse/winery/model/tosca/TQProvUrl.java b/org.eclipse.winery.model/org.eclipse.winery.model.tosca.canonical/src/main/java/org/eclipse/winery/model/tosca/TQProvUrl.java new file mode 100644 index 0000000000..d386475f4a --- /dev/null +++ b/org.eclipse.winery.model/org.eclipse.winery.model.tosca.canonical/src/main/java/org/eclipse/winery/model/tosca/TQProvUrl.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + *******************************************************************************/ + +package org.eclipse.winery.model.tosca; + +import java.io.Serializable; +import java.util.Objects; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlType; + +import org.eclipse.winery.model.tosca.visitor.Visitor; + +import org.eclipse.jdt.annotation.NonNull; + +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "tQProvUrl") +public class TQProvUrl implements Serializable { + + @XmlAttribute(name = "xmlns") + protected String qProvUrl; + + public String getQProvUrl() { + return qProvUrl; + } + + public void setQProvUrl(String qProvUrl) { + this.qProvUrl = qProvUrl; + } + + @Deprecated // used for XML deserialization of API request content + public TQProvUrl() { + } + + public TQProvUrl(@NonNull String qProvUrl) { + this.qProvUrl = qProvUrl; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof TQProvUrl)) return false; + TQProvUrl that = (TQProvUrl) o; + return Objects.equals(qProvUrl, that.qProvUrl); + } + + @Override + public int hashCode() { + return Objects.hash(qProvUrl); + } + + public void accept(Visitor visitor) { + visitor.visit(this); + } +} diff --git a/org.eclipse.winery.model/org.eclipse.winery.model.tosca.canonical/src/main/java/org/eclipse/winery/model/tosca/TSelfServiceApplicationUrl.java b/org.eclipse.winery.model/org.eclipse.winery.model.tosca.canonical/src/main/java/org/eclipse/winery/model/tosca/TSelfServiceApplicationUrl.java new file mode 100644 index 0000000000..dfe5834f03 --- /dev/null +++ b/org.eclipse.winery.model/org.eclipse.winery.model.tosca.canonical/src/main/java/org/eclipse/winery/model/tosca/TSelfServiceApplicationUrl.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + *******************************************************************************/ + +package org.eclipse.winery.model.tosca; + +import java.io.Serializable; +import java.util.Objects; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlType; + +import org.eclipse.winery.model.tosca.visitor.Visitor; + +import org.eclipse.jdt.annotation.NonNull; + +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "tSelfServiceApplicationUrl") +public class TSelfServiceApplicationUrl implements Serializable { + + public String getSelfServiceApplicationUrl() { + return selfServiceApplicationUrl; + } + + public void setSelfServiceApplicationUrl(String selfServiceApplicationUrl) { + this.selfServiceApplicationUrl = selfServiceApplicationUrl; + } + + @XmlAttribute(name = "xmlns") + protected String selfServiceApplicationUrl; + + @Deprecated // used for XML deserialization of API request content + public TSelfServiceApplicationUrl() { + } + + public TSelfServiceApplicationUrl(@NonNull String selfServiceApplicationUrl) { + this.selfServiceApplicationUrl = selfServiceApplicationUrl; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof org.eclipse.winery.model.tosca.TSelfServiceApplicationUrl)) return false; + org.eclipse.winery.model.tosca.TSelfServiceApplicationUrl that = (org.eclipse.winery.model.tosca.TSelfServiceApplicationUrl) o; + return Objects.equals(selfServiceApplicationUrl, that.selfServiceApplicationUrl); + } + + @Override + public int hashCode() { + return Objects.hash(selfServiceApplicationUrl); + } + + public void accept(Visitor visitor) { + visitor.visit(this); + } +} diff --git a/org.eclipse.winery.model/org.eclipse.winery.model.tosca.canonical/src/main/java/org/eclipse/winery/model/tosca/TServiceTemplate.java b/org.eclipse.winery.model/org.eclipse.winery.model.tosca.canonical/src/main/java/org/eclipse/winery/model/tosca/TServiceTemplate.java index 0474cd60cf..cdab66c53d 100644 --- a/org.eclipse.winery.model/org.eclipse.winery.model.tosca.canonical/src/main/java/org/eclipse/winery/model/tosca/TServiceTemplate.java +++ b/org.eclipse.winery.model/org.eclipse.winery.model.tosca.canonical/src/main/java/org/eclipse/winery/model/tosca/TServiceTemplate.java @@ -215,6 +215,11 @@ public Builder setSubstitutableNodeType(QName substitutableNodeType) { return this; } + public Builder setTags(List tags) { + super.addTags(tags); + return this; + } + @Override public Builder self() { return this; diff --git a/org.eclipse.winery.model/org.eclipse.winery.model.tosca.canonical/src/main/java/org/eclipse/winery/model/tosca/visitor/Visitor.java b/org.eclipse.winery.model/org.eclipse.winery.model.tosca.canonical/src/main/java/org/eclipse/winery/model/tosca/visitor/Visitor.java index c619d52415..ce86046cb7 100644 --- a/org.eclipse.winery.model/org.eclipse.winery.model.tosca.canonical/src/main/java/org/eclipse/winery/model/tosca/visitor/Visitor.java +++ b/org.eclipse.winery.model/org.eclipse.winery.model.tosca.canonical/src/main/java/org/eclipse/winery/model/tosca/visitor/Visitor.java @@ -23,6 +23,7 @@ import org.eclipse.winery.model.tosca.HasId; import org.eclipse.winery.model.tosca.RelationshipSourceOrTarget; import org.eclipse.winery.model.tosca.TBoundaryDefinitions; +import org.eclipse.winery.model.tosca.TBoundaryXML; import org.eclipse.winery.model.tosca.TCapability; import org.eclipse.winery.model.tosca.TCapabilityRef; import org.eclipse.winery.model.tosca.TCondition; @@ -44,7 +45,9 @@ import org.eclipse.winery.model.tosca.TRelationshipTemplate; import org.eclipse.winery.model.tosca.TRequirement; import org.eclipse.winery.model.tosca.TRequirementRef; +import org.eclipse.winery.model.tosca.TSelfServiceApplicationUrl; import org.eclipse.winery.model.tosca.TServiceTemplate; +import org.eclipse.winery.model.tosca.TQProvUrl; import org.eclipse.winery.model.tosca.TTag; import org.eclipse.winery.model.tosca.TTopologyTemplate; @@ -326,6 +329,18 @@ public void visit(TBoundaryDefinitions.Properties properties) { } } + public void visit(TSelfServiceApplicationUrl applicationUrl) { + // this is a leaf, so no action to take + } + + public void visit(TBoundaryXML boundaryXML) { + // this is a leaf, so no action to take + } + + public void visit(TQProvUrl tqProvUrl) { + // this is a leaf, so no action to take + } + public void visit(TPropertyMapping propertyMapping) { // this is a leaf, so no action to take } diff --git a/org.eclipse.winery.model/org.eclipse.winery.model.tosca.xml/src/main/java/org/eclipse/winery/model/tosca/xml/XTBoundaryDefinitions.java b/org.eclipse.winery.model/org.eclipse.winery.model.tosca.xml/src/main/java/org/eclipse/winery/model/tosca/xml/XTBoundaryDefinitions.java index 180f0737a2..d234923071 100644 --- a/org.eclipse.winery.model/org.eclipse.winery.model.tosca.xml/src/main/java/org/eclipse/winery/model/tosca/xml/XTBoundaryDefinitions.java +++ b/org.eclipse.winery.model/org.eclipse.winery.model.tosca.xml/src/main/java/org/eclipse/winery/model/tosca/xml/XTBoundaryDefinitions.java @@ -150,7 +150,10 @@ public void accept(Visitor visitor) { @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "", propOrder = { "any", - "propertyMappings" + "propertyMappings", + "selfServiceApplicationUrl", + "qprovUrl", + "boundaryXML" }) public static class Properties implements Serializable { @@ -159,7 +162,37 @@ public static class Properties implements Serializable { @XmlElementWrapper(name = "PropertyMappings") @XmlElement(name = "PropertyMapping", required = true) protected List propertyMappings; + @XmlElement(name = "selfServiceApplicationUrl") + protected XTSelfServiceApplicationUrl selfServiceApplicationUrl; + @XmlElement(name = "qprovUrl") + protected XTQProvUrl qprovUrl; + @XmlElement(name = "ServiceTemplateProperties") + protected XTBoundaryXML boundaryXML; + + public XTBoundaryXML getBoundaryXML() { + return boundaryXML; + } + + public void setBoundaryXML(XTBoundaryXML boundaryXML) { + this.boundaryXML = boundaryXML; + } + public XTQProvUrl getQprovUrl() { + return qprovUrl; + } + + public void setQprovUrl(XTQProvUrl qprovUrl) { + this.qprovUrl = qprovUrl; + } + + @Nullable + public XTSelfServiceApplicationUrl getSelfServiceApplicationUrl() { + return selfServiceApplicationUrl; + } + + public void setSelfServiceApplicationUrl(XTSelfServiceApplicationUrl selfServiceApplicationUrl) { + this.selfServiceApplicationUrl = selfServiceApplicationUrl; + } @Nullable public Object getAny() { return any; diff --git a/org.eclipse.winery.model/org.eclipse.winery.model.tosca.xml/src/main/java/org/eclipse/winery/model/tosca/xml/XTBoundaryXML.java b/org.eclipse.winery.model/org.eclipse.winery.model.tosca.xml/src/main/java/org/eclipse/winery/model/tosca/xml/XTBoundaryXML.java new file mode 100644 index 0000000000..67480d43e8 --- /dev/null +++ b/org.eclipse.winery.model/org.eclipse.winery.model.tosca.xml/src/main/java/org/eclipse/winery/model/tosca/xml/XTBoundaryXML.java @@ -0,0 +1,112 @@ +/******************************************************************************* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + *******************************************************************************/ + +package org.eclipse.winery.model.tosca.xml; + +import java.io.Serializable; +import java.util.Objects; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlType; +import org.eclipse.winery.model.tosca.xml.visitor.Visitor; + +import org.eclipse.jdt.annotation.NonNull; + +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "qProvUrl", + "selfServiceApplicationUrl" +}) +public class XTBoundaryXML implements Serializable { + @XmlAttribute(name = "xmlns") + protected String namespace; + @XmlElement(name = "qProvUrl") + protected XTQProvUrl qProvUrl; + + @XmlElement(name = "selfServiceApplicationUrl") + protected XTSelfServiceApplicationUrl selfServiceApplicationUrl; + + public XTSelfServiceApplicationUrl getSelfserviceApplicationUrl() { + return selfServiceApplicationUrl; + } + + public void setSelfserviceApplicationUrl(XTSelfServiceApplicationUrl selfServiceApplicationUrl) { + this.selfServiceApplicationUrl = selfServiceApplicationUrl; + } + + public XTQProvUrl getQProvUrl() { + return qProvUrl; + } + + public void setQProvUrl(XTQProvUrl qProvUrl) { + this.qProvUrl = qProvUrl; + } + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + @Deprecated // used for XML deserialization of API request content + public XTBoundaryXML() { + } + + public XTBoundaryXML(Builder builder) { + this.qProvUrl = builder.qProvUrl; + this.selfServiceApplicationUrl = builder.selfServiceApplicationUrl; + this.namespace = builder.namespace; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof XTBoundaryXML)) return false; + XTBoundaryXML that = (XTBoundaryXML) o; + return Objects.equals(qProvUrl, that.qProvUrl); + } + + @Override + public int hashCode() { + return Objects.hash(selfServiceApplicationUrl, qProvUrl, namespace); + } + + + public static class Builder { + + private final XTSelfServiceApplicationUrl selfServiceApplicationUrl; + private final XTQProvUrl qProvUrl; + private final String namespace; + + public Builder(XTSelfServiceApplicationUrl selfServiceApplicationUrl, XTQProvUrl qProvUrl, String namespace) { + this.qProvUrl = qProvUrl; + this.selfServiceApplicationUrl = selfServiceApplicationUrl; + this.namespace = namespace; + } + + public XTBoundaryXML build() { + return new XTBoundaryXML(this); + } + } + + + public void accept(Visitor visitor) { + visitor.visit(this); + } +} diff --git a/org.eclipse.winery.model/org.eclipse.winery.model.tosca.xml/src/main/java/org/eclipse/winery/model/tosca/xml/XTQProvUrl.java b/org.eclipse.winery.model/org.eclipse.winery.model.tosca.xml/src/main/java/org/eclipse/winery/model/tosca/xml/XTQProvUrl.java new file mode 100644 index 0000000000..76aa9fa2a9 --- /dev/null +++ b/org.eclipse.winery.model/org.eclipse.winery.model.tosca.xml/src/main/java/org/eclipse/winery/model/tosca/xml/XTQProvUrl.java @@ -0,0 +1,86 @@ +package org.eclipse.winery.model.tosca.xml; /******************************************************************************* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + *******************************************************************************/ + + +import java.io.Serializable; +import java.util.Objects; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlType; + +import org.eclipse.winery.model.tosca.xml.visitor.Visitor; + +import org.eclipse.jdt.annotation.NonNull; + +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "tQProvUrl") +public class XTQProvUrl implements Serializable { + + public String getqProvUrl() { + return qProvUrl; + } + + public void setqProvUrl(String qProvUrl) { + this.qProvUrl = qProvUrl; + } + + @XmlAttribute(name = "xmlns") + protected String qProvUrl; + + @Deprecated // used for XML deserialization of API request content + public XTQProvUrl() { + } + + public XTQProvUrl(@NonNull String qProvUrl) { + this.qProvUrl = qProvUrl; + } + + private XTQProvUrl(XTQProvUrl.Builder builder) { + this.qProvUrl = builder.QProvUrl; + } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof XTQProvUrl)) return false; + XTQProvUrl that = (XTQProvUrl) o; + return Objects.equals(qProvUrl, that.qProvUrl); + } + + @Override + public int hashCode() { + return Objects.hash(qProvUrl); + } + + + public static class Builder { + + private final String QProvUrl; + + public Builder(String QProvUrl) { + this.QProvUrl = QProvUrl; + } + + public XTQProvUrl build() { + return new XTQProvUrl(this); + } + } + + public void accept(Visitor visitor) { + visitor.visit(this); + } +} diff --git a/org.eclipse.winery.model/org.eclipse.winery.model.tosca.xml/src/main/java/org/eclipse/winery/model/tosca/xml/XTSelfServiceApplicationUrl.java b/org.eclipse.winery.model/org.eclipse.winery.model.tosca.xml/src/main/java/org/eclipse/winery/model/tosca/xml/XTSelfServiceApplicationUrl.java new file mode 100644 index 0000000000..068aa008e3 --- /dev/null +++ b/org.eclipse.winery.model/org.eclipse.winery.model.tosca.xml/src/main/java/org/eclipse/winery/model/tosca/xml/XTSelfServiceApplicationUrl.java @@ -0,0 +1,86 @@ +package org.eclipse.winery.model.tosca.xml; /******************************************************************************* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + *******************************************************************************/ + + +import java.io.Serializable; +import java.util.Objects; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlType; + +import org.eclipse.winery.model.tosca.xml.visitor.Visitor; + +import org.eclipse.jdt.annotation.NonNull; + +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "tSelfServiceApplicationUrl") +public class XTSelfServiceApplicationUrl implements Serializable { + + public String getSelfServiceApplicationUrl() { + return selfServiceApplicationUrl; + } + + public void setSelfServiceApplicationUrl(String selfServiceApplicationUrl) { + this.selfServiceApplicationUrl = selfServiceApplicationUrl; + } + + @XmlAttribute(name = "xmlns") + protected String selfServiceApplicationUrl; + + @Deprecated // used for XML deserialization of API request content + public XTSelfServiceApplicationUrl() { + } + + public XTSelfServiceApplicationUrl(@NonNull String selfServiceApplicationUrl) { + this.selfServiceApplicationUrl = selfServiceApplicationUrl; + } + + private XTSelfServiceApplicationUrl(XTSelfServiceApplicationUrl.Builder builder) { + this.selfServiceApplicationUrl = builder.selfServiceApplicationUrl; + } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof org.eclipse.winery.model.tosca.xml.XTSelfServiceApplicationUrl)) return false; + org.eclipse.winery.model.tosca.xml.XTSelfServiceApplicationUrl that = (org.eclipse.winery.model.tosca.xml.XTSelfServiceApplicationUrl) o; + return Objects.equals(selfServiceApplicationUrl, that.selfServiceApplicationUrl); + } + + @Override + public int hashCode() { + return Objects.hash(selfServiceApplicationUrl); + } + + + public static class Builder { + + private final String selfServiceApplicationUrl; + + public Builder(String selfServiceApplicationUrl) { + this.selfServiceApplicationUrl = selfServiceApplicationUrl; + } + + public XTSelfServiceApplicationUrl build() { + return new XTSelfServiceApplicationUrl(this); + } + } + + public void accept(Visitor visitor) { + visitor.visit(this); + } +} diff --git a/org.eclipse.winery.model/org.eclipse.winery.model.tosca.xml/src/main/java/org/eclipse/winery/model/tosca/xml/visitor/Visitor.java b/org.eclipse.winery.model/org.eclipse.winery.model.tosca.xml/src/main/java/org/eclipse/winery/model/tosca/xml/visitor/Visitor.java index f927f5a5be..850b15dafa 100644 --- a/org.eclipse.winery.model/org.eclipse.winery.model.tosca.xml/src/main/java/org/eclipse/winery/model/tosca/xml/visitor/Visitor.java +++ b/org.eclipse.winery.model/org.eclipse.winery.model.tosca.xml/src/main/java/org/eclipse/winery/model/tosca/xml/visitor/Visitor.java @@ -23,6 +23,7 @@ import org.eclipse.winery.model.tosca.xml.XHasId; import org.eclipse.winery.model.tosca.xml.XRelationshipSourceOrTarget; import org.eclipse.winery.model.tosca.xml.XTBoundaryDefinitions; +import org.eclipse.winery.model.tosca.xml.XTBoundaryXML; import org.eclipse.winery.model.tosca.xml.XTCapability; import org.eclipse.winery.model.tosca.xml.XTCapabilityRef; import org.eclipse.winery.model.tosca.xml.XTCondition; @@ -39,9 +40,11 @@ import org.eclipse.winery.model.tosca.xml.XTPolicy; import org.eclipse.winery.model.tosca.xml.XTPropertyConstraint; import org.eclipse.winery.model.tosca.xml.XTPropertyMapping; +import org.eclipse.winery.model.tosca.xml.XTQProvUrl; import org.eclipse.winery.model.tosca.xml.XTRelationshipTemplate; import org.eclipse.winery.model.tosca.xml.XTRequirement; import org.eclipse.winery.model.tosca.xml.XTRequirementRef; +import org.eclipse.winery.model.tosca.xml.XTSelfServiceApplicationUrl; import org.eclipse.winery.model.tosca.xml.XTServiceTemplate; import org.eclipse.winery.model.tosca.xml.XTTag; import org.eclipse.winery.model.tosca.xml.XTTopologyTemplate; @@ -311,7 +314,20 @@ public void visit(XTBoundaryDefinitions.Properties properties) { } } } + + public void visit(XTSelfServiceApplicationUrl selfServiceApplicationUrl) { + // this is a leaf, so no action to take + } + + public void visit(XTQProvUrl xtqProvUrl) { + // this is a leaf, so no action to take + } + + public void visit(XTBoundaryXML boundaryXML) { + // this is a leaf, so no action to take + } + public void visit(XTPropertyMapping propertyMapping) { // this is a leaf, so no action to take } diff --git a/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/servicetemplates/topologytemplates/CompletionInputData.java b/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/servicetemplates/topologytemplates/CompletionInputData.java new file mode 100644 index 0000000000..43b42d992d --- /dev/null +++ b/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/servicetemplates/topologytemplates/CompletionInputData.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + *******************************************************************************/ + +package org.eclipse.winery.repository.rest.resources.servicetemplates.topologytemplates; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.namespace.QName; + +public class CompletionInputData { + // Nodetypes not allowed due to, e.g., unfulfilled requirements + private List blacklistedNodetypes = new ArrayList<>(); + + // Policies, e.g., location, cloudType + private Map policies = new HashMap<>(); + + public CompletionInputData() { } + + public List getBlacklistedNodetypes() { + return blacklistedNodetypes; + } + + public void setBlacklistedNodetypes(List blacklistedNodetypes) { + this.blacklistedNodetypes = blacklistedNodetypes; + } + + public Map getPolicies() { + return policies; + } + + public void setPolicies(Map policies) { + this.policies = policies; + } +} diff --git a/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/servicetemplates/topologytemplates/TopologyTemplateResource.java b/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/servicetemplates/topologytemplates/TopologyTemplateResource.java index 5f09867e81..0a8992dadd 100644 --- a/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/servicetemplates/topologytemplates/TopologyTemplateResource.java +++ b/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/servicetemplates/topologytemplates/TopologyTemplateResource.java @@ -27,6 +27,7 @@ import java.util.stream.Collectors; import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; @@ -597,4 +598,53 @@ public Response applyGroupingAndPlacement(@Context UriInfo uriInfo) { return Response.serverError().entity(e.getMessage()).build(); } } + + @POST + @Path("completemodel") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response completeModel(@Context UriInfo uriInfo, CompletionInputData completionInputData) { + List blacklist = completionInputData == null ? new ArrayList<>() : completionInputData.getBlacklistedNodetypes(); + Map policies = completionInputData == null ? new HashMap<>() : completionInputData.getPolicies(); + try { + ServiceTemplateId newServiceTemplateId = + PlacementUtils.completeModelBasedOnReqs((ServiceTemplateId) this.parent.getId(), this.topologyTemplate, blacklist, policies); + URI url = uriInfo.getBaseUri().resolve(RestUtils.getAbsoluteURL(newServiceTemplateId)); + return Response.created(url).build(); + } catch (Exception e) { + LOGGER.debug("Error while completing model: {}", e.getMessage()); + return Response.serverError().entity(e.getMessage()).build(); + } + } + + @POST + @Path("checkforequivalentcsars") + @Produces(MediaType.APPLICATION_JSON) + public Response checkEquivalency(@Context UriInfo uriInfo, + @DefaultValue("false") + @QueryParam(value = "includeSelf") Boolean includeSelf) { + try { + List equivalentTemplateIds = + PlacementUtils.checkServiceTemplateForEquivalency((ServiceTemplateId) this.parent.getId(), this.topologyTemplate, includeSelf); + List urls = equivalentTemplateIds.stream().map( id -> uriInfo.getBaseUri().resolve(RestUtils.getAbsoluteURL(id))).toList(); + return Response.ok(urls).build(); + } catch (InvalidParameterException e) { + LOGGER.debug("Error while completing model: {}", e.getMessage()); + return Response.serverError().entity(e.getMessage()).build(); + } + } + + @POST + @Path("iscomplete") + @Produces(MediaType.TEXT_PLAIN) + public Response isComplete(@Context UriInfo uriInfo) { + try { + Boolean isComplete = + PlacementUtils.checkIfDeploymentModelIsComplete((ServiceTemplateId) this.parent.getId(), this.topologyTemplate); + return Response.ok(isComplete).build(); + } catch (InvalidParameterException e) { + LOGGER.debug("Error while completing model: {}", e.getMessage()); + return Response.serverError().entity(e.getMessage()).build(); + } + } } diff --git a/org.eclipse.winery.repository/src/main/java/org/eclipse/winery/repository/splitting/ProviderRepository.java b/org.eclipse.winery.repository/src/main/java/org/eclipse/winery/repository/splitting/ProviderRepository.java index f1ab5915f4..bf51c6a04a 100644 --- a/org.eclipse.winery.repository/src/main/java/org/eclipse/winery/repository/splitting/ProviderRepository.java +++ b/org.eclipse.winery.repository/src/main/java/org/eclipse/winery/repository/splitting/ProviderRepository.java @@ -27,6 +27,7 @@ import org.eclipse.winery.model.ids.definitions.RequirementTypeId; import org.eclipse.winery.model.ids.definitions.ServiceTemplateId; +import org.eclipse.winery.model.tosca.TCapability; import org.eclipse.winery.model.tosca.TEntityTemplate; import org.eclipse.winery.model.tosca.TNodeTemplate; import org.eclipse.winery.model.tosca.TRelationshipTemplate; @@ -40,7 +41,7 @@ public class ProviderRepository { public static final ProviderRepository INSTANCE = new ProviderRepository(); - private static final String NS_NAME_START = "http://www.opentosca.org/providers/"; + private static final String NS_NAME_START = "http://quantil.org"; /** * Pointing to a concrete node template has to be done by putting this node template into a separate namespace

@@ -55,12 +56,21 @@ public List getAllTopologyFragmentsForLocationAndOfferingCapab RequirementTypeId reqTypeId = new RequirementTypeId(reqTypeQName); QName requiredCapabilityType = RepositoryFactory.getRepository().getElement(reqTypeId).getRequiredCapabilityType(); + ArrayList testcap = new ArrayList<>(); + System.out.println(testcap); return getAllTopologyFragmentsForLocation(targetLocation).stream() + .filter(tf -> { + assert tf.getName() != null; + return !tf.getName().contains("placement"); + }) .filter(tf -> getNodesWithOpenCapabilities(tf.getTopologyTemplate()) != null) .filter(tf -> getNodesWithOpenCapabilities(tf.getTopologyTemplate()).stream() .anyMatch(nt -> nt.getCapabilities().stream() - .anyMatch(cap -> cap.getType().equals(requiredCapabilityType)) + .anyMatch(cap -> { + testcap.add(cap); + return cap.getType().equals(requiredCapabilityType); + }) ) ) .collect(Collectors.toList()); @@ -174,4 +184,44 @@ private List breadthFirstSearch(TNodeTemplate nodeTemplate, TTo } return topologyFragmentElements; } + + public List getNodeTemplatesForCapability(String targetLocation, TRequirement requirement) { + QName reqTypeQName = requirement.getType(); + RequirementTypeId reqTypeId = new RequirementTypeId(reqTypeQName); + QName requiredCapabilityType = RepositoryFactory.getRepository().getElement(reqTypeId).getRequiredCapabilityType(); + + ArrayList testcap = new ArrayList<>(); + System.out.println(testcap); + List matchingServiceTemplates = getAllTopologyFragmentsForLocation(targetLocation).stream() + .filter(tf -> { + assert tf.getName() != null; + return !tf.getName().contains("placement"); + }) + .filter(tf -> + { + assert tf.getTopologyTemplate() != null; + return getNodesWithOpenCapabilities(tf.getTopologyTemplate()) != null; + }) + .filter(tf -> getNodesWithOpenCapabilities(tf.getTopologyTemplate()).stream() + .anyMatch(nt -> nt.getCapabilities().stream() + .anyMatch(cap -> { + testcap.add(cap); + return cap.getType().equals(requiredCapabilityType); + }) + ) + ) + .toList(); + + List matchingNodeTemplates = new ArrayList<>(); + matchingServiceTemplates.forEach( st -> { + assert st.getTopologyTemplate() != null; + List ntFullfillingCaps = getNodesWithOpenCapabilities(st.getTopologyTemplate()).stream() + .filter(nt -> nt.getCapabilities().stream() + .anyMatch(cap -> cap.getType().equals(requiredCapabilityType)) + ).toList(); + matchingNodeTemplates.addAll(ntFullfillingCaps); + }); + + return matchingNodeTemplates; + } } diff --git a/org.eclipse.winery.repository/src/main/java/org/eclipse/winery/repository/splitting/Splitting.java b/org.eclipse.winery.repository/src/main/java/org/eclipse/winery/repository/splitting/Splitting.java index 075bf701c2..595825c970 100644 --- a/org.eclipse.winery.repository/src/main/java/org/eclipse/winery/repository/splitting/Splitting.java +++ b/org.eclipse.winery.repository/src/main/java/org/eclipse/winery/repository/splitting/Splitting.java @@ -24,6 +24,7 @@ import java.util.Optional; import java.util.Set; import java.util.SortedSet; +import java.util.UUID; import java.util.stream.Collectors; import javax.xml.namespace.QName; @@ -45,6 +46,7 @@ import org.eclipse.winery.model.tosca.TRequirement; import org.eclipse.winery.model.tosca.TRequirementType; import org.eclipse.winery.model.tosca.TServiceTemplate; +import org.eclipse.winery.model.tosca.TTag; import org.eclipse.winery.model.tosca.TTopologyTemplate; import org.eclipse.winery.model.tosca.constants.OpenToscaBaseTypes; import org.eclipse.winery.model.tosca.constants.ToscaBaseTypes; @@ -1713,4 +1715,177 @@ private boolean equalsWithDifferentId(TNodeTemplate node1, TNodeTemplate node2) Objects.equals(node1.getMinInstances(), node2.getMinInstances()) && Objects.equals(node1.getMaxInstances(), node2.getMaxInstances()); } + + /** + * Splits the topology template of the given service template. Creates a new service template with "-split" suffix + * as id. Any existing "-split" service template will be deleted. Matches the split topology template to the cloud + * providers according to the target labels. Creates a new service template with "-matched" suffix as id. Any + * existing "-matched" service template will be deleted. + * + * @param id of the ServiceTemplate switch should be split and matched to cloud providers + * @return id of the ServiceTemplate which contains the matched topology + */ + public ServiceTemplateId completeNextRequirement(ServiceTemplateId id, List blacklist, Map policies) throws Exception { + long start = System.currentTimeMillis(); + IRepository repository = RepositoryFactory.getRepository(); + + TServiceTemplate serviceTemplate = repository.getElement(id); + TTopologyTemplate topologyTemplate = serviceTemplate.getTopologyTemplate(); + + // create wrapper service template + ServiceTemplateId matchedServiceTemplateId = new ServiceTemplateId( + id.getNamespace().getDecoded(), + VersionSupport.getNewComponentVersionId(id, "matched"), + false); + RepositoryFactory.getRepository().forceDelete(matchedServiceTemplateId); + RepositoryFactory.getRepository().flagAsExisting(matchedServiceTemplateId); + repository.flagAsExisting(matchedServiceTemplateId); + TServiceTemplate matchedServiceTemplate = BackendUtils.clone(serviceTemplate); + matchedServiceTemplate.setName(matchedServiceTemplateId.getXmlId().getDecoded()); + matchedServiceTemplate.setId(matchedServiceTemplate.getName()); + matchedServiceTemplate.setTargetNamespace(id.getNamespace().getDecoded()); + matchedServiceTemplate.setTags(serviceTemplate.getTags()); + assert topologyTemplate != null; + topologyTemplate.getNodeTemplates().forEach(t -> ModelUtilities.setTargetLabel(t, "*")); + matchedServiceTemplate.setTopologyTemplate(getMatchingTopology(BackendUtils.clone(matchedServiceTemplate), blacklist, policies)); + + TTopologyTemplate daSpecifiedTopology = matchedServiceTemplate.getTopologyTemplate(); + + matchedServiceTemplate.setTopologyTemplate(daSpecifiedTopology); + LOGGER.debug("Persisting..."); + repository.setElement(matchedServiceTemplateId, matchedServiceTemplate); + LOGGER.debug("Persisted."); + + long duration = System.currentTimeMillis() - start; + LOGGER.debug("Execution Time in millisec: " + duration + "ms"); + + return matchedServiceTemplateId; + } + + public TTopologyTemplate getMatchingTopology(TServiceTemplate serviceTemplate, List blacklist, Map policies) throws SplittingException { + ProviderRepository repository = new ProviderRepository(); + List lowLevelNodeTemplates = getNodeTemplatesWithoutOutgoingHostedOnRelationships(serviceTemplate); + List nodesToCheck = new ArrayList<>(); + + //Find lowest level nodes with open requirements which means they can be hosted by an other component + for (TNodeTemplate nodeTemplateCandidate : lowLevelNodeTemplates) { + if (hasNodeOpenRequirement(serviceTemplate, nodeTemplateCandidate)) { + if (nodeTemplateCandidate.getRequirements() != null) { + nodesToCheck.add(nodeTemplateCandidate); + } + } + } + + TTopologyTemplate topologyToComplete = serviceTemplate.getTopologyTemplate(); + + LOGGER.debug("Start..."); + if (!nodesToCheck.isEmpty()) { + //Check all lowest level nodes with open requirements if a compatible node is available + + // Counter is used for horizontally distributed positioning of new nodes + int newNodesAddedCounter = 0; + for (TNodeTemplate needHostNode : nodesToCheck) { + Optional label = ModelUtilities.getTargetLabel(needHostNode); + if (!label.isPresent()) { + LOGGER.error("No target label present"); + LOGGER.error("id " + needHostNode.getId()); + throw new SplittingException("No target label present for Node Template " + needHostNode.getId()); + } + + String targetLabel = ModelUtilities.getTargetLabel(needHostNode).get(); + List openHostedOnRequirements = needHostNode.getRequirements(); + + for (TRequirement requirement: openHostedOnRequirements) { + List compatibleNodeTemplates = repository + .getNodeTemplatesForCapability(targetLabel, requirement); + + LOGGER.debug("Found {} compatible topology fragments for NodeTemplate {}", + compatibleNodeTemplates.size(), needHostNode.getId()); + + // Filter NodeTemplates based on Blacklist + compatibleNodeTemplates = compatibleNodeTemplates.stream().filter(nt -> !blacklist.contains(nt.getType())).toList(); +// compatibleNodeTemplates = compatibleNodeTemplates.stream().filter(nt -> blacklist.stream().noneMatch(bl -> bl.getLocalPart().equals(nt.getType().getLocalPart()))).toList(); + + // Filter NodeTemplates based on Policies + IRepository repositoryFactory = RepositoryFactory.getRepository(); + Map nodeTypes = repositoryFactory.getQNameToElementMapping(NodeTypeId.class); + for (Map.Entry entry : policies.entrySet()) { + compatibleNodeTemplates = compatibleNodeTemplates.stream().filter(nt -> { + //get NodeType of NodeTemplate + TNodeType matchingType = nodeTypes.get(nt.getType()); + + if (matchingType.getTags() == null) { + return true; + } + // check if NodeType has given Tag -> If it does have the tag it must be set as desrcibed by the policy, otherwise it is ignored + List filteredTagList = matchingType.getTags().stream().filter(tag -> tag.getName().equalsIgnoreCase(entry.getKey())).toList(); + if (filteredTagList.size() > 0) { + return entry.getValue().equalsIgnoreCase(filteredTagList.get(0).getValue()); + } + return true; + }).toList(); + } + + if (compatibleNodeTemplates.size() == 0){ + LOGGER.error("All matched nodetemplates were filtered - hence the completion had to be aborted"); + throw new SplittingException("All matched nodetemplates were filtered - hence the completion had to be aborted"); + } + TNodeTemplate selectedTemplate = compatibleNodeTemplates.get(0); + + // check if suitable nodetemplate already exists in topology, if so reuse it instead of creating new one. + assert topologyToComplete != null; + boolean suitableNodeTemplateAlreadyExists = false; + for (TNodeTemplate nt: topologyToComplete.getNodeTemplates()) { + if (nt.getType().equals(selectedTemplate.getType())) { + selectedTemplate = nt; + suitableNodeTemplateAlreadyExists = true; + } + } + if (!suitableNodeTemplateAlreadyExists) { + assert needHostNode.getX() != null; + assert needHostNode.getY() != null; + int coordinateX = Integer.parseInt(needHostNode.getX()) - newNodesAddedCounter * 350; + int coordinateY = Integer.parseInt(needHostNode.getY()) + 180; + selectedTemplate.setX(String.valueOf(coordinateX)); + selectedTemplate.setY(String.valueOf(coordinateY)); + + topologyToComplete.addNodeTemplate(selectedTemplate); + newNodesAddedCounter += 1; + } + + TRelationshipTemplate newHostedOnRelationship = new TRelationshipTemplate(); + int idNumber = (int) (Math.random() * 200000000) + 1; + String id = "con_HostedOn_" + idNumber; + newHostedOnRelationship.setId(id); + newHostedOnRelationship.setName("hostedOn"); + + TRelationshipTemplate.SourceOrTargetElement sourceElement = new TRelationshipTemplate.SourceOrTargetElement(); + TRelationshipTemplate.SourceOrTargetElement targetElement = new TRelationshipTemplate.SourceOrTargetElement(); + sourceElement.setRef(needHostNode); + targetElement.setRef(selectedTemplate); + newHostedOnRelationship.setSourceElement(sourceElement); + newHostedOnRelationship.setTargetElement(targetElement); + + // HostedOn is always selected as relationshipType + SortedSet relTypeIds = RepositoryFactory.getRepository().getAllDefinitionsChildIds(RelationshipTypeId.class); + List relationshipTypes = new ArrayList<>(); + for (RelationshipTypeId typeId : relTypeIds) { + relationshipTypes.add(RepositoryFactory.getRepository().getElement(typeId)); + } + TRelationshipType relationshipType = relationshipTypes.stream().filter(type -> { + assert type.getName() != null; + return type.getName().equals("HostedOn"); + }).toList().get(0); + + if (relationshipType != null) { + assert relationshipType.getName() != null; + QName relationshipTypeQName = new QName(relationshipType.getTargetNamespace(), relationshipType.getName()); + newHostedOnRelationship.setType(relationshipTypeQName); + topologyToComplete.addRelationshipTemplate(newHostedOnRelationship); + } + } + } + } + return topologyToComplete; + } } diff --git a/org.eclipse.winery.repository/src/main/java/org/eclipse/winery/repository/xml/converter/FromCanonical.java b/org.eclipse.winery.repository/src/main/java/org/eclipse/winery/repository/xml/converter/FromCanonical.java index f4b50adaa2..dca374cde4 100644 --- a/org.eclipse.winery.repository/src/main/java/org/eclipse/winery/repository/xml/converter/FromCanonical.java +++ b/org.eclipse.winery.repository/src/main/java/org/eclipse/winery/repository/xml/converter/FromCanonical.java @@ -27,6 +27,7 @@ import org.eclipse.winery.model.tosca.TArtifactTemplate; import org.eclipse.winery.model.tosca.TArtifactType; import org.eclipse.winery.model.tosca.TBoundaryDefinitions; +import org.eclipse.winery.model.tosca.TBoundaryXML; import org.eclipse.winery.model.tosca.TCapability; import org.eclipse.winery.model.tosca.TCapabilityDefinition; import org.eclipse.winery.model.tosca.TCapabilityRef; @@ -58,6 +59,7 @@ import org.eclipse.winery.model.tosca.TPolicyType; import org.eclipse.winery.model.tosca.TPropertyConstraint; import org.eclipse.winery.model.tosca.TPropertyMapping; +import org.eclipse.winery.model.tosca.TQProvUrl; import org.eclipse.winery.model.tosca.TRelationshipTemplate; import org.eclipse.winery.model.tosca.TRelationshipType; import org.eclipse.winery.model.tosca.TRelationshipTypeImplementation; @@ -66,6 +68,7 @@ import org.eclipse.winery.model.tosca.TRequirementDefinition; import org.eclipse.winery.model.tosca.TRequirementRef; import org.eclipse.winery.model.tosca.TRequirementType; +import org.eclipse.winery.model.tosca.TSelfServiceApplicationUrl; import org.eclipse.winery.model.tosca.TServiceTemplate; import org.eclipse.winery.model.tosca.TTag; import org.eclipse.winery.model.tosca.TTopologyTemplate; @@ -94,6 +97,7 @@ import org.eclipse.winery.model.tosca.xml.XTArtifactType; import org.eclipse.winery.model.tosca.xml.XTBoolean; import org.eclipse.winery.model.tosca.xml.XTBoundaryDefinitions; +import org.eclipse.winery.model.tosca.xml.XTBoundaryXML; import org.eclipse.winery.model.tosca.xml.XTCapability; import org.eclipse.winery.model.tosca.xml.XTCapabilityDefinition; import org.eclipse.winery.model.tosca.xml.XTCapabilityRef; @@ -125,6 +129,7 @@ import org.eclipse.winery.model.tosca.xml.XTPolicyType; import org.eclipse.winery.model.tosca.xml.XTPropertyConstraint; import org.eclipse.winery.model.tosca.xml.XTPropertyMapping; +import org.eclipse.winery.model.tosca.xml.XTQProvUrl; import org.eclipse.winery.model.tosca.xml.XTRelationshipTemplate; import org.eclipse.winery.model.tosca.xml.XTRelationshipType; import org.eclipse.winery.model.tosca.xml.XTRelationshipTypeImplementation; @@ -133,6 +138,7 @@ import org.eclipse.winery.model.tosca.xml.XTRequirementDefinition; import org.eclipse.winery.model.tosca.xml.XTRequirementRef; import org.eclipse.winery.model.tosca.xml.XTRequirementType; +import org.eclipse.winery.model.tosca.xml.XTSelfServiceApplicationUrl; import org.eclipse.winery.model.tosca.xml.XTServiceTemplate; import org.eclipse.winery.model.tosca.xml.XTTag; import org.eclipse.winery.model.tosca.xml.XTTopologyTemplate; @@ -710,6 +716,21 @@ private XTBoundaryDefinitions convert(@Nullable TBoundaryDefinitions canonical) if (canonical.getProperties() != null) { XTBoundaryDefinitions.Properties props = new XTBoundaryDefinitions.Properties(); props.setAny(canonical.getProperties().getAny()); + if (canonical.getProperties().getSelfServiceApplicationUrl() != null){ + props.setSelfServiceApplicationUrl( + convert(canonical.getProperties().getSelfServiceApplicationUrl()) + ); + } + if (canonical.getProperties().getQprovUrl() != null){ + props.setQprovUrl( + convert(canonical.getProperties().getQprovUrl()) + ); + } + if (canonical.getProperties().getBoundaryXML() != null){ + props.setBoundaryXML( + convert(canonical.getProperties().getBoundaryXML()) + ); + } if (canonical.getProperties().getPropertyMappings() != null) { props.setPropertyMappings( canonical.getProperties().getPropertyMappings().stream() @@ -851,6 +872,27 @@ private XTPropertyMapping convert(TPropertyMapping canonical) { ).build(); } + private XTBoundaryXML convert(TBoundaryXML canonical) { + return new XTBoundaryXML.Builder( + convert(canonical.getSelfserviceApplicationUrl()), + convert(canonical.getQProvUrl()), + canonical.getNamespace() + ).build(); + } + + + private XTSelfServiceApplicationUrl convert(TSelfServiceApplicationUrl canonical) { + return new XTSelfServiceApplicationUrl.Builder( + canonical.getSelfServiceApplicationUrl() + ).build(); + } + + private XTQProvUrl convert(TQProvUrl canonical) { + return new XTQProvUrl.Builder( + canonical.getQProvUrl() + ).build(); + } + @Nullable private XTTopologyTemplate convert(@Nullable TTopologyTemplate canonical) { if (canonical == null) { diff --git a/org.eclipse.winery.repository/src/main/java/org/eclipse/winery/repository/xml/converter/ToCanonical.java b/org.eclipse.winery.repository/src/main/java/org/eclipse/winery/repository/xml/converter/ToCanonical.java index fa9d80d759..911b397fed 100644 --- a/org.eclipse.winery.repository/src/main/java/org/eclipse/winery/repository/xml/converter/ToCanonical.java +++ b/org.eclipse.winery.repository/src/main/java/org/eclipse/winery/repository/xml/converter/ToCanonical.java @@ -30,6 +30,7 @@ import org.eclipse.winery.model.tosca.TArtifactTemplate; import org.eclipse.winery.model.tosca.TArtifactType; import org.eclipse.winery.model.tosca.TBoundaryDefinitions; +import org.eclipse.winery.model.tosca.TBoundaryXML; import org.eclipse.winery.model.tosca.TCapability; import org.eclipse.winery.model.tosca.TCapabilityDefinition; import org.eclipse.winery.model.tosca.TCapabilityRef; @@ -62,6 +63,7 @@ import org.eclipse.winery.model.tosca.TPolicyType; import org.eclipse.winery.model.tosca.TPropertyConstraint; import org.eclipse.winery.model.tosca.TPropertyMapping; +import org.eclipse.winery.model.tosca.TQProvUrl; import org.eclipse.winery.model.tosca.TRelationshipTemplate; import org.eclipse.winery.model.tosca.TRelationshipType; import org.eclipse.winery.model.tosca.TRelationshipTypeImplementation; @@ -70,6 +72,7 @@ import org.eclipse.winery.model.tosca.TRequirementDefinition; import org.eclipse.winery.model.tosca.TRequirementRef; import org.eclipse.winery.model.tosca.TRequirementType; +import org.eclipse.winery.model.tosca.TSelfServiceApplicationUrl; import org.eclipse.winery.model.tosca.TServiceTemplate; import org.eclipse.winery.model.tosca.TTag; import org.eclipse.winery.model.tosca.TTopologyTemplate; @@ -99,6 +102,7 @@ import org.eclipse.winery.model.tosca.xml.XTArtifactType; import org.eclipse.winery.model.tosca.xml.XTBoolean; import org.eclipse.winery.model.tosca.xml.XTBoundaryDefinitions; +import org.eclipse.winery.model.tosca.xml.XTBoundaryXML; import org.eclipse.winery.model.tosca.xml.XTCapability; import org.eclipse.winery.model.tosca.xml.XTCapabilityDefinition; import org.eclipse.winery.model.tosca.xml.XTCapabilityRef; @@ -129,6 +133,7 @@ import org.eclipse.winery.model.tosca.xml.XTPolicyType; import org.eclipse.winery.model.tosca.xml.XTPropertyConstraint; import org.eclipse.winery.model.tosca.xml.XTPropertyMapping; +import org.eclipse.winery.model.tosca.xml.XTQProvUrl; import org.eclipse.winery.model.tosca.xml.XTRelationshipTemplate; import org.eclipse.winery.model.tosca.xml.XTRelationshipType; import org.eclipse.winery.model.tosca.xml.XTRelationshipTypeImplementation; @@ -137,6 +142,7 @@ import org.eclipse.winery.model.tosca.xml.XTRequirementDefinition; import org.eclipse.winery.model.tosca.xml.XTRequirementRef; import org.eclipse.winery.model.tosca.xml.XTRequirementType; +import org.eclipse.winery.model.tosca.xml.XTSelfServiceApplicationUrl; import org.eclipse.winery.model.tosca.xml.XTServiceTemplate; import org.eclipse.winery.model.tosca.xml.XTTag; import org.eclipse.winery.model.tosca.xml.XTTopologyTemplate; @@ -810,11 +816,26 @@ private TBoundaryDefinitions convert(XTBoundaryDefinitions xml) { if (xml.getProperties() != null) { TBoundaryDefinitions.Properties props = new TBoundaryDefinitions.Properties(); props.setAny(xml.getProperties().getAny()); + if (xml.getProperties().getSelfServiceApplicationUrl() != null) { + props.setSelfServiceApplicationUrl( + convert(xml.getProperties().getSelfServiceApplicationUrl()) + ); + } + if (xml.getProperties().getQprovUrl() != null) { + props.setQprovUrl( + convert(xml.getProperties().getQprovUrl()) + ); + } if (xml.getProperties().getPropertyMappings() != null) { props.setPropertyMappings( convertList(xml.getProperties().getPropertyMappings(), this::convert) ); } + if (xml.getProperties().getBoundaryXML() != null) { + props.setBoundaryXML( + convert(xml.getProperties().getBoundaryXML()) + ); + } builder.setProperties(props); } if (xml.getRequirements() != null) { @@ -944,6 +965,25 @@ private TPropertyMapping convert(XTPropertyMapping xml) { ); } + private TBoundaryXML convert(XTBoundaryXML xml) { + return new TBoundaryXML( + convert(xml.getSelfserviceApplicationUrl()), + convert(xml.getQProvUrl()), + xml.getNamespace() + ); + } + + private TSelfServiceApplicationUrl convert(XTSelfServiceApplicationUrl xml) { + return new TSelfServiceApplicationUrl( + xml.getSelfServiceApplicationUrl() + ); + } + + private TQProvUrl convert(XTQProvUrl xml) { + return new TQProvUrl( + xml.getqProvUrl() + ); + } @Nullable private TTopologyTemplate convert(XTTopologyTemplate xml) { if (xml == null) {