@Arg(dest = "debug")
public boolean debug;
+ @Arg(dest = "notification-file")
+ public File notificationFile;
+
static ArgumentParser getParser() {
final ArgumentParser parser = ArgumentParsers.newArgumentParser("netconf testool");
.help("Directory containing yang schemas to describe simulated devices. Some schemas e.g. netconf monitoring and inet types are included by default")
.dest("schemas-dir");
+ parser.addArgument("--notification-file")
+ .type(File.class)
+ .help("Xml file containing notifications that should be sent to clients after create subscription is called")
+ .dest("notification-file");
+
parser.addArgument("--starting-port")
.type(Integer.class)
.setDefault(17830)
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.HashedWheelTimer;
import java.io.Closeable;
+import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.opendaylight.controller.netconf.ssh.SshProxyServer;
import org.opendaylight.controller.netconf.ssh.SshProxyServerConfiguration;
import org.opendaylight.controller.netconf.ssh.SshProxyServerConfigurationBuilder;
+import org.opendaylight.controller.netconf.test.tool.rpc.DataList;
+import org.opendaylight.controller.netconf.test.tool.rpc.SimulatedCommit;
+import org.opendaylight.controller.netconf.test.tool.rpc.SimulatedCreateSubscription;
+import org.opendaylight.controller.netconf.test.tool.rpc.SimulatedEditConfig;
+import org.opendaylight.controller.netconf.test.tool.rpc.SimulatedGet;
+import org.opendaylight.controller.netconf.test.tool.rpc.SimulatedGetConfig;
+import org.opendaylight.controller.netconf.test.tool.rpc.SimulatedLock;
+import org.opendaylight.controller.netconf.test.tool.rpc.SimulatedUnLock;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
this.nioExecutor = nioExecutor;
}
- private NetconfServerDispatcher createDispatcher(final Map<ModuleBuilder, String> moduleBuilders, final boolean exi, final int generateConfigsTimeout) {
+ private NetconfServerDispatcher createDispatcher(final Map<ModuleBuilder, String> moduleBuilders, final boolean exi, final int generateConfigsTimeout, final Optional<File> notificationsFile) {
final Set<Capability> capabilities = Sets.newHashSet(Collections2.transform(moduleBuilders.keySet(), new Function<ModuleBuilder, Capability>() {
@Override
final SessionIdProvider idProvider = new SessionIdProvider();
- final SimulatedOperationProvider simulatedOperationProvider = new SimulatedOperationProvider(idProvider, capabilities);
+ final SimulatedOperationProvider simulatedOperationProvider = new SimulatedOperationProvider(idProvider, capabilities, notificationsFile);
final NetconfMonitoringOperationService monitoringService = new NetconfMonitoringOperationService(new NetconfMonitoringServiceImpl(simulatedOperationProvider));
simulatedOperationProvider.addService(monitoringService);
final Map<ModuleBuilder, String> moduleBuilders = parseSchemasToModuleBuilders(params);
- final NetconfServerDispatcher dispatcher = createDispatcher(moduleBuilders, params.exi, params.generateConfigsTimeout);
+ final NetconfServerDispatcher dispatcher = createDispatcher(moduleBuilders, params.exi, params.generateConfigsTimeout, Optional.fromNullable(params.notificationFile));
int currentPort = params.startingPort;
private final Set<NetconfOperationService> netconfOperationServices;
- public SimulatedOperationProvider(final SessionIdProvider idProvider, final Set<Capability> caps) {
+ public SimulatedOperationProvider(final SessionIdProvider idProvider, final Set<Capability> caps, final Optional<File> notificationsFile) {
this.idProvider = idProvider;
- final SimulatedOperationService simulatedOperationService = new SimulatedOperationService(caps, idProvider.getCurrentSessionId());
+ final SimulatedOperationService simulatedOperationService = new SimulatedOperationService(caps, idProvider.getCurrentSessionId(), notificationsFile);
this.netconfOperationServices = Sets.<NetconfOperationService>newHashSet(simulatedOperationService);
}
static class SimulatedOperationService implements NetconfOperationService {
private final Set<Capability> capabilities;
private final long currentSessionId;
+ private final Optional<File> notificationsFile;
- public SimulatedOperationService(final Set<Capability> capabilities, final long currentSessionId) {
+ public SimulatedOperationService(final Set<Capability> capabilities, final long currentSessionId, final Optional<File> notificationsFile) {
this.capabilities = capabilities;
this.currentSessionId = currentSessionId;
+ this.notificationsFile = notificationsFile;
}
@Override
final SimulatedCommit sCommit = new SimulatedCommit(String.valueOf(currentSessionId));
final SimulatedLock sLock = new SimulatedLock(String.valueOf(currentSessionId));
final SimulatedUnLock sUnlock = new SimulatedUnLock(String.valueOf(currentSessionId));
- return Sets.<NetconfOperation>newHashSet(sGet, sGetConfig, sEditConfig, sCommit, sLock, sUnlock);
+ final SimulatedCreateSubscription sCreateSubs = new SimulatedCreateSubscription(String.valueOf(currentSessionId), notificationsFile);
+ return Sets.<NetconfOperation>newHashSet(sGet, sGetConfig, sEditConfig, sCommit, sLock, sUnlock, sCreateSubs);
}
@Override
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.controller.netconf.test.tool;
+package org.opendaylight.controller.netconf.test.tool.rpc;
import java.util.Collections;
import java.util.List;
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.controller.netconf.test.tool;
+package org.opendaylight.controller.netconf.test.tool.rpc;
import com.google.common.base.Optional;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
-class SimulatedCommit extends AbstractConfigNetconfOperation {
+public class SimulatedCommit extends AbstractConfigNetconfOperation {
- SimulatedCommit(final String netconfSessionIdForReporting) {
+ public SimulatedCommit(final String netconfSessionIdForReporting) {
super(null, netconfSessionIdForReporting);
}
--- /dev/null
+/*
+ * Copyright (c) 2015 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.controller.netconf.test.tool.rpc;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import java.io.File;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.bind.annotation.XmlRootElement;
+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.impl.NetconfServerSession;
+import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultNetconfOperation;
+import org.opendaylight.controller.netconf.util.mapping.AbstractLastNetconfOperation;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+public class SimulatedCreateSubscription extends AbstractLastNetconfOperation implements DefaultNetconfOperation {
+
+ private NetconfServerSession session;
+ private final Optional<Notifications> notifications;
+ private ScheduledExecutorService scheduledExecutorService;
+
+ public SimulatedCreateSubscription(final String id, final Optional<File> notificationsFile) {
+ super(id);
+ if(notificationsFile.isPresent()) {
+ notifications = Optional.of(loadNotifications(notificationsFile.get()));
+ scheduledExecutorService = Executors.newScheduledThreadPool(1);
+ } else {
+ notifications = Optional.absent();
+ }
+ }
+
+ private Notifications loadNotifications(final File file) {
+ try {
+ final JAXBContext jaxbContext = JAXBContext.newInstance(Notifications.class);
+ final Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
+ return (Notifications) jaxbUnmarshaller.unmarshal(file);
+ } catch (final JAXBException e) {
+ throw new IllegalArgumentException("Canot parse file " + file + " as a notifications file", e);
+ }
+ }
+
+ @Override
+ protected String getOperationName() {
+ return "create-subscription";
+ }
+
+ @Override
+ protected String getOperationNamespace() {
+ return "urn:ietf:params:xml:ns:netconf:notification:1.0";
+ }
+
+ @Override
+ protected Element handleWithNoSubsequentOperations(final Document document, final XmlElement operationElement) throws NetconfDocumentedException {
+
+
+ if(notifications.isPresent()) {
+ long delayAggregator = 0;
+ System.console().writer().println("Scheduling notifications " + notifications.get());
+
+ for (final Notification notification : notifications.get().getNotificationList()) {
+ for (int i = 0; i <= notification.getTimes(); i++) {
+
+ delayAggregator += notification.getDelayInSeconds();
+
+ System.console().writer().println("Times " + notification.getTimes());
+ scheduledExecutorService.schedule(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ System.console().writer().println("Sending actual notification " + notification);
+ Preconditions.checkState(session != null, "Session is not set, cannot process notifications");
+ session.sendMessage(parseNetconfNotification(notification.getContent()));
+ } catch (IOException | SAXException e) {
+ throw new IllegalStateException("Unable to process notification " + notification, e);
+ }
+ }
+ }, delayAggregator, TimeUnit.SECONDS);
+ }
+ }
+ }
+ return XmlUtil.createElement(document, XmlNetconfConstants.OK, Optional.<String>absent());
+ }
+
+ private static NetconfMessage parseNetconfNotification(String content) throws IOException, SAXException {
+ final int startEventTime = content.indexOf("<eventTime>") + "<eventTime>".length();
+ final int endEventTime = content.indexOf("</eventTime>");
+ final String eventTime = content.substring(startEventTime, endEventTime);
+ if(eventTime.equals("XXXX")) {
+ content = content.replace(eventTime, new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX").format(new Date()));
+ }
+
+ return new NetconfMessage(XmlUtil.readXmlToDocument(content));
+ }
+
+ @Override
+ public void setNetconfSession(final NetconfServerSession s) {
+ this.session = s;
+ }
+
+ @XmlRootElement(name = "notifications")
+ public static final class Notifications {
+
+ @javax.xml.bind.annotation.XmlElement(nillable = false, name = "notification", required = true)
+ private List<Notification> notificationList;
+
+ public List<Notification> getNotificationList() {
+ return notificationList;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuffer sb = new StringBuffer("Notifications{");
+ sb.append("notificationList=").append(notificationList);
+ sb.append('}');
+ return sb.toString();
+ }
+ }
+
+ public static final class Notification {
+
+ @javax.xml.bind.annotation.XmlElement(nillable = false, name = "delay")
+ private long delayInSeconds;
+
+ @javax.xml.bind.annotation.XmlElement(nillable = false, name = "times")
+ private long times;
+
+ @javax.xml.bind.annotation.XmlElement(nillable = false, name = "content", required = true)
+ private String content;
+
+ public long getDelayInSeconds() {
+ return delayInSeconds;
+ }
+
+ public long getTimes() {
+ return times;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuffer sb = new StringBuffer("Notification{");
+ sb.append("delayInSeconds=").append(delayInSeconds);
+ sb.append(", times=").append(times);
+ sb.append(", content='").append(content).append('\'');
+ sb.append('}');
+ return sb.toString();
+ }
+ }
+}
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.controller.netconf.test.tool;
+package org.opendaylight.controller.netconf.test.tool.rpc;
import com.google.common.base.Optional;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
-class SimulatedEditConfig extends AbstractConfigNetconfOperation {
+public class SimulatedEditConfig extends AbstractConfigNetconfOperation {
private static final String DELETE_EDIT_CONFIG = "delete";
private static final String OPERATION = "operation";
private static final String REMOVE_EDIT_CONFIG = "remove";
private final DataList storage;
- SimulatedEditConfig(final String netconfSessionIdForReporting, final DataList storage) {
+ public SimulatedEditConfig(final String netconfSessionIdForReporting, final DataList storage) {
super(null, netconfSessionIdForReporting);
this.storage = storage;
}
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.controller.netconf.test.tool;
+package org.opendaylight.controller.netconf.test.tool.rpc;
import com.google.common.base.Optional;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
-class SimulatedGet extends AbstractConfigNetconfOperation {
+public class SimulatedGet extends AbstractConfigNetconfOperation {
private final DataList storage;
- SimulatedGet(final String netconfSessionIdForReporting, final DataList storage) {
+ public SimulatedGet(final String netconfSessionIdForReporting, final DataList storage) {
super(null, netconfSessionIdForReporting);
this.storage = storage;
}
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.controller.netconf.test.tool;
+package org.opendaylight.controller.netconf.test.tool.rpc;
import com.google.common.base.Optional;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
-class SimulatedGetConfig extends AbstractConfigNetconfOperation {
+public class SimulatedGetConfig extends AbstractConfigNetconfOperation {
private final DataList storage;
- SimulatedGetConfig(final String netconfSessionIdForReporting, final DataList storage) {
+ public SimulatedGetConfig(final String netconfSessionIdForReporting, final DataList storage) {
super(null, netconfSessionIdForReporting);
this.storage = storage;
}
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.controller.netconf.test.tool;
+package org.opendaylight.controller.netconf.test.tool.rpc;
import com.google.common.base.Optional;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
-class SimulatedLock extends AbstractConfigNetconfOperation {
+public class SimulatedLock extends AbstractConfigNetconfOperation {
- SimulatedLock(final String netconfSessionIdForReporting) {
+ public SimulatedLock(final String netconfSessionIdForReporting) {
super(null, netconfSessionIdForReporting);
}
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.controller.netconf.test.tool;
+package org.opendaylight.controller.netconf.test.tool.rpc;
import com.google.common.base.Optional;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
-class SimulatedUnLock extends AbstractConfigNetconfOperation {
+public class SimulatedUnLock extends AbstractConfigNetconfOperation {
- SimulatedUnLock(final String netconfSessionIdForReporting) {
+ public SimulatedUnLock(final String netconfSessionIdForReporting) {
super(null, netconfSessionIdForReporting);
}