import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
import org.opendaylight.netconf.client.NetconfClientDispatcher;
+import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory;
import org.opendaylight.netconf.topology.AbstractNetconfTopology;
import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider;
final SchemaRepositoryProvider schemaRepositoryProvider,
final DataBroker dataBroker,
final DOMMountPointService mountPointService,
- final AAAEncryptionService encryptionService) {
+ final AAAEncryptionService encryptionService,
+ final DeviceActionFactory deviceActionFactory) {
super(topologyId, clientDispatcher, eventExecutor, keepaliveExecutor,
processingExecutor, schemaRepositoryProvider, dataBroker, mountPointService,
- encryptionService);
+ encryptionService, deviceActionFactory);
}
}
import org.opendaylight.netconf.client.NetconfClientSession;
import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
+import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory;
import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider;
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;
}
};
+ private DeviceActionFactory deviceActionFactory;
+
public CallHomeMountDispatcher(final String topologyId, final EventExecutor eventExecutor,
final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor,
final SchemaRepositoryProvider schemaRepositoryProvider, final DataBroker dataBroker,
final DOMMountPointService mountService,
final AAAEncryptionService encryptionService) {
+ this(topologyId, eventExecutor, keepaliveExecutor, processingExecutor, schemaRepositoryProvider, dataBroker,
+ mountService, encryptionService, null);
+ }
+
+ public CallHomeMountDispatcher(final String topologyId, final EventExecutor eventExecutor,
+ final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor,
+ final SchemaRepositoryProvider schemaRepositoryProvider, final DataBroker dataBroker,
+ final DOMMountPointService mountService,
+ final AAAEncryptionService encryptionService, DeviceActionFactory deviceActionFactory) {
this.topologyId = topologyId;
this.eventExecutor = eventExecutor;
this.keepaliveExecutor = keepaliveExecutor;
this.processingExecutor = processingExecutor;
this.schemaRepositoryProvider = schemaRepositoryProvider;
+ this.deviceActionFactory = deviceActionFactory;
this.sessionManager = new CallHomeMountSessionManager();
this.dataBroker = dataBroker;
this.mountService = mountService;
void createTopology() {
this.topology = new CallHomeTopology(topologyId, this, eventExecutor, keepaliveExecutor, processingExecutor,
- schemaRepositoryProvider, dataBroker, mountService, encryptionService);
+ schemaRepositoryProvider, dataBroker, mountService, encryptionService, deviceActionFactory);
}
@Override
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
import org.opendaylight.netconf.client.NetconfClientDispatcher;
+import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory;
import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceSalFacade;
public class CallHomeTopology extends BaseCallHomeTopology {
+ public CallHomeTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher,
+ final EventExecutor eventExecutor,
+ final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor,
+ final SchemaRepositoryProvider schemaRepositoryProvider,
+ final DataBroker dataBroker, final DOMMountPointService mountPointService,
+ final AAAEncryptionService encryptionService) {
+ this(topologyId, clientDispatcher, eventExecutor,
+ keepaliveExecutor, processingExecutor, schemaRepositoryProvider,
+ dataBroker, mountPointService, encryptionService, null);
+ }
+
public CallHomeTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher,
final EventExecutor eventExecutor,
final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor,
final SchemaRepositoryProvider schemaRepositoryProvider,
final DataBroker dataBroker, final DOMMountPointService mountPointService,
- final AAAEncryptionService encryptionService) {
+ final AAAEncryptionService encryptionService,
+ final DeviceActionFactory deviceActionFactory) {
super(topologyId, clientDispatcher, eventExecutor,
keepaliveExecutor, processingExecutor, schemaRepositoryProvider,
- dataBroker, mountPointService, encryptionService);
+ dataBroker, mountPointService, encryptionService, deviceActionFactory);
}
@Override
import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder;
import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
+import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory;
import org.opendaylight.netconf.sal.connect.api.RemoteDevice;
import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas;
}
}
- protected final String topologyId;
private final NetconfClientDispatcher clientDispatcher;
private final EventExecutor eventExecutor;
+ private final DeviceActionFactory deviceActionFactory;
+ private final NetconfKeystoreAdapter keystoreAdapter;
protected final ScheduledThreadPool keepaliveExecutor;
protected final ThreadPool processingExecutor;
protected final SharedSchemaRepository sharedSchemaRepository;
protected final DataBroker dataBroker;
protected final DOMMountPointService mountPointService;
- private final NetconfKeystoreAdapter keystoreAdapter;
+ protected final String topologyId;
protected SchemaSourceRegistry schemaRegistry = DEFAULT_SCHEMA_REPOSITORY;
protected SchemaRepository schemaRepository = DEFAULT_SCHEMA_REPOSITORY;
protected SchemaContextFactory schemaContextFactory = DEFAULT_SCHEMA_CONTEXT_FACTORY;
final ThreadPool processingExecutor,
final SchemaRepositoryProvider schemaRepositoryProvider,
final DataBroker dataBroker, final DOMMountPointService mountPointService,
- final AAAEncryptionService encryptionService) {
+ final AAAEncryptionService encryptionService,
+ final DeviceActionFactory deviceActionFactory) {
this.topologyId = topologyId;
this.clientDispatcher = clientDispatcher;
this.eventExecutor = eventExecutor;
this.keepaliveExecutor = keepaliveExecutor;
this.processingExecutor = processingExecutor;
+ this.deviceActionFactory = deviceActionFactory;
this.sharedSchemaRepository = schemaRepositoryProvider.getSharedSchemaRepository();
this.dataBroker = dataBroker;
this.mountPointService = mountPointService;
if (keepaliveDelay > 0) {
LOG.warn("Adding keepalive facade, for device {}", nodeId);
- salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, keepaliveExecutor.getExecutor(),
+ salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, this.keepaliveExecutor.getExecutor(),
keepaliveDelay, defaultRequestTimeoutMillis);
}
if (node.isSchemaless()) {
device = new SchemalessNetconfDevice(remoteDeviceId, salFacade);
} else {
- device = new NetconfDeviceBuilder()
+ NetconfDeviceBuilder netconfDeviceBuilder = new NetconfDeviceBuilder()
.setReconnectOnSchemasChange(reconnectOnChangedSchema)
.setSchemaResourcesDTO(schemaResourcesDTO)
- .setGlobalProcessingExecutor(processingExecutor.getExecutor())
+ .setGlobalProcessingExecutor(this.processingExecutor.getExecutor())
.setId(remoteDeviceId)
- .setSalFacade(salFacade)
- .build();
+ .setSalFacade(salFacade);
+ if (this.deviceActionFactory != null) {
+ netconfDeviceBuilder.setDeviceActionFactory(this.deviceActionFactory);
+ }
+ device = netconfDeviceBuilder.build();
}
final Optional<UserPreferences> userCapabilities = getUserCapabilities(node);
import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
import org.opendaylight.mdsal.common.api.CommitInfo;
import org.opendaylight.netconf.client.NetconfClientDispatcher;
+import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory;
import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceSalFacade;
private ListenerRegistration<NetconfTopologyImpl> datastoreListenerRegistration = null;
public NetconfTopologyImpl(final String topologyId, final NetconfClientDispatcher clientDispatcher,
- final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
- final ThreadPool processingExecutor,
- final SchemaRepositoryProvider schemaRepositoryProvider,
- final DataBroker dataBroker, final DOMMountPointService mountPointService,
- final AAAEncryptionService encryptionService) {
+ final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
+ final ThreadPool processingExecutor,
+ final SchemaRepositoryProvider schemaRepositoryProvider,
+ final DataBroker dataBroker, final DOMMountPointService mountPointService,
+ final AAAEncryptionService encryptionService) {
+ this(topologyId, clientDispatcher, eventExecutor, keepaliveExecutor, processingExecutor,
+ schemaRepositoryProvider, dataBroker, mountPointService, encryptionService, null);
+ }
+
+ public NetconfTopologyImpl(final String topologyId, final NetconfClientDispatcher clientDispatcher,
+ final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
+ final ThreadPool processingExecutor,
+ final SchemaRepositoryProvider schemaRepositoryProvider,
+ final DataBroker dataBroker, final DOMMountPointService mountPointService,
+ final AAAEncryptionService encryptionService,
+ final DeviceActionFactory deviceActionFactory) {
super(topologyId, clientDispatcher, eventExecutor, keepaliveExecutor, processingExecutor,
- schemaRepositoryProvider, dataBroker, mountPointService, encryptionService);
+ schemaRepositoryProvider, dataBroker, mountPointService, encryptionService, deviceActionFactory);
}
@Override
--- /dev/null
+/*
+ * Copyright (c) 2018 Pantheon Technologies s.r.o. 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.sal.connect.api;
+
+import org.opendaylight.controller.md.sal.dom.api.DOMActionService;
+import org.opendaylight.netconf.api.NetconfMessage;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public interface DeviceActionFactory {
+
+ /**
+ * Allows user to create DOMActionService for specific device.
+ *
+ * @param messageTransformer - message transformer (for action in this case)
+ * @param listener - allows specific service to send and receive messages to/from device
+ * @param schemaContext - schema context of device
+ * @return {@link DOMActionService} of specific device
+ */
+ default DOMActionService createDeviceAction(MessageTransformer<NetconfMessage> messageTransformer,
+ RemoteDeviceCommunicator<NetconfMessage> listener, SchemaContext schemaContext) {
+ return null;
+ }
+}
+
import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.mdsal.dom.api.DOMActionResult;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
DOMRpcResult toRpcResult(M message, SchemaPath rpc);
+ /**
+ * Parse action into message for request.
+ *
+ * @param action - action schema path
+ * @param domDataTreeIdentifier - identifier of action
+ * @param payload - input of action
+ * @return message
+ */
+ default M toActionRequest(SchemaPath action, DOMDataTreeIdentifier domDataTreeIdentifier, NormalizedNode<?,
+ ?> payload) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Parse result of invoking action into DOM result.
+ *
+ * @param action - action schema path
+ * @param message - message to parsing
+ * @return {@link DOMActionResult}
+ */
+ default DOMActionResult toActionResult(SchemaPath action, M message) {
+ throw new UnsupportedOperationException();
+ }
}
*/
package org.opendaylight.netconf.sal.connect.api;
+import org.opendaylight.controller.md.sal.dom.api.DOMActionService;
import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
public interface RemoteDeviceHandler<PREF> extends AutoCloseable {
- void onDeviceConnected(SchemaContext remoteSchemaContext,
- PREF netconfSessionPreferences, DOMRpcService deviceRpc);
+ /**
+ * When device connected, init new mount point with specific schema context and DOM services.
+ *
+ * @param remoteSchemaContext - schema context of connected device
+ * @param netconfSessionPreferences - session of device
+ * @param deviceRpc - {@link DOMRpcService} of device
+ */
+ default void onDeviceConnected(SchemaContext remoteSchemaContext, PREF netconfSessionPreferences,
+ DOMRpcService deviceRpc) {
+ // DO NOTHING
+ }
+
+ /**
+ * When device connected, init new mount point with specific schema context and DOM services.
+ *
+ * @param remoteSchemaContext - schema context of connected device
+ * @param netconfSessionPreferences - session of device
+ * @param deviceRpc - {@link DOMRpcService} of device
+ * @param deviceAction - {@link DOMActionService} of device
+ */
+ default void onDeviceConnected(SchemaContext remoteSchemaContext, PREF netconfSessionPreferences,
+ DOMRpcService deviceRpc, DOMActionService deviceAction) {
+ // DO NOTHING
+ }
void onDeviceDisconnected();
import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
import org.opendaylight.netconf.api.NetconfMessage;
+import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory;
import org.opendaylight.netconf.sal.connect.api.MessageTransformer;
import org.opendaylight.netconf.sal.connect.api.NetconfDeviceSchemas;
import org.opendaylight.netconf.sal.connect.api.NetconfDeviceSchemasResolver;
private static final Logger LOG = LoggerFactory.getLogger(NetconfDevice.class);
protected final RemoteDeviceId id;
- private final boolean reconnectOnSchemasChange;
-
protected final SchemaContextFactory schemaContextFactory;
- private final RemoteDeviceHandler<NetconfSessionPreferences> salFacade;
- private final ListeningExecutorService processingExecutor;
protected final SchemaSourceRegistry schemaRegistry;
protected final SchemaRepository schemaRepository;
- private final NetconfDeviceSchemasResolver stateSchemasResolver;
- private final NotificationHandler notificationHandler;
+
protected final List<SchemaSourceRegistration<? extends SchemaSourceRepresentation>> sourceRegistrations =
new ArrayList<>();
+
+ private final RemoteDeviceHandler<NetconfSessionPreferences> salFacade;
+ private final ListeningExecutorService processingExecutor;
+ private final DeviceActionFactory deviceActionFactory;
+ private final NetconfDeviceSchemasResolver stateSchemasResolver;
+ private final NotificationHandler notificationHandler;
+ private final boolean reconnectOnSchemasChange;
+
@GuardedBy("this")
private boolean connected = false;
public NetconfDevice(final SchemaResourcesDTO schemaResourcesDTO, final RemoteDeviceId id,
final RemoteDeviceHandler<NetconfSessionPreferences> salFacade,
final ExecutorService globalProcessingExecutor, final boolean reconnectOnSchemasChange) {
+ this(schemaResourcesDTO, id, salFacade, globalProcessingExecutor, reconnectOnSchemasChange, null);
+ }
+
+ public NetconfDevice(final SchemaResourcesDTO schemaResourcesDTO, final RemoteDeviceId id,
+ final RemoteDeviceHandler<NetconfSessionPreferences> salFacade,
+ final ExecutorService globalProcessingExecutor, final boolean reconnectOnSchemasChange,
+ final DeviceActionFactory deviceActionFactory) {
this.id = id;
this.reconnectOnSchemasChange = reconnectOnSchemasChange;
+ this.deviceActionFactory = deviceActionFactory;
this.schemaRegistry = schemaResourcesDTO.getSchemaRegistry();
this.schemaRepository = schemaResourcesDTO.getSchemaRepository();
this.schemaContextFactory = schemaResourcesDTO.getSchemaContextFactory();
private synchronized void handleSalInitializationSuccess(final SchemaContext result,
final NetconfSessionPreferences remoteSessionCapabilities,
- final DOMRpcService deviceRpc) {
+ final DOMRpcService deviceRpc,
+ final RemoteDeviceCommunicator<NetconfMessage> listener) {
//NetconfDevice.SchemaSetup can complete after NetconfDeviceCommunicator was closed. In that case do nothing,
//since salFacade.onDeviceDisconnected was already called.
if (connected) {
final BaseSchema baseSchema =
remoteSessionCapabilities.isNotificationsSupported()
? BaseSchema.BASE_NETCONF_CTX_WITH_NOTIFICATIONS : BaseSchema.BASE_NETCONF_CTX;
- messageTransformer = new NetconfMessageTransformer(result, true, baseSchema);
+ this.messageTransformer = new NetconfMessageTransformer(result, true, baseSchema);
- updateTransformer(messageTransformer);
+ updateTransformer(this.messageTransformer);
// salFacade.onDeviceConnected has to be called before the notification handler is initialized
- salFacade.onDeviceConnected(result, remoteSessionCapabilities, deviceRpc);
- notificationHandler.onRemoteSchemaUp(messageTransformer);
+ this.salFacade.onDeviceConnected(result, remoteSessionCapabilities, deviceRpc,
+ this.deviceActionFactory == null ? null : this.deviceActionFactory.createDeviceAction(
+ this.messageTransformer, listener, result));
+ this.notificationHandler.onRemoteSchemaUp(this.messageTransformer);
LOG.info("{}: Netconf connector initialized successfully", id);
} else {
/**
* Build schema context, in case of success or final failure notify device.
+ *
+ * @param requiredSources required sources
*/
@SuppressWarnings("checkstyle:IllegalCatch")
private void setUpSchema(Collection<SourceIdentifier> requiredSources) {
remoteSessionCapabilities.getNonModuleBasedCapsOrigin().get(entry)).build())
.collect(Collectors.toList()));
- handleSalInitializationSuccess(result, remoteSessionCapabilities, getDeviceSpecificRpc(result));
+ handleSalInitializationSuccess(result, remoteSessionCapabilities, getDeviceSpecificRpc(result),
+ listener);
return;
} catch (final ExecutionException e) {
// schemaBuilderFuture.checkedGet() throws only SchemaResolutionException
import com.google.common.base.Preconditions;
import java.util.concurrent.ExecutorService;
+import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory;
import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
private RemoteDeviceId id;
private RemoteDeviceHandler<NetconfSessionPreferences> salFacade;
private ExecutorService globalProcessingExecutor;
+ private DeviceActionFactory deviceActionFactory;
public NetconfDeviceBuilder() {
}
return this;
}
+ public NetconfDeviceBuilder setDeviceActionFactory(DeviceActionFactory deviceActionFactory) {
+ this.deviceActionFactory = deviceActionFactory;
+ return this;
+ }
+
public NetconfDevice build() {
validation();
- return new NetconfDevice(schemaResourcesDTO, id, salFacade, globalProcessingExecutor, reconnectOnSchemasChange);
+ return new NetconfDevice(this.schemaResourcesDTO, this.id, this.salFacade, this.globalProcessingExecutor,
+ this.reconnectOnSchemasChange, this.deviceActionFactory);
}
private void validation() {
- Preconditions.checkNotNull(id, "RemoteDeviceId is not initialized");
- Preconditions.checkNotNull(salFacade, "RemoteDeviceHandler is not initialized");
- Preconditions.checkNotNull(globalProcessingExecutor, "ExecutorService is not initialized");
- Preconditions.checkNotNull(schemaResourcesDTO, "SchemaResourceDTO is not initialized");
+ Preconditions.checkNotNull(this.id, "RemoteDeviceId is not initialized");
+ Preconditions.checkNotNull(this.salFacade, "RemoteDeviceHandler is not initialized");
+ Preconditions.checkNotNull(this.globalProcessingExecutor, "ExecutorService is not initialized");
+ Preconditions.checkNotNull(this.schemaResourcesDTO, "SchemaResourceDTO is not initialized");
}
}
import com.google.common.collect.Lists;
import java.util.List;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMActionService;
import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
private final List<AutoCloseable> salRegistrations = Lists.newArrayList();
public NetconfDeviceSalFacade(final RemoteDeviceId id, final DOMMountPointService mountPointService,
- final DataBroker dataBroker) {
+ final DataBroker dataBroker) {
this.id = id;
this.salProvider = new NetconfDeviceSalProvider(id, mountPointService, dataBroker);
}
@Override
public synchronized void onDeviceConnected(final SchemaContext schemaContext,
final NetconfSessionPreferences netconfSessionPreferences,
- final DOMRpcService deviceRpc) {
+ final DOMRpcService deviceRpc, DOMActionService deviceAction) {
final DOMDataBroker domBroker =
new NetconfDeviceDataBroker(id, schemaContext, deviceRpc, netconfSessionPreferences);
final NetconfDeviceNotificationService notificationService = new NetconfDeviceNotificationService();
salProvider.getMountInstance()
- .onTopologyDeviceConnected(schemaContext, domBroker, deviceRpc, notificationService);
+ .onTopologyDeviceConnected(schemaContext, domBroker, deviceRpc, notificationService, deviceAction);
salProvider.getTopologyDatastoreAdapter()
.updateDeviceData(true, netconfSessionPreferences.getNetconfDeviceCapabilities());
}
import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMActionService;
import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
}
};
- public NetconfDeviceSalProvider(final RemoteDeviceId deviceId, final DOMMountPointService mountService,
- final DataBroker dataBroker) {
- this.id = deviceId;
- mountInstance = new MountInstance(mountService, id);
- this.dataBroker = dataBroker;
- txChain = Preconditions.checkNotNull(dataBroker).createTransactionChain(transactionChainListener);
-
- topologyDatastoreAdapter = new NetconfDeviceTopologyAdapter(id, txChain);
+ public NetconfDeviceSalProvider(final RemoteDeviceId deviceId, final DOMMountPointService mountService) {
+ this(deviceId, mountService, null);
}
- public NetconfDeviceSalProvider(final RemoteDeviceId deviceId, final DOMMountPointService mountService) {
+ public NetconfDeviceSalProvider(final RemoteDeviceId deviceId, final DOMMountPointService mountService,
+ final DataBroker dataBroker) {
this.id = deviceId;
mountInstance = new MountInstance(mountService, id);
- this.dataBroker = null;
+ this.dataBroker = dataBroker;
+ if (dataBroker != null) {
+ txChain = Preconditions.checkNotNull(dataBroker).createTransactionChain(transactionChainListener);
+ topologyDatastoreAdapter = new NetconfDeviceTopologyAdapter(id, txChain);
+ }
}
public MountInstance getMountInstance() {
private final DOMMountPointService mountService;
private final RemoteDeviceId id;
- private NetconfDeviceNotificationService notificationService;
+ private NetconfDeviceNotificationService notificationService;
private ObjectRegistration<DOMMountPoint> topologyRegistration;
MountInstance(final DOMMountPointService mountService, final RemoteDeviceId id) {
this.id = Preconditions.checkNotNull(id);
}
- public synchronized void onTopologyDeviceConnected(final SchemaContext initialCtx,
+ public void onTopologyDeviceConnected(final SchemaContext initialCtx,
final DOMDataBroker broker, final DOMRpcService rpc,
final NetconfDeviceNotificationService newNotificationService) {
+ onTopologyDeviceConnected(initialCtx, broker, rpc, newNotificationService, null);
+ }
+
+ public synchronized void onTopologyDeviceConnected(final SchemaContext initialCtx,
+ final DOMDataBroker broker, final DOMRpcService rpc,
+ final NetconfDeviceNotificationService newNotificationService, DOMActionService deviceAction) {
Preconditions.checkNotNull(mountService, "Closed");
Preconditions.checkState(topologyRegistration == null, "Already initialized");
mountBuilder.addService(DOMDataBroker.class, broker);
mountBuilder.addService(DOMRpcService.class, rpc);
mountBuilder.addService(DOMNotificationService.class, newNotificationService);
+ if (deviceAction != null) {
+ mountBuilder.addService(DOMActionService.class, deviceAction);
+ }
this.notificationService = newNotificationService;
topologyRegistration = mountBuilder.register();
LOG.debug("{}: TOPOLOGY Mountpoint exposed into MD-SAL {}", id, topologyRegistration);
-
}
@SuppressWarnings("checkstyle:IllegalCatch")
import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_URI;
import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.toPath;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import java.util.Collections;
import java.util.Date;
import java.util.Map;
+import java.util.Set;
import javax.annotation.Nonnull;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLStreamException;
import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
+import org.opendaylight.mdsal.dom.api.DOMActionResult;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult;
import org.opendaylight.netconf.api.NetconfMessage;
import org.opendaylight.netconf.api.xml.MissingNameSpaceException;
import org.opendaylight.netconf.api.xml.XmlElement;
import org.opendaylight.netconf.sal.connect.util.MessageCounter;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
+import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaNode;
private final MessageCounter counter;
private final Map<QName, RpcDefinition> mappedRpcs;
private final Multimap<QName, NotificationDefinition> mappedNotifications;
-
private final boolean strictParsing;
+ private final Set<ActionDefinition> actions;
public NetconfMessageTransformer(final SchemaContext schemaContext, final boolean strictParsing) {
this(schemaContext, strictParsing, BaseSchema.BASE_NETCONF_CTX);
final BaseSchema baseSchema) {
this.counter = new MessageCounter();
this.schemaContext = schemaContext;
- mappedRpcs = Maps.uniqueIndex(schemaContext.getOperations(), SchemaNode::getQName);
- mappedNotifications = Multimaps.index(schemaContext.getNotifications(),
+ this.mappedRpcs = Maps.uniqueIndex(schemaContext.getOperations(), SchemaNode::getQName);
+ this.actions = getActions();
+ this.mappedNotifications = Multimaps.index(schemaContext.getNotifications(),
node -> node.getQName().withoutRevision());
this.baseSchema = baseSchema;
this.strictParsing = strictParsing;
}
+ @VisibleForTesting
+ Set<ActionDefinition> getActions() {
+ Builder<ActionDefinition> builder = ImmutableSet.builder();
+ for (DataSchemaNode dataSchemaNode : schemaContext.getChildNodes()) {
+ if (dataSchemaNode instanceof ActionNodeContainer) {
+ findAction(dataSchemaNode, builder);
+ }
+ }
+ return builder.build();
+ }
+
+ private void findAction(DataSchemaNode dataSchemaNode, Builder<ActionDefinition> builder) {
+ if (dataSchemaNode instanceof ActionNodeContainer) {
+ final ActionNodeContainer containerSchemaNode = (ActionNodeContainer) dataSchemaNode;
+ for (ActionDefinition actionDefinition : containerSchemaNode.getActions()) {
+ builder.add(actionDefinition);
+ }
+ }
+ if (dataSchemaNode instanceof DataNodeContainer) {
+ for (DataSchemaNode innerDataSchemaNode : ((DataNodeContainer) dataSchemaNode).getChildNodes()) {
+ findAction(innerDataSchemaNode, builder);
+ }
+ }
+ }
+
@Override
public synchronized DOMNotification toNotification(final NetconfMessage message) {
final Map.Entry<Date, XmlElement> stripped = NetconfMessageTransformUtil.stripNotification(message);
}
Preconditions.checkNotNull(payload, "Transforming an rpc with input: %s, payload cannot be null", rpcQName);
+
Preconditions.checkArgument(payload instanceof ContainerNode,
"Transforming an rpc with input: %s, payload has to be a container, but was: %s", rpcQName, payload);
-
// Set the path to the input of rpc for the node stream writer
rpc = rpc.createChild(QName.create(rpcQName, "input").intern());
final DOMResult result = NetconfMessageTransformUtil.prepareDomResultForRpcRequest(rpcQName, counter);
return new NetconfMessage(node);
}
+ @Override
+ public NetconfMessage toActionRequest(SchemaPath action, DOMDataTreeIdentifier domDataTreeIdentifier,
+ final NormalizedNode<?, ?> payload) {
+ ActionDefinition actionDefinition = null;
+ SchemaPath schemaPath = action;
+ for (ActionDefinition actionDef : actions) {
+ if (actionDef.getPath().getLastComponent().equals(action.getLastComponent())) {
+ actionDefinition = actionDef;
+ schemaPath = actionDef.getPath();
+ }
+ }
+ Preconditions.checkNotNull(actionDefinition, "Action does not exist: %s", action.getLastComponent());
+
+ if (actionDefinition.getInput().getChildNodes().isEmpty()) {
+ return new NetconfMessage(NetconfMessageTransformUtil.prepareDomResultForActionRequest(
+ domDataTreeIdentifier, action, counter, actionDefinition.getQName().getLocalName())
+ .getNode().getOwnerDocument());
+ }
+
+ Preconditions.checkNotNull(payload, "Transforming an action with input: %s, payload cannot be null",
+ action.getLastComponent());
+ Preconditions.checkArgument(payload instanceof ContainerNode,
+ "Transforming an rpc with input: %s, payload has to be a container, but was: %s",
+ action.getLastComponent(), payload);
+ // Set the path to the input of rpc for the node stream writer
+ action = action.createChild(QName.create(action.getLastComponent(), "input").intern());
+ final DOMResult result = NetconfMessageTransformUtil.prepareDomResultForActionRequest(
+ domDataTreeIdentifier, action, counter, actionDefinition.getQName().getLocalName());
+
+ try {
+ NetconfMessageTransformUtil.writeNormalizedRpc((ContainerNode) payload, result,
+ schemaPath.createChild(QName.create(action.getLastComponent(), "input").intern()), schemaContext);
+ } catch (final XMLStreamException | IOException | IllegalStateException e) {
+ throw new IllegalStateException("Unable to serialize " + action, e);
+ }
+
+ final Document node = result.getNode().getOwnerDocument();
+
+ return new NetconfMessage(node);
+ }
+
private static boolean isBaseOrNotificationRpc(final QName rpc) {
return rpc.getNamespace().equals(NETCONF_URI)
|| rpc.getNamespace().equals(IETF_NETCONF_NOTIFICATIONS.getNamespace())
"Unable to parse response of %s, the rpc is unknown", rpcQName);
// In case no input for rpc is defined, we can simply construct the payload here
- if (rpcDefinition.getOutput().getChildNodes().isEmpty()) {
- Preconditions.checkArgument(XmlElement.fromDomDocument(
- message.getDocument()).getOnlyChildElementWithSameNamespaceOptionally("ok").isPresent(),
- "Unexpected content in response of rpc: %s, %s", rpcDefinition.getQName(), message);
- normalizedNode = null;
- } else {
- final Element element = message.getDocument().getDocumentElement();
- try {
- final NormalizedNodeResult resultHolder = new NormalizedNodeResult();
- final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder);
- final XmlParserStream xmlParser = XmlParserStream.create(writer, schemaContext,
- rpcDefinition.getOutput(), strictParsing);
- xmlParser.traverse(new DOMSource(element));
- normalizedNode = resultHolder.getResult();
- } catch (XMLStreamException | URISyntaxException | IOException | ParserConfigurationException
- | SAXException e) {
- throw new IllegalArgumentException(String.format("Failed to parse RPC response %s", element), e);
- }
- }
+ normalizedNode = parseResult(message, rpcDefinition);
}
return new DefaultDOMRpcResult(normalizedNode);
}
+ @Override
+ public DOMActionResult toActionResult(SchemaPath action, NetconfMessage message) {
+ ActionDefinition actionDefinition = null;
+ for (ActionDefinition actionDef : actions) {
+ if (actionDef.getPath().getLastComponent().equals(action.getLastComponent())) {
+ actionDefinition = actionDef;
+ }
+ }
+ Preconditions.checkNotNull(actionDefinition, "Action does not exist: %s", action);
+ ContainerNode normalizedNode = (ContainerNode) parseResult(message, actionDefinition);
+
+ return new SimpleDOMActionResult(normalizedNode, Collections.<RpcError>emptyList());
+ }
+
+ private NormalizedNode<?, ?> parseResult(final NetconfMessage message,
+ final OperationDefinition operationDefinition) {
+ if (operationDefinition.getOutput().getChildNodes().isEmpty()) {
+ Preconditions.checkArgument(XmlElement.fromDomDocument(
+ message.getDocument()).getOnlyChildElementWithSameNamespaceOptionally("ok").isPresent(),
+ "Unexpected content in response of rpc: %s, %s", operationDefinition.getQName(), message);
+ return null;
+ } else {
+ final Element element = message.getDocument().getDocumentElement();
+ try {
+ final NormalizedNodeResult resultHolder = new NormalizedNodeResult();
+ final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder);
+ final XmlParserStream xmlParser = XmlParserStream.create(writer, schemaContext,
+ operationDefinition.getOutput(), strictParsing);
+ xmlParser.traverse(new DOMSource(element));
+ return resultHolder.getResult();
+ } catch (XMLStreamException | URISyntaxException | IOException | ParserConfigurationException
+ | SAXException e) {
+ throw new IllegalArgumentException(String.format("Failed to parse RPC response %s", element), e);
+ }
+ }
+ }
+
static class NetconfDeviceNotification implements DOMNotification, DOMEvent {
private final ContainerNode content;
private final SchemaPath schemaPath;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
import org.opendaylight.netconf.api.DocumentedException;
import org.opendaylight.netconf.api.FailedNetconfMessage;
import org.opendaylight.netconf.api.NetconfDocumentedException;
import org.opendaylight.yangtools.yang.data.api.ModifyAction;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
public static final QName NETCONF_FILTER_QNAME = QName.create(NETCONF_QNAME, "filter").intern();
public static final QName NETCONF_GET_QNAME = QName.create(NETCONF_QNAME, "get").intern();
public static final QName NETCONF_RPC_QNAME = QName.create(NETCONF_QNAME, "rpc").intern();
+ public static final QName YANG_QNAME = null;
+ public static final URI NETCONF_ACTION_NAMESPACE = URI.create("urn:ietf:params:xml:ns:yang:1");
+ public static final String NETCONF_ACTION = "action";
public static final URI NETCONF_ROLLBACK_ON_ERROR_URI = URI
.create("urn:ietf:params:netconf:capability:rollback-on-error:1.0");
return new DOMResult(elementNS);
}
+ public static DOMResult prepareDomResultForActionRequest(DOMDataTreeIdentifier domDataTreeIdentifier,
+ final SchemaPath actionSchemaPath, final MessageCounter counter, String action) {
+ final Document document = XmlUtil.newDocument();
+ final Element rpcNS =
+ document.createElementNS(NETCONF_RPC_QNAME.getNamespace().toString(), NETCONF_RPC_QNAME.getLocalName());
+ // set msg id
+ rpcNS.setAttribute(MESSAGE_ID_ATTR, counter.getNewMessageId(MESSAGE_ID_PREFIX));
+
+ final Element actionNS = document.createElementNS(NETCONF_ACTION_NAMESPACE.toString(), NETCONF_ACTION);
+
+ final Element actionData = prepareActionData(actionNS,
+ domDataTreeIdentifier.getRootIdentifier().getPathArguments().iterator(), document);
+
+ Element specificActionElement = document.createElement(action);
+ actionData.appendChild(specificActionElement);
+ rpcNS.appendChild(actionNS);
+ document.appendChild(rpcNS);
+ return new DOMResult(specificActionElement);
+ }
+
+ private static Element prepareActionData(Element actionNS, Iterator<PathArgument> iterator, Document document) {
+ if (iterator.hasNext()) {
+ PathArgument next = iterator.next();
+ final QName actualNS = next.getNodeType();
+
+ final Element actualElement = document.createElementNS(actualNS.getNamespace().toString(),
+ actualNS.getLocalName());
+ if (next instanceof NodeWithValue) {
+ actualElement.setNodeValue(((NodeWithValue) next).getValue().toString());
+ } else if (next instanceof NodeIdentifierWithPredicates) {
+ for (Entry<QName, Object> entry : ((NodeIdentifierWithPredicates) next).getKeyValues().entrySet()) {
+ final Element entryElement = document.createElementNS(entry.getKey().getNamespace().toString(),
+ entry.getKey().getLocalName());
+ entryElement.setTextContent(entry.getValue().toString());
+ entryElement.setNodeValue(entry.getValue().toString());
+ actualElement.appendChild(entryElement);
+ }
+ }
+ actionNS.appendChild(actualElement);
+ return prepareActionData(actualElement, iterator, document);
+ } else {
+ return actionNS;
+ }
+ }
+
@SuppressWarnings("checkstyle:IllegalCatch")
public static void writeNormalizedRpc(final ContainerNode normalized, final DOMResult result,
final SchemaPath schemaPath,
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
+import org.opendaylight.controller.md.sal.dom.api.DOMActionService;
import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
device.onRemoteSessionUp(sessionCaps, listener);
Mockito.verify(facade, Mockito.timeout(5000)).onDeviceConnected(
- any(SchemaContext.class), any(NetconfSessionPreferences.class), any(NetconfDeviceRpc.class));
+ any(SchemaContext.class), any(NetconfSessionPreferences.class), any(NetconfDeviceRpc.class),
+ any(DOMActionService.class));
Mockito.verify(schemaFactory, times(2)).createSchemaContext(anyCollectionOf(SourceIdentifier.class));
}
device.onRemoteSessionUp(sessionCaps, listener);
Mockito.verify(facade, Mockito.timeout(5000)).onDeviceConnected(
- any(SchemaContext.class), any(NetconfSessionPreferences.class), any(NetconfDeviceRpc.class));
+ any(SchemaContext.class), any(NetconfSessionPreferences.class), any(NetconfDeviceRpc.class),
+ any(DOMActionService.class));
Mockito.verify(schemaFactory, times(1)).createSchemaContext(anyCollectionOf(SourceIdentifier.class));
}
verify(schemaContextProviderFactory, timeout(5000)).createSchemaContext(any(Collection.class));
verify(facade, timeout(5000)).onDeviceConnected(
- any(SchemaContext.class), any(NetconfSessionPreferences.class), any(DOMRpcService.class));
+ any(SchemaContext.class), any(NetconfSessionPreferences.class), any(DOMRpcService.class),
+ any(DOMActionService.class));
device.onRemoteSessionDown();
verify(facade, timeout(5000)).onDeviceDisconnected();
verify(schemaContextProviderFactory, timeout(5000).times(2)).createSchemaContext(any(Collection.class));
verify(facade, timeout(5000).times(2)).onDeviceConnected(
- any(SchemaContext.class), any(NetconfSessionPreferences.class), any(DOMRpcService.class));
+ any(SchemaContext.class), any(NetconfSessionPreferences.class), any(DOMRpcService.class),
+ any(DOMActionService.class));
}
@Test
//complete schema setup
schemaFuture.set(getSchema());
//facade.onDeviceDisconnected() was called, so facade.onDeviceConnected() shouldn't be called anymore
- verify(facade, after(1000).never()).onDeviceConnected(any(), any(), any());
+ verify(facade, after(1000).never()).onDeviceConnected(any(), any(), any(), any(DOMActionService.class));
}
@Test
final ArgumentCaptor<NetconfSessionPreferences> argument =
ArgumentCaptor.forClass(NetconfSessionPreferences.class);
verify(facade, timeout(5000))
- .onDeviceConnected(any(SchemaContext.class), argument.capture(), any(DOMRpcService.class));
+ .onDeviceConnected(any(SchemaContext.class), argument.capture(), any(DOMRpcService.class),
+ any(DOMActionService.class));
final NetconfDeviceCapabilities netconfDeviceCaps = argument.getValue().getNetconfDeviceCapabilities();
netconfDeviceCaps.getResolvedCapabilities()
final RemoteDeviceHandler<NetconfSessionPreferences> remoteDeviceHandler =
mockCloseableClass(RemoteDeviceHandler.class);
doNothing().when(remoteDeviceHandler).onDeviceConnected(
- any(SchemaContext.class), any(NetconfSessionPreferences.class), any(NetconfDeviceRpc.class));
+ any(SchemaContext.class), any(NetconfSessionPreferences.class), any(NetconfDeviceRpc.class),
+ any(DOMActionService.class));
doNothing().when(remoteDeviceHandler).onDeviceDisconnected();
doNothing().when(remoteDeviceHandler).onNotification(any(DOMNotification.class));
return remoteDeviceHandler;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
+import org.opendaylight.controller.md.sal.dom.api.DOMActionService;
import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
NetconfSessionPreferences.fromStrings(getCapabilities());
final DOMRpcService deviceRpc = mock(DOMRpcService.class);
- deviceFacade.onDeviceConnected(schemaContext, netconfSessionPreferences, deviceRpc);
+ deviceFacade.onDeviceConnected(schemaContext, netconfSessionPreferences, deviceRpc, null);
verify(mountInstance, times(1)).onTopologyDeviceConnected(eq(schemaContext),
- any(DOMDataBroker.class), eq(deviceRpc), any(NetconfDeviceNotificationService.class));
+ any(DOMDataBroker.class), eq(deviceRpc), any(NetconfDeviceNotificationService.class),
+ any(DOMActionService.class));
verify(netconfDeviceTopologyAdapter,
times(1)).updateDeviceData(true, netconfSessionPreferences.getNetconfDeviceCapabilities());
}
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import java.io.IOException;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import javax.xml.transform.dom.DOMSource;
+import org.apache.xerces.dom.TextImpl;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.ElementNameAndAttributeQualifier;
import org.custommonkey.xmlunit.XMLUnit;
import org.junit.Test;
import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
import org.opendaylight.mdsal.binding.generator.impl.ModuleInfoBackedContext;
+import org.opendaylight.mdsal.dom.api.DOMActionResult;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
import org.opendaylight.netconf.api.NetconfMessage;
import org.opendaylight.netconf.api.xml.XmlUtil;
import org.opendaylight.netconf.sal.connect.netconf.schema.NetconfRemoteSchemaYangSourceProvider;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.Schema;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+import org.w3c.dom.Node;
import org.xml.sax.SAXException;
public class NetconfMessageTransformerTest {
+ private static final String REVISION_EXAMPLE_SERVER_FARM = "2018-08-07";
+ private static final String URN_EXAMPLE_SERVER_FARM = "urn:example:server-farm";
+
+ private NetconfMessageTransformer actionNetconfMessageTransformer;
private NetconfMessageTransformer netconfMessageTransformer;
private SchemaContext schema;
schema = getSchema(true);
netconfMessageTransformer = getTransformer(schema);
-
+ actionNetconfMessageTransformer = getActionMessageTransformer();
}
@Test
}
@Test
- public void tesGetSchemaRequest() throws Exception {
+ public void testGetSchemaRequest() throws Exception {
final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(toPath(GET_SCHEMA_QNAME),
NetconfRemoteSchemaYangSourceProvider.createGetSchemaRequest("module", Optional.of("2012-12-12")));
assertSimilarXml(netconfMessage, "<rpc message-id=\"m-0\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
}
@Test
- public void tesGetSchemaResponse() throws Exception {
+ public void testGetSchemaResponse() throws Exception {
final NetconfMessageTransformer transformer = getTransformer(getSchema(true));
final NetconfMessage response = new NetconfMessage(XmlUtil.readXmlToDocument(
"<rpc-reply message-id=\"101\"\n"
.netconf.monitoring.rev101004.$YangModuleInfoImpl.getInstance()));
return moduleInfoBackedContext.tryToCreateSchemaContext().get();
}
+
+ @Test
+ public void getActionsTest() {
+ QName reset = QName.create(URN_EXAMPLE_SERVER_FARM, REVISION_EXAMPLE_SERVER_FARM, "reset");
+ QName start = QName.create(reset, "start");
+ QName open = QName.create(start, "open");
+ Set<QName> qnames = new HashSet<>(Arrays.asList(reset, start, open));
+ Set<ActionDefinition> actions = actionNetconfMessageTransformer.getActions();
+ assertTrue(!actions.isEmpty());
+ for (ActionDefinition actionDefinition : actions) {
+ QName qname = actionDefinition.getQName();
+ assertTrue(qnames.contains(qname));
+ qnames.remove(qname);
+ }
+ }
+
+ @Test
+ public void toActionRequestListTopLevelTest() {
+ QName qname = QName.create(URN_EXAMPLE_SERVER_FARM, REVISION_EXAMPLE_SERVER_FARM, "server");
+ QName nameQname = QName.create(qname, "name");
+ QName actionResetQName = QName.create(qname, "reset");
+
+ Set<PathArgument> nodeIdentifiers =
+ Collections.singleton(new NodeIdentifierWithPredicates(qname, nameQname, "test"));
+ DOMDataTreeIdentifier domDataTreeIdentifier = prepareDataTreeId(nodeIdentifiers);
+
+ ContainerNode data = initInputAction(QName.create(qname, "reset-at"), "now");
+
+ NetconfMessage actionRequest = actionNetconfMessageTransformer.toActionRequest(
+ SchemaPath.create(true, actionResetQName), domDataTreeIdentifier, data);
+
+ Node childAction = checkBasePartOfActionRequest(actionRequest);
+
+ Node childServer = childAction.getFirstChild();
+ checkNode(childServer, "server", "server", qname.getNamespace().toString());
+
+ Node childName = childServer.getFirstChild();
+ checkNode(childName, "name", "name", qname.getNamespace().toString());
+
+ TextImpl childTest = (TextImpl) childName.getFirstChild();
+ assertEquals(childTest.getData(), "test");
+
+ checkAction(actionResetQName, childName.getNextSibling(), "reset-at", "reset-at", "now");
+ }
+
+ @Test
+ public void toActionRequestContainerTopLevelTest() {
+ QName qname = QName.create(URN_EXAMPLE_SERVER_FARM, REVISION_EXAMPLE_SERVER_FARM, "device");
+ QName actionStartQName = QName.create(qname, "start");
+
+ Set<PathArgument> nodeIdentifiers = Collections.singleton(NodeIdentifier.create(qname));
+ DOMDataTreeIdentifier domDataTreeIdentifier = prepareDataTreeId(nodeIdentifiers);
+
+ NormalizedNode<?, ?> payload = initInputAction(QName.create(qname, "start-at"), "now");
+ NetconfMessage actionRequest = actionNetconfMessageTransformer.toActionRequest(
+ SchemaPath.create(true, actionStartQName), domDataTreeIdentifier, payload);
+
+ Node childAction = checkBasePartOfActionRequest(actionRequest);
+
+ Node childDevice = childAction.getFirstChild();
+ checkNode(childDevice, "device", "device", qname.getNamespace().toString());
+
+ checkAction(actionStartQName, childDevice.getFirstChild(), "start-at", "start-at", "now");
+ }
+
+ @Test
+ public void toActionRequestContainerInContainerTest() {
+ QName boxOutQName = QName.create(URN_EXAMPLE_SERVER_FARM, REVISION_EXAMPLE_SERVER_FARM, "box-out");
+ QName boxInQName = QName.create(URN_EXAMPLE_SERVER_FARM, REVISION_EXAMPLE_SERVER_FARM, "box-in");
+ QName actionOpenQName = QName.create(boxOutQName, "open");
+
+ Set<PathArgument> nodeIdentifiers = new HashSet<>();
+ nodeIdentifiers.add(NodeIdentifier.create(boxOutQName));
+ nodeIdentifiers.add(NodeIdentifier.create(boxInQName));
+
+ DOMDataTreeIdentifier domDataTreeIdentifier = prepareDataTreeId(nodeIdentifiers);
+
+ NormalizedNode<?, ?> payload = initInputAction(QName.create(boxOutQName, "start-at"), "now");
+ NetconfMessage actionRequest = actionNetconfMessageTransformer.toActionRequest(
+ SchemaPath.create(true, actionOpenQName), domDataTreeIdentifier, payload);
+
+ Node childAction = checkBasePartOfActionRequest(actionRequest);
+
+ Node childBoxOut = childAction.getFirstChild();
+ checkNode(childBoxOut, "box-out", "box-out", boxOutQName.getNamespace().toString());
+
+ Node childBoxIn = childBoxOut.getFirstChild();
+ checkNode(childBoxIn, "box-in", "box-in", boxOutQName.getNamespace().toString());
+
+ Node action = childBoxIn.getFirstChild();
+ checkNode(action, null, actionOpenQName.getLocalName(), null);
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ @Test
+ public void toActionResultTest() throws Exception {
+ QName qname = QName.create(URN_EXAMPLE_SERVER_FARM, REVISION_EXAMPLE_SERVER_FARM, "reset");
+
+ NetconfMessage message = new NetconfMessage(XmlUtil.readXmlToDocument(
+ "<rpc-reply message-id=\"101\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"
+ + "<reset-finished-at xmlns=\"urn:example:server-farm\">"
+ + "now"
+ + "</reset-finished-at>"
+ + "</rpc-reply>"));
+ DOMActionResult actionResult = actionNetconfMessageTransformer.toActionResult(
+ SchemaPath.create(true, qname), message);
+ assertNotNull(actionResult);
+ ContainerNode containerNode = actionResult.getOutput().get();
+ assertNotNull(containerNode);
+ LeafNode<String> leaf = (LeafNode) containerNode.getValue().iterator().next();
+ assertEquals("now", leaf.getValue());
+ }
+
+ private void checkAction(QName actionQname, Node action , String inputLocalName, String inputNodeName,
+ String inputValue) {
+ checkNode(action, null, actionQname.getLocalName(), null);
+
+ Node childResetAt = action.getFirstChild();
+ checkNode(childResetAt, inputLocalName, inputNodeName, actionQname.getNamespace().toString());
+
+ TextImpl firstChild = (TextImpl) childResetAt.getFirstChild();
+ assertEquals(firstChild.getData(), inputValue);
+ }
+
+ private Node checkBasePartOfActionRequest(NetconfMessage actionRequest) {
+ Node baseRpc = actionRequest.getDocument().getFirstChild();
+ checkNode(baseRpc, "rpc", "rpc", NetconfMessageTransformUtil.NETCONF_QNAME.getNamespace().toString());
+ assertTrue(baseRpc.getLocalName().equals("rpc"));
+ assertTrue(baseRpc.getNodeName().equals("rpc"));
+
+ Node messageId = baseRpc.getAttributes().getNamedItem("message-id");
+ assertNotNull(messageId);
+ assertTrue(messageId.getNodeValue().contains("m-"));
+
+ Node childAction = baseRpc.getFirstChild();
+ checkNode(childAction, "action", "action", NetconfMessageTransformUtil.NETCONF_ACTION_NAMESPACE.toString());
+ return childAction;
+ }
+
+ private DOMDataTreeIdentifier prepareDataTreeId(Set<PathArgument> nodeIdentifiers) {
+ YangInstanceIdentifier yangInstanceIdentifier =
+ YangInstanceIdentifier.builder().append(nodeIdentifiers).build();
+ DOMDataTreeIdentifier domDataTreeIdentifier =
+ new DOMDataTreeIdentifier(org.opendaylight.mdsal.common.api.LogicalDatastoreType.CONFIGURATION,
+ yangInstanceIdentifier);
+ return domDataTreeIdentifier;
+ }
+
+ private ContainerNode initInputAction(QName qname, String value) {
+ ImmutableLeafNodeBuilder<String> immutableLeafNodeBuilder = new ImmutableLeafNodeBuilder<>();
+ DataContainerChild<NodeIdentifier, String> build = immutableLeafNodeBuilder.withNodeIdentifier(
+ NodeIdentifier.create(qname)).withValue(value).build();
+ ContainerNode data = ImmutableContainerNodeBuilder.create().withNodeIdentifier(NodeIdentifier.create(
+ QName.create(qname, "input"))).withChild(build).build();
+ return data;
+ }
+
+ private void checkNode(Node childServer, String expectedLocalName, String expectedNodeName,
+ String expectedNamespace) {
+ assertNotNull(childServer);
+ assertEquals(childServer.getLocalName(), expectedLocalName);
+ assertEquals(childServer.getNodeName(), expectedNodeName);
+ assertEquals(childServer.getNamespaceURI(), expectedNamespace);
+ }
+
+ private SchemaContext getActionSchema() {
+ return YangParserTestUtils.parseYangResource("/schemas/example-server-farm.yang");
+ }
+
+ private NetconfMessageTransformer getActionMessageTransformer() {
+ return new NetconfMessageTransformer(getActionSchema(), true);
+ }
}
--- /dev/null
+module example-server-farm {
+ yang-version 1.1;
+ namespace "urn:example:server-farm";
+ prefix "sfarm";
+ revision 2018-08-07;
+
+ list server {
+ key name;
+ leaf name {
+ type string;
+ }
+ action reset {
+ input {
+ leaf reset-at {
+ type string;
+ mandatory true;
+ }
+ }
+ output {
+ leaf reset-finished-at {
+ type string;
+ mandatory true;
+ }
+ }
+ }
+ }
+
+ container device {
+ action start {
+ input {
+ leaf start-at {
+ type string;
+ mandatory true;
+ }
+ }
+ output {
+ leaf start-finished-at {
+ type string;
+ mandatory true;
+ }
+ }
+ }
+ }
+
+ container box-out {
+ container box-in {
+ action open {
+ }
+ }
+ }
+}
\ No newline at end of file