From e0d230dfee7bcc7e384e2d8f933854ad449d441e Mon Sep 17 00:00:00 2001 From: Tom Pantelis Date: Tue, 7 Feb 2017 07:15:18 -0500 Subject: [PATCH] Add DOMDataTreeCommitCohort example for the cars model Change-Id: If15c748ceb718d9902ee6c0d5d5a7337a4cbd211 Signed-off-by: Tom Pantelis --- .../model/src/main/yang/car.yang | 9 ++ .../CarEntryDataTreeCommitCohort.java | 82 ++++++++++++++++ .../clustering/it/provider/CarProvider.java | 98 +++++++++++++++---- .../blueprint/cluster-test-app.xml | 7 +- 4 files changed, 177 insertions(+), 19 deletions(-) create mode 100644 opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/provider/CarEntryDataTreeCommitCohort.java diff --git a/opendaylight/md-sal/samples/clustering-test-app/model/src/main/yang/car.yang b/opendaylight/md-sal/samples/clustering-test-app/model/src/main/yang/car.yang index 14f12e4217..50df56f78c 100644 --- a/opendaylight/md-sal/samples/clustering-test-app/model/src/main/yang/car.yang +++ b/opendaylight/md-sal/samples/clustering-test-app/model/src/main/yang/car.yang @@ -128,4 +128,13 @@ module car { rpc unregister-logging-dtcls { description "Unregisters the logging DTCL(s) for the cars container."; } + + rpc register-commit-cohort { + description "Registers a sample commit cohort that validates car entry input."; + } + + rpc unregister-commit-cohort { + description "Unregisters the sample commit cohort."; + } + } diff --git a/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/provider/CarEntryDataTreeCommitCohort.java b/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/provider/CarEntryDataTreeCommitCohort.java new file mode 100644 index 0000000000..1bc6581b91 --- /dev/null +++ b/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/provider/CarEntryDataTreeCommitCohort.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017 Brocade Communications 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.clustering.it.provider; + +import com.google.common.base.Optional; +import com.google.common.base.Verify; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; +import org.opendaylight.mdsal.common.api.DataValidationFailedException; +import org.opendaylight.mdsal.common.api.PostCanCommitStep; +import org.opendaylight.mdsal.dom.api.DOMDataTreeCandidate; +import org.opendaylight.mdsal.dom.api.DOMDataTreeCommitCohort; +import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.sal.clustering.it.car.rev140818.Cars; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Example implementation of a DOMDataTreeCommitCohort that validates car entry data. + * + * @author Thomas Pantelis + */ +public class CarEntryDataTreeCommitCohort implements DOMDataTreeCommitCohort { + private static final Logger LOG = LoggerFactory.getLogger(CarEntryDataTreeCommitCohort.class); + + private static final QName YEAR_QNAME = QName.create(Cars.QNAME, "year").intern(); + private static final NodeIdentifier YEAR_NODE_ID = new NodeIdentifier(YEAR_QNAME); + + @SuppressWarnings("unchecked") + @Override + public CheckedFuture canCommit(Object txId, + DOMDataTreeCandidate candidate, SchemaContext ctx) { + + // Simple data validation - verify the year, if present, is >= 1990 + + final DataTreeCandidateNode rootNode = candidate.getRootNode(); + final Optional> dataAfter = rootNode.getDataAfter(); + + LOG.info("In canCommit: modificationType: {}, dataBefore: {}, dataAfter: {}", rootNode.getModificationType(), + rootNode.getDataBefore(), dataAfter); + + // Note: we don't want to process DELETE modifications but we don't need to explicitly check the + // ModificationType because dataAfter will not be present. Also dataAfter *should* always contain a + // MapEntryNode but we verify anyway. + if (dataAfter.isPresent()) { + final NormalizedNode normalizedNode = dataAfter.get(); + Verify.verify(normalizedNode instanceof DataContainerNode, "Expected type DataContainerNode, actual was %s", + normalizedNode.getClass()); + DataContainerNode entryNode = (DataContainerNode) normalizedNode; + final Optional> possibleYear = + entryNode.getChild(YEAR_NODE_ID); + if (possibleYear.isPresent()) { + final Number year = (Number) possibleYear.get().getValue(); + + LOG.info("year is {}", year); + + if (!(year.longValue() >= 1990)) { + return Futures.immediateFailedCheckedFuture(new DataValidationFailedException( + DOMDataTreeIdentifier.class, candidate.getRootPath(), + String.format("Invalid year %d - year must be >= 1990", year))); + } + } + } + + // Return the noop PostCanCommitStep as we're only validating input data and not participating in the + // remaining 3PC stages (pre-commit and commit). + return (CheckedFuture) PostCanCommitStep.NOOP_SUCCESS_FUTURE; + } +} diff --git a/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/provider/CarProvider.java b/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/provider/CarProvider.java index 69535f7060..d70ac410d1 100644 --- a/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/provider/CarProvider.java +++ b/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/provider/CarProvider.java @@ -18,6 +18,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.DataChangeListener; import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier; @@ -30,6 +31,9 @@ import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipS import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; +import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeCommitCohortRegistry; +import org.opendaylight.mdsal.dom.api.DOMDataTreeCommitCohortRegistration; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.sal.clustering.it.car.rev140818.CarId; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.sal.clustering.it.car.rev140818.CarService; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.sal.clustering.it.car.rev140818.Cars; @@ -46,6 +50,7 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.common.RpcError.ErrorType; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.common.RpcResultBuilder; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,33 +60,42 @@ import org.slf4j.LoggerFactory; public class CarProvider implements CarService { private static final Logger log = LoggerFactory.getLogger(PurchaseCarProvider.class); - private final DataBroker dataProvider; - private final EntityOwnershipService ownershipService; private static final Logger LOG = LoggerFactory.getLogger(CarProvider.class); private static final String ENTITY_TYPE = "cars"; + private static final InstanceIdentifier CARS_IID = InstanceIdentifier.builder(Cars.class).build(); + private static final DataTreeIdentifier CARS_DTID = new DataTreeIdentifier<>( + LogicalDatastoreType.CONFIGURATION, CARS_IID); - private AtomicLong succcessCounter = new AtomicLong(); - private AtomicLong failureCounter = new AtomicLong(); + private final DataBroker dataProvider; + private final DOMDataBroker domDataBroker; + private final EntityOwnershipService ownershipService; + private final AtomicLong succcessCounter = new AtomicLong(); + private final AtomicLong failureCounter = new AtomicLong(); private final CarEntityOwnershipListener ownershipListener = new CarEntityOwnershipListener(); private final AtomicBoolean registeredListener = new AtomicBoolean(); - private volatile Thread testThread; - private volatile boolean stopThread; - - private static final InstanceIdentifier CARS_IID = InstanceIdentifier.builder(Cars.class).build(); - private static final DataTreeIdentifier CARS_DTID = new DataTreeIdentifier<>( - LogicalDatastoreType.CONFIGURATION, CARS_IID); - - private Collection> carsDclRegistrations = + private final Collection> carsDclRegistrations = Sets.newConcurrentHashSet(); - private Collection> carsDtclRegistrations = + private final Collection> carsDtclRegistrations = Sets.newConcurrentHashSet(); - public CarProvider(DataBroker dataProvider, EntityOwnershipService ownershipService) { + private volatile Thread testThread; + private volatile boolean stopThread; + private final AtomicReference> commitCohortReg = + new AtomicReference<>(); + + public CarProvider(DataBroker dataProvider, EntityOwnershipService ownershipService, + DOMDataBroker domDataBroker) { this.dataProvider = dataProvider; this.ownershipService = ownershipService; + this.domDataBroker = domDataBroker; + } + + public void close() { + stopThread(); + unregisterCommitCohort(); } private void stopThread() { @@ -101,7 +115,7 @@ public class CarProvider implements CarService { final long inputCount; // If rate is not provided, or given as zero, then just return. - if ((input.getRate() == null) || (input.getRate() == 0)) { + if (input.getRate() == null || input.getRate() == 0) { log.info("Exiting stress test as no rate is given."); return Futures.immediateFuture(RpcResultBuilder.failed() .withError(ErrorType.PROTOCOL, "invalid rate") @@ -170,12 +184,12 @@ public class CarProvider implements CarService { break; } - if((count.get() % 1000) == 0) { + if(count.get() % 1000 == 0) { log.info("Cars created {}, time: {}",count.get(),sw.elapsed(TimeUnit.SECONDS)); } // Check if a count is specified in input and we have created that many cars. - if ((inputCount != 0) && (count.get() >= inputCount)) { + if (inputCount != 0 && count.get() >= inputCount) { stopThread = true; } } @@ -291,4 +305,54 @@ public class CarProvider implements CarService { } return RpcResultBuilder.success().buildFuture(); } + + @Override + @SuppressWarnings("checkstyle:IllegalCatch") + public Future> unregisterCommitCohort() { + final DOMDataTreeCommitCohortRegistration reg = commitCohortReg.getAndSet(null); + if (reg != null) { + try { + reg.close(); + LOG.info("Unregistered commit cohort"); + } catch (Exception e) { + return RpcResultBuilder.failed().withError(ErrorType.APPLICATION, + "Error closing commit cohort registration", e).buildFuture(); + } + } + + return RpcResultBuilder.success().buildFuture(); + } + + @Override + public synchronized Future> registerCommitCohort() { + if (commitCohortReg.get() != null) { + return RpcResultBuilder.success().buildFuture(); + } + + final DOMDataTreeCommitCohortRegistry commitCohortRegistry = (DOMDataTreeCommitCohortRegistry) + domDataBroker.getSupportedExtensions().get(DOMDataTreeCommitCohortRegistry.class); + + if (commitCohortRegistry == null) { + // Shouldn't happen + return RpcResultBuilder.failed().withError(ErrorType.APPLICATION, + "DOMDataTreeCommitCohortRegistry not found").buildFuture(); + } + + // Note: it may look strange that we specify the CarEntry.QNAME twice in the path below. This must be done in + // order to register the commit cohort for CarEntry instances. In the underlying data tree, a yang list is + // represented as a MapNode with MapEntryNodes representing the child list entries. Therefore, in order to + // address a list entry, you must specify the path argument for the MapNode and the path argument for the + // MapEntryNode. In the path below, the first CarEntry.QNAME argument addresses the MapNode and, since we want + // to address all list entries, the second path argument is wild-carded by specifying just the CarEntry.QNAME. + final YangInstanceIdentifier carEntryPath = YangInstanceIdentifier.builder( + YangInstanceIdentifier.of(Cars.QNAME)).node(CarEntry.QNAME).node(CarEntry.QNAME).build(); + commitCohortReg.set(commitCohortRegistry.registerCommitCohort( + new org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier( + org.opendaylight.mdsal.common.api.LogicalDatastoreType.CONFIGURATION, + carEntryPath), new CarEntryDataTreeCommitCohort())); + + LOG.info("Registered commit cohort"); + + return RpcResultBuilder.success().buildFuture(); + } } diff --git a/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/resources/org/opendaylight/blueprint/cluster-test-app.xml b/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/resources/org/opendaylight/blueprint/cluster-test-app.xml index c6f3aed401..4a969d5502 100644 --- a/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/resources/org/opendaylight/blueprint/cluster-test-app.xml +++ b/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/resources/org/opendaylight/blueprint/cluster-test-app.xml @@ -9,6 +9,7 @@ + @@ -21,9 +22,11 @@ - + + @@ -40,4 +43,4 @@ - \ No newline at end of file + -- 2.36.6