X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=blobdiff_plain;f=opendaylight%2Fnetconf%2Fconfig-persister-impl%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fnetconf%2Fpersist%2Fimpl%2FConfigPusherImpl.java;h=c41a2f4d16b7e325e228704d58f6e63f798a1700;hp=5f311b5232ed676263925ce6af94b779c662df2e;hb=12613b3cfb0af8d185e430ffd404298191bee518;hpb=961b5b9260565194a863a25bd697f171ec2405af diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusherImpl.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusherImpl.java index 5f311b5232..c41a2f4d16 100644 --- a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusherImpl.java +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusherImpl.java @@ -10,6 +10,9 @@ package org.opendaylight.controller.netconf.persist.impl; import static com.google.common.base.Preconditions.checkNotNull; +import com.google.common.base.Function; +import com.google.common.base.Stopwatch; +import com.google.common.collect.Collections2; import java.io.IOException; import java.io.InputStream; import java.util.Collection; @@ -23,18 +26,17 @@ import java.util.TreeMap; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; - +import javax.annotation.Nonnull; import javax.annotation.concurrent.Immutable; import javax.management.MBeanServerConnection; - import org.opendaylight.controller.config.api.ConflictingVersionException; import org.opendaylight.controller.config.persist.api.ConfigPusher; import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder; import org.opendaylight.controller.config.persist.api.Persister; +import org.opendaylight.controller.netconf.api.Capability; import org.opendaylight.controller.netconf.api.NetconfDocumentedException; import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants; -import org.opendaylight.controller.netconf.mapping.api.Capability; import org.opendaylight.controller.netconf.mapping.api.HandlingPriority; import org.opendaylight.controller.netconf.mapping.api.NetconfOperation; import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution; @@ -49,13 +51,9 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xml.sax.SAXException; -import com.google.common.base.Function; -import com.google.common.base.Stopwatch; -import com.google.common.collect.Collections2; - @Immutable public class ConfigPusherImpl implements ConfigPusher { - private static final Logger logger = LoggerFactory.getLogger(ConfigPusherImpl.class); + private static final Logger LOG = LoggerFactory.getLogger(ConfigPusherImpl.class); private final long maxWaitForCapabilitiesMillis; private final long conflictingVersionTimeoutMillis; @@ -80,40 +78,39 @@ public class ConfigPusherImpl implements ConfigPusher { synchronized (autoCloseables) { autoCloseables.add(jmxNotificationHandler); } - /* - * We have completed initial configuration. At this point - * it is good idea to perform garbage collection to prune - * any garbage we have accumulated during startup. - */ - logger.debug("Running post-initialization garbage collection..."); - System.gc(); - logger.debug("Post-initialization garbage collection completed."); - logger.debug("ConfigPusher has pushed configs {}, gc completed", configs); - } - catch (NetconfDocumentedException e) { - logger.error("Error pushing configs {}",configs); + + LOG.debug("ConfigPusher has pushed configs {}", configs); + } catch (NetconfDocumentedException e) { + LOG.error("Error pushing configs {}",configs); throw new IllegalStateException(e); } } } public void pushConfigs(List configs) throws InterruptedException { - logger.debug("Requested to push configs {}", configs); + LOG.debug("Requested to push configs {}", configs); this.queue.put(configs); } private LinkedHashMap internalPushConfigs(List configs) throws NetconfDocumentedException { - logger.debug("Last config snapshots to be pushed to netconf: {}", configs); + LOG.debug("Last config snapshots to be pushed to netconf: {}", configs); LinkedHashMap result = new LinkedHashMap<>(); // start pushing snapshots: for (ConfigSnapshotHolder configSnapshotHolder : configs) { if(configSnapshotHolder != null) { - EditAndCommitResponse editAndCommitResponseWithRetries = pushConfigWithConflictingVersionRetries(configSnapshotHolder); - logger.debug("Config snapshot pushed successfully: {}, result: {}", configSnapshotHolder, result); + EditAndCommitResponse editAndCommitResponseWithRetries = null; + try { + editAndCommitResponseWithRetries = pushConfigWithConflictingVersionRetries(configSnapshotHolder); + } catch (ConfigSnapshotFailureException e) { + LOG.warn("Failed to apply configuration snapshot: {}. Config snapshot is not semantically correct and will be IGNORED. " + + "for detailed information see enclosed exception.", e.getConfigIdForReporting(), e); + throw new IllegalStateException("Failed to apply configuration snapshot " + e.getConfigIdForReporting(), e); + } + LOG.debug("Config snapshot pushed successfully: {}, result: {}", configSnapshotHolder, result); result.put(configSnapshotHolder, editAndCommitResponseWithRetries); } } - logger.debug("All configuration snapshots have been pushed successfully."); + LOG.debug("All configuration snapshots have been pushed successfully."); return result; } @@ -123,19 +120,22 @@ public class ConfigPusherImpl implements ConfigPusher { * is caught, whole process is retried - new service instance need to be obtained from the factory. Closes * {@link NetconfOperationService} after each use. */ - private synchronized EditAndCommitResponse pushConfigWithConflictingVersionRetries(ConfigSnapshotHolder configSnapshotHolder) throws NetconfDocumentedException { + private synchronized EditAndCommitResponse pushConfigWithConflictingVersionRetries(ConfigSnapshotHolder configSnapshotHolder) throws ConfigSnapshotFailureException { ConflictingVersionException lastException; - Stopwatch stopwatch = new Stopwatch().start(); + Stopwatch stopwatch = Stopwatch.createUnstarted(); do { String idForReporting = configSnapshotHolder.toString(); SortedSet expectedCapabilities = checkNotNull(configSnapshotHolder.getCapabilities(), "Expected capabilities must not be null - %s, check %s", idForReporting, configSnapshotHolder.getClass().getName()); try (NetconfOperationService operationService = getOperationServiceWithRetries(expectedCapabilities, idForReporting)) { + if(!stopwatch.isRunning()) { + stopwatch.start(); + } return pushConfig(configSnapshotHolder, operationService); } catch (ConflictingVersionException e) { lastException = e; - logger.debug("Conflicting version detected, will retry after timeout"); + LOG.info("Conflicting version detected, will retry after timeout"); sleep(); } } while (stopwatch.elapsed(TimeUnit.MILLISECONDS) < conflictingVersionTimeoutMillis); @@ -144,29 +144,80 @@ public class ConfigPusherImpl implements ConfigPusher { } private NetconfOperationService getOperationServiceWithRetries(Set expectedCapabilities, String idForReporting) { - Stopwatch stopwatch = new Stopwatch().start(); - NotEnoughCapabilitiesException lastException; + Stopwatch stopwatch = Stopwatch.createStarted(); + ConfigPusherException lastException; do { try { return getOperationService(expectedCapabilities, idForReporting); - } catch (NotEnoughCapabilitiesException e) { - logger.debug("Not enough capabilities: " + e.toString()); + } catch (ConfigPusherException e) { + LOG.debug("Not enough capabilities: {}", e.toString()); lastException = e; sleep(); } } while (stopwatch.elapsed(TimeUnit.MILLISECONDS) < maxWaitForCapabilitiesMillis); - throw new IllegalStateException("Max wait for capabilities reached." + lastException.getMessage(), lastException); + + if(lastException instanceof NotEnoughCapabilitiesException) { + LOG.error("Unable to push configuration due to missing yang models." + + " Yang models that are missing, but required by the configuration: {}." + + " For each mentioned model check: " + + " 1. that the mentioned yang model namespace/name/revision is identical to those in the yang model itself" + + " 2. the yang file is present in the system" + + " 3. the bundle with that yang file is present in the system and active" + + " 4. the yang parser did not fail while attempting to parse that model", + ((NotEnoughCapabilitiesException) lastException).getMissingCaps()); + throw new IllegalStateException("Unable to push configuration due to missing yang models." + + " Required yang models that are missing: " + + ((NotEnoughCapabilitiesException) lastException).getMissingCaps(), lastException); + } else { + final String msg = "Unable to push configuration due to missing netconf service"; + LOG.error(msg, lastException); + throw new IllegalStateException(msg, lastException); + } } - private static class NotEnoughCapabilitiesException extends Exception { - private static final long serialVersionUID = 1L; + private static class ConfigPusherException extends Exception { + + public ConfigPusherException(final String message) { + super(message); + } - private NotEnoughCapabilitiesException(String message, Throwable cause) { + public ConfigPusherException(final String message, final Throwable cause) { super(message, cause); } + } + + private static class NotEnoughCapabilitiesException extends ConfigPusherException { + private static final long serialVersionUID = 1L; + private Set missingCaps; - private NotEnoughCapabilitiesException(String message) { + private NotEnoughCapabilitiesException(String message, Set missingCaps) { super(message); + this.missingCaps = missingCaps; + } + + public Set getMissingCaps() { + return missingCaps; + } + } + + private static final class NetconfServiceNotAvailableException extends ConfigPusherException { + + public NetconfServiceNotAvailableException(final String s, final RuntimeException e) { + super(s, e); + } + } + + private static final class ConfigSnapshotFailureException extends ConfigPusherException { + + private final String configIdForReporting; + + public ConfigSnapshotFailureException(final String configIdForReporting, final String operationNameForReporting, final Exception e) { + super(String.format("Failed to apply config snapshot: %s during phase: %s", configIdForReporting, operationNameForReporting), e); + this.configIdForReporting = configIdForReporting; + } + + public String getConfigIdForReporting() { + return configIdForReporting; } } @@ -177,30 +228,31 @@ public class ConfigPusherImpl implements ConfigPusher { * @param idForReporting * @return service if capabilities are present, otherwise absent value */ - private NetconfOperationService getOperationService(Set expectedCapabilities, String idForReporting) throws NotEnoughCapabilitiesException { + private NetconfOperationService getOperationService(Set expectedCapabilities, String idForReporting) throws ConfigPusherException { NetconfOperationService serviceCandidate; try { serviceCandidate = configNetconfConnector.createService(idForReporting); } catch(RuntimeException e) { - throw new NotEnoughCapabilitiesException("Netconf service not stable for " + idForReporting, e); + throw new NetconfServiceNotAvailableException("Netconf service not stable for config pusher." + + " Cannot push any configuration", e); } - Set notFoundDiff = computeNotFoundCapabilities(expectedCapabilities, serviceCandidate); + Set notFoundDiff = computeNotFoundCapabilities(expectedCapabilities, configNetconfConnector); if (notFoundDiff.isEmpty()) { return serviceCandidate; } else { serviceCandidate.close(); - logger.trace("Netconf server did not provide required capabilities for {} " + + LOG.debug("Netconf server did not provide required capabilities for {} ", idForReporting, "Expected but not found: {}, all expected {}, current {}", - idForReporting, notFoundDiff, expectedCapabilities, serviceCandidate.getCapabilities() + notFoundDiff, expectedCapabilities, configNetconfConnector.getCapabilities() ); - throw new NotEnoughCapabilitiesException("Not enough capabilities for " + idForReporting + ". Expected but not found: " + notFoundDiff); + throw new NotEnoughCapabilitiesException("Not enough capabilities for " + idForReporting + ". Expected but not found: " + notFoundDiff, notFoundDiff); } } - private static Set computeNotFoundCapabilities(Set expectedCapabilities, NetconfOperationService serviceCandidate) { + private static Set computeNotFoundCapabilities(Set expectedCapabilities, NetconfOperationServiceFactory serviceCandidate) { Collection actual = Collections2.transform(serviceCandidate.getCapabilities(), new Function() { @Override - public String apply(Capability input) { + public String apply(@Nonnull final Capability input) { return input.getCapabilityUri(); } }); @@ -209,8 +261,6 @@ public class ConfigPusherImpl implements ConfigPusher { return allNotFound; } - - private void sleep() { try { Thread.sleep(100); @@ -228,7 +278,7 @@ public class ConfigPusherImpl implements ConfigPusher { * @throws java.lang.RuntimeException if edit-config or commit fails otherwise */ private synchronized EditAndCommitResponse pushConfig(ConfigSnapshotHolder configSnapshotHolder, NetconfOperationService operationService) - throws ConflictingVersionException, NetconfDocumentedException { + throws ConflictingVersionException, ConfigSnapshotFailureException { Element xmlToBePersisted; try { @@ -236,8 +286,8 @@ public class ConfigPusherImpl implements ConfigPusher { } catch (SAXException | IOException e) { throw new IllegalStateException("Cannot parse " + configSnapshotHolder); } - logger.trace("Pushing last configuration to netconf: {}", configSnapshotHolder); - Stopwatch stopwatch = new Stopwatch().start(); + LOG.trace("Pushing last configuration to netconf: {}", configSnapshotHolder); + Stopwatch stopwatch = Stopwatch.createStarted(); NetconfMessage editConfigMessage = createEditConfigMessage(xmlToBePersisted); Document editResponseMessage = sendRequestGetResponseCheckIsOK(editConfigMessage, operationService, @@ -246,28 +296,33 @@ public class ConfigPusherImpl implements ConfigPusher { Document commitResponseMessage = sendRequestGetResponseCheckIsOK(getCommitMessage(), operationService, "commit", configSnapshotHolder.toString()); - if (logger.isTraceEnabled()) { + if (LOG.isTraceEnabled()) { StringBuilder response = new StringBuilder("editConfig response = {"); response.append(XmlUtil.toString(editResponseMessage)); response.append("}"); response.append("commit response = {"); response.append(XmlUtil.toString(commitResponseMessage)); response.append("}"); - logger.trace("Last configuration loaded successfully"); - logger.trace("Detailed message {}", response); - logger.trace("Total time spent {} ms", stopwatch.elapsed(TimeUnit.MILLISECONDS)); + LOG.trace("Last configuration loaded successfully"); + LOG.trace("Detailed message {}", response); + LOG.trace("Total time spent {} ms", stopwatch.elapsed(TimeUnit.MILLISECONDS)); } return new EditAndCommitResponse(editResponseMessage, commitResponseMessage); } - private NetconfOperation findOperation(NetconfMessage request, NetconfOperationService operationService) throws NetconfDocumentedException { + private NetconfOperation findOperation(NetconfMessage request, NetconfOperationService operationService) { TreeMap allOperations = new TreeMap<>(); Set netconfOperations = operationService.getNetconfOperations(); if (netconfOperations.isEmpty()) { throw new IllegalStateException("Possible code error: no config operations"); } for (NetconfOperation netconfOperation : netconfOperations) { - HandlingPriority handlingPriority = netconfOperation.canHandle(request.getDocument()); + HandlingPriority handlingPriority = null; + try { + handlingPriority = netconfOperation.canHandle(request.getDocument()); + } catch (NetconfDocumentedException e) { + throw new IllegalStateException("Possible code error: canHandle threw exception", e); + } allOperations.put(handlingPriority, netconfOperation); } Entry highestEntry = allOperations.lastEntry(); @@ -279,24 +334,23 @@ public class ConfigPusherImpl implements ConfigPusher { private Document sendRequestGetResponseCheckIsOK(NetconfMessage request, NetconfOperationService operationService, String operationNameForReporting, String configIdForReporting) - throws ConflictingVersionException, NetconfDocumentedException { + throws ConflictingVersionException, ConfigSnapshotFailureException { NetconfOperation operation = findOperation(request, operationService); Document response; try { response = operation.handle(request.getDocument(), NetconfOperationChainedExecution.EXECUTION_TERMINATION_POINT); - } catch (NetconfDocumentedException | RuntimeException e) { - if (e instanceof NetconfDocumentedException && e.getCause() instanceof ConflictingVersionException) { + return NetconfUtil.checkIsMessageOk(response); + } catch (NetconfDocumentedException e) { + if (e.getCause() instanceof ConflictingVersionException) { throw (ConflictingVersionException) e.getCause(); } - throw new IllegalStateException("Failed to send " + operationNameForReporting + - " for configuration " + configIdForReporting, e); + throw new ConfigSnapshotFailureException(configIdForReporting, operationNameForReporting, e); } - return NetconfUtil.checkIsMessageOk(response); } // load editConfig.xml template, populate /rpc/edit-config/config with parameter - private static NetconfMessage createEditConfigMessage(Element dataElement) throws NetconfDocumentedException { + private static NetconfMessage createEditConfigMessage(Element dataElement) { String editConfigResourcePath = "/netconfOp/editConfig.xml"; try (InputStream stream = ConfigPersisterNotificationHandler.class.getResourceAsStream(editConfigResourcePath)) { checkNotNull(stream, "Unable to load resource " + editConfigResourcePath); @@ -312,7 +366,7 @@ public class ConfigPusherImpl implements ConfigPusher { } editConfigElement.appendChild(configWrapper.getDomElement()); return new NetconfMessage(doc); - } catch (IOException | SAXException e) { + } catch (IOException | SAXException | NetconfDocumentedException e) { // error reading the xml file bundled into the jar throw new IllegalStateException("Error while opening local resource " + editConfigResourcePath, e); }