|
| 1 | +package org.opentosca.planbuilder.core.bpel.typebasedplanbuilder; |
| 2 | + |
| 3 | +import java.io.IOException; |
| 4 | +import java.net.URLEncoder; |
| 5 | +import java.util.ArrayList; |
| 6 | +import java.util.HashMap; |
| 7 | +import java.util.List; |
| 8 | +import java.util.Map; |
| 9 | +import java.util.Objects; |
| 10 | + |
| 11 | +import javax.xml.namespace.QName; |
| 12 | +import javax.xml.parsers.ParserConfigurationException; |
| 13 | + |
| 14 | +import org.opentosca.container.core.tosca.convention.Interfaces; |
| 15 | +import org.opentosca.planbuilder.AbstractManagementFeaturePlanBuilder; |
| 16 | +import org.opentosca.planbuilder.core.bpel.context.BPELPlanContext; |
| 17 | +import org.opentosca.planbuilder.core.bpel.fragments.BPELProcessFragments; |
| 18 | +import org.opentosca.planbuilder.core.bpel.handlers.BPELFinalizer; |
| 19 | +import org.opentosca.planbuilder.core.bpel.handlers.BPELPlanHandler; |
| 20 | +import org.opentosca.planbuilder.core.bpel.handlers.CorrelationIDInitializer; |
| 21 | +import org.opentosca.planbuilder.core.bpel.tosca.handlers.NodeRelationInstanceVariablesHandler; |
| 22 | +import org.opentosca.planbuilder.core.bpel.tosca.handlers.PropertyVariableHandler; |
| 23 | +import org.opentosca.planbuilder.core.bpel.tosca.handlers.SimplePlanBuilderServiceInstanceHandler; |
| 24 | +import org.opentosca.planbuilder.model.plan.AbstractPlan; |
| 25 | +import org.opentosca.planbuilder.model.plan.AbstractPlan.PlanType; |
| 26 | +import org.opentosca.planbuilder.model.plan.ActivityType; |
| 27 | +import org.opentosca.planbuilder.model.plan.bpel.BPELPlan; |
| 28 | +import org.opentosca.planbuilder.model.plan.bpel.BPELScope; |
| 29 | +import org.opentosca.planbuilder.model.tosca.AbstractDefinitions; |
| 30 | +import org.opentosca.planbuilder.model.tosca.AbstractNodeTemplate; |
| 31 | +import org.opentosca.planbuilder.model.tosca.AbstractOperation; |
| 32 | +import org.opentosca.planbuilder.model.tosca.AbstractParameter; |
| 33 | +import org.opentosca.planbuilder.model.tosca.AbstractServiceTemplate; |
| 34 | +import org.opentosca.planbuilder.model.utils.ModelUtils; |
| 35 | +import org.opentosca.planbuilder.plugins.context.Property2VariableMapping; |
| 36 | +import org.opentosca.planbuilder.plugins.context.Variable; |
| 37 | +import org.slf4j.Logger; |
| 38 | +import org.slf4j.LoggerFactory; |
| 39 | +import org.w3c.dom.Node; |
| 40 | +import org.xml.sax.SAXException; |
| 41 | + |
| 42 | +/** |
| 43 | + * <p> |
| 44 | + * This process builder creates a backup management plan if one of the NodeTemplates in the topology |
| 45 | + * is of a type that defines the freeze interface. |
| 46 | + * </p> |
| 47 | + * |
| 48 | + * Copyright 2019 IAAS University of Stuttgart <br> |
| 49 | + * <br> |
| 50 | + */ |
| 51 | +public class BPELBackupManagementProcessBuilder extends AbstractManagementFeaturePlanBuilder { |
| 52 | + |
| 53 | + private final static Logger LOG = LoggerFactory.getLogger(BPELBackupManagementProcessBuilder.class); |
| 54 | + |
| 55 | + // handler for abstract buildplan operations |
| 56 | + public BPELPlanHandler planHandler; |
| 57 | + |
| 58 | + // class for initializing properties inside the build plan |
| 59 | + private final PropertyVariableHandler propertyInitializer; |
| 60 | + |
| 61 | + // adds serviceInstance Variable and instanceDataAPIUrl to buildPlans |
| 62 | + private SimplePlanBuilderServiceInstanceHandler serviceInstanceVarsHandler; |
| 63 | + |
| 64 | + // adds nodeInstanceIDs to each templatePlan |
| 65 | + private NodeRelationInstanceVariablesHandler instanceVarsHandler; |
| 66 | + |
| 67 | + // class for finalizing build plans (e.g when some template didn't receive |
| 68 | + // some provisioning logic and they must be filled with empty elements) |
| 69 | + private final BPELFinalizer finalizer; |
| 70 | + |
| 71 | + private BPELProcessFragments bpelFragments; |
| 72 | + |
| 73 | + private CorrelationIDInitializer correlationHandler; |
| 74 | + |
| 75 | + /** |
| 76 | + * <p> |
| 77 | + * Default Constructor |
| 78 | + * </p> |
| 79 | + */ |
| 80 | + public BPELBackupManagementProcessBuilder() { |
| 81 | + try { |
| 82 | + this.planHandler = new BPELPlanHandler(); |
| 83 | + this.serviceInstanceVarsHandler = new SimplePlanBuilderServiceInstanceHandler(); |
| 84 | + this.instanceVarsHandler = new NodeRelationInstanceVariablesHandler(this.planHandler); |
| 85 | + this.bpelFragments = new BPELProcessFragments(); |
| 86 | + this.correlationHandler = new CorrelationIDInitializer(); |
| 87 | + } |
| 88 | + catch (final ParserConfigurationException e) { |
| 89 | + LOG.error("Error while initializing BuildPlanHandler", e); |
| 90 | + } |
| 91 | + this.propertyInitializer = new PropertyVariableHandler(this.planHandler); |
| 92 | + this.finalizer = new BPELFinalizer(); |
| 93 | + } |
| 94 | + |
| 95 | + /* |
| 96 | + * (non-Javadoc) |
| 97 | + * |
| 98 | + * @see org.opentosca.planbuilder.IPlanBuilder#buildPlan(java.lang.String, |
| 99 | + * org.opentosca.planbuilder.model.tosca.AbstractDefinitions, javax.xml.namespace.QName) |
| 100 | + */ |
| 101 | + @Override |
| 102 | + public BPELPlan buildPlan(final String csarName, final AbstractDefinitions definitions, |
| 103 | + final AbstractServiceTemplate serviceTemplate) { |
| 104 | + LOG.debug("Creating Backup Management Plan..."); |
| 105 | + |
| 106 | + if (Objects.isNull(serviceTemplate)) { |
| 107 | + LOG.error("Unable to generate Backup Plan with ServiceTempolate equal to null."); |
| 108 | + return null; |
| 109 | + } |
| 110 | + |
| 111 | + final String processName = ModelUtils.makeValidNCName(serviceTemplate.getId() + "_backupManagementPlan"); |
| 112 | + final String processNamespace = serviceTemplate.getTargetNamespace() + "_backupManagementPlan"; |
| 113 | + |
| 114 | + final AbstractPlan abstractBackupPlan = |
| 115 | + generateMOG(new QName(processNamespace, processName).toString(), definitions, serviceTemplate, |
| 116 | + Interfaces.OPENTOSCA_DECLARATIVE_INTERFACE_STATE, ActivityType.BACKUP, true); |
| 117 | + |
| 118 | + LOG.debug("Generated the following abstract backup plan: "); |
| 119 | + LOG.debug(abstractBackupPlan.toString()); |
| 120 | + |
| 121 | + abstractBackupPlan.setType(PlanType.MANAGE); |
| 122 | + final BPELPlan newBackupPlan = |
| 123 | + this.planHandler.createEmptyBPELPlan(processNamespace, processName, abstractBackupPlan, "backup"); |
| 124 | + |
| 125 | + this.planHandler.initializeBPELSkeleton(newBackupPlan, csarName); |
| 126 | + |
| 127 | + newBackupPlan.setTOSCAInterfaceName("OpenTOSCA-Management-Feature-Interface"); |
| 128 | + newBackupPlan.setTOSCAOperationname("backup"); |
| 129 | + |
| 130 | + this.instanceVarsHandler.addInstanceURLVarToTemplatePlans(newBackupPlan, serviceTemplate); |
| 131 | + this.instanceVarsHandler.addInstanceIDVarToTemplatePlans(newBackupPlan, serviceTemplate); |
| 132 | + |
| 133 | + final Property2VariableMapping propMap = |
| 134 | + this.propertyInitializer.initializePropertiesAsVariables(newBackupPlan, serviceTemplate); |
| 135 | + |
| 136 | + // initialize instanceData handling |
| 137 | + this.planHandler.registerExtension("http://www.apache.org/ode/bpel/extensions/bpel4restlight", true, |
| 138 | + newBackupPlan); |
| 139 | + this.serviceInstanceVarsHandler.addServiceInstanceHandlingFromInput(newBackupPlan); |
| 140 | + final String serviceTemplateURLVarName = |
| 141 | + this.serviceInstanceVarsHandler.getServiceTemplateURLVariableName(newBackupPlan); |
| 142 | + this.serviceInstanceVarsHandler.appendInitPropertyVariablesFromServiceInstanceData(newBackupPlan, propMap, |
| 143 | + serviceTemplateURLVarName, |
| 144 | + serviceTemplate, null); |
| 145 | + |
| 146 | + // fetch all node instances that are running |
| 147 | + this.instanceVarsHandler.addNodeInstanceFindLogic(newBackupPlan, |
| 148 | + "?state=STARTED&state=CREATED&state=CONFIGURED", |
| 149 | + serviceTemplate); |
| 150 | + this.instanceVarsHandler.addPropertyVariableUpdateBasedOnNodeInstanceID(newBackupPlan, propMap, |
| 151 | + serviceTemplate); |
| 152 | + |
| 153 | + try { |
| 154 | + appendGenerateStatefulServiceTemplateLogic(newBackupPlan); |
| 155 | + } |
| 156 | + catch (final IOException e) { |
| 157 | + e.printStackTrace(); |
| 158 | + } |
| 159 | + catch (final SAXException e) { |
| 160 | + e.printStackTrace(); |
| 161 | + } |
| 162 | + |
| 163 | + runPlugins(newBackupPlan, propMap, csarName); |
| 164 | + |
| 165 | + this.correlationHandler.addCorrellationID(newBackupPlan); |
| 166 | + this.finalizer.finalize(newBackupPlan); |
| 167 | + |
| 168 | + LOG.debug("Created Plan:"); |
| 169 | + LOG.debug(ModelUtils.getStringFromDoc(newBackupPlan.getBpelDocument())); |
| 170 | + |
| 171 | + return newBackupPlan; |
| 172 | + } |
| 173 | + |
| 174 | + private void runPlugins(final BPELPlan plan, final Property2VariableMapping propMap, final String csarName) { |
| 175 | + |
| 176 | + final String statefulServiceTemplateUrlVarName = findStatefulServiceTemplateUrlVar(plan); |
| 177 | + |
| 178 | + final String serviceInstanceUrl = this.serviceInstanceVarsHandler.findServiceInstanceUrlVariableName(plan); |
| 179 | + final String serviceInstanceId = this.serviceInstanceVarsHandler.findServiceInstanceIdVarName(plan); |
| 180 | + final String serviceTemplateUrl = this.serviceInstanceVarsHandler.findServiceTemplateUrlVariableName(plan); |
| 181 | + |
| 182 | + |
| 183 | + for (final BPELScope templatePlan : plan.getTemplateBuildPlans()) { |
| 184 | + final BPELPlanContext context = new BPELPlanContext(plan, templatePlan, propMap, plan.getServiceTemplate(), |
| 185 | + serviceInstanceUrl, serviceInstanceId, serviceTemplateUrl, csarName); |
| 186 | + |
| 187 | + // only handle NodeTemplates of type with save state interface |
| 188 | + final AbstractNodeTemplate nodeTemplate = templatePlan.getNodeTemplate(); |
| 189 | + if (Objects.nonNull(nodeTemplate) |
| 190 | + && Objects.nonNull(ModelUtils.getInterfaceOfNode(nodeTemplate, |
| 191 | + Interfaces.OPENTOSCA_DECLARATIVE_INTERFACE_STATE))) { |
| 192 | + LOG.debug("Adding backup logic for NodeTemplate {}", nodeTemplate.getName()); |
| 193 | + |
| 194 | + final String saveStateUrlVarName = |
| 195 | + this.planHandler.addGlobalStringVariable("nodeTemplateStateSaveURL", plan); |
| 196 | + |
| 197 | + final String xpathQuery = "concat($" + statefulServiceTemplateUrlVarName |
| 198 | + + ",'/topologytemplate/nodetemplates/" + nodeTemplate.getId() + "/state')"; |
| 199 | + try { |
| 200 | + Node assignSaveStateURL = |
| 201 | + this.bpelFragments.createAssignVarToVarWithXpathQueryAsNode("assignNodeTemplate" |
| 202 | + + nodeTemplate.getId() + "state" + System.currentTimeMillis(), |
| 203 | + statefulServiceTemplateUrlVarName, |
| 204 | + saveStateUrlVarName, xpathQuery); |
| 205 | + assignSaveStateURL = context.importNode(assignSaveStateURL); |
| 206 | + context.getPrePhaseElement().appendChild(assignSaveStateURL); |
| 207 | + } |
| 208 | + catch (final IOException e) { |
| 209 | + e.printStackTrace(); |
| 210 | + } |
| 211 | + catch (final SAXException e) { |
| 212 | + e.printStackTrace(); |
| 213 | + } |
| 214 | + |
| 215 | + final AbstractOperation freezeOp = |
| 216 | + ModelUtils.getOperationOfNode(nodeTemplate, Interfaces.OPENTOSCA_DECLARATIVE_INTERFACE_STATE, |
| 217 | + Interfaces.OPENTOSCA_DECLARATIVE_INTERFACE_STATE_FREEZE); |
| 218 | + if (Objects.nonNull(freezeOp)) { |
| 219 | + final Variable saveStateUrlVar = BPELPlanContext.getVariable(saveStateUrlVarName); |
| 220 | + |
| 221 | + final Map<AbstractParameter, Variable> inputs = new HashMap<>(); |
| 222 | + |
| 223 | + // retrieve input parameters from all nodes which are downwards in the same topology stack |
| 224 | + final List<AbstractNodeTemplate> nodesForMatching = new ArrayList<>(); |
| 225 | + ModelUtils.getNodesFromNodeToSink(nodeTemplate, nodesForMatching); |
| 226 | + |
| 227 | + LOG.debug("Backup on NodeTemplate {} needs the following input parameters:", |
| 228 | + nodeTemplate.getName()); |
| 229 | + for (final AbstractParameter param : freezeOp.getInputParameters()) { |
| 230 | + LOG.debug("Input param: {}", param.getName()); |
| 231 | + found: for (final AbstractNodeTemplate nodeForMatching : nodesForMatching) { |
| 232 | + for (final String propName : ModelUtils.getPropertyNames(nodeForMatching)) { |
| 233 | + if (param.getName().equals(propName)) { |
| 234 | + inputs.put(param, context.getPropertyVariable(nodeForMatching, propName)); |
| 235 | + break found; |
| 236 | + } |
| 237 | + } |
| 238 | + } |
| 239 | + } |
| 240 | + |
| 241 | + // add special parameter with winery URL |
| 242 | + inputs.put(getSaveStateParameter(freezeOp), saveStateUrlVar); |
| 243 | + |
| 244 | + LOG.debug("Found {} of {} input parameters.", inputs.size(), freezeOp.getInputParameters().size()); |
| 245 | + |
| 246 | + context.executeOperation(nodeTemplate, Interfaces.OPENTOSCA_DECLARATIVE_INTERFACE_STATE, |
| 247 | + Interfaces.OPENTOSCA_DECLARATIVE_INTERFACE_STATE_FREEZE, inputs); |
| 248 | + } |
| 249 | + } |
| 250 | + } |
| 251 | + } |
| 252 | + |
| 253 | + @Override |
| 254 | + public List<AbstractPlan> buildPlans(final String csarName, final AbstractDefinitions definitions) { |
| 255 | + LOG.info("Building the Backup Management Plans"); |
| 256 | + final List<AbstractPlan> plans = new ArrayList<>(); |
| 257 | + for (final AbstractServiceTemplate serviceTemplate : definitions.getServiceTemplates()) { |
| 258 | + |
| 259 | + if (containsManagementInterface(serviceTemplate, Interfaces.OPENTOSCA_DECLARATIVE_INTERFACE_STATE)) { |
| 260 | + LOG.debug("ServiceTemplate {} contains NodeTypes with defined backup interface.", |
| 261 | + serviceTemplate.getName()); |
| 262 | + final BPELPlan newBuildPlan = buildPlan(csarName, definitions, serviceTemplate); |
| 263 | + if (Objects.nonNull(newBuildPlan)) { |
| 264 | + LOG.debug("Created Backup Management Plan " |
| 265 | + + newBuildPlan.getBpelProcessElement().getAttribute("name")); |
| 266 | + plans.add(newBuildPlan); |
| 267 | + } |
| 268 | + } else { |
| 269 | + LOG.debug("No backup interface defined in ServiceTemplate {}", serviceTemplate.getName()); |
| 270 | + } |
| 271 | + } |
| 272 | + return plans; |
| 273 | + } |
| 274 | + |
| 275 | + private void appendGenerateStatefulServiceTemplateLogic(final BPELPlan plan) throws IOException, SAXException { |
| 276 | + final QName serviceTemplateId = plan.getServiceTemplate().getQName(); |
| 277 | + |
| 278 | + this.planHandler.addStringElementToPlanRequest(Interfaces.OPENTOSCA_DECLARATIVE_INTERFACE_STATE_FREEZE_MANDATORY_PARAM_ENDPOINT, |
| 279 | + plan); |
| 280 | + |
| 281 | + // var to save serviceTemplate url on storage service |
| 282 | + final String statefulServiceTemplateVarName = |
| 283 | + this.planHandler.addGlobalStringVariable("statefulServiceTemplateUrl" + System.currentTimeMillis(), plan); |
| 284 | + final String responseVarName = this.planHandler.createAnyTypeVar(plan); |
| 285 | + |
| 286 | + // assign variable with the original service template url |
| 287 | + Node assignStatefuleServiceTemplateStorageVar = |
| 288 | + this.bpelFragments.createAssignVarToVarWithXpathQueryAsNode("assignServiceTemplateStorageUrl" |
| 289 | + + System.currentTimeMillis(), "input", statefulServiceTemplateVarName, |
| 290 | + "concat(//*[local-name()='" |
| 291 | + + Interfaces.OPENTOSCA_DECLARATIVE_INTERFACE_STATE_FREEZE_MANDATORY_PARAM_ENDPOINT |
| 292 | + + "']/text(),'/servicetemplates/" |
| 293 | + + URLEncoder.encode(URLEncoder.encode(serviceTemplateId.getNamespaceURI(), |
| 294 | + "UTF-8"), |
| 295 | + "UTF-8") |
| 296 | + + "','/" + serviceTemplateId.getLocalPart() |
| 297 | + + "','/createnewstatefulversion')"); |
| 298 | + assignStatefuleServiceTemplateStorageVar = |
| 299 | + plan.getBpelDocument().importNode(assignStatefuleServiceTemplateStorageVar, true); |
| 300 | + plan.getBpelMainSequenceElement().insertBefore(assignStatefuleServiceTemplateStorageVar, |
| 301 | + plan.getBpelMainSequencePropertyAssignElement()); |
| 302 | + |
| 303 | + // create append POST for creating a stateful service template version |
| 304 | + Node createStatefulServiceTemplatePOST = |
| 305 | + this.bpelFragments.createHTTPPOST(statefulServiceTemplateVarName, responseVarName); |
| 306 | + |
| 307 | + createStatefulServiceTemplatePOST = plan.getBpelDocument().importNode(createStatefulServiceTemplatePOST, true); |
| 308 | + |
| 309 | + plan.getBpelMainSequenceElement().insertBefore(createStatefulServiceTemplatePOST, |
| 310 | + plan.getBpelMainSequencePropertyAssignElement()); |
| 311 | + |
| 312 | + // read response and assign url of created stateful service template query the localname from the |
| 313 | + // response |
| 314 | + final String xpathQuery1 = |
| 315 | + "concat(substring-before($" + statefulServiceTemplateVarName + ",'" + serviceTemplateId.getLocalPart() |
| 316 | + + "'),encode-for-uri(encode-for-uri(//*[local-name()='QName']/*[local-name()='localname']/text())))"; |
| 317 | + |
| 318 | + // query original service template url without the last path fragment(/service template localname) |
| 319 | + final String xpathQuery2 = "string($" + statefulServiceTemplateVarName + ")"; |
| 320 | + Node assignCreatedStatefulServiceTemplate = |
| 321 | + this.bpelFragments.createAssignVarToVarWithXpathQueriesAsNode("assignCreatedStatefuleServiceTemplateUrl", |
| 322 | + responseVarName, null, |
| 323 | + statefulServiceTemplateVarName, null, |
| 324 | + xpathQuery1, xpathQuery2, |
| 325 | + "change the url from original service template to stateful", |
| 326 | + null); |
| 327 | + |
| 328 | + assignCreatedStatefulServiceTemplate = |
| 329 | + plan.getBpelDocument().importNode(assignCreatedStatefulServiceTemplate, true); |
| 330 | + plan.getBpelMainSequenceElement().insertBefore(assignCreatedStatefulServiceTemplate, |
| 331 | + plan.getBpelMainSequencePropertyAssignElement()); |
| 332 | + } |
| 333 | + |
| 334 | + private String findStatefulServiceTemplateUrlVar(final BPELPlan plan) { |
| 335 | + return this.planHandler.getMainVariableNames(plan).stream() |
| 336 | + .filter(varName -> varName.contains("statefulServiceTemplateUrl")).findFirst() |
| 337 | + .orElse(null); |
| 338 | + } |
| 339 | + |
| 340 | + private AbstractParameter getSaveStateParameter(final AbstractOperation op) { |
| 341 | + return op.getInputParameters().stream() |
| 342 | + .filter(param -> param.getName() |
| 343 | + .equals(Interfaces.OPENTOSCA_DECLARATIVE_INTERFACE_STATE_FREEZE_MANDATORY_PARAM_ENDPOINT)) |
| 344 | + .findFirst().orElse(null); |
| 345 | + } |
| 346 | +} |
0 commit comments