2 * Copyright (c) 2016 Cisco and/or its affiliates.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package io.fd.honeycomb.v3po.impl;
19 import static com.google.common.base.Preconditions.checkNotNull;
21 import com.google.common.base.Optional;
22 import com.google.common.base.Preconditions;
23 import com.google.common.util.concurrent.AsyncFunction;
24 import com.google.common.util.concurrent.Futures;
25 import com.google.common.util.concurrent.ListenableFuture;
26 import io.fd.honeycomb.v3po.data.ModifiableDataTree;
27 import io.fd.honeycomb.v3po.data.ReadableDataTree;
28 import io.fd.honeycomb.v3po.data.impl.ConfigDataTree;
29 import io.fd.honeycomb.v3po.data.impl.DataBroker;
30 import io.fd.honeycomb.v3po.data.impl.OperationalDataTree;
31 import io.fd.honeycomb.v3po.translate.Context;
32 import io.fd.honeycomb.v3po.translate.TranslationException;
33 import io.fd.honeycomb.v3po.translate.read.ReadContext;
34 import io.fd.honeycomb.v3po.translate.read.ReaderRegistry;
35 import io.fd.honeycomb.v3po.translate.write.WriterRegistry;
36 import java.util.ArrayList;
37 import java.util.Collection;
38 import java.util.Collections;
39 import java.util.List;
41 import javax.annotation.Nonnull;
42 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
43 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
44 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
45 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
46 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
47 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
48 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
49 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
50 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
51 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
52 import org.opendaylight.controller.sal.core.api.Broker;
53 import org.opendaylight.controller.sal.core.api.Provider;
54 import org.opendaylight.controller.sal.core.api.model.SchemaService;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.Vpp;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppState;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomainKey;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.state.BridgeDomains;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.state.bridge.domains.BridgeDomain;
61 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
62 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
63 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
64 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
65 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
66 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
67 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
68 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
69 import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
70 import org.opendaylight.yangtools.concepts.ObjectRegistration;
71 import org.opendaylight.yangtools.yang.binding.DataObject;
72 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
73 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
74 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
75 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
76 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
77 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
78 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
79 import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
80 import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory;
81 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
82 import org.slf4j.Logger;
83 import org.slf4j.LoggerFactory;
86 * Creates VppDataBroker which uses DataTree instead of DataStore internally in order to obtain better control over the
87 * data processing in Honeycomb agent
89 public final class VppDataBrokerInitializationProvider implements Provider, AutoCloseable {
91 private static final Logger LOG = LoggerFactory.getLogger(VppDataBrokerInitializationProvider.class);
93 private final TopologyId VPP_TOPOLOGY_ID = TopologyId.getDefaultInstance("vpp-topology");
94 private final NodeId VPP_TOPOLOGY_NODE_ID = NodeId.getDefaultInstance("vpp");
95 private final org.opendaylight.controller.md.sal.binding.api.DataBroker bindingBroker;
96 private final ReaderRegistry readerRegistry;
97 private final InstanceIdentifier<Node> mountPointPath;
98 private final WriterRegistry writerRegistry;
99 private final BindingNormalizedNodeSerializer serializer;
100 private ObjectRegistration<DOMMountPoint> mountPointRegistration;
101 private DOMDataBroker broker;
103 public VppDataBrokerInitializationProvider(
104 @Nonnull final org.opendaylight.controller.md.sal.binding.api.DataBroker bindingBroker,
105 final ReaderRegistry readerRegistry,
106 final WriterRegistry writerRegistry,
107 final BindingNormalizedNodeSerializer serializer) {
108 this.bindingBroker = checkNotNull(bindingBroker, "bindingBroker should not be null");
109 this.readerRegistry = checkNotNull(readerRegistry, "readerRegistry should not be null");
110 this.writerRegistry = checkNotNull(writerRegistry, "writerRegistry should not be null");
111 this.serializer = checkNotNull(serializer, "serializer should not be null");
112 this.mountPointPath = getMountPointPath();
115 // TODO make configurable
116 private InstanceIdentifier<Node> getMountPointPath() {
117 final InstanceIdentifier<NetworkTopology> networkTopology =
118 InstanceIdentifier.builder(NetworkTopology.class).build();
119 final KeyedInstanceIdentifier<Topology, TopologyKey> topology =
120 networkTopology.child(Topology.class, new TopologyKey(VPP_TOPOLOGY_ID));
121 return topology.child(Node.class, new NodeKey(VPP_TOPOLOGY_NODE_ID));
125 public void onSessionInitiated(final Broker.ProviderSession providerSession) {
126 LOG.info("Session initialized, providerSession={}", providerSession);
127 Preconditions.checkState(!isMountPointRegistered(), "Mount point is already registered");
129 final DOMMountPointService mountPointService = providerSession.getService(DOMMountPointService.class);
130 final SchemaService schemaService = providerSession.getService(SchemaService.class);
132 final SchemaContext globalContext = schemaService.getGlobalContext();
133 // final BindingNormalizedNodeSerializer serializer = initSerializer(globalContext);
134 final YangInstanceIdentifier path = serializer.toYangInstanceIdentifier(mountPointPath);
136 final DOMMountPointService.DOMMountPointBuilder mountPointBuilder = mountPointService.createMountPoint(path);
137 mountPointBuilder.addInitialSchemaContext(globalContext);
139 broker = initVppDataBroker(globalContext, serializer);
140 mountPointBuilder.addService(DOMDataBroker.class, broker);
142 mountPointRegistration = mountPointBuilder.register();
143 final DOMMountPoint mountPoint = mountPointRegistration.getInstance();
144 LOG.debug("Created mountPoint: identifier={}, schemaContext={}", mountPoint.getIdentifier(),
145 mountPoint.getSchemaContext());
147 createMountPointPlaceholder();
149 // TODO initial sync has to go out of here
150 // initialVppConfigSynchronization(broker);
154 public Collection<ProviderFunctionality> getProviderFunctionality() {
155 return Collections.EMPTY_LIST;
158 private boolean isMountPointRegistered() {
159 final ReadOnlyTransaction readTx = bindingBroker.newReadOnlyTransaction();
161 final Optional<Node> cfgPlaceholder =
162 readTx.read(LogicalDatastoreType.CONFIGURATION, mountPointPath).checkedGet();
163 final Optional<Node> operPlaceholder =
164 readTx.read(LogicalDatastoreType.OPERATIONAL, mountPointPath).checkedGet();
165 return cfgPlaceholder.isPresent() || operPlaceholder.isPresent();
166 } catch (ReadFailedException e) {
167 throw new IllegalStateException("Failed to read mountpoint placeholder data", e);
171 private DOMDataBroker initVppDataBroker(final SchemaContext globalContext,
172 final BindingNormalizedNodeSerializer serializer) {
173 final ReadableDataTree operationalDataTree =
174 new OperationalDataTree(serializer, globalContext, readerRegistry); // TODO make configurable
176 final DataTree dataTree =
177 InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION); // TODO make configurable
178 dataTree.setSchemaContext(globalContext);
180 final ModifiableDataTree configDataTree =
181 new ConfigDataTree(serializer, dataTree, writerRegistry); // TODO make configurable
183 // init operational data tree before data broker is initialized
186 initConfig(serializer, configDataTree);
187 } catch (Exception e) {
188 LOG.warn("Failed to initialize config", e);
191 return new DataBroker(operationalDataTree, configDataTree);
194 private void initConfig(final BindingNormalizedNodeSerializer serializer, final ModifiableDataTree configDataTree)
195 throws TranslationException, DataValidationFailedException {
196 LOG.info("Config initialization");
198 final Optional<? extends DataObject> data = readerRegistry.read(InstanceIdentifier.create(VppState.class), new ReadContextImpl());
199 LOG.info("Config initialization data={}", data);
201 if (data.isPresent()) {
203 VppState vppOperationalData = (VppState) data.get();
204 final Vpp vppConfigData = convert(vppOperationalData);
206 final Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalizedData =
207 serializer.toNormalizedNode(InstanceIdentifier.create(Vpp.class), vppConfigData);
209 final DataTreeModification modification = configDataTree.takeSnapshot().newModification();
210 final YangInstanceIdentifier biPath = normalizedData.getKey();
211 final NormalizedNode<?, ?> biData = normalizedData.getValue();
212 LOG.info("Config initialization biPath={}, biData={}", biPath, biData);
213 modification.write(biPath, biData);
214 modification.ready();
216 LOG.info("Config writing modification ...");
217 configDataTree.modify(modification); // TODO do not write to VPP
218 LOG.info("Config writing modification written successfully.");
220 LOG.info("Data is not present");
224 private Vpp convert(final VppState vppState) {
225 final BridgeDomains bridgeDomains = vppState.getBridgeDomains();
226 final List<BridgeDomain> bridgeDomainList = bridgeDomains.getBridgeDomain();
228 VppBuilder vppBuilder = new VppBuilder();
229 org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.BridgeDomainsBuilder bdsBuilder = new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.BridgeDomainsBuilder();
230 final List<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomain>
231 listOfBDs = new ArrayList<>();
233 // TODO use reflexions
234 for (BridgeDomain bd : bridgeDomainList) {
235 org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomainBuilder bdBuilder = new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomainBuilder();
236 bdBuilder.setLearn(bd.isLearn());
237 bdBuilder.setUnknownUnicastFlood(bd.isUnknownUnicastFlood());
238 bdBuilder.setArpTermination(bd.isArpTermination());
239 bdBuilder.setFlood(bd.isFlood());
240 bdBuilder.setForward(bd.isForward());
241 bdBuilder.setKey(new BridgeDomainKey(bd.getKey().getName()));
242 // TODO bdBuilder.setL2Fib(bd.getL2Fib());
243 bdBuilder.setName(bd.getName());
244 listOfBDs.add(bdBuilder.build());
247 bdsBuilder.setBridgeDomain(listOfBDs);
248 vppBuilder.setBridgeDomains(bdsBuilder.build());
249 return vppBuilder.build();
253 // TODO move to utility module
254 private static final class ReadContextImpl implements ReadContext {
255 public final Context ctx = new Context();
259 public Context getContext() {
264 public void close() {
265 // Make sure to clear the storage in case some customizer stored it to prevent memory leaks
272 * Writes placeholder data into MD-SAL's global datastore to indicate the presence of VPP mountpoint.
274 private void createMountPointPlaceholder() {
275 final NodeBuilder nodeBuilder = new NodeBuilder();
276 nodeBuilder.setKey(new NodeKey(VPP_TOPOLOGY_NODE_ID));
277 final Node node = nodeBuilder.build();
279 final WriteTransaction writeTx = bindingBroker.newWriteOnlyTransaction();
280 writeTx.merge(LogicalDatastoreType.CONFIGURATION, mountPointPath, node, true);
281 writeTx.merge(LogicalDatastoreType.OPERATIONAL, mountPointPath, node, true);
284 writeTx.submit().checkedGet();
285 } catch (TransactionCommitFailedException e) {
286 throw new IllegalStateException("Failed to create mountpoint placeholder", e);
290 // TODO operational and config models are not 1-1
291 // decide what part of operational data should be written to config during initialization
292 private void initialVppConfigSynchronization(final DOMDataBroker broker) {
293 // read from operational
294 final DOMDataReadOnlyTransaction readTx = broker.newReadOnlyTransaction();
296 final YangInstanceIdentifier
297 id = YangInstanceIdentifier.builder().node(VppState.QNAME).node(BridgeDomains.QNAME).build();
299 LOG.trace("initialVppStateSynchronization id: {}", id);
301 final ListenableFuture<Void> writeFuture = Futures.transform(
302 readTx.read(LogicalDatastoreType.OPERATIONAL, id),
303 new AsyncFunction<Optional<NormalizedNode<?, ?>>, Void>() {
305 public ListenableFuture<Void> apply(final Optional<NormalizedNode<?, ?>> readResult)
307 if (readResult.isPresent()) {
308 final DOMDataWriteTransaction writeTx = broker.newWriteOnlyTransaction();
309 final NormalizedNode<?, ?> node = readResult.get();
310 LOG.trace("Read result: {}", node);
313 // this will fail because we are reading OPERATIONAL data and writing to CONFIGURATION
314 // we need to provide extensible way to register initializer that would
315 // translate between models
317 // writeTx.put(LogicalDatastoreType.CONFIGURATION, id, node);
318 return writeTx.submit();
321 .immediateFailedFuture(
322 new IllegalStateException("Failed to read data from VPP."));
327 Futures.addCallback(writeFuture,
328 new LoggingFuturesCallBack<Void>("Initializing VPP config DataTree failed", LOG));
331 public Optional<DOMDataBroker> getBroker() {
332 return Optional.fromNullable(broker);
336 public void close() throws Exception {
337 if (mountPointRegistration != null) {
338 mountPointRegistration.close();
341 if (broker != null) {
345 // remove MD-SAL placeholder data for VPP mount point:
346 final WriteTransaction rwTx = bindingBroker.newWriteOnlyTransaction();
347 // does not fail if data is not present:
348 rwTx.delete(LogicalDatastoreType.CONFIGURATION, mountPointPath);
349 rwTx.delete(LogicalDatastoreType.OPERATIONAL, mountPointPath);
351 rwTx.submit().checkedGet();
352 } catch (TransactionCommitFailedException e) {
353 throw new IllegalStateException("Failed to remove mountpoint's placeholder from MD-SAL's global datastore",