diff --git a/common/artifact-manager/src/main/java/com/vmware/pscoe/iac/artifact/aria/operations/rest/RestClientVrops.java b/common/artifact-manager/src/main/java/com/vmware/pscoe/iac/artifact/aria/operations/rest/RestClientVrops.java index 2dbbe24a1..1960e19e7 100644 --- a/common/artifact-manager/src/main/java/com/vmware/pscoe/iac/artifact/aria/operations/rest/RestClientVrops.java +++ b/common/artifact-manager/src/main/java/com/vmware/pscoe/iac/artifact/aria/operations/rest/RestClientVrops.java @@ -61,6 +61,7 @@ import com.vmware.pscoe.iac.artifact.aria.operations.models.AlertDefinitionDTO; import com.vmware.pscoe.iac.artifact.aria.operations.models.AlertDefinitionDTO.AlertDefinition.State; import com.vmware.pscoe.iac.artifact.aria.operations.models.AlertDefinitionDTO.AlertDefinition.SymptomSet; +import com.vmware.pscoe.iac.artifact.aria.operations.models.SupermetricDTO.SuperMetric; import com.vmware.pscoe.iac.artifact.aria.operations.models.AuthGroupDTO; import com.vmware.pscoe.iac.artifact.aria.operations.models.AuthGroupsDTO; import com.vmware.pscoe.iac.artifact.aria.operations.models.AuthUserDTO; @@ -99,10 +100,18 @@ public class RestClientVrops extends RestClient { * ALERT_DEFS_API. */ private static final String ALERT_DEFS_API = PUBLIC_API_PREFIX + "alertdefinitions/"; + /** + * GET_ALL_ALERT_DEFS_API. + */ + private static final String GET_ALL_ALERT_DEFS_API = PUBLIC_API_PREFIX + "alertdefinitions"; /** * SYMPTOM_DEFS_API. */ private static final String SYMPTOM_DEFS_API = PUBLIC_API_PREFIX + "symptomdefinitions/"; + /** + * GET_ALL_SYMPTOM_DEFS_API. + */ + private static final String GET_ALL_SYMPTOM_DEFS_API = PUBLIC_API_PREFIX + "symptomdefinitions"; /** * POLICIES_API. */ @@ -151,6 +160,10 @@ public class RestClientVrops extends RestClient { * RECOMMENDATIONS_API. */ private static final String RECOMMENDATIONS_API = PUBLIC_API_PREFIX + "recommendations/"; + /** + * GET_ALL_RECOMMENDATIONS_API. + */ + private static final String GET_ALL_RECOMMENDATIONS_API = PUBLIC_API_PREFIX + "recommendations"; /** * RESOURCES_API. */ @@ -726,6 +739,74 @@ public void applyPolicyToCustomGroups(PolicyDTO.Policy policy, List getAllDefinitionsOfType(VropsPackageMemberType definitionType) { + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + // older vROPs versions use internal API for policies, thus internal header + // needs to be set. + if (!this.isVersionAbove812()) { + headers.set(INTERNAL_API_HEADER_NAME, Boolean.TRUE.toString()); + } + HttpEntity entity = new HttpEntity<>(headers); + ResponseEntity response = new ResponseEntity(HttpStatus.OK); + + String url = ""; + switch (definitionType) { + case ALERT_DEFINITION: + url = GET_ALL_ALERT_DEFS_API; + break; + case SYMPTOM_DEFINITION: + url = GET_ALL_SYMPTOM_DEFS_API; + break; + case RECOMMENDATION: + url = GET_ALL_RECOMMENDATIONS_API; + break; + default: + throw new RuntimeException("Unknown definition type used"); + } + + try { + UriComponentsBuilder uriBuilder; + + uriBuilder = UriComponentsBuilder.fromUri(getURI(getURIBuilder().setPath(url))); + uriBuilder.queryParam("pageSize", DEFAULT_PAGE_SIZE); + URI restUri = uriBuilder.build().toUri(); + response = restTemplate.exchange(restUri, HttpMethod.GET, entity, String.class); + } catch (HttpClientErrorException e) { + if (HttpStatus.NOT_FOUND.equals(e.getStatusCode())) { + return new ArrayList<>(); + } + throw new RuntimeException( + String.format("HTTP error ocurred trying to fetching definitions of type %s. Message: %s, Server error: %s", + definitionType.toString(), e.getMessage(), e.getStatusText())); + } catch (Exception e) { + throw new RuntimeException( + String.format("General error while fetching definitions of type %s. Message: %s, Server error: %s", + definitionType.toString(), e.getMessage(), response.getBody())); + } + + switch (definitionType) { + case ALERT_DEFINITION: + AlertDefinitionDTO alarmDefinitionDTO = (AlertDefinitionDTO) deserializeDefinitions(definitionType, response.getBody()); + return alarmDefinitionDTO.getAlertDefinitions(); + case SYMPTOM_DEFINITION: + SymptomDefinitionDTO symptomDefinitionDTO = (SymptomDefinitionDTO) deserializeDefinitions(definitionType, response.getBody()); + return symptomDefinitionDTO.getSymptomDefinitions(); + case RECOMMENDATION: + RecommendationDTO recommendationDTO = (RecommendationDTO) deserializeDefinitions(definitionType, response.getBody()); + return recommendationDTO.getRecommendations(); + default: + return new ArrayList<>(); + } + } + /** * Export custom groups filtered by list of custom group names. * @@ -1175,6 +1256,14 @@ public SupermetricDTO getAllSupermetrics() { return retVal; } + /** + * Imports all Supermetrics specified in the content.yaml + */ + public void importSupermetrics() { + + List existingSupermetrics = getAllSupermetrics().getSuperMetrics(); + } + /** * Get all vROPs view definitions. * diff --git a/common/artifact-manager/src/main/java/com/vmware/pscoe/iac/artifact/aria/operations/store/VropsPackageStore.java b/common/artifact-manager/src/main/java/com/vmware/pscoe/iac/artifact/aria/operations/store/VropsPackageStore.java index 56216f285..6cc88ba5f 100644 --- a/common/artifact-manager/src/main/java/com/vmware/pscoe/iac/artifact/aria/operations/store/VropsPackageStore.java +++ b/common/artifact-manager/src/main/java/com/vmware/pscoe/iac/artifact/aria/operations/store/VropsPackageStore.java @@ -48,6 +48,7 @@ import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.util.FileSystemUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; @@ -71,7 +72,11 @@ import com.vmware.pscoe.iac.artifact.aria.operations.models.ReportDefinitionDTO; import com.vmware.pscoe.iac.artifact.aria.operations.models.SupermetricDTO; import com.vmware.pscoe.iac.artifact.aria.operations.models.SymptomDefinitionDTO; +import com.vmware.pscoe.iac.artifact.aria.operations.models.SymptomDefinitionDTO.SymptomDefinition; import com.vmware.pscoe.iac.artifact.aria.operations.models.ViewDefinitionDTO; +import com.vmware.pscoe.iac.artifact.aria.operations.models.AlertDefinitionDTO.AlertDefinition; +import com.vmware.pscoe.iac.artifact.aria.operations.models.PolicyDTO.Policy; +import com.vmware.pscoe.iac.artifact.aria.operations.models.RecommendationDTO.Recommendation; import com.vmware.pscoe.iac.artifact.aria.operations.rest.RestClientVrops; import com.vmware.pscoe.iac.artifact.aria.operations.store.models.VropsPackageDescriptor; import com.vmware.pscoe.iac.artifact.aria.operations.store.models.VropsPackageMemberType; @@ -83,6 +88,7 @@ import com.vmware.pscoe.iac.artifact.common.store.Version; import com.vmware.pscoe.iac.artifact.common.store.models.PackageContent; import com.vmware.pscoe.iac.artifact.common.store.models.PackageContent.Content; +import com.vmware.pscoe.iac.artifact.common.utils.XmlUtilities; import com.vmware.pscoe.iac.artifact.common.utils.ZipUtilities; /** @@ -1139,6 +1145,148 @@ private String readCustomGroupFile(final File customGroupFile) throws IOExceptio } } + + /** + * Sets the ids of the policy to be equal to the ids on the server to be pushed + * @param policy zip file with the policy + * @param allPolicies all policies read from the server + * @return new .zip file with adjusted ids + */ + private File setPolicyIds(final File policy, List allPolicies) { + String path = policy.getParent().toString(); + String fullPath = policy.getPath(); + try { + logger.info("Unzipping file " + policy.getName()); + File pathForUnzipping = new File(path); + ZipUtilities.unzip(policy, pathForUnzipping); + File exportedPoliciesXml = new File(path + "/exportedPolicies.xml"); + String exportedPoliciesXmlContent = XmlUtilities.readXmlFileAsString(exportedPoliciesXml); + exportedPoliciesXmlContent = exportedPoliciesXmlContent.replace(">", ">\n"); + XmlUtilities.writeToXmlFile(exportedPoliciesXml, exportedPoliciesXmlContent); + Element document = XmlUtilities.initializeXmlFile(exportedPoliciesXml); + Node policyTag = XmlUtilities.findTagInXmlDocument(document, "Policies"); + NodeList policies = policyTag.getChildNodes(); + + HashMap> values = new HashMap<>(); + List policyElements = new ArrayList<>(); + + for (int i = 0; i < policies.getLength(); i++) { + Node node = policies.item(i); + if (node.getNodeType() == Node.ELEMENT_NODE) { + Element element = (Element) policies.item(i); + policyElements.add(element); + } + } + + for (int i = 0; i < policyElements.size(); i++) { + Element policyElement = policyElements.get(i); + String policyName = policyElement.getAttribute("name"); + HashMap childMap = new HashMap<>(); + Optional currentPolicy = allPolicies.stream().filter(pol -> pol.getName().equals(policyElement.getAttribute("name"))).findAny(); + if (currentPolicy.isPresent()) { + childMap.put("key", currentPolicy.get().getId()); + } else { + childMap.put("key", policyElement.getAttribute("key")); + } + if (policyElement.hasAttribute("parentPolicy")) { + String parentPolicyId = policyElement.getAttribute("parentPolicy"); + Optional parentPolicyElement = policyElements.stream().filter(el -> el.getAttribute("key").equals(parentPolicyId)).findAny(); + String parentPolicyName = parentPolicyElement.get().getAttribute("name"); + Optional parentPolicy = allPolicies.stream().filter(pol -> pol.getName().equals(parentPolicyName)).findAny(); + if (parentPolicy.isPresent()) { + childMap.put("parentPolicy", parentPolicy.get().getId()); + } else { + childMap.put("parentPolicy", parentPolicyId); + } + } + values.put(policyName, childMap); + } + XmlUtilities.setAttributesInXmlFile(exportedPoliciesXml, "Policies", values); + FileSystemUtils.deleteRecursively(policy); + ZipUtilities.zip(exportedPoliciesXml, fullPath); + FileSystemUtils.deleteRecursively(exportedPoliciesXml); + return new File(fullPath); + } catch (IOException e) { + throw new RuntimeException(String.format("An error occurred while setting the ids of policy %s : %s %n", policy.getName(), + e.getMessage())); + } + } + + /** + * Checking the target system for missing alert definitions, symptom definitions and recommendations. + * It compares it with the alert definitions, symptom definitions and recommendations of the policy + * @param policy The policy .zip file + * @param alertIds The ids of all the alert definitions on the target system + * @param symptomIds The ids of all the symptom definitions on the target system + * @param recommendationsIds The ids of all the recommendations on the target system + */ + private void checkPolicyForMissingComponents(File policy, List alertIds, List symptomIds, List recommendationsIds) { + + String path = policy.getParent().toString(); + try { + logger.info("Unzipping file " + policy.getName()); + File pathForUnzipping = new File(path); + ZipUtilities.unzip(policy, pathForUnzipping); + File exportedPoliciesXml = new File(path + "/exportedPolicies.xml"); + String exportedPoliciesXmlContent = XmlUtilities.readXmlFileAsString(exportedPoliciesXml); + exportedPoliciesXmlContent = exportedPoliciesXmlContent.replace(">", ">\n"); + Element document = XmlUtilities.initializeXmlFile(exportedPoliciesXml); + + List alertDefinitions = XmlUtilities.getAllTagsWithAttributes(document, "AlertDefinition"); + List missingAlerts = alertDefinitions.stream().filter(alert -> alertIds.indexOf(alert.getAttribute("id")) == -1).collect(Collectors.toList()); + + List symptomDefinitions = XmlUtilities.getAllTagsWithAttributes(document, "SymptomDefinition"); + List missingSymptoms = symptomDefinitions.stream().filter(symp -> symptomIds.indexOf(symp.getAttribute("id")) == -1).collect(Collectors.toList()); + + List recommendations = XmlUtilities.getAllTagsWithAttribute(document, "Recommendation", "key"); + List missingReccoms = recommendations.stream().filter(rec -> recommendationsIds.indexOf(rec.getAttribute("key")) == -1).collect(Collectors.toList()); + + FileSystemUtils.deleteRecursively(exportedPoliciesXml); + + if (missingAlerts.size() > 0 || missingSymptoms.size() > 0 || missingReccoms.size() > 0) { + + logger.info("Policy '" + policy.getName() + "' has missing dependencies"); + logger.info("Total missing alert definitions: " + missingAlerts.size()); + logger.info("Total missing symptom definitions: " + missingSymptoms.size()); + logger.info("Total missing recommendations: " + missingReccoms.size()); + String errorMessage = ""; + String allMissingAlerts = missingAlerts.size() > 0 ? missingAlerts.stream() + .map(alert -> alert.getAttribute("name")) + .reduce((a, b) -> String.format("%s\n%s", a, b)).get() : ""; + String allMissingSymptoms = missingSymptoms.size() > 0 ? missingSymptoms.stream() + .map(symp -> symp.getAttribute("name")) + .reduce((a, b) -> String.format("%s\n%s", a, b)).get() : ""; + String allMissingReccoms = missingReccoms.size() > 0 ? missingReccoms.stream() + .map(recomm -> { + Node description = recomm.getFirstChild().getNextSibling(); + if (description != null) { + return description.getTextContent(); + } else { + return ""; + } + }) + .reduce((a, b) -> String.format("%s\n%s", a, b)).get() : ""; + + if (missingAlerts.size() > 0) { + errorMessage = String.format("\nthe following alert definitions:\n%s", allMissingAlerts); + } + + if (missingSymptoms.size() > 0) { + errorMessage += String.format("\nthe following symptom definitions:\n%s", allMissingSymptoms); + } + if (missingReccoms.size() > 0) { + errorMessage += String.format("\nthe following recommendations:\n%s", allMissingReccoms); + } + throw new RuntimeException(String.format("Policy with name '%s' is missing the following entities on the target system:%s", policy.getName(), errorMessage)); + } + + + } catch (IOException e) { + throw new RuntimeException(String.format("An error occurred while deleting the disabled items from policy '%s'. Reason: %n", policy.getName(), + e.getMessage())); + } + } + /** * Import policies to vROPs. * @@ -1147,6 +1295,22 @@ private String readCustomGroupFile(final File customGroupFile) throws IOExceptio * @throws RuntimeException if the polices cannot be imported. */ private void importPolicies(final Package vropsPackage, final File tmpDir) { + + List alertDefinitions = restClient.getAllDefinitionsOfType(VropsPackageMemberType.ALERT_DEFINITION) + .stream() + .map(AlertDefinitionDTO.AlertDefinition.class::cast) + .collect(Collectors.toList()); + List alertIds = alertDefinitions.stream().map(alert -> alert.getId()).toList(); + List sympAlertDefinitions = restClient.getAllDefinitionsOfType(VropsPackageMemberType.SYMPTOM_DEFINITION) + .stream() + .map(SymptomDefinitionDTO.SymptomDefinition.class::cast) + .collect(Collectors.toList()); + List symptomIds = sympAlertDefinitions.stream().map(symp -> symp.getId()).toList(); + List recommendations = restClient.getAllDefinitionsOfType(VropsPackageMemberType.RECOMMENDATION) + .stream() + .map(RecommendationDTO.Recommendation.class::cast) + .collect(Collectors.toList()); + List recommIds = recommendations.stream().map(rec -> rec.getId()).toList(); File policiesDir = new File(tmpDir.getPath(), "policies"); if (!policiesDir.exists()) { return; @@ -1156,6 +1320,8 @@ private void importPolicies(final Package vropsPackage, final File tmpDir) { for (File policy : FileUtils.listFiles(policiesDir, new String[] { "zip" }, Boolean.FALSE)) { String policyName = FilenameUtils.removeExtension(policy.getName()); try { + logger.info("Checking for missing dependencies for policy: '{}'", policyName); + checkPolicyForMissingComponents(policy, alertIds, symptomIds, recommIds); logger.info("Importing policy: '{}'", policyName); restClient.importPolicyFromZip(policyName, policy, Boolean.TRUE); logger.info("Imported policy: '{}'", policyName); @@ -1607,31 +1773,30 @@ public Package importPackage(final Package pkg, final boolean dryrun, final bool try { new PackageManager(pkg).unpack(tmpDir); - - addViewToImportList(pkg, tmpDir); - addDashboardToImportList(pkg, tmpDir); - addReportToImportList(pkg, tmpDir); - addSuperMetricToImportList(pkg, tmpDir); - addMetricConfigToImportList(pkg, tmpDir); - - if (cliManager.hasAnyCommands()) { - cliManager.connect(); - cliManager.importFilesToVrops(); - } - - importDefinitions(pkg, tmpDir); - importPolicies(pkg, tmpDir); - importCustomGroups(pkg, tmpDir); - // manage dashboard sharing per groups - manageDashboardSharing(tmpDir); - // manage dashboard activation per groups - manageDashboardActivation(tmpDir, true); - // manage dashboard activation per users - manageDashboardActivation(tmpDir, false); - // set default policy after importing policies - setDefaultPolicy(pkg, tmpDir); - // set policy priorities - setPolicyPriorities(pkg, tmpDir); + addViewToImportList(pkg, tmpDir); + addDashboardToImportList(pkg, tmpDir); + addReportToImportList(pkg, tmpDir); + addSuperMetricToImportList(pkg, tmpDir); + addMetricConfigToImportList(pkg, tmpDir); + + if (cliManager.hasAnyCommands()) { + cliManager.connect(); + cliManager.importFilesToVrops(); + } + + importDefinitions(pkg, tmpDir); + importPolicies(pkg, tmpDir); + importCustomGroups(pkg, tmpDir); + // manage dashboard sharing per groups + manageDashboardSharing(tmpDir); + // manage dashboard activation per groups + manageDashboardActivation(tmpDir, true); + // manage dashboard activation per users + manageDashboardActivation(tmpDir, false); + // set default policy after importing policies + setDefaultPolicy(pkg, tmpDir); + // set policy priorities + setPolicyPriorities(pkg, tmpDir); } catch (IOException | JSchException | ConfigurationException e) { String message = String.format("Unable to push package '%s' to vROps Server '%s' : %s : %s", pkg.getFQName(), cliManager, e.getClass().getName(), diff --git a/common/artifact-manager/src/main/java/com/vmware/pscoe/iac/artifact/common/utils/XmlUtilities.java b/common/artifact-manager/src/main/java/com/vmware/pscoe/iac/artifact/common/utils/XmlUtilities.java new file mode 100644 index 000000000..5c2041444 --- /dev/null +++ b/common/artifact-manager/src/main/java/com/vmware/pscoe/iac/artifact/common/utils/XmlUtilities.java @@ -0,0 +1,347 @@ +package com.vmware.pscoe.iac.artifact.common.utils; + +import java.io.BufferedReader; +import java.io.BufferedWriter; + +/*- + * #%L + * artifact-manager + * %% + * Copyright (C) 2023 - 2025 VMware + * %% + * Build Tools for VMware Aria + * Copyright 2023 VMware, Inc. + * + * This product is licensed to you under the BSD-2 license (the "License"). You may not use this product except in compliance with the BSD-2 License. + * + * This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. + * #L% + */ + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.security.InvalidParameterException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Optional; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +public final class XmlUtilities { + + private XmlUtilities() { + + } + + /** + * Initializes the xml file by getting the first element(tag) of the xml file + * @param xmlFile the input xml file + * @return an object of type Element in order to have access to attributes + */ + public static Element initializeXmlFile(File xmlFile) { + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document document = builder.parse(xmlFile); + Element documentElement = document.getDocumentElement(); + documentElement.normalize(); + return documentElement; + } catch (ParserConfigurationException | SAXException | IOException e) { + throw new RuntimeException(String.format("An error occurred while initializing xml file for %s : %s %n", xmlFile.getName(), + e.getMessage())); + } + } + + /** + * @param xmlFile the input xml file + * @return the string content of the xml file + */ + public static String readXmlFileAsString(File xmlFile) throws IOException { + FileReader fileReader = new FileReader(xmlFile); + BufferedReader bufReader = new BufferedReader(fileReader); + StringBuilder sb = new StringBuilder(); + String line; + + while ((line = bufReader.readLine()) != null) { + sb.append(line).append("\n"); // Append each line and a newline character + } + + bufReader.close(); + return sb.toString(); + } + + /** + * Writes string content to an xml file + * @param file the file to write to + * @param xmlContent the xml content as string + */ + public static void writeToXmlFile(File file, String xmlContent) { + + try { + + // Create a FileWriter to write to the file + FileWriter fileWriter = new FileWriter(file); + + // Create a BufferedWriter for efficient writing + BufferedWriter bufferedWriter = new BufferedWriter(fileWriter); + + // Write the XML content string to the file + bufferedWriter.write(xmlContent); + + // Close the BufferedWriter to flush and release resources + bufferedWriter.close(); + + System.out.println("XML content successfully written to " + file.getPath()); + + } catch (IOException e) { + throw new RuntimeException("Error writing XML to file: " + e.getMessage()); + } + } + + /** + * Sets certain tags with certain attributes + * @param xmlFile the xml file about to be edited + * @param tagName the tag name where attribute changes will happen + * @param valuesToSet map with values. The keys of the main map are the values of the attributes to search by, each child map is a key value pair for the attributes wanted to be set + */ + public static void setAttributesInXmlFile(File xmlFile, String tagName, HashMap> valuesToSet) { + + Element document = initializeXmlFile(xmlFile); + + Node tag = findTagInXmlDocument(document, tagName); + NodeList childNodes = tag.getChildNodes(); + + int childNodesLength = 0; + + for (int i = 0; i < childNodes.getLength(); i++) { + Node child = childNodes.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + childNodesLength++; + } + } + + if (childNodesLength != valuesToSet.size()) { + throw new InvalidParameterException("Number of provided values does not match the number of tags found"); + } + + for (int i = 0; i < childNodes.getLength(); i++) { + Node child = childNodes.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + Element element = (Element) child; + NamedNodeMap attributeElements = element.getAttributes(); + List attributes = new ArrayList(); + for (int attributeIndex = 0; attributeIndex < attributeElements.getLength(); attributeIndex++) { + Node nextAttribute = attributeElements.item(attributeIndex); + attributes.add(nextAttribute); + } + + String[] hashMapKeys = valuesToSet.keySet().toArray(new String[0]); + for (String key : hashMapKeys) { + Optional searchAttribute = attributes.stream().filter(attr -> attr.getNodeValue().equals(key)).findFirst(); + if (searchAttribute.isPresent()) { + HashMap childMap = valuesToSet.get(key); + String[] childMapKeys = childMap.keySet().toArray(new String[0]); + for (String childKey : childMapKeys) { + element.setAttribute(childKey, childMap.get(childKey)); + } + break; + } + } + } + } + saveXmlDocument(xmlFile, document); + } + + /** + * Saves changes of an xml file + * @param xmlFile the xml file to save + * @param document the initialized xml file + */ + public static void saveXmlDocument(File xmlFile, Element document) { + + try { + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + DOMSource source = new DOMSource(document); + StreamResult result = new StreamResult(xmlFile); + transformer.transform(source, result); + System.out.println("XML document saved successfully"); + } catch (TransformerException e) { + throw new RuntimeException("Saving document " + document.getNodeName() + " failed. Reason: " + e); + } + + } + + /** + * Finds node by tag name + * @param node the parent node to search under + * @param tagName the tag name to be searched + * @return the found node or null + */ + public static Node findTagInXmlDocument(Node node, String tagName) { + + Node result = null; + + if (node.getNodeName().equals(tagName)) { + result = node; + return result; + } + + NodeList childNodes = node.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) { + Node child = childNodes.item(i); + result = findTagInXmlDocument(child, tagName); + if (result != null) { + break; + } + } + + return result; + } + + /** + * Gets all tags with specified name which contain attributes + * @param document the initialized xml file + * @param tagName the tag name of the tag needed to be found + * @return a list of the tags found which contain attributes + */ + public static List getAllTagsWithAttributes(Element document, String tagName) { + + NodeList tagNodes = document.getElementsByTagName(tagName); + List tags = new ArrayList<>(); + + for (int i = 0; i < tagNodes.getLength(); i++) { + Node node = tagNodes.item(i); + if (node.getNodeType() == Node.ELEMENT_NODE) { + tags.add((Element) node); + } + + } + + return tags; + } + + /** + * Gets all tags with specified tag name which have certain attribute + * @param document the initialized xml document + * @param tagName the name of the tags to be found + * @param attributeName the name of the attribute + * @return a list of tags with attributes + */ + public static List getAllTagsWithAttribute(Element document, String tagName, String attributeName) { + + NodeList tagNodes = document.getElementsByTagName(tagName); + List tags = new ArrayList<>(); + + for (int i = 0; i < tagNodes.getLength(); i++) { + Node node = tagNodes.item(i); + if (node.getNodeType() == Node.ELEMENT_NODE) { + Element element = (Element) node; + if (element.hasAttribute(attributeName)) { + tags.add((Element) node); + } + } + + } + + return tags; + } + + /** + * Gets all tags with specified tag name which do not attributes + * @param document the initialized xml document + * @param tagName the name of the tags to be found + * @return a list of tags without attributes + */ + public static Node[] getAllTagsWithoutAttributes(Element document, String tagName) { + + NodeList tagNodes = document.getElementsByTagName("*"); + + List result = new ArrayList<>(); + for (int i = 0; i < tagNodes.getLength(); i++) { + Node node = tagNodes.item(i); + if (node.getNodeName().equals(tagName)) { + result.add(node); + } + } + return result.toArray(new Node[0]); + } + + /** + * Gets the values of certain attribute for a list of tags + * @param tags the tags to take the attrubute values from + * @param attributeName the name of the attribute + * @return a list of values of the attributes + */ + public static List getAllValuesOfAttributeOfTag(Element[] tags, String attributeName) { + + List result = new ArrayList<>(); + for (int i = 0; i < tags.length; i++) { + if (!tags[i].hasAttribute(attributeName)) { + continue; + } + result.add(tags[i].getAttribute(attributeName)); + } + + return result; + } + + /** + * Gets all tags with specified tag name which are in the xml document + * @param document the initialized xml document + * @param tagName the name of the tags to be found + * @return a list of tags found + */ + public static Element[] getChildrenTagsOfTag(Node document, String tagName) throws Exception { + + List children = new ArrayList<>(); + + Node tag = findTagInXmlDocument(document, tagName); + + if (tag == null) { + throw new RuntimeException("Tag with name '" + tagName + "' not found in document"); + } + + NodeList childNodes = tag.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) { + Node child = childNodes.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + children.add((Element) child); + } + } + + return children.toArray(new Element[0]); + } + + /** + * Gets the text content from a list of tags + * @param tags the tag list + * @return a list of strings representing the text between the opening and closing tag + */ + public static List getTextContentBetweenTags(Node[] tags) { + + List result = new ArrayList<>(); + for (int i = 0; i < tags.length; i++) { + result.add(tags[i].getTextContent()); + } + + return result; + } +} diff --git a/common/artifact-manager/src/main/java/com/vmware/pscoe/iac/artifact/common/utils/ZipUtilities.java b/common/artifact-manager/src/main/java/com/vmware/pscoe/iac/artifact/common/utils/ZipUtilities.java index cd6a0b484..c03e3ceb5 100644 --- a/common/artifact-manager/src/main/java/com/vmware/pscoe/iac/artifact/common/utils/ZipUtilities.java +++ b/common/artifact-manager/src/main/java/com/vmware/pscoe/iac/artifact/common/utils/ZipUtilities.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; public final class ZipUtilities { private static final int PRIME_NUMBER_1024 = 1024; @@ -27,6 +28,12 @@ public final class ZipUtilities { private ZipUtilities() { } + /** + * Unzips .zip files + * @param zipFile the input .zip file + * @param outputLocation the location of the output file + * @throws IOException + */ public static void unzip(File zipFile, File outputLocation) throws IOException { try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile.getAbsolutePath()))) { ZipEntry ze = null; @@ -52,4 +59,31 @@ public static void unzip(File zipFile, File outputLocation) throws IOException { System.out.println(e.getMessage()); } } + + /** + * Zips a file + * @param file the input file + * @param destinationDir the destination directory where the zip will be + */ + public static void zip(File file, String destinationDir) { + + try (FileOutputStream fos = new FileOutputStream(destinationDir); + ZipOutputStream zos = new ZipOutputStream(fos); + FileInputStream fis = new FileInputStream(file.getPath())) { + + ZipEntry zipEntry = new ZipEntry(new File(file.getPath()).getName()); + zos.putNextEntry(zipEntry); + + byte[] buffer = new byte[PRIME_NUMBER_1024]; + int length; + while ((length = fis.read(buffer)) > 0) { + zos.write(buffer, 0, length); + } + zos.closeEntry(); + System.out.println("File zipped successfully!"); + + } catch (IOException e) { + e.printStackTrace(); + } + } } diff --git a/docs/versions/latest/Release.md b/docs/versions/latest/Release.md index 1bdc9fcf7..dc394bd68 100644 --- a/docs/versions/latest/Release.md +++ b/docs/versions/latest/Release.md @@ -33,16 +33,11 @@ ## Improvements -[//]: # (### *Improvement Name* ) -[//]: # (Talk ONLY regarding the improvement) -[//]: # (Optional But higlhy recommended) -[//]: # (#### Previous Behavior) -[//]: # (Explain how it used to behave, regarding to the change) -[//]: # (Optional But higlhy recommended) -[//]: # (#### New Behavior) -[//]: # (Explain how it behaves now, regarding to the change) -[//]: # (Optional But higlhy recommended Specify *NONE* if missing) -[//]: # (#### Relevant Documentation:) +### *Bugfix for pushing pushing policies in Aria Operations* +#### Previous Behavior +It is not possible to push policies if they have missing dependencies (alert definitions, symptom definitions, recommendations) on the target server +#### New Behavior +There is an error message which shows the user when BTVA is trying to import policies if they have missing dependencies (alert definitions, symptom definitions, recommendations) on the target server ## Upgrade procedure