diff options
26 files changed, 1579 insertions, 702 deletions
diff --git a/extra/sql/bulkload/create-tables.sql b/extra/sql/bulkload/create-tables.sql index 121c5e68..aef3a7e7 100644 --- a/extra/sql/bulkload/create-tables.sql +++ b/extra/sql/bulkload/create-tables.sql @@ -24,6 +24,7 @@ global_properties_json json, last_computed_state varchar(255) not null, model_properties_json json, + operational_policy_schema json not null, svg_representation MEDIUMTEXT, primary key (name) ) engine=InnoDB; diff --git a/src/main/java/org/onap/clamp/loop/Loop.java b/src/main/java/org/onap/clamp/loop/Loop.java index 2393f249..37d597ee 100644 --- a/src/main/java/org/onap/clamp/loop/Loop.java +++ b/src/main/java/org/onap/clamp/loop/Loop.java @@ -23,9 +23,13 @@ package org.onap.clamp.loop; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; import com.google.gson.JsonObject; +import com.google.gson.JsonSyntaxException; import com.google.gson.annotations.Expose; +import java.io.IOException; import java.io.Serializable; import java.util.HashMap; import java.util.HashSet; @@ -59,6 +63,7 @@ import org.onap.clamp.loop.components.external.PolicyComponent; import org.onap.clamp.loop.log.LoopLog; import org.onap.clamp.policy.microservice.MicroServicePolicy; import org.onap.clamp.policy.operational.OperationalPolicy; +import org.onap.clamp.policy.operational.OperationalPolicyRepresentationBuilder; @Entity @Table(name = "loops") @@ -70,6 +75,9 @@ public class Loop implements Serializable { */ private static final long serialVersionUID = -286522707701388642L; + @Transient + private static final EELFLogger logger = EELFManager.getInstance().getLogger(Loop.class); + @Id @Expose @Column(nullable = false, name = "name", unique = true) @@ -92,6 +100,11 @@ public class Loop implements Serializable { @Expose @Type(type = "json") + @Column(columnDefinition = "json", name = "operational_policy_schema") + private JsonObject operationalPolicySchema; + + @Expose + @Type(type = "json") @Column(columnDefinition = "json", name = "global_properties_json") private JsonObject globalPropertiesJson; @@ -131,6 +144,9 @@ public class Loop implements Serializable { this.addComponent(new DcaeComponent()); } + /** + * Public constructor. + */ public Loop() { initializeExternalComponents(); } @@ -256,6 +272,13 @@ public class Loop implements Serializable { void setModelPropertiesJson(JsonObject modelPropertiesJson) { this.modelPropertiesJson = modelPropertiesJson; + try { + this.operationalPolicySchema = OperationalPolicyRepresentationBuilder + .generateOperationalPolicySchema(this.getModelPropertiesJson()); + } catch (JsonSyntaxException | IOException | NullPointerException e) { + logger.error("Unable to generate the operational policy Schema ... ", e); + this.operationalPolicySchema = new JsonObject(); + } } public Map<String, ExternalComponent> getComponents() { @@ -273,20 +296,16 @@ public class Loop implements Serializable { /** * Generate the loop name. * - * @param serviceName - * The service name - * @param serviceVersion - * The service version - * @param resourceName - * The resource name - * @param blueprintFileName - * The blueprint file name + * @param serviceName The service name + * @param serviceVersion The service version + * @param resourceName The resource name + * @param blueprintFileName The blueprint file name * @return The generated loop name */ static String generateLoopName(String serviceName, String serviceVersion, String resourceName, - String blueprintFilename) { + String blueprintFilename) { StringBuilder buffer = new StringBuilder("LOOP_").append(serviceName).append("_v").append(serviceVersion) - .append("_").append(resourceName).append("_").append(blueprintFilename.replaceAll(".yaml", "")); + .append("_").append(resourceName).append("_").append(blueprintFilename.replaceAll(".yaml", "")); return buffer.toString().replace('.', '_').replaceAll(" ", ""); } diff --git a/src/main/java/org/onap/clamp/loop/LoopCsarInstaller.java b/src/main/java/org/onap/clamp/loop/LoopCsarInstaller.java index ad13ad34..41d34a22 100644 --- a/src/main/java/org/onap/clamp/loop/LoopCsarInstaller.java +++ b/src/main/java/org/onap/clamp/loop/LoopCsarInstaller.java @@ -99,10 +99,10 @@ public class LoopCsarInstaller implements CsarInstaller { boolean alreadyInstalled = true; for (Entry<String, BlueprintArtifact> blueprint : csar.getMapOfBlueprints().entrySet()) { alreadyInstalled = alreadyInstalled - && loopRepository.existsById(Loop.generateLoopName(csar.getSdcNotification().getServiceName(), - csar.getSdcNotification().getServiceVersion(), - blueprint.getValue().getResourceAttached().getResourceInstanceName(), - blueprint.getValue().getBlueprintArtifactName())); + && loopRepository.existsById(Loop.generateLoopName(csar.getSdcNotification().getServiceName(), + csar.getSdcNotification().getServiceVersion(), + blueprint.getValue().getResourceAttached().getResourceInstanceName(), + blueprint.getValue().getBlueprintArtifactName())); } return alreadyInstalled; } @@ -110,7 +110,7 @@ public class LoopCsarInstaller implements CsarInstaller { @Override @Transactional(propagation = Propagation.REQUIRED) public void installTheCsar(CsarHandler csar) - throws SdcArtifactInstallerException, InterruptedException, PolicyModelException { + throws SdcArtifactInstallerException, InterruptedException, PolicyModelException { try { logger.info("Installing the CSAR " + csar.getFilePath()); for (Entry<String, BlueprintArtifact> blueprint : csar.getMapOfBlueprints().entrySet()) { @@ -126,53 +126,53 @@ public class LoopCsarInstaller implements CsarInstaller { } private Loop createLoopFromBlueprint(CsarHandler csar, BlueprintArtifact blueprintArtifact) - throws IOException, ParseException, InterruptedException { + throws IOException, ParseException, InterruptedException { Loop newLoop = new Loop(); newLoop.setBlueprint(blueprintArtifact.getDcaeBlueprint()); newLoop.setName(Loop.generateLoopName(csar.getSdcNotification().getServiceName(), - csar.getSdcNotification().getServiceVersion(), - blueprintArtifact.getResourceAttached().getResourceInstanceName(), - blueprintArtifact.getBlueprintArtifactName())); + csar.getSdcNotification().getServiceVersion(), + blueprintArtifact.getResourceAttached().getResourceInstanceName(), + blueprintArtifact.getBlueprintArtifactName())); newLoop.setLastComputedState(LoopState.DESIGN); List<MicroService> microServicesChain = chainGenerator - .getChainOfMicroServices(blueprintParser.getMicroServices(blueprintArtifact.getDcaeBlueprint())); + .getChainOfMicroServices(blueprintParser.getMicroServices(blueprintArtifact.getDcaeBlueprint())); if (microServicesChain.isEmpty()) { microServicesChain = blueprintParser.fallbackToOneMicroService(blueprintArtifact.getDcaeBlueprint()); } - - newLoop - .setMicroServicePolicies(createMicroServicePolicies(microServicesChain, csar, blueprintArtifact, newLoop)); + newLoop.setModelPropertiesJson(createModelPropertiesJson(csar)); + newLoop.setMicroServicePolicies( + createMicroServicePolicies(microServicesChain, csar, blueprintArtifact, newLoop)); newLoop.setOperationalPolicies(createOperationalPolicies(csar, blueprintArtifact, newLoop)); newLoop.setSvgRepresentation(svgFacade.getSvgImage(microServicesChain)); newLoop.setGlobalPropertiesJson(createGlobalPropertiesJson(blueprintArtifact, newLoop)); - newLoop.setModelPropertiesJson(createModelPropertiesJson(csar)); + DcaeInventoryResponse dcaeResponse = queryDcaeToGetServiceTypeId(blueprintArtifact); newLoop.setDcaeBlueprintId(dcaeResponse.getTypeId()); return newLoop; } private HashSet<OperationalPolicy> createOperationalPolicies(CsarHandler csar, BlueprintArtifact blueprintArtifact, - Loop newLoop) { + Loop newLoop) { return new HashSet<>(Arrays.asList(new OperationalPolicy(Policy.generatePolicyName("OPERATIONAL", - csar.getSdcNotification().getServiceName(), csar.getSdcNotification().getServiceVersion(), - blueprintArtifact.getResourceAttached().getResourceInstanceName(), - blueprintArtifact.getBlueprintArtifactName()), newLoop, new JsonObject()))); + csar.getSdcNotification().getServiceName(), csar.getSdcNotification().getServiceVersion(), + blueprintArtifact.getResourceAttached().getResourceInstanceName(), + blueprintArtifact.getBlueprintArtifactName()), newLoop, new JsonObject()))); } private HashSet<MicroServicePolicy> createMicroServicePolicies(List<MicroService> microServicesChain, - CsarHandler csar, BlueprintArtifact blueprintArtifact, Loop newLoop) throws IOException { + CsarHandler csar, BlueprintArtifact blueprintArtifact, Loop newLoop) throws IOException { HashSet<MicroServicePolicy> newSet = new HashSet<>(); for (MicroService microService : microServicesChain) { MicroServicePolicy microServicePolicy = new MicroServicePolicy( - Policy.generatePolicyName(microService.getName(), csar.getSdcNotification().getServiceName(), - csar.getSdcNotification().getServiceVersion(), - blueprintArtifact.getResourceAttached().getResourceInstanceName(), - blueprintArtifact.getBlueprintArtifactName()), - microService.getModelType(), csar.getPolicyModelYaml().orElse(""), false, - new HashSet<>(Arrays.asList(newLoop))); + Policy.generatePolicyName(microService.getName(), csar.getSdcNotification().getServiceName(), + csar.getSdcNotification().getServiceVersion(), + blueprintArtifact.getResourceAttached().getResourceInstanceName(), + blueprintArtifact.getBlueprintArtifactName()), + microService.getModelType(), csar.getPolicyModelYaml().orElse(""), false, + new HashSet<>(Arrays.asList(newLoop))); newSet.add(microServicePolicy); microService.setMappedNameJpa(microServicePolicy.getName()); @@ -191,8 +191,8 @@ public class LoopCsarInstaller implements CsarInstaller { // Loop on all Groups defined in the service (VFModule entries type: // org.openecomp.groups.VfModule) for (IEntityDetails entity : csar.getSdcCsarHelper().getEntity( - EntityQuery.newBuilder(EntityTemplateType.GROUP).build(), - TopologyTemplateQuery.newBuilder(SdcTypes.SERVICE).build(), false)) { + EntityQuery.newBuilder(EntityTemplateType.GROUP).build(), + TopologyTemplateQuery.newBuilder(SdcTypes.SERVICE).build(), false)) { // Get all metadata info JsonObject allVfProps = (JsonObject) JsonUtils.GSON.toJsonTree(entity.getMetadata().getAllProperties()); vfModuleProps.add(entity.getMetadata().getAllProperties().get("vfModuleModelName"), allVfProps); @@ -200,7 +200,7 @@ public class LoopCsarInstaller implements CsarInstaller { // volume_group, etc ... fields under the VFmodule name for (Entry<String, Property> additionalProp : entity.getProperties().entrySet()) { allVfProps.add(additionalProp.getValue().getName(), - JsonUtils.GSON.toJsonTree(additionalProp.getValue().getValue())); + JsonUtils.GSON.toJsonTree(additionalProp.getValue().getValue())); } } return vfModuleProps; @@ -214,7 +214,7 @@ public class LoopCsarInstaller implements CsarInstaller { // For each type, get the metadata of each nodetemplate for (NodeTemplate nodeTemplate : csar.getSdcCsarHelper().getServiceNodeTemplateBySdcType(type)) { resourcesPropByType.add(nodeTemplate.getName(), - JsonUtils.GSON.toJsonTree(nodeTemplate.getMetaData().getAllProperties())); + JsonUtils.GSON.toJsonTree(nodeTemplate.getMetaData().getAllProperties())); } resourcesProp.add(type.getValue(), resourcesPropByType); } @@ -225,7 +225,7 @@ public class LoopCsarInstaller implements CsarInstaller { JsonObject modelProperties = new JsonObject(); // Add service details modelProperties.add("serviceDetails", JsonUtils.GSON.fromJson( - JsonUtils.GSON.toJson(csar.getSdcCsarHelper().getServiceMetadataAllProperties()), JsonObject.class)); + JsonUtils.GSON.toJson(csar.getSdcCsarHelper().getServiceMetadataAllProperties()), JsonObject.class)); // Add properties details for each type, VfModule, VF, VFC, .... JsonObject resourcesProp = createServicePropertiesByType(csar); resourcesProp.add("VFModule", createVfModuleProperties(csar)); @@ -237,7 +237,7 @@ public class LoopCsarInstaller implements CsarInstaller { JsonObject node = new JsonObject(); Yaml yaml = new Yaml(); Map<String, Object> inputsNodes = ((Map<String, Object>) ((Map<String, Object>) yaml - .load(blueprintArtifact.getDcaeBlueprint())).get("inputs")); + .load(blueprintArtifact.getDcaeBlueprint())).get("inputs")); inputsNodes.entrySet().stream().filter(e -> !e.getKey().contains("policy_id")).forEach(elem -> { Object defaultValue = ((Map<String, Object>) elem.getValue()).get("default"); if (defaultValue != null) { @@ -258,10 +258,10 @@ public class LoopCsarInstaller implements CsarInstaller { * @return The DcaeInventoryResponse object containing the dcae values */ private DcaeInventoryResponse queryDcaeToGetServiceTypeId(BlueprintArtifact blueprintArtifact) - throws IOException, ParseException, InterruptedException { + throws IOException, ParseException, InterruptedException { return dcaeInventoryService.getDcaeInformation(blueprintArtifact.getBlueprintArtifactName(), - blueprintArtifact.getBlueprintInvariantServiceUuid(), - blueprintArtifact.getResourceAttached().getResourceInvariantUUID()); + blueprintArtifact.getBlueprintInvariantServiceUuid(), + blueprintArtifact.getResourceAttached().getResourceInvariantUUID()); } private void addPropertyToNode(JsonObject node, String key, Object value) { diff --git a/src/main/java/org/onap/clamp/policy/operational/LegacyOperationalPolicy.java b/src/main/java/org/onap/clamp/policy/operational/LegacyOperationalPolicy.java index 33148f0b..dd156d8f 100644 --- a/src/main/java/org/onap/clamp/policy/operational/LegacyOperationalPolicy.java +++ b/src/main/java/org/onap/clamp/policy/operational/LegacyOperationalPolicy.java @@ -25,6 +25,7 @@ package org.onap.clamp.policy.operational; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import java.util.ArrayList; import java.util.List; @@ -33,16 +34,15 @@ import java.util.Map.Entry; import java.util.TreeMap; import org.apache.commons.lang3.math.NumberUtils; +import org.onap.clamp.loop.Loop; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.DumperOptions.ScalarStyle; import org.yaml.snakeyaml.Yaml; /** - * * This class contains the code required to support the sending of Legacy * operational payload to policy engine. This will probably disappear in El * Alto. - * */ public class LegacyOperationalPolicy { @@ -76,6 +76,13 @@ public class LegacyOperationalPolicy { return jsonElement; } + /** + * This method rework the payload attribute (yaml) that is normally wrapped in a + * string when coming from the UI. + * + * @param policyJson The operational policy json config + * @return The same object reference but modified + */ public static JsonElement reworkPayloadAttributes(JsonElement policyJson) { for (JsonElement policy : policyJson.getAsJsonObject().get("policies").getAsJsonArray()) { JsonElement payloadElem = policy.getAsJsonObject().get("payload"); @@ -135,9 +142,15 @@ public class LegacyOperationalPolicy { return mapResult; } + /** + * This method transforms the configuration json to a Yaml format. + * + * @param operationalPolicyJsonElement The operational policy json config + * @return The Yaml as string + */ public static String createPolicyPayloadYamlLegacy(JsonElement operationalPolicyJsonElement) { JsonElement opPolicy = fulfillPoliciesTreeField( - removeAllQuotes(reworkPayloadAttributes(operationalPolicyJsonElement.getAsJsonObject().deepCopy()))); + removeAllQuotes(reworkPayloadAttributes(operationalPolicyJsonElement.getAsJsonObject().deepCopy()))); Map<?, ?> jsonMap = createMap(opPolicy); DumperOptions options = new DumperOptions(); options.setDefaultScalarStyle(ScalarStyle.PLAIN); @@ -147,4 +160,22 @@ public class LegacyOperationalPolicy { options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); return (new Yaml(options)).dump(jsonMap); } + + /** + * This method load mandatory field in the operational policy configuration + * JSON. + * + * @param configurationsJson The operational policy JSON + * @param loop The parent loop object + */ + public static void preloadConfiguration(JsonObject configurationsJson, Loop loop) { + if (configurationsJson.entrySet().isEmpty()) { + JsonObject controlLoopName = new JsonObject(); + controlLoopName.addProperty("controlLoopName", + loop != null ? loop.getName() : "Empty (NO loop loaded yet)"); + JsonObject controlLoop = new JsonObject(); + controlLoop.add("controlLoop", controlLoopName); + configurationsJson.add("operational_policy", controlLoop); + } + } } diff --git a/src/main/java/org/onap/clamp/policy/operational/OperationalPolicy.java b/src/main/java/org/onap/clamp/policy/operational/OperationalPolicy.java index 62c5a1e9..86f8ac39 100644 --- a/src/main/java/org/onap/clamp/policy/operational/OperationalPolicy.java +++ b/src/main/java/org/onap/clamp/policy/operational/OperationalPolicy.java @@ -38,7 +38,6 @@ import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; -import java.util.Map.Entry; import javax.persistence.Column; import javax.persistence.Entity; @@ -91,17 +90,16 @@ public class OperationalPolicy implements Serializable, Policy { /** * The constructor. * - * @param name - * The name of the operational policy - * @param loop - * The loop that uses this operational policy - * @param configurationsJson - * The operational policy property in the format of json + * @param name The name of the operational policy + * @param loop The loop that uses this operational policy + * @param configurationsJson The operational policy property in the format of + * json */ public OperationalPolicy(String name, Loop loop, JsonObject configurationsJson) { this.name = name; this.loop = loop; this.configurationsJson = configurationsJson; + LegacyOperationalPolicy.preloadConfiguration(this.configurationsJson, loop); } @Override @@ -117,11 +115,6 @@ public class OperationalPolicy implements Serializable, Policy { return loop; } - @Override - public JsonObject getJsonRepresentation() { - return configurationsJson; - } - public JsonObject getConfigurationsJson() { return configurationsJson; } @@ -131,6 +124,11 @@ public class OperationalPolicy implements Serializable, Policy { } @Override + public JsonObject getJsonRepresentation() { + return null; + } + + @Override public int hashCode() { final int prime = 31; int result = 1; @@ -184,7 +182,7 @@ public class OperationalPolicy implements Serializable, Policy { metadata.addProperty("policy-id", this.name); operationalPolicyDetails.add("properties", LegacyOperationalPolicy - .reworkPayloadAttributes(this.configurationsJson.get("operational_policy").deepCopy())); + .reworkPayloadAttributes(this.configurationsJson.get("operational_policy").deepCopy())); Gson gson = new GsonBuilder().create(); @@ -204,9 +202,8 @@ public class OperationalPolicy implements Serializable, Policy { // Now using the legacy payload fo Dublin JsonObject payload = new JsonObject(); payload.addProperty("policy-id", this.getName()); - payload.addProperty("content", URLEncoder.encode( - LegacyOperationalPolicy.createPolicyPayloadYamlLegacy(this.configurationsJson.get("operational_policy")), - StandardCharsets.UTF_8.toString())); + payload.addProperty("content", URLEncoder.encode(LegacyOperationalPolicy.createPolicyPayloadYamlLegacy( + this.configurationsJson.get("operational_policy")), StandardCharsets.UTF_8.toString())); String opPayload = new GsonBuilder().setPrettyPrinting().create().toJson(payload); logger.info("Operational policy payload: " + opPayload); return opPayload; @@ -222,11 +219,9 @@ public class OperationalPolicy implements Serializable, Policy { JsonElement guardsList = this.getConfigurationsJson().get("guard_policies"); if (guardsList != null) { - for (Entry<String, JsonElement> guardElem : guardsList.getAsJsonObject().entrySet()) { - JsonObject guard = new JsonObject(); - guard.addProperty("policy-id", guardElem.getKey()); - guard.add("content", guardElem.getValue()); - result.put(guardElem.getKey(), new GsonBuilder().create().toJson(guard)); + for (JsonElement guardElem : guardsList.getAsJsonArray()) { + result.put(guardElem.getAsJsonObject().get("policy-id").getAsString(), + new GsonBuilder().create().toJson(guardElem)); } } logger.info("Guard policy payload: " + result); diff --git a/src/main/java/org/onap/clamp/policy/operational/OperationalPolicyRepresentationBuilder.java b/src/main/java/org/onap/clamp/policy/operational/OperationalPolicyRepresentationBuilder.java new file mode 100644 index 00000000..f6f3f498 --- /dev/null +++ b/src/main/java/org/onap/clamp/policy/operational/OperationalPolicyRepresentationBuilder.java @@ -0,0 +1,146 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP CLAMP + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + * =================================================================== + * + */ + +package org.onap.clamp.policy.operational; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSyntaxException; + +import java.io.IOException; +import java.util.Map.Entry; + +import org.onap.clamp.clds.util.JsonUtils; +import org.onap.clamp.clds.util.ResourceFileUtil; + +public class OperationalPolicyRepresentationBuilder { + + /** + * This method generates the operational policy json representation that will be + * used by ui for rendering. It uses the model (VF and VFModule) defined in the + * loop object to do so, so it's dynamic. It also uses the operational policy + * schema template defined in the resource folder. + * + * @param modelJson The loop model json + * @return The json representation + * @throws JsonSyntaxException If the schema template cannot be parsed + * @throws IOException In case of issue when opening the schema template + */ + public static JsonObject generateOperationalPolicySchema(JsonObject modelJson) + throws JsonSyntaxException, IOException { + JsonObject jsonSchema = JsonUtils.GSON.fromJson( + ResourceFileUtil.getResourceAsString("clds/json-schema/operational_policies/operational_policy.json"), + JsonObject.class); + jsonSchema.get("schema").getAsJsonObject().get("items").getAsJsonObject().get("properties").getAsJsonObject() + .get("configurationsJson").getAsJsonObject().get("properties").getAsJsonObject() + .get("operational_policy").getAsJsonObject().get("properties").getAsJsonObject().get("policies") + .getAsJsonObject().get("items").getAsJsonObject().get("properties").getAsJsonObject().get("target") + .getAsJsonObject().get("anyOf").getAsJsonArray().addAll(createAnyOfArray(modelJson)); + return jsonSchema; + } + + private static JsonObject createSchemaProperty(String title, String type, String defaultValue, String readOnlyFlag, + String[] enumArray) { + JsonObject property = new JsonObject(); + property.addProperty("title", title); + property.addProperty("type", type); + property.addProperty("default", defaultValue); + property.addProperty("readOnly", readOnlyFlag); + + if (enumArray != null) { + JsonArray jsonArray = new JsonArray(); + property.add("enum", jsonArray); + for (String val : enumArray) { + jsonArray.add(val); + } + } + return property; + } + + private static JsonArray createVnfSchema(JsonObject modelJson) { + JsonArray vnfSchemaArray = new JsonArray(); + JsonObject modelVnfs = modelJson.get("resourceDetails").getAsJsonObject().get("VF").getAsJsonObject(); + + for (Entry<String, JsonElement> entry : modelVnfs.entrySet()) { + JsonObject vnfOneOfSchema = new JsonObject(); + vnfOneOfSchema.addProperty("title", "VNF" + "-" + entry.getKey()); + JsonObject properties = new JsonObject(); + properties.add("type", createSchemaProperty("Type", "string", "VNF", "True", null)); + properties.add("resourceID", createSchemaProperty("Resource ID", "string", + modelVnfs.get(entry.getKey()).getAsJsonObject().get("name").getAsString(), "True", null)); + + vnfOneOfSchema.add("properties", properties); + vnfSchemaArray.add(vnfOneOfSchema); + } + return vnfSchemaArray; + } + + private static JsonArray createVfModuleSchema(JsonObject modelJson) { + JsonArray vfModuleOneOfSchemaArray = new JsonArray(); + JsonObject modelVfModules = modelJson.get("resourceDetails").getAsJsonObject().get("VFModule") + .getAsJsonObject(); + + for (Entry<String, JsonElement> entry : modelVfModules.entrySet()) { + JsonObject vfModuleOneOfSchema = new JsonObject(); + vfModuleOneOfSchema.addProperty("title", "VFMODULE" + "-" + entry.getKey()); + JsonObject properties = new JsonObject(); + properties.add("type", createSchemaProperty("Type", "string", "VFMODULE", "True", null)); + properties.add("resourceID", + createSchemaProperty("Resource ID", "string", + modelVfModules.get(entry.getKey()).getAsJsonObject().get("vfModuleModelName").getAsString(), + "True", null)); + properties.add("modelInvariantId", + createSchemaProperty("Model Invariant Id (ModelInvariantUUID)", "string", modelVfModules + .get(entry.getKey()).getAsJsonObject().get("vfModuleModelInvariantUUID").getAsString(), + "True", null)); + properties.add("modelVersionId", + createSchemaProperty("Model Version Id (ModelUUID)", "string", + modelVfModules.get(entry.getKey()).getAsJsonObject().get("vfModuleModelUUID").getAsString(), + "True", null)); + properties.add("modelName", + createSchemaProperty("Model Name", "string", + modelVfModules.get(entry.getKey()).getAsJsonObject().get("vfModuleModelName").getAsString(), + "True", null)); + properties.add("modelVersion", createSchemaProperty("Model Version", "string", + modelVfModules.get(entry.getKey()).getAsJsonObject().get("vfModuleModelVersion").getAsString(), + "True", null)); + properties + .add("modelCustomizationId", + createSchemaProperty("Customization ID", "string", modelVfModules.get(entry.getKey()) + .getAsJsonObject().get("vfModuleModelCustomizationUUID").getAsString(), "True", + null)); + + vfModuleOneOfSchema.add("properties", properties); + vfModuleOneOfSchemaArray.add(vfModuleOneOfSchema); + } + return vfModuleOneOfSchemaArray; + } + + private static JsonArray createAnyOfArray(JsonObject modelJson) { + JsonArray targetOneOfStructure = new JsonArray(); + targetOneOfStructure.addAll(createVnfSchema(modelJson)); + targetOneOfStructure.addAll(createVfModuleSchema(modelJson)); + return targetOneOfStructure; + } +} diff --git a/src/main/resources/clds/json-schema/operational_policies/operational_policy.json b/src/main/resources/clds/json-schema/operational_policies/operational_policy.json new file mode 100644 index 00000000..93738c80 --- /dev/null +++ b/src/main/resources/clds/json-schema/operational_policies/operational_policy.json @@ -0,0 +1,362 @@ +{ + "schema": { + "uniqueItems": "true", + "format": "tabs", + "type": "array", + "minItems": 1, + "maxItems": 1, + "title": "Operational policies", + "items": { + "type": "object", + "title": "Operational Policy Item", + "id": "operational_policy_item", + "headerTemplate": "{{self.name}}", + "required": [ + "name", + "configurationsJson" + ], + "properties": { + "name": { + "type": "string", + "title": "Operational policy name", + "readOnly": "True" + }, + "configurationsJson": { + "type": "object", + "title": "Configuration", + "required": [ + "operational_policy", + "guard_policies" + ], + "properties": { + "operational_policy": { + "type": "object", + "title": "Related Parameters", + "required": [ + "controlLoop", + "policies" + ], + "properties": { + "controlLoop": { + "type": "object", + "title": "Control Loop details", + "required": [ + "timeout", + "abatement", + "trigger_policy", + "controlLoopName" + ], + "properties": { + "timeout": { + "type": "string", + "title": "Overall Time Limit", + "default": "0", + "format": "number" + }, + "abatement": { + "type": "string", + "title": "Abatement", + "enum": [ + "True", + "False" + ] + }, + "trigger_policy": { + "type": "string", + "title": "Policy Decision Entry" + }, + "controlLoopName": { + "type": "string", + "title": "Control loop name", + "readOnly": "True" + } + } + }, + "policies": { + "uniqueItems": "true", + "id": "policies_array", + "type": "array", + "title": "Policy Decision Tree", + "format": "tabs-top", + "items": { + "title": "Policy Decision", + "type": "object", + "id": "policy_item", + "headerTemplate": "{{self.id}} - {{self.recipe}}", + "format": "categories", + "basicCategoryTitle": "recipe", + "required": [ + "id", + "recipe", + "retry", + "timeout", + "actor", + "success", + "failure", + "failure_timeout", + "failure_retries", + "failure_exception", + "failure_guard", + "target" + ], + "properties": { + "id": { + "default": "Policy 1", + "title": "Policy ID", + "type": "string" + }, + "recipe": { + "title": "Recipe", + "type": "string", + "enum": [ + "Restart", + "Rebuild", + "Migrate", + "Health-Check", + "ModifyConfig", + "VF Module Create", + "VF Module Delete", + "Reroute" + ] + }, + "retry": { + "default": "0", + "title": "Number of Retry", + "type": "string", + "format": "number" + }, + "timeout": { + "default": "0", + "title": "Timeout", + "type": "string", + "format": "number" + }, + "actor": { + "title": "Actor", + "type": "string", + "enum": [ + "APPC", + "SO", + "VFC", + "SDNC", + "SDNR" + ] + }, + "payload": { + "title": "Payload (YAML)", + "type": "string", + "format": "textarea" + }, + "success": { + "default": "final_success", + "title": "When Success", + "type": "string" + }, + "failure": { + "default": "final_failure", + "title": "When Failure", + "type": "string" + }, + "failure_timeout": { + "default": "final_failure_timeout", + "title": "When Failure Timeout", + "type": "string" + }, + "failure_retries": { + "default": "final_failure_retries", + "title": "When Failure Retries", + "type": "string" + }, + "failure_exception": { + "default": "final_failure_exception", + "title": "When Failure Exception", + "type": "string" + }, + "failure_guard": { + "default": "final_failure_guard", + "title": "When Failure Guard", + "type": "string" + }, + "target": { + "type": "object", + "required": [ + "type", + "resourceID" + ], + "anyOf": [ + { + "title": "User Defined", + "additionalProperties":"True", + "properties": { + "type": { + "title": "Target type", + "type": "string", + "default": "", + "enum": [ + "VNF", + "VFMODULE", + "VM" + ] + }, + "resourceID": { + "title": "Target type", + "type": "string", + "default": "" + } + } + } + ] + } + } + } + } + } + }, + "guard_policies": { + "type": "array", + "format": "tabs-top", + "title": "Associated Guard policies", + "items": { + "headerTemplate": "{{self.policy-id}} - {{self.content.recipe}}", + "anyOf": [ + { + "title": "Guard MinMax", + "type": "object", + "properties": { + "policy-id": { + "type": "string", + "default": "guard.minmax.new", + "pattern": "^(guard.minmax\\..*)$" + }, + "content": { + "properties": { + "actor": { + "type": "string", + "enum": [ + "APPC", + "SO", + "VFC", + "SDNC", + "SDNR" + ] + }, + "recipe": { + "type": "string", + "enum": [ + "Restart", + "Rebuild", + "Migrate", + "Health-Check", + "ModifyConfig", + "VF Module Create", + "VF Module Delete", + "Reroute" + ] + }, + "targets": { + "type": "string", + "default": ".*" + }, + "clname": { + "type": "string", + "template": "{{loopName}}", + "watch": { + "loopName": "operational_policy_item.configurationsJson.operational_policy.controlLoop.controlLoopName" + } + }, + "guardActiveStart": { + "type": "string", + "default": "00:00:00Z" + }, + "guardActiveEnd": { + "type": "string", + "default": "10:00:00Z" + }, + "min": { + "type": "string", + "default": "0" + }, + "max": { + "type": "string", + "default": "1" + } + } + } + } + }, + { + "title": "Guard Frequency", + "type": "object", + "properties": { + "policy-id": { + "type": "string", + "default": "guard.frequency.new", + "pattern": "^(guard.frequency\\..*)$" + }, + "content": { + "properties": { + "actor": { + "type": "string", + "enum": [ + "APPC", + "SO", + "VFC", + "SDNC", + "SDNR" + ] + }, + "recipe": { + "type": "string", + "enum": [ + "Restart", + "Rebuild", + "Migrate", + "Health-Check", + "ModifyConfig", + "VF Module Create", + "VF Module Delete", + "Reroute" + ] + }, + "targets": { + "type": "string", + "default": ".*" + }, + "clname": { + "type": "string", + "template": "{{loopName}}", + "watch": { + "loopName": "operational_policy_item.configurationsJson.operational_policy.controlLoop.controlLoopName" + } + }, + "guardActiveStart": { + "type": "string", + "default": "00:00:00Z" + }, + "guardActiveEnd": { + "type": "string", + "default": "10:00:00Z" + }, + "limit": { + "type": "string" + }, + "timeWindow": { + "type": "string" + }, + "timeUnits": { + "type": "string", + "enum":["minute","hour","day","week","month","year"] + } + } + } + } + } + ] + } + } + } + } + } + } + } +} diff --git a/src/test/java/org/onap/clamp/policy/microservice/OperationalPolicyPayloadTest.java b/src/test/java/org/onap/clamp/policy/microservice/OperationalPolicyPayloadTest.java index 8972e511..728b61cc 100644 --- a/src/test/java/org/onap/clamp/policy/microservice/OperationalPolicyPayloadTest.java +++ b/src/test/java/org/onap/clamp/policy/microservice/OperationalPolicyPayloadTest.java @@ -42,29 +42,29 @@ public class OperationalPolicyPayloadTest { @Test public void testOperationalPolicyPayloadConstruction() throws IOException { JsonObject jsonConfig = new GsonBuilder().create().fromJson( - ResourceFileUtil.getResourceAsString("tosca/operational-policy-properties.json"), JsonObject.class); + ResourceFileUtil.getResourceAsString("tosca/operational-policy-properties.json"), JsonObject.class); OperationalPolicy policy = new OperationalPolicy("testPolicy", null, jsonConfig); assertThat(policy.createPolicyPayloadYaml()) - .isEqualTo(ResourceFileUtil.getResourceAsString("tosca/operational-policy-payload.yaml")); + .isEqualTo(ResourceFileUtil.getResourceAsString("tosca/operational-policy-payload.yaml")); assertThat(policy.createPolicyPayload()) - .isEqualTo(ResourceFileUtil.getResourceAsString("tosca/operational-policy-payload.json")); + .isEqualTo(ResourceFileUtil.getResourceAsString("tosca/operational-policy-payload.json")); } @Test public void testLegacyOperationalPolicyPayloadConstruction() throws IOException { JsonObject jsonConfig = new GsonBuilder().create().fromJson( - ResourceFileUtil.getResourceAsString("tosca/operational-policy-properties.json"), JsonObject.class); + ResourceFileUtil.getResourceAsString("tosca/operational-policy-properties.json"), JsonObject.class); assertThat(LegacyOperationalPolicy.createPolicyPayloadYamlLegacy(jsonConfig.get("operational_policy"))) - .isEqualTo(ResourceFileUtil.getResourceAsString("tosca/operational-policy-payload-legacy.yaml")); + .isEqualTo(ResourceFileUtil.getResourceAsString("tosca/operational-policy-payload-legacy.yaml")); } @Test public void testGuardPolicyEmptyPayloadConstruction() throws IOException { JsonObject jsonConfig = new GsonBuilder().create().fromJson( - ResourceFileUtil.getResourceAsString("tosca/operational-policy-no-guard-properties.json"), - JsonObject.class); + ResourceFileUtil.getResourceAsString("tosca/operational-policy-no-guard-properties.json"), + JsonObject.class); OperationalPolicy policy = new OperationalPolicy("testPolicy", null, jsonConfig); Map<String, String> guardsMap = policy.createGuardPolicyPayloads(); assertThat(guardsMap).isEmpty(); @@ -74,15 +74,15 @@ public class OperationalPolicyPayloadTest { @Test public void testGuardPolicyPayloadConstruction() throws IOException { JsonObject jsonConfig = new GsonBuilder().create().fromJson( - ResourceFileUtil.getResourceAsString("tosca/operational-policy-properties.json"), JsonObject.class); + ResourceFileUtil.getResourceAsString("tosca/operational-policy-properties.json"), JsonObject.class); OperationalPolicy policy = new OperationalPolicy("testPolicy", null, jsonConfig); Map<String, String> guardsMap = policy.createGuardPolicyPayloads(); JSONAssert.assertEquals(ResourceFileUtil.getResourceAsString("tosca/guard1-policy-payload.json"), - guardsMap.get("guard1"), false); + guardsMap.get("guard.minmax.new"), false); JSONAssert.assertEquals(ResourceFileUtil.getResourceAsString("tosca/guard2-policy-payload.json"), - guardsMap.get("guard2"), false); + guardsMap.get("guard.frequency.new"), false); } } diff --git a/src/test/java/org/onap/clamp/policy/operational/OperationalPolicyRepresentationBuilderTest.java b/src/test/java/org/onap/clamp/policy/operational/OperationalPolicyRepresentationBuilderTest.java new file mode 100644 index 00000000..904525be --- /dev/null +++ b/src/test/java/org/onap/clamp/policy/operational/OperationalPolicyRepresentationBuilderTest.java @@ -0,0 +1,52 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP CLAMP + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + * =================================================================== + * + */ + +package org.onap.clamp.policy.operational; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; + +import java.io.IOException; + +import org.junit.Test; +import org.onap.clamp.clds.util.ResourceFileUtil; +import org.skyscreamer.jsonassert.JSONAssert; + +public class OperationalPolicyRepresentationBuilderTest { + + @Test + public void testOperationalPolicyPayloadConstruction() throws IOException { + JsonObject jsonModel = new GsonBuilder().create() + .fromJson(ResourceFileUtil.getResourceAsString("tosca/model-properties.json"), JsonObject.class); + + JsonObject jsonSchema = OperationalPolicyRepresentationBuilder.generateOperationalPolicySchema(jsonModel); + + assertThat(jsonSchema).isNotNull(); + + JSONAssert.assertEquals(ResourceFileUtil.getResourceAsString("tosca/operational-policy-json-schema.json"), + new GsonBuilder().create().toJson(jsonSchema), false); + } + +} diff --git a/src/test/resources/clds/OperationalPolicyRepresentationBuilderTest.java b/src/test/resources/clds/OperationalPolicyRepresentationBuilderTest.java new file mode 100644 index 00000000..904525be --- /dev/null +++ b/src/test/resources/clds/OperationalPolicyRepresentationBuilderTest.java @@ -0,0 +1,52 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP CLAMP + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + * =================================================================== + * + */ + +package org.onap.clamp.policy.operational; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; + +import java.io.IOException; + +import org.junit.Test; +import org.onap.clamp.clds.util.ResourceFileUtil; +import org.skyscreamer.jsonassert.JSONAssert; + +public class OperationalPolicyRepresentationBuilderTest { + + @Test + public void testOperationalPolicyPayloadConstruction() throws IOException { + JsonObject jsonModel = new GsonBuilder().create() + .fromJson(ResourceFileUtil.getResourceAsString("tosca/model-properties.json"), JsonObject.class); + + JsonObject jsonSchema = OperationalPolicyRepresentationBuilder.generateOperationalPolicySchema(jsonModel); + + assertThat(jsonSchema).isNotNull(); + + JSONAssert.assertEquals(ResourceFileUtil.getResourceAsString("tosca/operational-policy-json-schema.json"), + new GsonBuilder().create().toJson(jsonSchema), false); + } + +} diff --git a/src/test/resources/tosca/guard1-policy-payload.json b/src/test/resources/tosca/guard1-policy-payload.json index b4e0809c..1c03df30 100644 --- a/src/test/resources/tosca/guard1-policy-payload.json +++ b/src/test/resources/tosca/guard1-policy-payload.json @@ -1,16 +1,13 @@ { - "policy-id": "guard1", + "policy-id": "guard.minmax.new", "content": { - "recipe": "Rebuild", - "actor": "SO", - "clname": "testloop", + "actor": "APPC", + "recipe": "Restart", "targets": ".*", - "min": "3", - "max": "7", - "limit": "", - "timeUnits": "", - "timeWindow": "", - "guardActiveStart": "00:00:01-05:00", - "guardActiveEnd": "23:59:01-05:00" + "clname": "LOOP_ASJOy_v1_0_ResourceInstanceName1_tca", + "guardActiveStart": "00:00:00Z", + "guardActiveEnd": "10:00:00Z", + "min": "0", + "max": "1" } }
\ No newline at end of file diff --git a/src/test/resources/tosca/guard2-policy-payload.json b/src/test/resources/tosca/guard2-policy-payload.json index 29beb6b9..559a5684 100644 --- a/src/test/resources/tosca/guard2-policy-payload.json +++ b/src/test/resources/tosca/guard2-policy-payload.json @@ -1,16 +1,14 @@ { - "policy-id": "guard2", + "policy-id": "guard.frequency.new", "content": { - "recipe": "Migrate", - "actor": "SO", - "clname": "testloop", + "actor": "APPC", + "recipe": "Rebuild", "targets": ".*", - "min": "1", - "max": "2", - "limit": "", - "timeUnits": "", - "timeWindow": "", - "guardActiveStart": "00:00:01-05:00", - "guardActiveEnd": "23:59:01-05:00" + "clname": "LOOP_ASJOy_v1_0_ResourceInstanceName1_tca", + "guardActiveStart": "00:00:00Z", + "guardActiveEnd": "10:00:00Z", + "limit": "1", + "timeWindow": "2", + "timeUnits": "minute" } }
\ No newline at end of file diff --git a/src/test/resources/tosca/operational-policy-json-schema.json b/src/test/resources/tosca/operational-policy-json-schema.json new file mode 100644 index 00000000..d6870dc9 --- /dev/null +++ b/src/test/resources/tosca/operational-policy-json-schema.json @@ -0,0 +1,574 @@ +{ + "schema": { + "uniqueItems": "true", + "format": "tabs", + "type": "array", + "minItems": 1, + "maxItems": 1, + "title": "Operational policies", + "items": { + "type": "object", + "title": "Operational Policy Item", + "id": "operational_policy_item", + "headerTemplate": "{{self.name}}", + "required": [ + "name", + "configurationsJson" + ], + "properties": { + "name": { + "type": "string", + "title": "Operational policy name", + "readOnly": "True" + }, + "configurationsJson": { + "type": "object", + "title": "Configuration", + "required": [ + "operational_policy", + "guard_policies" + ], + "properties": { + "operational_policy": { + "type": "object", + "title": "Related Parameters", + "required": [ + "controlLoop", + "policies" + ], + "properties": { + "controlLoop": { + "type": "object", + "title": "Control Loop details", + "required": [ + "timeout", + "abatement", + "trigger_policy", + "controlLoopName" + ], + "properties": { + "timeout": { + "type": "string", + "title": "Overall Time Limit", + "default": "0", + "format": "number" + }, + "abatement": { + "type": "string", + "title": "Abatement", + "enum": [ + "True", + "False" + ] + }, + "trigger_policy": { + "type": "string", + "title": "Policy Decision Entry" + }, + "controlLoopName": { + "type": "string", + "title": "Control loop name", + "readOnly": "True" + } + } + }, + "policies": { + "uniqueItems": "true", + "id": "policies_array", + "type": "array", + "title": "Policy Decision Tree", + "format": "tabs-top", + "items": { + "title": "Policy Decision", + "type": "object", + "id": "policy_item", + "headerTemplate": "{{self.id}} - {{self.recipe}}", + "format": "categories", + "basicCategoryTitle": "recipe", + "required": [ + "id", + "recipe", + "retry", + "timeout", + "actor", + "success", + "failure", + "failure_timeout", + "failure_retries", + "failure_exception", + "failure_guard", + "target" + ], + "properties": { + "id": { + "default": "Policy 1", + "title": "Policy ID", + "type": "string" + }, + "recipe": { + "title": "Recipe", + "type": "string", + "enum": [ + "Restart", + "Rebuild", + "Migrate", + "Health-Check", + "ModifyConfig", + "VF Module Create", + "VF Module Delete", + "Reroute" + ] + }, + "retry": { + "default": "0", + "title": "Number of Retry", + "type": "string", + "format": "number" + }, + "timeout": { + "default": "0", + "title": "Timeout", + "type": "string", + "format": "number" + }, + "actor": { + "title": "Actor", + "type": "string", + "enum": [ + "APPC", + "SO", + "VFC", + "SDNC", + "SDNR" + ] + }, + "payload": { + "title": "Payload (YAML)", + "type": "string", + "format": "textarea" + }, + "success": { + "default": "final_success", + "title": "When Success", + "type": "string" + }, + "failure": { + "default": "final_failure", + "title": "When Failure", + "type": "string" + }, + "failure_timeout": { + "default": "final_failure_timeout", + "title": "When Failure Timeout", + "type": "string" + }, + "failure_retries": { + "default": "final_failure_retries", + "title": "When Failure Retries", + "type": "string" + }, + "failure_exception": { + "default": "final_failure_exception", + "title": "When Failure Exception", + "type": "string" + }, + "failure_guard": { + "default": "final_failure_guard", + "title": "When Failure Guard", + "type": "string" + }, + "target": { + "type": "object", + "required": [ + "type", + "resourceID" + ], + "anyOf": [ + { + "title": "User Defined", + "additionalProperties": "True", + "properties": { + "type": { + "title": "Target type", + "type": "string", + "default": "", + "enum": [ + "VNF", + "VFMODULE", + "VM" + ] + }, + "resourceID": { + "title": "Target type", + "type": "string", + "default": "" + } + } + }, + { + "title": "VNF-vLoadBalancerMS 0", + "properties": { + "type": { + "title": "Type", + "type": "string", + "default": "VNF", + "readOnly": "True" + }, + "resourceID": { + "title": "Resource ID", + "type": "string", + "default": "vLoadBalancerMS", + "readOnly": "True" + } + } + }, + { + "title": "VFMODULE-Vloadbalancerms..vpkg..module-1", + "properties": { + "type": { + "title": "Type", + "type": "string", + "default": "VFMODULE", + "readOnly": "True" + }, + "resourceID": { + "title": "Resource ID", + "type": "string", + "default": "Vloadbalancerms..vpkg..module-1", + "readOnly": "True" + }, + "modelInvariantId": { + "title": "Model Invariant Id (ModelInvariantUUID)", + "type": "string", + "default": "ca052563-eb92-4b5b-ad41-9111768ce043", + "readOnly": "True" + }, + "modelVersionId": { + "title": "Model Version Id (ModelUUID)", + "type": "string", + "default": "1e725ccc-b823-4f67-82b9-4f4367070dbc", + "readOnly": "True" + }, + "modelName": { + "title": "Model Name", + "type": "string", + "default": "Vloadbalancerms..vpkg..module-1", + "readOnly": "True" + }, + "modelVersion": { + "title": "Model Version", + "type": "string", + "default": "1", + "readOnly": "True" + }, + "modelCustomizationId": { + "title": "Customization ID", + "type": "string", + "default": "1bffdc31-a37d-4dee-b65c-dde623a76e52", + "readOnly": "True" + } + } + }, + { + "title": "VFMODULE-Vloadbalancerms..vdns..module-3", + "properties": { + "type": { + "title": "Type", + "type": "string", + "default": "VFMODULE", + "readOnly": "True" + }, + "resourceID": { + "title": "Resource ID", + "type": "string", + "default": "Vloadbalancerms..vdns..module-3", + "readOnly": "True" + }, + "modelInvariantId": { + "title": "Model Invariant Id (ModelInvariantUUID)", + "type": "string", + "default": "4c10ba9b-f88f-415e-9de3-5d33336047fa", + "readOnly": "True" + }, + "modelVersionId": { + "title": "Model Version Id (ModelUUID)", + "type": "string", + "default": "4fa73b49-8a6c-493e-816b-eb401567b720", + "readOnly": "True" + }, + "modelName": { + "title": "Model Name", + "type": "string", + "default": "Vloadbalancerms..vdns..module-3", + "readOnly": "True" + }, + "modelVersion": { + "title": "Model Version", + "type": "string", + "default": "1", + "readOnly": "True" + }, + "modelCustomizationId": { + "title": "Customization ID", + "type": "string", + "default": "bafcdab0-801d-4d81-9ead-f464640a38b1", + "readOnly": "True" + } + } + }, + { + "title": "VFMODULE-Vloadbalancerms..base_template..module-0", + "properties": { + "type": { + "title": "Type", + "type": "string", + "default": "VFMODULE", + "readOnly": "True" + }, + "resourceID": { + "title": "Resource ID", + "type": "string", + "default": "Vloadbalancerms..base_template..module-0", + "readOnly": "True" + }, + "modelInvariantId": { + "title": "Model Invariant Id (ModelInvariantUUID)", + "type": "string", + "default": "921f7c96-ebdd-42e6-81b9-1cfc0c9796f3", + "readOnly": "True" + }, + "modelVersionId": { + "title": "Model Version Id (ModelUUID)", + "type": "string", + "default": "63734409-f745-4e4d-a38b-131638a0edce", + "readOnly": "True" + }, + "modelName": { + "title": "Model Name", + "type": "string", + "default": "Vloadbalancerms..base_template..module-0", + "readOnly": "True" + }, + "modelVersion": { + "title": "Model Version", + "type": "string", + "default": "1", + "readOnly": "True" + }, + "modelCustomizationId": { + "title": "Customization ID", + "type": "string", + "default": "86baddea-c730-4fb8-9410-cd2e17fd7f27", + "readOnly": "True" + } + } + }, + { + "title": "VFMODULE-Vloadbalancerms..vlb..module-2", + "properties": { + "type": { + "title": "Type", + "type": "string", + "default": "VFMODULE", + "readOnly": "True" + }, + "resourceID": { + "title": "Resource ID", + "type": "string", + "default": "Vloadbalancerms..vlb..module-2", + "readOnly": "True" + }, + "modelInvariantId": { + "title": "Model Invariant Id (ModelInvariantUUID)", + "type": "string", + "default": "a772a1f4-0064-412c-833d-4749b15828dd", + "readOnly": "True" + }, + "modelVersionId": { + "title": "Model Version Id (ModelUUID)", + "type": "string", + "default": "0f5c3f6a-650a-4303-abb6-fff3e573a07a", + "readOnly": "True" + }, + "modelName": { + "title": "Model Name", + "type": "string", + "default": "Vloadbalancerms..vlb..module-2", + "readOnly": "True" + }, + "modelVersion": { + "title": "Model Version", + "type": "string", + "default": "1", + "readOnly": "True" + }, + "modelCustomizationId": { + "title": "Customization ID", + "type": "string", + "default": "96a78aad-4ffb-4ef0-9c4f-deb03bf1d806", + "readOnly": "True" + } + } + } + ] + } + } + } + } + } + }, + "guard_policies": { + "type": "array", + "format": "tabs-top", + "title": "Associated Guard policies", + "items": { + "headerTemplate": "{{self.policy-id}} - {{self.content.recipe}}", + "anyOf": [ + { + "title": "Guard MinMax", + "type": "object", + "properties": { + "policy-id": { + "type": "string", + "default": "guard.minmax.new", + "pattern": "^(guard.minmax\\..*)$" + }, + "content": { + "properties": { + "actor": { + "type": "string", + "enum": [ + "APPC", + "SO", + "VFC", + "SDNC", + "SDNR" + ] + }, + "recipe": { + "type": "string", + "enum": [ + "Restart", + "Rebuild", + "Migrate", + "Health-Check", + "ModifyConfig", + "VF Module Create", + "VF Module Delete", + "Reroute" + ] + }, + "targets": { + "type": "string", + "default": ".*" + }, + "clname": { + "type": "string", + "template": "{{loopName}}", + "watch": { + "loopName": "operational_policy_item.configurationsJson.operational_policy.controlLoop.controlLoopName" + } + }, + "guardActiveStart": { + "type": "string", + "default": "00:00:00Z" + }, + "guardActiveEnd": { + "type": "string", + "default": "10:00:00Z" + }, + "min": { + "type": "string", + "default": "0" + }, + "max": { + "type": "string", + "default": "1" + } + } + } + } + }, + { + "title": "Guard Frequency", + "type": "object", + "properties": { + "policy-id": { + "type": "string", + "default": "guard.frequency.new", + "pattern": "^(guard.frequency\\..*)$" + }, + "content": { + "properties": { + "actor": { + "type": "string", + "enum": [ + "APPC", + "SO", + "VFC", + "SDNC", + "SDNR" + ] + }, + "recipe": { + "type": "string", + "enum": [ + "Restart", + "Rebuild", + "Migrate", + "Health-Check", + "ModifyConfig", + "VF Module Create", + "VF Module Delete", + "Reroute" + ] + }, + "targets": { + "type": "string", + "default": ".*" + }, + "clname": { + "type": "string", + "template": "{{loopName}}", + "watch": { + "loopName": "operational_policy_item.configurationsJson.operational_policy.controlLoop.controlLoopName" + } + }, + "guardActiveStart": { + "type": "string", + "default": "00:00:00Z" + }, + "guardActiveEnd": { + "type": "string", + "default": "10:00:00Z" + }, + "limit": { + "type": "string" + }, + "timeWindow": { + "type": "string" + }, + "timeUnits": { + "type": "string", + "enum": [ + "minute", + "hour", + "day", + "week", + "month", + "year" + ] + } + } + } + } + } + ] + } + } + } + } + } + } + } +}
\ No newline at end of file diff --git a/src/test/resources/tosca/operational-policy-payload-legacy.yaml b/src/test/resources/tosca/operational-policy-payload-legacy.yaml index 41184c9c..4d2b9f36 100644 --- a/src/test/resources/tosca/operational-policy-payload-legacy.yaml +++ b/src/test/resources/tosca/operational-policy-payload-legacy.yaml @@ -1,39 +1,43 @@ controlLoop: abatement: true - controlLoopName: control loop - timeout: 30 - trigger_policy: new1 - version: 2.0.0 + controlLoopName: LOOP_ASJOy_v1_0_ResourceInstanceName1_tca + timeout: 0 + trigger_policy: policy1 policies: -- actor: SO - failure: new2 - failure_exception: new2 - failure_guard: new2 - failure_retries: new2 - failure_timeout: new2 - id: new1 +- actor: APPC + failure: policy2 + failure_exception: final_failure_exception + failure_guard: final_failure_guard + failure_retries: final_failure_retries + failure_timeout: final_failure_timeout + id: policy1 payload: configurationParameters: '[{"ip-addr":"$.vf-module-topology.vf-module-parameters.param[10].value","oam-ip-addr":"$.vf-module-topology.vf-module-parameters.param[15].value","enabled":"$.vf-module-topology.vf-module-parameters.param[22].value"}]' requestParameters: '{"usePreload":true,"userParams":[]}' - recipe: Rebuild - retry: 10 - success: new2 + recipe: Restart + retry: 0 + success: final_success target: - resourceTargetId: test - type: VFC - timeout: 20 -- actor: SDNC + resourceID: vLoadBalancerMS + type: VNF + timeout: 0 +- actor: SO failure: final_failure failure_exception: final_failure_exception failure_guard: final_failure_guard failure_retries: final_failure_retries failure_timeout: final_failure_timeout - id: new2 + id: policy2 payload: '' - recipe: Migrate - retry: 30 + recipe: VF Module Create + retry: 0 success: final_success target: - resourceTargetId: test - type: VFC - timeout: 40 + modelCustomizationId: 1bffdc31-a37d-4dee-b65c-dde623a76e52 + modelInvariantId: ca052563-eb92-4b5b-ad41-9111768ce043 + modelName: Vloadbalancerms..vpkg..module-1 + modelVersion: 1 + modelVersionId: 1e725ccc-b823-4f67-82b9-4f4367070dbc + resourceID: Vloadbalancerms..vpkg..module-1 + type: VFMODULE + timeout: 0 diff --git a/src/test/resources/tosca/operational-policy-payload.json b/src/test/resources/tosca/operational-policy-payload.json index 5097654d..ed019760 100644 --- a/src/test/resources/tosca/operational-policy-payload.json +++ b/src/test/resources/tosca/operational-policy-payload.json @@ -1,4 +1,4 @@ { "policy-id": "testPolicy", - "content": "controlLoop%3A%0A++abatement%3A+true%0A++controlLoopName%3A+control+loop%0A++timeout%3A+30%0A++trigger_policy%3A+new1%0A++version%3A+2.0.0%0Apolicies%3A%0A-+actor%3A+SO%0A++failure%3A+new2%0A++failure_exception%3A+new2%0A++failure_guard%3A+new2%0A++failure_retries%3A+new2%0A++failure_timeout%3A+new2%0A++id%3A+new1%0A++payload%3A%0A++++configurationParameters%3A+%27%5B%7B%22ip-addr%22%3A%22%24.vf-module-topology.vf-module-parameters.param%5B10%5D.value%22%2C%22oam-ip-addr%22%3A%22%24.vf-module-topology.vf-module-parameters.param%5B15%5D.value%22%2C%22enabled%22%3A%22%24.vf-module-topology.vf-module-parameters.param%5B22%5D.value%22%7D%5D%27%0A++++requestParameters%3A+%27%7B%22usePreload%22%3Atrue%2C%22userParams%22%3A%5B%5D%7D%27%0A++recipe%3A+Rebuild%0A++retry%3A+10%0A++success%3A+new2%0A++target%3A%0A++++resourceTargetId%3A+test%0A++++type%3A+VFC%0A++timeout%3A+20%0A-+actor%3A+SDNC%0A++failure%3A+final_failure%0A++failure_exception%3A+final_failure_exception%0A++failure_guard%3A+final_failure_guard%0A++failure_retries%3A+final_failure_retries%0A++failure_timeout%3A+final_failure_timeout%0A++id%3A+new2%0A++payload%3A+%27%27%0A++recipe%3A+Migrate%0A++retry%3A+30%0A++success%3A+final_success%0A++target%3A%0A++++resourceTargetId%3A+test%0A++++type%3A+VFC%0A++timeout%3A+40%0A" + "content": "controlLoop%3A%0A++abatement%3A+true%0A++controlLoopName%3A+LOOP_ASJOy_v1_0_ResourceInstanceName1_tca%0A++timeout%3A+0%0A++trigger_policy%3A+policy1%0Apolicies%3A%0A-+actor%3A+APPC%0A++failure%3A+policy2%0A++failure_exception%3A+final_failure_exception%0A++failure_guard%3A+final_failure_guard%0A++failure_retries%3A+final_failure_retries%0A++failure_timeout%3A+final_failure_timeout%0A++id%3A+policy1%0A++payload%3A%0A++++configurationParameters%3A+%27%5B%7B%22ip-addr%22%3A%22%24.vf-module-topology.vf-module-parameters.param%5B10%5D.value%22%2C%22oam-ip-addr%22%3A%22%24.vf-module-topology.vf-module-parameters.param%5B15%5D.value%22%2C%22enabled%22%3A%22%24.vf-module-topology.vf-module-parameters.param%5B22%5D.value%22%7D%5D%27%0A++++requestParameters%3A+%27%7B%22usePreload%22%3Atrue%2C%22userParams%22%3A%5B%5D%7D%27%0A++recipe%3A+Restart%0A++retry%3A+0%0A++success%3A+final_success%0A++target%3A%0A++++resourceID%3A+vLoadBalancerMS%0A++++type%3A+VNF%0A++timeout%3A+0%0A-+actor%3A+SO%0A++failure%3A+final_failure%0A++failure_exception%3A+final_failure_exception%0A++failure_guard%3A+final_failure_guard%0A++failure_retries%3A+final_failure_retries%0A++failure_timeout%3A+final_failure_timeout%0A++id%3A+policy2%0A++payload%3A+%27%27%0A++recipe%3A+VF+Module+Create%0A++retry%3A+0%0A++success%3A+final_success%0A++target%3A%0A++++modelCustomizationId%3A+1bffdc31-a37d-4dee-b65c-dde623a76e52%0A++++modelInvariantId%3A+ca052563-eb92-4b5b-ad41-9111768ce043%0A++++modelName%3A+Vloadbalancerms..vpkg..module-1%0A++++modelVersion%3A+1%0A++++modelVersionId%3A+1e725ccc-b823-4f67-82b9-4f4367070dbc%0A++++resourceID%3A+Vloadbalancerms..vpkg..module-1%0A++++type%3A+VFMODULE%0A++timeout%3A+0%0A" }
\ No newline at end of file diff --git a/src/test/resources/tosca/operational-policy-payload.yaml b/src/test/resources/tosca/operational-policy-payload.yaml index c3a6b5c2..ed03842f 100644 --- a/src/test/resources/tosca/operational-policy-payload.yaml +++ b/src/test/resources/tosca/operational-policy-payload.yaml @@ -8,35 +8,45 @@ topology_template: policy-id: testPolicy properties: controlLoop: - controlLoopName: control loop - version: 2.0.0 - trigger_policy: new1 - timeout: '30' - abatement: 'true' + timeout: '0' + abatement: 'True' + trigger_policy: policy1 + controlLoopName: LOOP_ASJOy_v1_0_ResourceInstanceName1_tca policies: - - id: new1 - recipe: Rebuild - retry: '10' - timeout: '20' - actor: SO + - id: policy1 + recipe: Restart + retry: '0' + timeout: '0' + actor: APPC payload: requestParameters: '{"usePreload":true,"userParams":[]}' configurationParameters: '[{"ip-addr":"$.vf-module-topology.vf-module-parameters.param[10].value","oam-ip-addr":"$.vf-module-topology.vf-module-parameters.param[15].value","enabled":"$.vf-module-topology.vf-module-parameters.param[22].value"}]' - success: new2 - failure: new2 - failure_timeout: new2 - failure_retries: new2 - failure_exception: new2 - failure_guard: new2 + success: final_success + failure: policy2 + failure_timeout: final_failure_timeout + failure_retries: final_failure_retries + failure_exception: final_failure_exception + failure_guard: final_failure_guard target: - type: VFC - resourceTargetId: test - - id: new2 - recipe: Migrate - retry: '30' - timeout: '40' - actor: SDNC + type: VNF + resourceID: vLoadBalancerMS + - id: policy2 + recipe: VF Module Create + retry: '0' + timeout: '0' + actor: SO payload: '' + success: final_success + failure: final_failure + failure_timeout: final_failure_timeout + failure_retries: final_failure_retries + failure_exception: final_failure_exception + failure_guard: final_failure_guard target: - type: VFC - resourceTargetId: test + type: VFMODULE + resourceID: Vloadbalancerms..vpkg..module-1 + modelInvariantId: ca052563-eb92-4b5b-ad41-9111768ce043 + modelVersionId: 1e725ccc-b823-4f67-82b9-4f4367070dbc + modelName: Vloadbalancerms..vpkg..module-1 + modelVersion: '1' + modelCustomizationId: 1bffdc31-a37d-4dee-b65c-dde623a76e52 diff --git a/src/test/resources/tosca/operational-policy-properties.json b/src/test/resources/tosca/operational-policy-properties.json index bfce6b33..ac1314ec 100644 --- a/src/test/resources/tosca/operational-policy-properties.json +++ b/src/test/resources/tosca/operational-policy-properties.json @@ -1,71 +1,82 @@ { - "guard_policies": { - "guard1":{ - "recipe": "Rebuild", - "actor": "SO", - "clname": "testloop", - "targets": ".*", - "min": "3", - "max": "7", - "limit": "", - "timeUnits": "", - "timeWindow": "", - "guardActiveStart": "00:00:01-05:00", - "guardActiveEnd": "23:59:01-05:00" - }, - "guard2":{ - "recipe": "Migrate", - "actor": "SO", - "clname": "testloop", - "targets": ".*", - "min": "1", - "max": "2", - "limit": "", - "timeUnits": "", - "timeWindow": "", - "guardActiveStart": "00:00:01-05:00", - "guardActiveEnd": "23:59:01-05:00" - } - }, - "operational_policy": { - "controlLoop": { - "controlLoopName": "control loop", - "version": "2.0.0", - "trigger_policy": "new1", - "timeout": "30", - "abatement": "true" - }, - "policies": [ - { - "id": "new1", - "recipe": "Rebuild", - "retry": "10", - "timeout": "20", - "actor": "SO", - "payload": "requestParameters: '{\"usePreload\":true,\"userParams\":[]}'\r\nconfigurationParameters: '[{\"ip-addr\":\"$.vf-module-topology.vf-module-parameters.param[10].value\",\"oam-ip-addr\":\"$.vf-module-topology.vf-module-parameters.param[15].value\",\"enabled\":\"$.vf-module-topology.vf-module-parameters.param[22].value\"}]'", - "success": "new2", - "failure": "new2", - "failure_timeout": "new2", - "failure_retries": "new2", - "failure_exception": "new2", - "failure_guard": "new2", - "target": { - "type": "VFC", - "resourceTargetId": "test" - } - }, - { - "id": "new2", - "recipe": "Migrate", - "retry": "30", - "timeout": "40", - "actor": "SDNC", - "payload": "", - "target": { - "type": "VFC", - "resourceTargetId": "test" - } - } - ] - } + "operational_policy": { + "controlLoop": { + "timeout": "0", + "abatement": "True", + "trigger_policy": "policy1", + "controlLoopName": "LOOP_ASJOy_v1_0_ResourceInstanceName1_tca" + }, + "policies": [ + { + "id": "policy1", + "recipe": "Restart", + "retry": "0", + "timeout": "0", + "actor": "APPC", + "payload": "requestParameters: '{\"usePreload\":true,\"userParams\":[]}'\r\nconfigurationParameters: '[{\"ip-addr\":\"$.vf-module-topology.vf-module-parameters.param[10].value\",\"oam-ip-addr\":\"$.vf-module-topology.vf-module-parameters.param[15].value\",\"enabled\":\"$.vf-module-topology.vf-module-parameters.param[22].value\"}]'", + "success": "final_success", + "failure": "policy2", + "failure_timeout": "final_failure_timeout", + "failure_retries": "final_failure_retries", + "failure_exception": "final_failure_exception", + "failure_guard": "final_failure_guard", + "target": { + "type": "VNF", + "resourceID": "vLoadBalancerMS" + } + }, + { + "id": "policy2", + "recipe": "VF Module Create", + "retry": "0", + "timeout": "0", + "actor": "SO", + "payload": "", + "success": "final_success", + "failure": "final_failure", + "failure_timeout": "final_failure_timeout", + "failure_retries": "final_failure_retries", + "failure_exception": "final_failure_exception", + "failure_guard": "final_failure_guard", + "target": { + "type": "VFMODULE", + "resourceID": "Vloadbalancerms..vpkg..module-1", + "modelInvariantId": "ca052563-eb92-4b5b-ad41-9111768ce043", + "modelVersionId": "1e725ccc-b823-4f67-82b9-4f4367070dbc", + "modelName": "Vloadbalancerms..vpkg..module-1", + "modelVersion": "1", + "modelCustomizationId": "1bffdc31-a37d-4dee-b65c-dde623a76e52" + } + } + ] + }, + "guard_policies": [ + { + "policy-id": "guard.minmax.new", + "content": { + "actor": "APPC", + "recipe": "Restart", + "targets": ".*", + "clname": "LOOP_ASJOy_v1_0_ResourceInstanceName1_tca", + "guardActiveStart": "00:00:00Z", + "guardActiveEnd": "10:00:00Z", + "min": "0", + "max": "1" + } + }, + { + "policy-id": "guard.frequency.new", + "content": { + "actor": "APPC", + "recipe": "Rebuild", + "targets": ".*", + "clname": "LOOP_ASJOy_v1_0_ResourceInstanceName1_tca", + "guardActiveStart": "00:00:00Z", + "guardActiveEnd": "10:00:00Z", + "limit": "1", + "timeWindow": "2", + "timeUnits": "minute" + } + } + ] } diff --git a/src/test/resources/tosca/pdp-group-policy-payload.json b/src/test/resources/tosca/pdp-group-policy-payload.json index bf941e51..ad3a5b4c 100644 --- a/src/test/resources/tosca/pdp-group-policy-payload.json +++ b/src/test/resources/tosca/pdp-group-policy-payload.json @@ -4,10 +4,10 @@ "policy-id": "GuardOpPolicyTest" }, { - "policy-id": "guard2" + "policy-id": "guard.minmax.new" }, { - "policy-id": "guard1" + "policy-id": "guard.frequency.new" }, { "policy-id": "configPolicyTest" diff --git a/ui-react/package.json b/ui-react/package.json index de7cb26d..9c94ccc3 100644 --- a/ui-react/package.json +++ b/ui-react/package.json @@ -13,7 +13,7 @@ "author": "ONAP Clamp Team", "license": "Apache-2.0", "dependencies": { - "@json-editor/json-editor": "1.3.5", + "@json-editor/json-editor": "1.4.0-beta.0", "react": "16.8.0", "react-dom": "16.8.0", "react-scripts": "3.0.1", diff --git a/ui-react/src/LoopUI.js b/ui-react/src/LoopUI.js index 5b8283f8..b64cfbaa 100644 --- a/ui-react/src/LoopUI.js +++ b/ui-react/src/LoopUI.js @@ -41,9 +41,9 @@ import ConfigurationPolicyModal from './components/dialogs/ConfigurationPolicy/C import LoopProperties from './components/dialogs/LoopProperties'; import UserInfo from './components/dialogs/UserInfo'; import LoopService from './api/LoopService'; -import PerformAction from './components/menu/PerformActions'; -import RefreshStatus from './components/menu/RefreshStatus'; -import DeployLoop from './components/menu/DeployLoop'; +import PerformAction from './components/dialogs/PerformActions'; +import RefreshStatus from './components/dialogs/RefreshStatus'; +import DeployLoop from './components/dialogs/DeployLoop'; const ProjectNameStyled = styled.a` vertical-align: middle; @@ -185,6 +185,7 @@ export default class LoopUI extends React.Component { this.setState({ loopCache: new LoopCache({}), loopName: LoopUI.defaultLoopName }); this.props.history.push('/'); } + render() { return ( <div id="main_div"> diff --git a/ui-react/src/api/LoopCache.js b/ui-react/src/api/LoopCache.js index 3ee5acc6..d83e3ce9 100644 --- a/ui-react/src/api/LoopCache.js +++ b/ui-react/src/api/LoopCache.js @@ -51,6 +51,10 @@ export default class LoopCache { getOperationalPolicyConfigurationJson() { return this.loopJsonCache["operationalPolicies"]["0"]["configurationsJson"]; } + + getOperationalPolicyJsonSchema() { + return this.loopJsonCache["operationalPolicySchema"]; + } getOperationalPolicies() { return this.loopJsonCache["operationalPolicies"]; diff --git a/ui-react/src/api/LoopService.js b/ui-react/src/api/LoopService.js index 031ec638..eece20c9 100644 --- a/ui-react/src/api/LoopService.js +++ b/ui-react/src/api/LoopService.js @@ -104,6 +104,30 @@ export default class LoopService { return ""; }); } + + static setOperationalPolicyProperties(loopName, jsonData) { + return fetch('/restservices/clds/v2/loop/updateOperationalPolicies/' + loopName, { + method: 'POST', + credentials: 'same-origin', + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(jsonData), + }) + .then(function (response) { + console.debug("updateOperationalPolicies response received: ", response.status); + if (response.ok) { + return response.text(); + } else { + console.error("updateOperationalPolicies query failed"); + return ""; + } + }) + .catch(function (error) { + console.error("updateOperationalPolicies error received", error); + return ""; + }); + } static updateGlobalProperties(loopName, jsonData) { return fetch('/restservices/clds/v2/loop/updateGlobalProperties/' + loopName, { diff --git a/ui-react/src/components/dialogs/ConfigurationPolicy/ConfigurationPolicyModal.js b/ui-react/src/components/dialogs/ConfigurationPolicy/ConfigurationPolicyModal.js index 4fbb7832..9863ef72 100644 --- a/ui-react/src/components/dialogs/ConfigurationPolicy/ConfigurationPolicyModal.js +++ b/ui-react/src/components/dialogs/ConfigurationPolicy/ConfigurationPolicyModal.js @@ -103,7 +103,7 @@ export default class ConfigurationPolicyModal extends React.Component { render() { return ( - <ModalStyled size="lg" show={this.state.show} onHide={this.handleClose}> + <ModalStyled size="xl" show={this.state.show} onHide={this.handleClose}> <Modal.Header closeButton> <Modal.Title>Configuration policies</Modal.Title> </Modal.Header> diff --git a/ui-react/src/components/dialogs/OperationalPolicy/OperationalPolicyModal.js b/ui-react/src/components/dialogs/OperationalPolicy/OperationalPolicyModal.js index 2a812c87..6db38fd2 100644 --- a/ui-react/src/components/dialogs/OperationalPolicy/OperationalPolicyModal.js +++ b/ui-react/src/components/dialogs/OperationalPolicy/OperationalPolicyModal.js @@ -24,8 +24,9 @@ import React from 'react' import Button from 'react-bootstrap/Button'; import Modal from 'react-bootstrap/Modal'; -import './OperationalPolicy.css' import styled from 'styled-components'; +import LoopService from '../../../api/LoopService'; +import JSONEditor from '@json-editor/json-editor'; const ModalStyled = styled(Modal)` background-color: transparent; @@ -36,512 +37,94 @@ export default class OperationalPolicyModal extends React.Component { state = { show: true, loopCache: this.props.loopCache, + jsonEditor: null, }; - allPolicies = []; - policyIds = []; - constructor(props, context) { super(props, context); - this.handleClose = this.handleClose.bind(this); - this.initPolicySelect = this.initPolicySelect.bind(this); - this.initPolicySelect(); + this.handleSave = this.handleSave.bind(this); + this.renderJsonEditor = this.renderJsonEditor.bind(this); + this.setDefaultJsonEditorOptions(); } - handleClose() { - this.setState({ show: false }); - this.props.history.push('/') - } + handleSave() { + var errors = this.state.jsonEditor.validate(); + var editorData = this.state.jsonEditor.getValue(); - initPolicySelect() { - if (this.allPolicies['operational_policy'] === undefined || this.allPolicies['operational_policy'] === null) { - this.allPolicies = this.state.loopCache.getOperationalPolicyConfigurationJson(); + if (errors.length !== 0) { + console.error("Errors detected during config policy data validation ", errors); + alert(errors); } - // Provision all policies ID first - if (this.policyIds.length === 0 && this.allPolicies['operational_policy'] !== undefined) { - - for (let i = 0; i < this.allPolicies['operational_policy']['policies'].length; i++) { - this.policyIds.push(this.allPolicies['operational_policy']['policies'][i]['id']); - } + else { + console.info("NO validation errors found in config policy data"); + this.state.loopCache.updateOperationalPolicyProperties(editorData); + LoopService.setOperationalPolicyProperties(this.state.loopCache.getLoopName(), this.state.loopCache.getOperationalPolicies()).then(resp => { + this.setState({ show: false }); + this.props.history.push('/'); + this.props.loadLoopFunction(this.state.loopCache.getLoopName()); + }); } } - renderPolicyIdSelect() { - return ( - <select type="text" id="trigger_policy" name="trigger_policy" - className="form-control"> - <option value="">-- choose an option --</option> - {this.policyIds.map(policyId => (<option key={policyId}>{policyId}</option>))} - </select> - ); + handleClose() { + this.setState({ show: false }); + this.props.history.push('/'); } - serializeElement(element) { - var o = {}; - element.serializeArray().forEach(function () { - if (o[this.name]) { - if (!o[this.name].push) { - o[this.name] = [o[this.name]]; - } - o[this.name].push(this.value || ''); - } else { - o[this.name] = this.value || ''; - } - }); - return o; + componentDidMount() { + this.renderJsonEditor(); } - // When we change the name of a policy - isDuplicatedId(event) { - // update policy id structure - var formNum = document.getElementById(event.target).closest('.formId').attr('id').substring(6); - var policyId = document.getElementById(event.target).val(); - if (this.policyIds.includes(policyId)) { - console.log("Duplicated ID, cannot proceed"); - return true; - } else { - this.duplicated = false; - this.policyIds.splice(this.policyIds.indexOf(document.getElementById("#formId" + formNum + " #id").val()), 1); - this.policyIds.push(document.getElementById(event.target).val()); - // Update the tab now - document.getElementById("#go_properties_tab" + formNum).text(document.getElementById(event.target).val()); - } + setDefaultJsonEditorOptions() { + JSONEditor.defaults.options.theme = 'bootstrap4'; + // JSONEditor.defaults.options.iconlib = 'bootstrap2'; + + JSONEditor.defaults.options.object_layout = 'grid'; + JSONEditor.defaults.options.disable_properties = true; + JSONEditor.defaults.options.disable_edit_json = false; + JSONEditor.defaults.options.disable_array_reorder = true; + JSONEditor.defaults.options.disable_array_delete_last_row = true; + JSONEditor.defaults.options.disable_array_delete_all_rows = false; + JSONEditor.defaults.options.array_controls_top=true; + JSONEditor.defaults.options.show_errors = 'always'; + JSONEditor.defaults.options.keep_oneof_values=false; + JSONEditor.defaults.options.ajax=true; + JSONEditor.defaults.options.collapsed=true; + //JSONEditor.defaults.options.template = 'default'; } + + renderJsonEditor() { + console.debug("Rendering OperationalPolicyModal"); + var schema_json = this.state.loopCache.getOperationalPolicyJsonSchema(); + + if (schema_json == null) { + console.error("NO Operational policy schema found"); + return; + } + var operationalPoliciesData = this.state.loopCache.getOperationalPolicies(); - configureComponents() { - console.log("Load properties to op policy"); - // Set the header - document.getElementsByClassName('form-control').forEach(function () { - this.val(this.allPolicies['operational_policy']['controlLoop'][this.id]); - }); - // Set the sub-policies - this.allPolicies['operational_policy']['policies'].forEach(function (opPolicyElemIndex, opPolicyElemValue) { - - /* var formNum = add_one_more(); - forEach(document.getElementsByClassName('policyProperties').find('.form-control'), function(opPolicyPropIndex, opPolicyPropValue) { - - $("#formId" + formNum + " .policyProperties").find("#" + opPolicyPropValue.id).val( - allPolicies['operational_policy']['policies'][opPolicyElemIndex][opPolicyPropValue.id]); - }); - - // Initial TargetResourceId options - initTargetResourceIdOptions(allPolicies['operational_policy']['policies'][opPolicyElemIndex]['target']['type'], formNum); - $.each($('.policyTarget').find('.form-control'), function(opPolicyTargetPropIndex, opPolicyTargetPropValue) { - - $("#formId" + formNum + " .policyTarget").find("#" + opPolicyTargetPropValue.id).val( - allPolicies['operational_policy']['policies'][opPolicyElemIndex]['target'][opPolicyTargetPropValue.id]); - }); - - // update the current tab label - $("#go_properties_tab" + formNum).text( - allPolicies['operational_policy']['policies'][opPolicyElemIndex]['id']); - // Check if there is a guard set for it - $.each(allPolicies['guard_policies'], function(guardElemId, guardElemValue) { - - if (guardElemValue.recipe === $($("#formId" + formNum + " #recipe")[0]).val()) { - // Found one, set all guard prop - $.each($('.guardProperties').find('.form-control'), function(guardPropElemIndex, - guardPropElemValue) { - - guardElemValue['id'] = guardElemId; - $("#formId" + formNum + " .guardProperties").find("#" + guardPropElemValue.id).val( - guardElemValue[guardPropElemValue.id]); - }); - iniGuardPolicyType(guardElemId, formNum); - // And finally enable the flag - $("#formId" + formNum + " #enableGuardPolicy").prop("checked", true); - } - });*/ - }); + this.setState({ + jsonEditor: new JSONEditor(document.getElementById("editor"), + { schema: schema_json.schema, startval: operationalPoliciesData }) + }) } render() { return ( - <ModalStyled size="lg" show={this.state.show} onHide={this.handleClose}> + <ModalStyled size="xl" show={this.state.show} onHide={this.handleClose}> <Modal.Header closeButton> <Modal.Title>Operational policies</Modal.Title> </Modal.Header> <Modal.Body> - <div attribute-test="policywindowproperties" id="configure-widgets" - className="disabled-block-container"> - <div attribute-test="policywindowpropertiesb" className="modal-body row"> - <div className="panel panel-default col-sm-10 policyPanel"> - <form id="operationalPolicyHeaderForm" className="form-horizontal"> - <div className="form-group clearfix"> - <label className="col-sm-2">Parent policy</label> - <div className="col-sm-3" style={{ padding: '0px' }}> - {this.renderPolicyIdSelect()} - </div> - - <label htmlFor="timeout" className="col-sm-3" - style={{ paddingLeft: '5px', paddingRight: '10px' }}>Overall - Time Limit</label> - <div className="col-sm-2" style={{ paddingLeft: '0px' }}> - <input type="text" ng-pattern="/^[0-9]*$/" ng-model="number" - className="form-control" id="timeout" name="timeout" /> - </div> - - <label htmlFor="abatement" className="col-sm-2">Abatement</label> - <div className="col-sm-2" style={{ paddingLeft: '0px' }}> - <select className="form-control" id="abatement" name="abatement"> - <option value="false">False</option> - <option value="true">True</option> - </select> - </div> - </div> - <div className="form-group clearfix row"> - <label className="col-sm-4 control-label" htmlFor="clname">ControlLoopName</label> - <div className="col-sm-8"> - <input type="text" className="form-control" name="controlLoopName" - readOnly="readonly" id="clname" value={this.state.loopCache.getLoopName()} /> - </div> - </div> - </form> - <div className="panel-heading" style={{ backgroundColor: 'white' }}> - <ul id="nav_Tabs" className="nav nav-tabs"> - <li> - <a id="add_one_more" href="#desc_tab"> - <span - className="glyphicon glyphicon-plus" aria-hidden="true"> - </span> - </a> - </li> - </ul> - </div> - <div className="panel-body"> - <div className="tab-content"> - <div id="properties_tab" className="tab-pane fade in active"></div> - </div> - </div> - </div> - - <span id="formSpan" style={{ display: 'none' }}> - <form className="policyProperties form-horizontal" - style={{ border: '2px dotted gray' }} - title="Operational Policy Properties"> - <div className="form-group clearfix"> - <label className="col-sm-4 control-label" htmlFor="id">ID</label> - <div className="col-sm-8"> - <input type="text" className="form-control" name="id" id="id" - onKeyUp="updateTabLabel($event)" /> - <span >ID must be unique</span> - </div> - </div> - <div className="form-group clearfix"> - <label className="col-sm-4 control-label" htmlFor="recipe">Recipe</label> - <div className="col-sm-8"> - <select className="form-control" name="recipe" id="recipe" - ng-model="recipe" ng-click="updateGuardRecipe($event)"> - <option value="">-- choose an option --</option> - <option value="Restart">Restart</option> - <option value="Rebuild">Rebuild</option> - <option value="Migrate">Migrate</option> - <option value="Health-Check">Health-Check</option> - <option value="ModifyConfig">ModifyConfig</option> - <option value="VF Module Create">VF Module Create</option> - <option value="VF Module Delete">VF Module Delete</option> - <option value="Reroute">Reroute</option> - </select> - </div> - </div> - <div className="form-group clearfix"> - <label htmlFor="retry" className="col-sm-4 control-label"> Retry</label> - <div className="col-sm-8"> - <input type="text" maxLength="5" className="form-control" id="retry" - ng-pattern="/^[0-9]*$/" ng-model="number" name="retry"> - </input> - </div> - </div> - <div className="form-group clearfix"> - <label htmlFor="timeout" className="col-sm-4 control-label"> - Timeout</label> - <div className="col-sm-8"> - <input type="text" maxLength="5" className="form-control" - id="timeout" ng-pattern="/^[0-9]*$/" ng-model="number" - name="timeout"></input> - </div> - </div> - - <div className="form-group clearfix"> - <label htmlFor="actor" className="col-sm-4 control-label"> Actor</label> - <div className="col-sm-8"> - <select className="form-control" id="actor" name="actor" ng-click="updateGuardActor($event)" ng-model="actor"> - <option value="">-- choose an option --</option> - <option value="APPC">APPC</option> - <option value="SO">SO</option> - <option value="VFC">VFC</option> - <option value="SDNC">SDNC</option>° - <option value="SDNR">SDNR</option>° - </select> - </div> - - <label htmlFor="payload" className="col-sm-4 control-label"> - Payload (YAML)</label> - <div className="col-sm-8"> - <textarea className="form-control" id="payload" name="payload"></textarea> - </div> - </div> - <div className="form-group clearfix"> - <label htmlFor="success" className="col-sm-4 control-label">When - Success</label> - <div className="col-sm-8"> - <select className="form-control" id="success" name="success" - ng-model="null_dump" - ng-options="policy for policy in policy_ids track by policy"> - <option value="">-- choose an option --</option> - </select> - </div> - </div> - <div className="form-group clearfix"> - <label htmlFor="failure" className="col-sm-4 control-label">When - Failure</label> - <div className="col-sm-8"> - <select className="form-control" id="failure" name="failure" - ng-model="null_dump" - ng-options="policy for policy in policy_ids track by policy"> - <option value="">-- choose an option --</option> - </select> - - </div> - </div> - <div className="form-group clearfix"> - <label htmlFor="failure_timeout" className="col-sm-4 control-label">When - Failure Timeout</label> - <div className="col-sm-8"> - <select className="form-control" id="failure_timeout" - name="failure_timeout" ng-model="null_dump" - ng-options="policy for policy in policy_ids track by policy"> - <option value="">-- choose an option --</option> - </select> - </div> - </div> - <div className="form-group clearfix"> - <label htmlFor="failure_retries" className="col-sm-4 control-label">When - Failure Retries</label> - <div className="col-sm-8"> - <select className="form-control" id="failure_retries" - name="failure_retries" ng-model="null_dump" - ng-options="policy for policy in policy_ids track by policy"> - <option value="">-- choose an option --</option> - </select> - </div> - </div> - <div className="form-group clearfix"> - <label htmlFor="failure_exception" className="col-sm-4 control-label">When - Failure Exception</label> - <div className="col-sm-8"> - <select className="form-control" id="failure_exception" - name="failure_exception" ng-model="null_dump" - ng-options="policy for policy in policy_ids track by policy"> - <option value="">-- choose an option --</option> - </select> - </div> - </div> - <div className="form-group clearfix"> - <label htmlFor="failure_guard" className="col-sm-4 control-label">When - Failure Guard</label> - <div className="col-sm-8"> - <select className="form-control" id="failure_guard" - name="failure_guard" ng-model="null_dump" - ng-options="policy for policy in policy_ids track by policy"> - <option value="">-- choose an option --</option> - </select> - </div> - </div> - </form> - <form className="policyTarget form-horizontal" - title="Operational Policy Target" style={{ border: '2px dotted gray' }}> - <div className="form-group clearfix"> - <label htmlFor="type" className="col-sm-4 control-label"> Target - Type</label> - <div className="col-sm-8"> - <select className="form-control" name="type" id="type" - ng-click="initTargetResourceId($event)" ng-model="type"> - <option value="">-- choose an option --</option> - <option value="VFMODULE">VFMODULE</option> - <option value="VM">VM</option> - <option value="VNF">VNF</option> - </select> - </div> - </div> - <div className="form-group clearfix"> - <label htmlFor="resourceID" className="col-sm-4 control-label"> - Target ResourceId</label> - <div className="col-sm-8"> - <select className="form-control" name="resourceID" id="resourceID" - ng-click="changeTargetResourceId($event)" - ng-model="resourceId"> - <option value="">-- choose an option --</option> - </select> - </div> - </div> - <div id="metadata"> - <div className="form-group clearfix"> - <label htmlFor="modelInvariantId" className="col-sm-4 control-label"> - Model Invariant Id</label> - <div className="col-sm-8"> - <input className="form-control" name="modelInvariantId" - id="modelInvariantId" readOnly /> - </div> - </div> - <div className="form-group clearfix"> - <label htmlFor="modelVersionId" className="col-sm-4 control-label"> - Model Version Id</label> - <div className="col-sm-8"> - <input className="form-control" name="modelVersionId" - id="modelVersionId" readOnly /> - </div> - </div> - <div className="form-group clearfix"> - <label htmlFor="modelName" className="col-sm-4 control-label"> - Model Name</label> - <div className="col-sm-8"> - <input className="form-control" name="modelName" id="modelName" - readOnly /> - </div> - </div> - <div className="form-group clearfix"> - <label htmlFor="modelVersion" className="col-sm-4 control-label"> - Model Version</label> - <div className="col-sm-8"> - <input className="form-control" name="modelVersion" - id="modelVersion" readOnly /> - </div> - </div> - <div className="form-group clearfix"> - <label htmlFor="modelCustomizationId" className="col-sm-4 control-label"> - Model Customization Id</label> - <div className="col-sm-8"> - <input className="form-control" name="modelCustomizationId" - id="modelCustomizationId" readOnly /> - </div> - </div> - </div> - </form> - <div className="form-group clearfix"> - <label htmlFor="enableGuardPolicy" className="col-sm-4 control-label"> - Enable Guard Policy</label> - <div className="col-sm-8"> - <input type="checkbox" className="form-control" - name="enableGuardPolicy" id="enableGuardPolicy" /> - </div> - - <div className="col-sm-8"> - <label htmlFor="guardPolicyType" className="col-sm-4 control-label"> - Guard Policy Type</label> <select className="form-control" - name="guardPolicyType" id="guardPolicyType" - ng-click="changeGuardPolicyType()" ng-model="guardType"> - <option value="GUARD_MIN_MAX">MinMax</option> - <option value="GUARD_YAML">FrequencyLimiter</option> - </select> - </div> - </div> - <form className="guardProperties form-horizontal" - title="Guard policy associated" style={{ border: '2px dotted gray' }}> - - <div className="form-group clearfix withnote"> - <label className="col-sm-4 control-label" htmlFor="id">Guard Policy ID</label> - <div className="col-sm-8"> - <input type="text" className="form-control" name="id" id="id" ng-blur="changeGuardId()" ng-model="id" /> - </div> - </div> - <div> - <label className="form-group note">Note: Prefix will be added to Guard Policy ID automatically based on Guard Policy Type</label> - </div> - <div className="form-group clearfix"> - <label className="col-sm-4 control-label" htmlFor="recipe">Recipe</label> - <div className="col-sm-8"> - <input type="text" className="form-control" name="recipe" - readOnly="readonly" id="recipe" /> - </div> - </div> - <div className="form-group clearfix"> - <label className="col-sm-4 control-label" htmlFor="clname">ControlLoopName</label> - <div className="col-sm-8"> - <input type="text" className="form-control" name="clname" - readOnly="readonly" id="clname" ng-model="clname" /> - </div> - </div> - <div className="form-group clearfix"> - <label htmlFor="actor" className="col-sm-4 control-label">Actor</label> - <div className="col-sm-8"> - <input type="text" className="form-control" name="actor" - readOnly="readonly" id="actor" /> - </div> - </div> - <div className="form-group clearfix"> - - <label htmlFor="targets" className="col-sm-4 control-label">Guard - targets</label> - <div className="col-sm-8"> - <input className="form-control" name="targets" id="targets" /> - </div> - </div> - - <div className="form-group clearfix" id="minMaxGuardPolicyDiv"> - <label htmlFor="min" className="col-sm-4 control-label"> Min - Guard</label> - <div className="col-sm-8"> - <input className="form-control" name="min" id="min" /> - </div> - <label htmlFor="max" className="col-sm-4 control-label"> Max - Guard</label> - <div className="col-sm-8"> - <input className="form-control" name="max" id="max" /> - </div> - </div> - <div className="form-group clearfix" - id="frequencyLimiterGuardPolicyDiv" style={{ display: 'none' }}> - <label htmlFor="limit" className="col-sm-4 control-label">Limit</label> - <div className="col-sm-8"> - <input className="form-control" name="limit" id="limit" /> - </div> - <label htmlFor="timeUnits" className="col-sm-4 control-label">Time Units</label> - <div className="col-sm-8"> - <select className="form-control" name="timeUnits" - id="timeUnits"> - <option value=""></option> - <option value="minute">minute</option> - <option value="hour">hour</option> - <option value="day">day</option> - <option value="week">week</option> - <option value="month">month</option> - <option value="year">year</option> - </select> - </div> - <label htmlFor="timeWindow" className="col-sm-4 control-label">Time Window</label> - <div className="col-sm-8"> - <input className="form-control" name="timeWindow" id="timeWindow" /> - </div> - </div> - <div className="form-group clearfix"> - <label htmlFor="guardActiveStart" className="col-sm-4 control-label"> - Guard Active Start</label> - <div className="col-sm-8"> - <input className="form-control" name="guardActiveStart" - id="guardActiveStart" value="00:00:00Z" /> - </div> - <label htmlFor="guardActiveEnd" className="col-sm-4 control-label"> - Guard Active End</label> - <div className="col-sm-8"> - <input className="form-control" name="guardActiveEnd" - id="guardActiveEnd" value="00:00:01Z" /> - </div> - </div> - - </form> - - </span> - </div> - </div> + <div id="editor" /> </Modal.Body> <Modal.Footer> <Button variant="secondary" onClick={this.handleClose}> Close </Button> - <Button variant="primary" onClick={this.handleClose}> + <Button variant="primary" onClick={this.handleSave}> Save Changes </Button> </Modal.Footer> diff --git a/ui-react/src/index.js b/ui-react/src/index.js index 39df3642..cbbdc65e 100644 --- a/ui-react/src/index.js +++ b/ui-react/src/index.js @@ -32,7 +32,7 @@ const routing = ( </BrowserRouter> ); -ReactDOM.render( +export var mainClamp = ReactDOM.render( routing, document.getElementById('root') ) diff --git a/ui-react/src/theme/globalStyle.js b/ui-react/src/theme/globalStyle.js index cbd86b19..0f6fb91c 100644 --- a/ui-react/src/theme/globalStyle.js +++ b/ui-react/src/theme/globalStyle.js @@ -65,6 +65,19 @@ export const GlobalClampStyle = createGlobalStyle` width: 100%; height: 100%; } + + button { + background-color: ${props => (props.theme.loopViewerHeaderBackgroundColor)}; + border: 1px; + color: white; + padding: 15px 32px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: ${props => props.theme.fontSize}; + font-family: ${props => props.theme.fontFamily}; + + } ` export const DefaultClampTheme = { |