import java.util.Map;
import org.opendaylight.aaa.api.AuthenticationException;
import org.opendaylight.aaa.api.Claim;
+import org.opendaylight.aaa.api.CredentialAuth;
import org.opendaylight.aaa.api.PasswordCredentials;
import org.opendaylight.netconf.auth.AuthProvider;
import org.osgi.framework.BundleContext;
*/
public static volatile Map.Entry<BundleContext, CredentialServiceAuthProvider> INSTANCE;
- private volatile PasswordCredentialAuth nullableCredService;
- private final ServiceTracker<PasswordCredentialAuth, PasswordCredentialAuth> listenerTracker;
+ // FIXME CredentialAuth is generic and it causes warnings during compilation
+ // Maybe there should be a PasswordCredentialAuth implements CredentialAuth<PasswordCredentials>
+ private volatile CredentialAuth<PasswordCredentials> nullableCredService;
+ private final ServiceTracker<CredentialAuth, CredentialAuth> listenerTracker;
public CredentialServiceAuthProvider(final BundleContext bundleContext) {
- final ServiceTrackerCustomizer<PasswordCredentialAuth, PasswordCredentialAuth> customizer = new ServiceTrackerCustomizer<PasswordCredentialAuth, PasswordCredentialAuth>() {
+ final ServiceTrackerCustomizer<CredentialAuth, CredentialAuth> customizer = new ServiceTrackerCustomizer<CredentialAuth, CredentialAuth>() {
@Override
- public PasswordCredentialAuth addingService(final ServiceReference<PasswordCredentialAuth> reference) {
+ public CredentialAuth addingService(final ServiceReference<CredentialAuth> reference) {
logger.trace("Credential service {} added", reference);
- nullableCredService = bundleContext.getService(reference);
+ nullableCredService = bundleContext.getService(reference);
return nullableCredService;
}
@Override
- public void modifiedService(final ServiceReference<PasswordCredentialAuth> reference, final PasswordCredentialAuth service) {
+ public void modifiedService(final ServiceReference<CredentialAuth> reference, final CredentialAuth service) {
logger.trace("Replacing modified Credential service {}", reference);
nullableCredService = service;
}
@Override
- public void removedService(final ServiceReference<PasswordCredentialAuth> reference, final PasswordCredentialAuth service) {
+ public void removedService(final ServiceReference<CredentialAuth> reference, final CredentialAuth service) {
logger.trace("Removing Credential service {}. This AuthProvider will fail to authenticate every time", reference);
synchronized (CredentialServiceAuthProvider.this) {
nullableCredService = null;
}
}
};
- listenerTracker = new ServiceTracker<>(bundleContext, PasswordCredentialAuth.class, customizer);
+ listenerTracker = new ServiceTracker<>(bundleContext, CredentialAuth.class, customizer);
listenerTracker.open();
}
+++ /dev/null
-/*
- * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.aaa.odl;
-
-import org.opendaylight.aaa.api.CredentialAuth;
-import org.opendaylight.aaa.api.PasswordCredentials;
-
-public interface PasswordCredentialAuth extends CredentialAuth<PasswordCredentials> {
-}
import org.mockito.stubbing.Answer;
import org.opendaylight.aaa.api.AuthenticationException;
import org.opendaylight.aaa.api.Claim;
+import org.opendaylight.aaa.api.CredentialAuth;
import org.opendaylight.aaa.api.PasswordCredentials;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Filter;
public class CredentialServiceAuthProviderTest {
@Mock
- private PasswordCredentialAuth credAuth;
+ private CredentialAuth<PasswordCredentials> credAuth;
@Mock
private BundleContext ctx;
// If the combined connection attempt failed, set the node to connection failed
LOG.debug("Futures aggregation failed");
naSalNodeWriter.update(nodeId, delegateTopologyHandler.getFailedState(nodeId, node));
- // FIXME disconnect those which succeeded
- // just issue a delete on delegateTopologyHandler that gets handled on lower level
}
}, TypedActor.context().dispatcher());
@Override
public void onFailure(final Throwable t) {
- // FIXME unable to disconnect all the connections, what do we do now ?
+
}
});
// If the combined connection attempt failed, set the node to connection failed
LOG.debug("Futures aggregation failed");
naSalNodeWriter.update(nodeId, delegateTopologyHandler.getFailedState(nodeId, null));
- // FIXME disconnect those which succeeded
- // just issue a delete on delegateTopologyHandler that gets handled on lower level
}
});
return;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-//TODO make a global TransactionProvider for all Netconf sessions instead of each session having one.
public class TransactionProvider implements AutoCloseable{
private static final Logger LOG = LoggerFactory.getLogger(TransactionProvider.class);
Preconditions.checkState(dataBroker != null);
final WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
tx.put(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.create(NetconfState.class), state);
- // FIXME first attempt (right after we register to binding broker) always fails
- // Is it due to the fact that we are writing from the onSessionInitiated callback ?
try {
tx.submit().checkedGet();
LOG.debug("Netconf state updated successfully");
public static final String SOURCE_KEY = "source";
public static final String RPC_KEY = "rpc";
public static final String NOTIFICATION_ELEMENT_NAME = "notification";
+ public static final String EVENT_TIME = "eventTime";
public static final String MESSAGE_ID = "message-id";
public static final String SESSION_ID = "session-id";
public static final String URN_IETF_PARAMS_NETCONF_BASE_1_0 = "urn:ietf:params:netconf:base:1.0";
public static final String URN_IETF_PARAMS_NETCONF_BASE_1_1 = "urn:ietf:params:netconf:base:1.1";
public static final String URN_IETF_PARAMS_XML_NS_NETCONF_EXI_1_0 = "urn:ietf:params:xml:ns:netconf:exi:1.0";
+ public static final String URN_IETF_PARAMS_NETCONF_CAPABILITY_NOTIFICATION_1_0 = "urn:ietf:params:netconf:capability:notification:1.0";
public static final String URN_IETF_PARAMS_NETCONF_CAPABILITY_EXI_1_0 = "urn:ietf:params:netconf:capability:exi:1.0";
public static final String URN_IETF_PARAMS_XML_NS_YANG_IETF_NETCONF_MONITORING = "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring";
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
-import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPassword;
import org.opendaylight.netconf.api.NetconfMessage;
import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
import org.opendaylight.netconf.client.conf.NetconfClientConfiguration.NetconfClientProtocol;
import org.opendaylight.netconf.client.conf.NetconfClientConfigurationBuilder;
+import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
+import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPassword;
import org.opendaylight.protocol.framework.NeverReconnectStrategy;
private final long sessionId;
public TestingNetconfClient(String clientLabel,
- NetconfClientDispatcher netconfClientDispatcher, final NetconfClientConfiguration config) throws InterruptedException {
+ NetconfClientDispatcher netconfClientDispatcher, final NetconfClientConfiguration config) throws InterruptedException {
this.label = clientLabel;
sessionListener = config.getSessionListener();
Future<NetconfClientSession> clientFuture = netconfClientDispatcher.createClient(config);
- clientSession = get(clientFuture);//TODO: make static
+ clientSession = get(clientFuture);
this.sessionId = clientSession.getSessionId();
}
- private NetconfClientSession get(Future<NetconfClientSession> clientFuture) throws InterruptedException {
+ private static NetconfClientSession get(Future<NetconfClientSession> clientFuture) throws InterruptedException {
try {
return clientFuture.get();
} catch (CancellationException e) {
- throw new RuntimeException("Cancelling " + this, e);
+ throw new RuntimeException("Cancelling " + TestingNetconfClient.class.getSimpleName(), e);
} catch (ExecutionException e) {
- throw new IllegalStateException("Unable to create " + this, e);
+ throw new IllegalStateException("Unable to create " + TestingNetconfClient.class.getSimpleName(), e);
}
}
import org.opendaylight.netconf.api.monitoring.NetconfMonitoringService;
import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.netconf.impl.osgi.NetconfOperationRouter;
+import org.opendaylight.netconf.util.messages.SubtreeFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
Document rpcReply = operationRouter.onNetconfMessage(incomingDocument, session);
- rpcReply = SubtreeFilter.applySubtreeFilter(incomingDocument, rpcReply);
+ rpcReply = SubtreeFilter.applyRpcSubtreeFilter(incomingDocument, rpcReply);
session.onIncommingRpcSuccess();
public AsyncSshHandler(final AuthenticationHandler authenticationHandler, final SshClient sshClient) throws IOException {
this.authenticationHandler = Preconditions.checkNotNull(authenticationHandler);
this.sshClient = Preconditions.checkNotNull(sshClient);
- // Start just in case
- sshClient.start();
}
public static AsyncSshHandler createForNetconfSubsystem(final AuthenticationHandler authenticationHandler) throws IOException {
*/
public static final Date UNKNOWN_EVENT_TIME = new Date(0);
+ private final Date eventTime;
+
/**
* Create new notification and capture the timestamp in the constructor
*/
*/
public NetconfNotification(final Document notificationContent, final Date eventTime) {
super(wrapNotification(notificationContent, eventTime));
+ this.eventTime = eventTime;
+ }
+
+ /**
+ * @return notification event time
+ */
+ public Date getEventTime() {
+ return eventTime;
}
private static Document wrapNotification(final Document notificationContent, final Date eventTime) {
import config { prefix config; revision-date 2013-04-05; }
- description "TODO";
+ description
+ "This module contains the base YANG definitions for
+ netconf northbound notifications API";
revision "2015-08-06" {
description "Initial revision.";
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
+import java.util.Date;
import java.util.List;
import org.opendaylight.controller.config.util.xml.DocumentedException;
import org.opendaylight.controller.config.util.xml.XmlElement;
import org.opendaylight.netconf.notifications.NotificationListenerRegistration;
import org.opendaylight.netconf.notifications.impl.NetconfNotificationManager;
import org.opendaylight.netconf.util.mapping.AbstractSingletonNetconfOperation;
+import org.opendaylight.netconf.util.messages.SubtreeFilter;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.CreateSubscriptionInput;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.StreamNameType;
import org.slf4j.Logger;
operationElement.checkName(CREATE_SUBSCRIPTION);
operationElement.checkNamespace(CreateSubscriptionInput.QNAME.getNamespace().toString());
// FIXME reimplement using CODEC_REGISTRY and parse everything into generated class instance
- // Waiting ofr https://git.opendaylight.org/gerrit/#/c/13763/
+ // Binding doesn't support anyxml nodes yet, so filter could not be retrieved
+ // xml -> normalized node -> CreateSubscriptionInput conversion could be slower than current approach
- // FIXME filter could be supported same way as netconf server filters get and get-config results
final Optional<XmlElement> filter = operationElement.getOnlyChildElementWithSameNamespaceOptionally("filter");
- Preconditions.checkArgument(filter.isPresent() == false, "Filter element not yet supported");
// Replay not supported
final Optional<XmlElement> startTime = operationElement.getOnlyChildElementWithSameNamespaceOptionally("startTime");
}
final NotificationListenerRegistration notificationListenerRegistration =
- notifications.registerNotificationListener(streamNameType, new NotificationSubscription(netconfSession));
+ notifications.registerNotificationListener(streamNameType, new NotificationSubscription(netconfSession, filter));
subscriptions.add(notificationListenerRegistration);
return XmlUtil.createElement(document, XmlNetconfConstants.OK, Optional.<String>absent());
private static class NotificationSubscription implements NetconfNotificationListener {
private final NetconfSession currentSession;
+ private final Optional<XmlElement> filter;
- public NotificationSubscription(final NetconfSession currentSession) {
+ public NotificationSubscription(final NetconfSession currentSession, final Optional<XmlElement> filter) {
this.currentSession = currentSession;
+ this.filter = filter;
}
@Override
public void onNotification(final StreamNameType stream, final NetconfNotification notification) {
- currentSession.sendMessage(notification);
+ if (filter.isPresent()) {
+ try {
+ final Optional<Document> filtered = SubtreeFilter.applySubtreeNotificationFilter(this.filter.get(), notification.getDocument());
+ if (filtered.isPresent()) {
+ final Date eventTime = notification.getEventTime();
+ currentSession.sendMessage(new NetconfNotification(filtered.get(), eventTime));
+ }
+ } catch (DocumentedException e) {
+ LOG.warn(e.toString());
+ currentSession.sendMessage(notification);
+ }
+ } else {
+ currentSession.sendMessage(notification);
+ }
}
}
}
import config { prefix config; revision-date 2013-04-05; }
import netconf-northbound-notification { prefix nnn; revision-date 2015-08-06; }
- description "TODO";
+ description
+ "This module contains the base YANG definitions for
+ netconf northbound notifications implementation";
revision "2015-08-07"{
description "Initial revision.";
protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId,
final NetconfNode node) {
- //setup default values since default value is not supported yet in mdsal
- // TODO remove this when mdsal starts supporting default values
+ //setup default values since default value is not supported in mdsal
final Long defaultRequestTimeoutMillis = node.getDefaultRequestTimeoutMillis() == null ? DEFAULT_REQUEST_TIMEOUT_MILLIS : node.getDefaultRequestTimeoutMillis();
final Long keepaliveDelay = node.getKeepaliveDelay() == null ? DEFAULT_KEEPALIVE_DELAY : node.getKeepaliveDelay();
final Boolean reconnectOnChangedSchema = node.isReconnectOnChangedSchema() == null ? DEFAULT_RECONNECT_ON_CHANGED_SCHEMA : node.isReconnectOnChangedSchema();
public NetconfReconnectingClientConfiguration getClientConfig(final NetconfClientSessionListener listener, NetconfNode node) {
- //setup default values since default value is not supported yet in mdsal
- // TODO remove this when mdsal starts supporting default values
+ //setup default values since default value is not supported in mdsal
final long clientConnectionTimeoutMillis = node.getConnectionTimeoutMillis() == null ? DEFAULT_CONNECTION_TIMEOUT_MILLIS : node.getConnectionTimeoutMillis();
final long maxConnectionAttempts = node.getMaxConnectionAttempts() == null ? DEFAULT_MAX_CONNECTION_ATTEMPTS : node.getMaxConnectionAttempts();
final int betweenAttemptsTimeoutMillis = node.getBetweenAttemptsTimeoutMillis() == null ? DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS : node.getBetweenAttemptsTimeoutMillis();
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
-// TODO maybe rename to NetconfTopologyDispatcher?
public interface NetconfTopology {
String getTopologyId();
@Override
protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId,
final NetconfNode node) {
- //setup default values since default value is not supported yet in mdsal
- // TODO remove this when mdsal starts supporting default values
+ //setup default values since default value is not supported in mdsal
final Long defaultRequestTimeoutMillis = node.getDefaultRequestTimeoutMillis() == null ? DEFAULT_REQUEST_TIMEOUT_MILLIS : node.getDefaultRequestTimeoutMillis();
final Long keepaliveDelay = node.getKeepaliveDelay() == null ? DEFAULT_KEEPALIVE_DELAY : node.getKeepaliveDelay();
final Boolean reconnectOnChangedSchema = node.isReconnectOnChangedSchema() == null ? DEFAULT_RECONNECT_ON_CHANGED_SCHEMA : node.isReconnectOnChangedSchema();
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.netconf.impl;
+package org.opendaylight.netconf.util.messages;
import com.google.common.base.Optional;
import java.io.IOException;
import org.opendaylight.controller.config.util.xml.DocumentedException;
import org.opendaylight.controller.config.util.xml.XmlElement;
import org.opendaylight.controller.config.util.xml.XmlUtil;
-import org.opendaylight.netconf.util.mapping.AbstractNetconfOperation.OperationNameAndNamespace;
import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
+import org.opendaylight.netconf.util.mapping.AbstractNetconfOperation.OperationNameAndNamespace;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Attr;
public class SubtreeFilter {
private static final Logger LOG = LoggerFactory.getLogger(SubtreeFilter.class);
- static Document applySubtreeFilter(Document requestDocument, Document rpcReply) throws DocumentedException {
+ public static Document applyRpcSubtreeFilter(Document requestDocument, Document rpcReply) throws DocumentedException {
OperationNameAndNamespace operationNameAndNamespace = new OperationNameAndNamespace(requestDocument);
if (XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0.equals(operationNameAndNamespace.getNamespace()) &&
XmlNetconfConstants.GET.equals(operationNameAndNamespace.getOperationName()) ||
return rpcReply;
}
- // FIXME: rpcReply document must be reread otherwise some nodes do not inherit namespaces. (services/service)
- try {
- rpcReply = XmlUtil.readXmlToDocument(XmlUtil.toString(rpcReply, true));
- } catch (SAXException | IOException e) {
- LOG.error("Cannot transform document", e);
- throw new DocumentedException("Cannot transform document" + e);
- }
+ rpcReply = reReadDocument(rpcReply);
XmlElement filter = maybeFilter.get();
- if ("subtree".equals(filter.getAttribute("type"))||
- "subtree".equals(filter.getAttribute("type", XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0))) {
+ if (isSupported(filter)) {
// do
return filtered(maybeFilter.get(), rpcReply);
return rpcReply; // return identical document
}
+ /**
+ * Filters notification content. If filter type isn't of type "subtree", returns unchanged notification content.
+ * If no match is found, absent is returned.
+ * @param filter filter
+ * @param notification notification
+ * @return document containing filtered notification content
+ * @throws DocumentedException
+ */
+ public static Optional<Document> applySubtreeNotificationFilter(XmlElement filter, Document notification) throws DocumentedException {
+ notification = reReadDocument(notification);
+ removeEventTimeNode(notification);
+ if (isSupported(filter)) {
+ return Optional.fromNullable(filteredNotification(filter, notification));
+ }
+ return Optional.of(extractNotificationContent(notification));
+ }
+
+ private static Document reReadDocument(Document notification) throws DocumentedException {
+ // FIXME: rpcReply document must be reread otherwise some nodes do not inherit namespaces. (services/service)
+ try {
+ notification = XmlUtil.readXmlToDocument(XmlUtil.toString(notification, true));
+ } catch (SAXException | IOException e) {
+ LOG.error("Cannot transform document", e);
+ throw new DocumentedException("Cannot transform document" + e);
+ }
+ return notification;
+ }
+
+ private static void removeEventTimeNode(Document document) {
+ final Node eventTimeNode = document.getDocumentElement().getElementsByTagNameNS(
+ XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_CAPABILITY_NOTIFICATION_1_0, XmlNetconfConstants.EVENT_TIME).item(0);
+ document.getDocumentElement().removeChild(eventTimeNode);
+ }
+
+ private static boolean isSupported(XmlElement filter) {
+ return "subtree".equals(filter.getAttribute("type"))||
+ "subtree".equals(filter.getAttribute("type", XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0));
+ }
+
+ private static Document extractNotificationContent(Document notification) throws DocumentedException {
+ XmlElement root = XmlElement.fromDomElement(notification.getDocumentElement());
+ XmlElement content = root.getOnlyChildElement();
+ notification.removeChild(root.getDomElement());
+ notification.appendChild(content.getDomElement());
+ return notification;
+ }
+
+ private static Document filteredNotification(XmlElement filter, Document originalNotification) throws DocumentedException {
+ Document result = XmlUtil.newDocument();
+ XmlElement dataSrc = XmlElement.fromDomDocument(originalNotification);
+ Element dataDst = (Element) result.importNode(dataSrc.getDomElement(), false);
+ for (XmlElement filterChild : filter.getChildElements()) {
+ addSubtree2(filterChild, dataSrc.getOnlyChildElement(), XmlElement.fromDomElement(dataDst));
+ }
+ if(dataDst.getFirstChild() != null) {
+ result.appendChild(dataDst.getFirstChild());
+ return result;
+ } else {
+ return null;
+ }
+ }
+
private static Document filtered(XmlElement filter, Document originalReplyDocument) throws DocumentedException {
Document result = XmlUtil.newDocument();
// even if filter is empty, copy /rpc/data
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netconf.util.messages;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.base.Optional;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.custommonkey.xmlunit.Diff;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.opendaylight.controller.config.util.xml.XmlElement;
+import org.opendaylight.controller.config.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+@RunWith(value = Parameterized.class)
+public class SubtreeFilterNotificationTest {
+ private static final Logger LOG = LoggerFactory.getLogger(SubtreeFilterRpcTest.class);
+
+ private final int directoryIndex;
+
+ @Parameters
+ public static Collection<Object[]> data() {
+ List<Object[]> result = new ArrayList<>();
+ for (int i = 0; i < 5; i++) {
+ result.add(new Object[]{i});
+ }
+ return result;
+ }
+
+ public SubtreeFilterNotificationTest(int directoryIndex) {
+ this.directoryIndex = directoryIndex;
+ }
+
+ @Before
+ public void setUp(){
+ XMLUnit.setIgnoreWhitespace(true);
+ }
+
+ @Test
+ public void testFilterNotification() throws Exception {
+ XmlElement filter = XmlElement.fromDomDocument(getDocument("filter.xml"));
+ Document preFilterDocument = getDocument("pre-filter.xml");
+ Document postFilterDocument = getDocument("post-filter.xml");
+ Optional<Document> actualPostFilterDocumentOpt = SubtreeFilter.applySubtreeNotificationFilter(filter, preFilterDocument);
+ if(actualPostFilterDocumentOpt.isPresent()) {
+ Document actualPostFilterDocument = actualPostFilterDocumentOpt.get();
+ LOG.info("Actual document: {}", XmlUtil.toString(actualPostFilterDocument));
+ Diff diff = XMLUnit.compareXML(postFilterDocument, actualPostFilterDocument);
+ assertTrue(diff.toString(), diff.similar());
+ } else {
+ assertEquals("empty", XmlElement.fromDomDocument(postFilterDocument).getName());
+ }
+ }
+
+ public Document getDocument(String fileName) throws SAXException, IOException {
+ return XmlUtil.readXmlToDocument(getClass().getResourceAsStream("/subtree/notification/" + directoryIndex + "/" +
+ fileName));
+ }
+}
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.netconf.impl;
+package org.opendaylight.netconf.util.messages;
import static org.junit.Assert.assertTrue;
import org.xml.sax.SAXException;
@RunWith(value = Parameterized.class)
-public class SubtreeFilterTest {
- private static final Logger LOG = LoggerFactory.getLogger(SubtreeFilterTest.class);
+public class SubtreeFilterRpcTest {
+ private static final Logger LOG = LoggerFactory.getLogger(SubtreeFilterRpcTest.class);
private final int directoryIndex;
return result;
}
- public SubtreeFilterTest(int directoryIndex) {
+ public SubtreeFilterRpcTest(int directoryIndex) {
this.directoryIndex = directoryIndex;
}
Document requestDocument = getDocument("request.xml");
Document preFilterDocument = getDocument("pre-filter.xml");
Document postFilterDocument = getDocument("post-filter.xml");
- Document actualPostFilterDocument = SubtreeFilter.applySubtreeFilter(requestDocument, preFilterDocument);
+ Document actualPostFilterDocument = SubtreeFilter.applyRpcSubtreeFilter(requestDocument, preFilterDocument);
LOG.info("Actual document: {}", XmlUtil.toString(actualPostFilterDocument));
Diff diff = XMLUnit.compareXML(postFilterDocument, actualPostFilterDocument);
assertTrue(diff.toString(), diff.similar());
}
public Document getDocument(String fileName) throws SAXException, IOException {
- return XmlUtil.readXmlToDocument(getClass().getResourceAsStream("/subtree/" + directoryIndex + "/" +
+ return XmlUtil.readXmlToDocument(getClass().getResourceAsStream("/subtree/rpc/" + directoryIndex + "/" +
fileName));
}
}
--- /dev/null
+<!--
+ ~ Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ ~
+ ~ This program and the accompanying materials are made available under the
+ ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ ~ and is available at http://www.eclipse.org/legal/epl-v10.html
+ -->
+<filter xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0" type="subtree">
+ <netconf-session-end xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-notifications">
+ <username/>
+ <session-id/>
+ </netconf-session-end>
+</filter>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ ~
+ ~ This program and the accompanying materials are made available under the
+ ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ ~ and is available at http://www.eclipse.org/legal/epl-v10.html
+ -->
+<netconf-session-end xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-notifications">
+ <username>admin</username>
+ <session-id>2</session-id>
+</netconf-session-end>
+
+
+
--- /dev/null
+<!--
+ ~ Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ ~
+ ~ This program and the accompanying materials are made available under the
+ ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ ~ and is available at http://www.eclipse.org/legal/epl-v10.html
+ -->
+<notification xmlns="urn:ietf:params:netconf:capability:notification:1.0">
+ <netconf-session-end xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-notifications">
+ <username>admin</username>
+ <session-id>2</session-id>
+ <source-host>127.0.0.1</source-host>
+ </netconf-session-end>
+ <eventTime>2016-03-17T13:15:12+01:00</eventTime>
+</notification>
--- /dev/null
+<!--
+ ~ Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ ~
+ ~ This program and the accompanying materials are made available under the
+ ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ ~ and is available at http://www.eclipse.org/legal/epl-v10.html
+ -->
+<filter xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0" type="unsupported">
+ <netconf-session-end xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-notifications">
+ <username/>
+ <session-id/>
+ </netconf-session-end>
+</filter>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ ~
+ ~ This program and the accompanying materials are made available under the
+ ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ ~ and is available at http://www.eclipse.org/legal/epl-v10.html
+ -->
+<netconf-session-end xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-notifications">
+ <username>admin</username>
+ <session-id>2</session-id>
+ <source-host>127.0.0.1</source-host>
+</netconf-session-end>
--- /dev/null
+<!--
+ ~ Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ ~
+ ~ This program and the accompanying materials are made available under the
+ ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ ~ and is available at http://www.eclipse.org/legal/epl-v10.html
+ -->
+<notification xmlns="urn:ietf:params:netconf:capability:notification:1.0">
+ <netconf-session-end xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-notifications">
+ <username>admin</username>
+ <session-id>2</session-id>
+ <source-host>127.0.0.1</source-host>
+ </netconf-session-end>
+ <eventTime>2016-03-17T13:15:12+01:00</eventTime>
+</notification>
--- /dev/null
+<!--
+ ~ Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ ~
+ ~ This program and the accompanying materials are made available under the
+ ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ ~ and is available at http://www.eclipse.org/legal/epl-v10.html
+ -->
+<filter xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0" type="unsupported">
+ <netconf-session-end xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-notifications">
+ <username/>
+ <session-id/>
+ </netconf-session-end>
+</filter>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ ~
+ ~ This program and the accompanying materials are made available under the
+ ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ ~ and is available at http://www.eclipse.org/legal/epl-v10.html
+ -->
+<netconf-session-start xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-notifications">
+ <username>admin</username>
+ <session-id>2</session-id>
+ <source-host>127.0.0.1</source-host>
+</netconf-session-start>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ ~
+ ~ This program and the accompanying materials are made available under the
+ ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ ~ and is available at http://www.eclipse.org/legal/epl-v10.html
+ -->
+<notification xmlns="urn:ietf:params:netconf:capability:notification:1.0">
+ <netconf-session-start xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-notifications">
+ <username>admin</username>
+ <session-id>2</session-id>
+ <source-host>127.0.0.1</source-host>
+ </netconf-session-start>
+ <eventTime>2016-03-17T13:15:04+01:00</eventTime>
+</notification>
--- /dev/null
+<!--
+ ~ Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ ~
+ ~ This program and the accompanying materials are made available under the
+ ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ ~ and is available at http://www.eclipse.org/legal/epl-v10.html
+ -->
+<filter xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0" type="subtree">
+ <netconf-session-end xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-notifications">
+ <username/>
+ <session-id/>
+ </netconf-session-end>
+</filter>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ ~
+ ~ This program and the accompanying materials are made available under the
+ ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ ~ and is available at http://www.eclipse.org/legal/epl-v10.html
+ -->
+<empty/>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ ~
+ ~ This program and the accompanying materials are made available under the
+ ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ ~ and is available at http://www.eclipse.org/legal/epl-v10.html
+ -->
+<notification xmlns="urn:ietf:params:netconf:capability:notification:1.0">
+ <netconf-session-start xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-notifications">
+ <username>admin</username>
+ <session-id>2</session-id>
+ <source-host>127.0.0.1</source-host>
+ </netconf-session-start>
+ <eventTime>2016-03-17T13:15:04+01:00</eventTime>
+</notification>
--- /dev/null
+<!--
+ ~ Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ ~
+ ~ This program and the accompanying materials are made available under the
+ ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ ~ and is available at http://www.eclipse.org/legal/epl-v10.html
+ -->
+<filter xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0" type="subtree">
+ <netconf-session-end xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-notifications"/>
+</filter>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<netconf-session-end xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-notifications">
+ <username>admin</username>
+ <session-id>2</session-id>
+ <source-host>127.0.0.1</source-host>
+</netconf-session-end>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ ~
+ ~ This program and the accompanying materials are made available under the
+ ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ ~ and is available at http://www.eclipse.org/legal/epl-v10.html
+ -->
+<notification xmlns="urn:ietf:params:netconf:capability:notification:1.0">
+ <netconf-session-end xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-notifications">
+ <username>admin</username>
+ <session-id>2</session-id>
+ <source-host>127.0.0.1</source-host>
+ </netconf-session-end>
+ <eventTime>2016-03-17T13:15:12+01:00</eventTime>
+</notification>
\ No newline at end of file
}
private void setUpSchema(final DeviceSources result) {
- processingExecutor.submit(new RecursiveSchemaSetup(result, remoteSessionCapabilities, listener));
+ processingExecutor.submit(new SchemaSetup(result, remoteSessionCapabilities, listener));
}
@Override
/**
* Schema builder that tries to build schema context from provided sources or biggest subset of it.
*/
- private final class RecursiveSchemaSetup implements Runnable {
+ private final class SchemaSetup implements Runnable {
private final DeviceSources deviceSources;
private final NetconfSessionPreferences remoteSessionCapabilities;
private final RemoteDeviceCommunicator<NetconfMessage> listener;
private final NetconfDeviceCapabilities capabilities;
- public RecursiveSchemaSetup(final DeviceSources deviceSources, final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceCommunicator<NetconfMessage> listener) {
+ public SchemaSetup(final DeviceSources deviceSources, final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceCommunicator<NetconfMessage> listener) {
this.deviceSources = deviceSources;
this.remoteSessionCapabilities = remoteSessionCapabilities;
this.listener = listener;
}
/**
- * Recursively build schema context, in case of success or final failure notify device
+ * Build schema context, in case of success or final failure notify device
*/
- // FIXME reimplement without recursion
- private void setUpSchema(final Collection<SourceIdentifier> requiredSources) {
- LOG.trace("{}: Trying to build schema context from {}", id, requiredSources);
-
- // If no more sources, fail
- if(requiredSources.isEmpty()) {
- final IllegalStateException cause = new IllegalStateException(id + ": No more sources for schema context");
- handleSalInitializationFailure(cause, listener);
- salFacade.onDeviceFailed(cause);
- return;
- }
-
- final CheckedFuture<SchemaContext, SchemaResolutionException> schemaBuilderFuture = schemaContextFactory.createSchemaContext(requiredSources);
-
- final FutureCallback<SchemaContext> RecursiveSchemaBuilderCallback = new FutureCallback<SchemaContext>() {
-
- @Override
- public void onSuccess(final SchemaContext result) {
+ private void setUpSchema(Collection<SourceIdentifier> requiredSources) {
+ while (!requiredSources.isEmpty()) {
+ LOG.trace("{}: Trying to build schema context from {}", id, requiredSources);
+ try {
+ final CheckedFuture<SchemaContext, SchemaResolutionException> schemaBuilderFuture = schemaContextFactory.createSchemaContext(requiredSources);
+ final SchemaContext result = schemaBuilderFuture.checkedGet();
LOG.debug("{}: Schema context built successfully from {}", id, requiredSources);
final Collection<QName> filteredQNames = Sets.difference(deviceSources.getRequiredSourcesQName(), capabilities.getUnresolvedCapabilites().keySet());
capabilities.addCapabilities(filteredQNames);
capabilities.addNonModuleBasedCapabilities(remoteSessionCapabilities.getNonModuleCaps());
handleSalInitializationSuccess(result, remoteSessionCapabilities, getDeviceSpecificRpc(result));
- }
-
- @Override
- public void onFailure(final Throwable t) {
- // In case source missing, try without it
- if (t instanceof MissingSchemaSourceException) {
+ return;
+ } catch (Throwable t) {
+ if (t instanceof MissingSchemaSourceException){
+ // In case source missing, try without it
final SourceIdentifier missingSource = ((MissingSchemaSourceException) t).getSourceId();
LOG.warn("{}: Unable to build schema context, missing source {}, will reattempt without it", id, missingSource);
LOG.debug("{}: Unable to build schema context, missing source {}, will reattempt without it", t);
if (!qNameOfMissingSource.isEmpty()) {
capabilities.addUnresolvedCapabilities(qNameOfMissingSource, UnavailableCapability.FailureReason.MissingSource);
}
- setUpSchema(stripMissingSource(requiredSources, missingSource));
-
- // In case resolution error, try only with resolved sources
+ requiredSources = stripMissingSource(requiredSources, missingSource);
} else if (t instanceof SchemaResolutionException) {
- // TODO check for infinite loop
- final SchemaResolutionException resolutionException = (SchemaResolutionException) t;
+ // In case resolution error, try only with resolved sources
+ SchemaResolutionException resolutionException = (SchemaResolutionException) t;
final Set<SourceIdentifier> unresolvedSources = resolutionException.getUnsatisfiedImports().keySet();
capabilities.addUnresolvedCapabilities(getQNameFromSourceIdentifiers(unresolvedSources), UnavailableCapability.FailureReason.UnableToResolve);
LOG.warn("{}: Unable to build schema context, unsatisfied imports {}, will reattempt with resolved only", id, resolutionException.getUnsatisfiedImports());
LOG.debug("{}: Unable to build schema context, unsatisfied imports {}, will reattempt with resolved only", resolutionException);
- setUpSchema(resolutionException.getResolvedSources());
- // unknown error, fail
+ requiredSources = resolutionException.getResolvedSources();
} else {
+ // unknown error, fail
handleSalInitializationFailure(t, listener);
+ return;
}
}
- };
-
- Futures.addCallback(schemaBuilderFuture, RecursiveSchemaBuilderCallback);
+ }
+ // No more sources, fail
+ final IllegalStateException cause = new IllegalStateException(id + ": No more sources for schema context");
+ handleSalInitializationFailure(cause, listener);
+ salFacade.onDeviceFailed(cause);
}
+
protected NetconfDeviceRpc getDeviceSpecificRpc(final SchemaContext result) {
return new NetconfDeviceRpc(result, listener, new NetconfMessageTransformer(result, true));
}
* reconnecting strategy runs out of reconnection attempts
*/
public ListenableFuture<NetconfDeviceCapabilities> initializeRemoteConnection(final NetconfClientDispatcher dispatcher, final NetconfClientConfiguration config) {
- // TODO 2313 extract listener from configuration
if(config instanceof NetconfReconnectingClientConfiguration) {
initFuture = dispatcher.createReconnectingClient((NetconfReconnectingClientConfiguration) config);
} else {
@Override
public void onFailure(@Nonnull final Throwable t) {
- // User/Application RPC failed (The RPC did not reach the remote device or .. TODO what other reasons could cause this ?)
+ // User/Application RPC failed (The RPC did not reach the remote device.
// There is no point in keeping this session. Reconnect.
LOG.warn("{}: Rpc failure detected. Reconnecting netconf session", id, t);
reconnect();
private static final Function<RpcDefinition, DOMRpcIdentifier> RPC_TO_RPC_IDENTIFIER = new Function<RpcDefinition, DOMRpcIdentifier>() {
@Override
public DOMRpcIdentifier apply(final RpcDefinition input) {
- // TODO add support for routed rpcs ... is it necessary in this case ?
return DOMRpcIdentifier.create(input.getPath());
}
};
final ListenableFuture<DOMRpcResult> future;
if (isFilterPresent(filterPath)) {
- // FIXME the source node has to be wrapped in a choice
final DataContainerChild<?, ?> node = toFilterStructure(filterPath.get(), schemaContext);
future = rpc.invokeRpc(toPath(NETCONF_GET_CONFIG_QNAME),
NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_QNAME, getSourceNode(datastore), node));
org.opendaylight.aaa.shiro.filters,
org.opendaylight.aaa.shiro.realm,
org.opendaylight.aaa.shiro.web.env,
+ org.opendaylight.aaa.filterchain.filters,
org.apache.shiro.web.env
</Import-Package>
<Embed-Dependency>stax-utils</Embed-Dependency>
<url-pattern>/*</url-pattern>
</filter-mapping>
+ <filter>
+ <filter-name>DynamicFilterChain</filter-name>
+ <filter-class>org.opendaylight.aaa.filterchain.filters.CustomFilterAdapter</filter-class>
+ </filter>
+
+ <filter-mapping>
+ <filter-name>DynamicFilterChain</filter-name>
+ <url-pattern>/*</url-pattern>
+ </filter-mapping>
+
+
<servlet-mapping>
<servlet-name>JAXRSRestconf</servlet-name>
<url-pattern>/*</url-pattern>