From 55a40f35dcd1c7e38f5315a2035686be12b6c6da Mon Sep 17 00:00:00 2001 From: Maros Marsalek Date: Wed, 10 Dec 2014 14:13:29 +0100 Subject: [PATCH] Implement basic locking for candidate in config-netconf-connector Change-Id: I4ec0efd7b1ac1bc33f90289d11cd28ca685e348b Signed-off-by: Maros Marsalek --- .../operations/Lock.java | 71 +++++++++++++++++++ .../operations/UnLock.java | 55 ++++++++++++++ .../osgi/NetconfOperationProvider.java | 4 ++ .../NetconfMappingTest.java | 19 +++++ .../test/resources/netconfMessages/lock.xml | 16 +++++ .../test/resources/netconfMessages/unlock.xml | 16 +++++ 6 files changed, 181 insertions(+) create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Lock.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/UnLock.java create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/lock.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unlock.xml diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Lock.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Lock.java new file mode 100644 index 0000000000..ea019642c5 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Lock.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014 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.confignetconfconnector.operations; + +import com.google.common.base.Optional; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants; +import org.opendaylight.controller.netconf.util.exception.MissingNameSpaceException; +import org.opendaylight.controller.netconf.util.exception.UnexpectedNamespaceException; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * Simple Lock implementation that pretends to lock candidate datastore. + * Candidate datastore is allocated per session and is private so no real locking is needed (JMX is the only possible interference) + */ +public class Lock extends AbstractLastNetconfOperation { + + private static final Logger LOG = LoggerFactory.getLogger(Lock.class); + + private static final String LOCK = "lock"; + private static final String TARGET_KEY = "target"; + + public Lock(final String netconfSessionIdForReporting) { + super(netconfSessionIdForReporting); + } + + @Override + protected Element handleWithNoSubsequentOperations(final Document document, final XmlElement operationElement) throws NetconfDocumentedException { + final Datastore targetDatastore = extractTargetParameter(operationElement); + if(targetDatastore == Datastore.candidate) { + // Since candidate datastore instances are allocated per session and not accessible anywhere else, no need to lock + LOG.debug("Locking {} datastore on session: {}", targetDatastore, getNetconfSessionIdForReporting()); + // TODO should this fail if we are already locked ? + return XmlUtil.createElement(document, XmlNetconfConstants.OK, Optional.absent()); + } + + // Not supported running lock + throw new NetconfDocumentedException("Unable to lock " + Datastore.running + " datastore", NetconfDocumentedException.ErrorType.application, + NetconfDocumentedException.ErrorTag.operation_not_supported, NetconfDocumentedException.ErrorSeverity.error); + } + + static Datastore extractTargetParameter(final XmlElement operationElement) throws NetconfDocumentedException { + final XmlElement targetChildNode; + try { + final XmlElement targetElement = operationElement.getOnlyChildElementWithSameNamespace(TARGET_KEY); + targetChildNode = targetElement.getOnlyChildElementWithSameNamespace(); + } catch (final MissingNameSpaceException | UnexpectedNamespaceException e) { + LOG.trace("Can't get only child element with same namespace", e); + throw NetconfDocumentedException.wrap(e); + } + + return Datastore.valueOf(targetChildNode.getName()); + } + + @Override + protected String getOperationName() { + return LOCK; + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/UnLock.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/UnLock.java new file mode 100644 index 0000000000..07b10aa327 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/UnLock.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2014 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.confignetconfconnector.operations; + +import com.google.common.base.Optional; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * Simple unlock implementation that pretends to unlock candidate datastore. + * Candidate datastore is allocated per session and is private so no real locking is needed (JMX is the only possible interference) + */ +public class UnLock extends AbstractLastNetconfOperation { + + private static final Logger LOG = LoggerFactory.getLogger(UnLock.class); + + private static final String UNLOCK = "unlock"; + + public UnLock(final String netconfSessionIdForReporting) { + super(netconfSessionIdForReporting); + } + + @Override + protected Element handleWithNoSubsequentOperations(final Document document, final XmlElement operationElement) throws NetconfDocumentedException { + final Datastore targetDatastore = Lock.extractTargetParameter(operationElement); + if(targetDatastore == Datastore.candidate) { + // Since candidate datastore instances are allocated per session and not accessible anywhere else, no need to lock + LOG.debug("Unlocking {} datastore on session: {}", targetDatastore, getNetconfSessionIdForReporting()); + // TODO this should fail if we are not locked + return XmlUtil.createElement(document, XmlNetconfConstants.OK, Optional.absent()); + } + + // Not supported running lock + throw new NetconfDocumentedException("Unable to unlock " + Datastore.running + " datastore", NetconfDocumentedException.ErrorType.application, + NetconfDocumentedException.ErrorTag.operation_not_supported, NetconfDocumentedException.ErrorSeverity.error); + } + + @Override + protected String getOperationName() { + return UNLOCK; + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationProvider.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationProvider.java index 294e0c8013..04d5d4bb6f 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationProvider.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationProvider.java @@ -14,6 +14,8 @@ import java.util.Set; import org.opendaylight.controller.config.util.ConfigRegistryClient; import org.opendaylight.controller.netconf.confignetconfconnector.operations.Commit; import org.opendaylight.controller.netconf.confignetconfconnector.operations.DiscardChanges; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.Lock; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.UnLock; import org.opendaylight.controller.netconf.confignetconfconnector.operations.Validate; import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfig; import org.opendaylight.controller.netconf.confignetconfconnector.operations.get.Get; @@ -48,6 +50,8 @@ final class NetconfOperationProvider { ops.add(new EditConfig(yangStoreSnapshot, transactionProvider, configRegistryClient, netconfSessionIdForReporting)); ops.add(new Commit(transactionProvider, configRegistryClient, netconfSessionIdForReporting)); + ops.add(new Lock(netconfSessionIdForReporting)); + ops.add(new UnLock(netconfSessionIdForReporting)); ops.add(new Get(yangStoreSnapshot, configRegistryClient, netconfSessionIdForReporting)); ops.add(new DiscardChanges(transactionProvider, configRegistryClient, netconfSessionIdForReporting)); ops.add(new Validate(transactionProvider, configRegistryClient, netconfSessionIdForReporting)); diff --git a/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java index beb3365f1c..6f9a62af1a 100644 --- a/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java +++ b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java @@ -83,6 +83,8 @@ import org.opendaylight.controller.netconf.api.NetconfDocumentedException; import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants; import org.opendaylight.controller.netconf.confignetconfconnector.operations.Commit; import org.opendaylight.controller.netconf.confignetconfconnector.operations.DiscardChanges; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.Lock; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.UnLock; import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfig; import org.opendaylight.controller.netconf.confignetconfconnector.operations.get.Get; import org.opendaylight.controller.netconf.confignetconfconnector.operations.getconfig.GetConfig; @@ -96,6 +98,7 @@ import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceSnap import org.opendaylight.controller.netconf.mapping.api.HandlingPriority; import org.opendaylight.controller.netconf.mapping.api.NetconfOperation; import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution; +import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil; import org.opendaylight.controller.netconf.util.test.XmlFileLoader; import org.opendaylight.controller.netconf.util.xml.XmlUtil; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.test.types.rev131127.TestIdentity1; @@ -236,6 +239,12 @@ public class NetconfMappingTest extends AbstractConfigTest { } + @Test + public void testUnLock() throws Exception { + assertTrue(NetconfMessageUtil.isOKMessage(lockCandidate())); + assertTrue(NetconfMessageUtil.isOKMessage(unlockCandidate())); + } + private void assertCorrectRefNamesForDependencies(Document config) throws NodeTestException { NodeList modulesList = config.getElementsByTagName("modules"); assertEquals(1, modulesList.getLength()); @@ -383,6 +392,16 @@ public class NetconfMappingTest extends AbstractConfigTest { executeOp(commitOp, "netconfMessages/commit.xml"); } + private Document lockCandidate() throws ParserConfigurationException, SAXException, IOException, NetconfDocumentedException { + Lock commitOp = new Lock(NETCONF_SESSION_ID); + return executeOp(commitOp, "netconfMessages/lock.xml"); + } + + private Document unlockCandidate() throws ParserConfigurationException, SAXException, IOException, NetconfDocumentedException { + UnLock commitOp = new UnLock(NETCONF_SESSION_ID); + return executeOp(commitOp, "netconfMessages/unlock.xml"); + } + private Document getConfigCandidate() throws ParserConfigurationException, SAXException, IOException, NetconfDocumentedException { GetConfig getConfigOp = new GetConfig(yangStoreSnapshot, Optional. absent(), transactionProvider, diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/lock.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/lock.xml new file mode 100644 index 0000000000..f5228b2e94 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/lock.xml @@ -0,0 +1,16 @@ + + + + + + + + + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unlock.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unlock.xml new file mode 100644 index 0000000000..e6e3770ae6 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unlock.xml @@ -0,0 +1,16 @@ + + + + + + + + + \ No newline at end of file -- 2.36.6