From 50005093051d43b0fb02edbb05b37c694c186257 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Sun, 31 Jul 2016 23:55:28 +0200 Subject: [PATCH] BUG-5280: add client connect messages When a frontend is attempting to re-establish communication with the backend it sends its coordinates and various other information to a backend. Sending ConnectClientRequest initiates a handshake, to which the backend will respond either with a failure, or with an adjusted ConnectClientSuccess. Change-Id: I58ba9a2103f80e528654222f82f07416f7d7815e Signed-off-by: Robert Varga --- .../controller/cluster/access/ABIVersion.java | 17 ++++ .../access/commands/ConnectClientFailure.java | 44 +++++++++ .../commands/ConnectClientFailureProxyV1.java | 40 ++++++++ .../access/commands/ConnectClientRequest.java | 91 +++++++++++++++++++ .../commands/ConnectClientRequestProxyV1.java | 67 ++++++++++++++ .../access/commands/ConnectClientSuccess.java | 84 +++++++++++++++++ .../commands/ConnectClientSuccessProxyV1.java | 85 +++++++++++++++++ .../commands/ExistsTransactionSuccess.java | 2 +- 8 files changed, 429 insertions(+), 1 deletion(-) create mode 100644 opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ConnectClientFailure.java create mode 100644 opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ConnectClientFailureProxyV1.java create mode 100644 opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ConnectClientRequest.java create mode 100644 opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ConnectClientRequestProxyV1.java create mode 100644 opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ConnectClientSuccess.java create mode 100644 opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ConnectClientSuccessProxyV1.java diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/ABIVersion.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/ABIVersion.java index c25c156a27..0e40e6e1cb 100644 --- a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/ABIVersion.java +++ b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/ABIVersion.java @@ -15,6 +15,8 @@ import java.io.DataOutput; import java.io.IOException; import javax.annotation.Nonnull; import org.opendaylight.yangtools.concepts.WritableObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Enumeration of all ABI versions supported by this implementation of the client access API. @@ -43,6 +45,8 @@ public enum ABIVersion implements WritableObject { @VisibleForTesting TEST_FUTURE_VERSION(65535); + private static final Logger LOG = LoggerFactory.getLogger(ABIVersion.class); + private final short value; ABIVersion(final int intVersion) { @@ -114,4 +118,17 @@ public enum ABIVersion implements WritableObject { throw new IOException("Unsupported version", e); } } + + public static ABIVersion inexactReadFrom(final @Nonnull DataInput in) throws IOException { + final short onWire = in.readShort(); + try { + return ABIVersion.valueOf(onWire); + } catch (FutureVersionException e) { + LOG.debug("Received future version", e); + return ABIVersion.TEST_FUTURE_VERSION; + } catch (PastVersionException e) { + LOG.debug("Received past version", e); + return ABIVersion.TEST_PAST_VERSION; + } + } } diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ConnectClientFailure.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ConnectClientFailure.java new file mode 100644 index 0000000000..c1380cc980 --- /dev/null +++ b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ConnectClientFailure.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016 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.cluster.access.commands; + +import com.google.common.annotations.Beta; +import org.opendaylight.controller.cluster.access.ABIVersion; +import org.opendaylight.controller.cluster.access.concepts.AbstractRequestFailureProxy; +import org.opendaylight.controller.cluster.access.concepts.ClientIdentifier; +import org.opendaylight.controller.cluster.access.concepts.RequestException; +import org.opendaylight.controller.cluster.access.concepts.RequestFailure; + +/** + * A {@link RequestFailure} reported when {@link ConnectClientRequest} fails. + * + * @author Robert Varga + */ +@Beta +public final class ConnectClientFailure extends RequestFailure { + private static final long serialVersionUID = 1L; + + ConnectClientFailure(final ClientIdentifier target, final RequestException cause) { + super(target, cause); + } + + private ConnectClientFailure(final ConnectClientFailure failure, final ABIVersion version) { + super(failure, version); + } + + @Override + protected AbstractRequestFailureProxy externalizableProxy( + final ABIVersion version) { + return new ConnectClientFailureProxyV1(this); + } + + @Override + protected ConnectClientFailure cloneAsVersion(final ABIVersion version) { + return new ConnectClientFailure(this, version); + } +} diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ConnectClientFailureProxyV1.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ConnectClientFailureProxyV1.java new file mode 100644 index 0000000000..5635754aee --- /dev/null +++ b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ConnectClientFailureProxyV1.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016 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.cluster.access.commands; + +import java.io.DataInput; +import java.io.IOException; +import org.opendaylight.controller.cluster.access.concepts.AbstractRequestFailureProxy; +import org.opendaylight.controller.cluster.access.concepts.ClientIdentifier; +import org.opendaylight.controller.cluster.access.concepts.RequestException; + +/** + * Serialization proxy for use with {@link ConnectClientFailure}. This class implements initial (Boron) serialization + * format. + * + * @author Robert Varga + */ +final class ConnectClientFailureProxyV1 extends AbstractRequestFailureProxy { + public ConnectClientFailureProxyV1() { + // For Externalizable + } + + ConnectClientFailureProxyV1(final ConnectClientFailure failure) { + super(failure); + } + + @Override + protected ConnectClientFailure createFailure(final ClientIdentifier target, final RequestException cause) { + return new ConnectClientFailure(target, cause); + } + + @Override + protected ClientIdentifier readTarget(final DataInput in) throws IOException { + return ClientIdentifier.readFrom(in); + } +} diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ConnectClientRequest.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ConnectClientRequest.java new file mode 100644 index 0000000000..4800cea5e2 --- /dev/null +++ b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ConnectClientRequest.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2016 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.cluster.access.commands; + +import akka.actor.ActorRef; +import com.google.common.annotations.Beta; +import com.google.common.base.MoreObjects.ToStringHelper; +import com.google.common.base.Preconditions; +import javax.annotation.Nonnull; +import org.opendaylight.controller.cluster.access.ABIVersion; +import org.opendaylight.controller.cluster.access.concepts.AbstractRequestProxy; +import org.opendaylight.controller.cluster.access.concepts.ClientIdentifier; +import org.opendaylight.controller.cluster.access.concepts.Request; +import org.opendaylight.controller.cluster.access.concepts.RequestException; + +/** + * Request to connect a frontend instance to the backend. It carries basic information about the frontend: + * - its coordinates in {@link #getReplyTo()}. + * - its minimum supported ABI version + * - its maximum supported ABI version + * + * It also includes request stream sequencing information. + * + * @author Robert Varga + */ +@Beta +public final class ConnectClientRequest extends Request { + private static final long serialVersionUID = 1L; + + private final ABIVersion minVersion; + private final ABIVersion maxVersion; + private final long resumeSequence; + + public ConnectClientRequest(final ClientIdentifier identifier, final ActorRef replyTo, final ABIVersion minVersion, + final ABIVersion maxVersion) { + this(identifier, replyTo, minVersion, maxVersion, 0); + } + + public ConnectClientRequest(final ClientIdentifier identifier, final ActorRef replyTo, final ABIVersion minVersion, + final ABIVersion maxVersion, final long resumeSequence) { + super(identifier, replyTo); + this.minVersion = Preconditions.checkNotNull(minVersion); + this.maxVersion = Preconditions.checkNotNull(maxVersion); + this.resumeSequence = resumeSequence; + } + + private ConnectClientRequest(final ConnectClientRequest request, final ABIVersion version) { + super(request, version); + this.minVersion = request.minVersion; + this.maxVersion = request.maxVersion; + this.resumeSequence = request.resumeSequence; + } + + public ABIVersion getMinVersion() { + return minVersion; + } + + public ABIVersion getMaxVersion() { + return maxVersion; + } + + public long getResumeSequence() { + return resumeSequence; + } + + @Override + public final ConnectClientFailure toRequestFailure(final RequestException cause) { + return new ConnectClientFailure(getTarget(), cause); + } + + @Override + protected AbstractRequestProxy externalizableProxy(final ABIVersion version) { + return new ConnectClientRequestProxyV1(this); + } + + @Override + protected ConnectClientRequest cloneAsVersion(final ABIVersion version) { + return new ConnectClientRequest(this, version); + } + + @Override + protected @Nonnull ToStringHelper addToStringAttributes(final @Nonnull ToStringHelper toStringHelper) { + return super.addToStringAttributes(toStringHelper).add("minVersion", minVersion).add("maxVersion", maxVersion) + .add("resumeSequence", resumeSequence); + } +} diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ConnectClientRequestProxyV1.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ConnectClientRequestProxyV1.java new file mode 100644 index 0000000000..5fb26d071b --- /dev/null +++ b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ConnectClientRequestProxyV1.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2016 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.cluster.access.commands; + +import akka.actor.ActorRef; +import java.io.DataInput; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import org.opendaylight.controller.cluster.access.ABIVersion; +import org.opendaylight.controller.cluster.access.concepts.AbstractRequestProxy; +import org.opendaylight.controller.cluster.access.concepts.ClientIdentifier; +import org.opendaylight.yangtools.concepts.WritableObjects; + +/** + * Externalizable proxy for use with {@link ConnectClientRequest}. It implements the initial (Boron) serialization + * format. + * + * @author Robert Varga + */ +final class ConnectClientRequestProxyV1 extends AbstractRequestProxy { + private ABIVersion minVersion; + private ABIVersion maxVersion; + private long resumeSequence; + + public ConnectClientRequestProxyV1() { + // for Externalizable + } + + ConnectClientRequestProxyV1(final ConnectClientRequest request) { + super(request); + this.minVersion = request.getMinVersion(); + this.maxVersion = request.getMaxVersion(); + this.resumeSequence = request.getResumeSequence(); + } + + @Override + public void writeExternal(final ObjectOutput out) throws IOException { + super.writeExternal(out); + minVersion.writeTo(out); + maxVersion.writeTo(out); + WritableObjects.writeLong(out, resumeSequence); + } + + @Override + public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException { + super.readExternal(in); + minVersion = ABIVersion.inexactReadFrom(in); + maxVersion = ABIVersion.inexactReadFrom(in); + resumeSequence = WritableObjects.readLong(in); + } + + @Override + protected ConnectClientRequest createRequest(final ClientIdentifier target, final ActorRef replyTo) { + return new ConnectClientRequest(target, replyTo, minVersion, maxVersion, resumeSequence); + } + + @Override + protected ClientIdentifier readTarget(final DataInput in) throws IOException { + return ClientIdentifier.readFrom(in); + } +} diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ConnectClientSuccess.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ConnectClientSuccess.java new file mode 100644 index 0000000000..c2302598e3 --- /dev/null +++ b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ConnectClientSuccess.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2016 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.cluster.access.commands; + +import akka.actor.ActorRef; +import akka.actor.ActorSelection; +import com.google.common.annotations.Beta; +import com.google.common.base.MoreObjects.ToStringHelper; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import java.util.List; +import java.util.Optional; +import javax.annotation.Nonnull; +import org.opendaylight.controller.cluster.access.ABIVersion; +import org.opendaylight.controller.cluster.access.concepts.ClientIdentifier; +import org.opendaylight.controller.cluster.access.concepts.RequestSuccess; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; + +/** + * Successful reply to an {@link ConnectClientRequest}. Client actor which initiated this connection should use + * the version reported via {@link #getVersion()} of this message to communicate with this backend. Should this backend + * fail, the client can try accessing the provided alternates. + * + * @author Robert Varga + */ +@Beta +public final class ConnectClientSuccess extends RequestSuccess { + private static final long serialVersionUID = 1L; + + private final List alternates; + private final DataTree dataTree; + private final ActorRef backend; + + ConnectClientSuccess(final ClientIdentifier target, final ActorRef backend, final List alternates, + final Optional dataTree) { + super(target); + this.backend = Preconditions.checkNotNull(backend); + this.alternates = ImmutableList.copyOf(alternates); + this.dataTree = dataTree.orElse(null); + } + + public ConnectClientSuccess(final @Nonnull ClientIdentifier target, final @Nonnull ActorRef backend, + final @Nonnull List alternates, + final @Nonnull DataTree dataTree) { + this(target, backend, alternates, Optional.of(dataTree)); + } + + /** + * Return the list of known alternate backends. The client can use this list to perform recovery procedures. + * + * @return a list of known backend alternates + */ + public @Nonnull List getAlternates() { + return alternates; + } + + public @Nonnull ActorRef getBackend() { + return backend; + } + + public Optional getDataTree() { + return Optional.ofNullable(dataTree); + } + + @Override + protected ConnectClientSuccessProxyV1 externalizableProxy(final ABIVersion version) { + return new ConnectClientSuccessProxyV1(this); + } + + @Override + protected ConnectClientSuccess cloneAsVersion(final ABIVersion version) { + return this; + } + + @Override + protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) { + return super.addToStringAttributes(toStringHelper).add("alternates", alternates).add("dataTree", dataTree); + } +} diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ConnectClientSuccessProxyV1.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ConnectClientSuccessProxyV1.java new file mode 100644 index 0000000000..d05ca37dd0 --- /dev/null +++ b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ConnectClientSuccessProxyV1.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2016 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.cluster.access.commands; + +import akka.actor.ActorRef; +import akka.actor.ActorSelection; +import akka.serialization.JavaSerializer; +import akka.serialization.Serialization; +import java.io.DataInput; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.opendaylight.controller.cluster.access.concepts.AbstractSuccessProxy; +import org.opendaylight.controller.cluster.access.concepts.ClientIdentifier; + +/** + * Externalizable proxy for use with {@link ConnectClientSuccess}. It implements the initial (Boron) serialization + * format. + * + * @author Robert Varga + */ +final class ConnectClientSuccessProxyV1 extends AbstractSuccessProxy { + private static final long serialVersionUID = 1L; + + private List alternates; + private ActorRef backend; + + public ConnectClientSuccessProxyV1() { + // For Externalizable + } + + ConnectClientSuccessProxyV1(final ConnectClientSuccess success) { + super(success); + this.alternates = success.getAlternates(); + this.backend = success.getBackend(); + // We are ignoring the DataTree, it is not serializable anyway + } + + @Override + public void writeExternal(final ObjectOutput out) throws IOException { + super.writeExternal(out); + + out.writeUTF(Serialization.serializedActorPath(backend)); + + out.writeInt(alternates.size()); + for (ActorSelection b : alternates) { + out.writeObject(b.toSerializationFormat()); + } + } + + @Override + public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException { + super.readExternal(in); + + backend = JavaSerializer.currentSystem().value().provider().resolveActorRef(in.readUTF()); + + final int backendsSize = in.readInt(); + if (backendsSize < 1) { + throw new IOException("Illegal number of backends " + backendsSize); + } + + alternates = new ArrayList<>(backendsSize); + for (int i = 0; i < backendsSize; ++i) { + alternates.add(ActorSelection.apply(ActorRef.noSender(), (String)in.readObject())); + } + } + + @Override + protected ConnectClientSuccess createSuccess(final ClientIdentifier target) { + return new ConnectClientSuccess(target, backend, alternates, Optional.empty()); + } + + @Override + protected ClientIdentifier readTarget(final DataInput in) throws IOException { + return ClientIdentifier.readFrom(in); + } +} diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ExistsTransactionSuccess.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ExistsTransactionSuccess.java index 33167921a3..5481d0de40 100644 --- a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ExistsTransactionSuccess.java +++ b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/commands/ExistsTransactionSuccess.java @@ -13,7 +13,7 @@ import org.opendaylight.controller.cluster.access.ABIVersion; import org.opendaylight.controller.cluster.access.concepts.TransactionIdentifier; /** - * Successuful reply to an {@link ExistsTransactionRequest}. It indicates presence of requested data via + * Successful reply to an {@link ExistsTransactionRequest}. It indicates presence of requested data via * {@link #getExists()}. * * @author Robert Varga -- 2.36.6