Add rolling upgrade test.

Change-Id: Id1b09361aa69f1665f19c312933798b5206d46ac
This commit is contained in:
Jordan Halterman 2018-04-04 23:43:23 -07:00 committed by Thomas Vachuska
parent a84936d87e
commit ca7660a289
6 changed files with 189 additions and 22 deletions

View File

@ -18,6 +18,7 @@ package org.onosproject.distributedprimitives.cli;
import org.apache.karaf.shell.commands.Argument; import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command; import org.apache.karaf.shell.commands.Command;
import org.onosproject.cli.AbstractShellCommand; import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.core.Version;
import org.onosproject.store.serializers.KryoNamespaces; import org.onosproject.store.serializers.KryoNamespaces;
import org.onosproject.store.service.ConsistentMap; import org.onosproject.store.service.ConsistentMap;
import org.onosproject.store.service.Serializer; import org.onosproject.store.service.Serializer;
@ -64,6 +65,8 @@ public class ConsistentMapTestCommand extends AbstractShellCommand {
map = storageService.<String, String>consistentMapBuilder() map = storageService.<String, String>consistentMapBuilder()
.withName(name) .withName(name)
.withSerializer(Serializer.using(KryoNamespaces.BASIC)) .withSerializer(Serializer.using(KryoNamespaces.BASIC))
.withVersion(Version.version("1.0.0"))
.withCompatibilityFunction((value, version) -> version + ":" + value)
.build(); .build();
if ("get".equals(operation)) { if ("get".equals(operation)) {
print(map.get(arg1)); print(map.get(arg1));
@ -95,6 +98,22 @@ public class ConsistentMapTestCommand extends AbstractShellCommand {
} else { } else {
print("%b", map.replace(arg1, arg2, arg3)); print("%b", map.replace(arg1, arg2, arg3));
} }
} else if ("compatiblePut".equals(operation)) {
ConsistentMap<String, String> map = storageService.<String, String>consistentMapBuilder()
.withName(name)
.withSerializer(Serializer.using(KryoNamespaces.BASIC))
.withCompatibilityFunction((value, version) -> version + ":" + value)
.withVersion(Version.version("2.0.0"))
.build();
print(map.put(arg1, arg2));
} else if ("compatibleGet".equals(operation)) {
ConsistentMap<String, String> map = storageService.<String, String>consistentMapBuilder()
.withName(name)
.withSerializer(Serializer.using(KryoNamespaces.BASIC))
.withCompatibilityFunction((value, version) -> version + ":" + value)
.withVersion(Version.version("2.0.0"))
.build();
print(map.get(arg1));
} }
} }

View File

@ -100,8 +100,9 @@ public final class Version implements Comparable<Version> {
public static Version fromInt(int version) { public static Version fromInt(int version) {
int major = (version >> 24) & 0xff; int major = (version >> 24) & 0xff;
int minor = (version >> 16) & 0xff; int minor = (version >> 16) & 0xff;
int patch = (version) & 0xffff; int patch = (version >> 8) & 0xff;
return new Version(major, minor, String.valueOf(patch), null); int build = version & 0xff;
return new Version(major, minor, String.valueOf(patch), String.valueOf(build));
} }
/** /**
@ -157,13 +158,30 @@ public final class Version implements Comparable<Version> {
public int toInt() { public int toInt() {
byte major = (byte) this.major; byte major = (byte) this.major;
byte minor = (byte) this.minor; byte minor = (byte) this.minor;
short patch;
byte patch;
if (this.patch != null) {
try { try {
patch = (short) Integer.parseInt(this.patch); patch = (byte) Integer.parseInt(this.patch.replaceAll("[^0-9]", ""));
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
patch = 0; patch = 0;
} }
return major << 24 | (minor & 0xff) << 16 | (patch & 0xffff); } else {
patch = 0;
}
byte build;
if (this.build != null) {
try {
build = (byte) Integer.parseInt(this.build.replaceAll("[^0-9]", ""));
} catch (NumberFormatException e) {
build = 0;
}
} else {
build = 0;
}
return major << 24 | (minor & 0xff) << 16 | (patch & 0xff) << 8 | (build & 0xff);
} }
@Override @Override

View File

@ -83,25 +83,30 @@ public class VersionTest {
version1 = version("1.2"); version1 = version("1.2");
version2 = Version.fromInt(version1.toInt()); version2 = Version.fromInt(version1.toInt());
assertEquals(version2, version(1, 2, "0", null)); assertEquals(version2, version(1, 2, "0", "0"));
version1 = version("1.2.foo.bar"); version1 = version("1.2.foo.bar");
version2 = Version.fromInt(version1.toInt()); version2 = Version.fromInt(version1.toInt());
assertEquals(version2, version(1, 2, "0", null)); assertEquals(version2, version(1, 2, "0", "0"));
version1 = version("1.2.3"); version1 = version("1.2.3");
version2 = Version.fromInt(version1.toInt()); version2 = Version.fromInt(version1.toInt());
assertEquals(version2, version(1, 2, "3", null)); assertEquals(version2, version(1, 2, "3", "0"));
version1 = version("255.254.65535.252"); version1 = version("1.2.3-SNAPSHOT");
version2 = Version.fromInt(version1.toInt()); version2 = Version.fromInt(version1.toInt());
assertEquals(version2, version(255, 254, "65535", null)); assertEquals(version2, version(1, 2, "3", "0"));
version1 = version("255.254.253.252");
version2 = Version.fromInt(version1.toInt());
assertEquals(version2, version(255, 254, "253", "252"));
assertTrue(version("0.0.2").toInt() > version("0.0.1").toInt()); assertTrue(version("0.0.2").toInt() > version("0.0.1").toInt());
assertTrue(version("0.1.0").toInt() > version("0.0.1").toInt()); assertTrue(version("0.1.0").toInt() > version("0.0.1").toInt());
assertTrue(version("1.0.0").toInt() > version("0.1.0").toInt()); assertTrue(version("1.0.0").toInt() > version("0.1.0").toInt());
assertTrue(version("1.1.0").toInt() > version("1.0.1").toInt()); assertTrue(version("1.1.0").toInt() > version("1.0.1").toInt());
assertTrue(version("2.1.1").toInt() > version("1.10.10").toInt()); assertTrue(version("2.1.1").toInt() > version("1.10.10").toInt());
assertTrue(version("0.1.0-rc2").toInt() > version("0.1.0-rc1").toInt());
} }
@Test @Test

View File

@ -105,13 +105,6 @@ public class UpgradeManager
Upgrade upgrade = getState(); Upgrade upgrade = getState();
// If the upgrade state is not initialized, ensure this node matches the version of the cluster.
if (!upgrade.status().active() && !Objects.equals(upgrade.source(), localVersion)) {
log.error("Node version {} inconsistent with cluster version {}", localVersion, upgrade.source());
throw new IllegalStateException("Node version " + localVersion +
" inconsistent with cluster version " + upgrade.source());
}
// If the upgrade state is initialized then check the node version. // If the upgrade state is initialized then check the node version.
if (upgrade.status() == Upgrade.Status.INITIALIZED) { if (upgrade.status() == Upgrade.Status.INITIALIZED) {
// If the source version equals the target version, attempt to update the target version. // If the source version equals the target version, attempt to update the target version.

View File

@ -19,11 +19,17 @@ import com.google.common.collect.Sets;
import io.atomix.protocols.raft.proxy.RaftProxy; import io.atomix.protocols.raft.proxy.RaftProxy;
import io.atomix.protocols.raft.service.RaftService; import io.atomix.protocols.raft.service.RaftService;
import org.junit.Test; import org.junit.Test;
import org.onlab.util.HexString;
import org.onlab.util.Tools; import org.onlab.util.Tools;
import org.onosproject.store.primitives.MapUpdate; import org.onosproject.store.primitives.MapUpdate;
import org.onosproject.store.primitives.TransactionId; import org.onosproject.store.primitives.TransactionId;
import org.onosproject.store.primitives.impl.CompatibleValue;
import org.onosproject.store.primitives.impl.DistributedPrimitives;
import org.onosproject.store.serializers.KryoNamespaces;
import org.onosproject.store.service.AsyncConsistentMap;
import org.onosproject.store.service.MapEvent; import org.onosproject.store.service.MapEvent;
import org.onosproject.store.service.MapEventListener; import org.onosproject.store.service.MapEventListener;
import org.onosproject.store.service.Serializer;
import org.onosproject.store.service.TransactionLog; import org.onosproject.store.service.TransactionLog;
import org.onosproject.store.service.Version; import org.onosproject.store.service.Version;
import org.onosproject.store.service.Versioned; import org.onosproject.store.service.Versioned;
@ -584,6 +590,50 @@ public class AtomixConsistentMapTest extends AtomixTestBase<AtomixConsistentMap>
assertTrue(Arrays.equals(value2, event.newValue().value())); assertTrue(Arrays.equals(value2, event.newValue().value()));
} }
@Test
public void testCompatibilityFunction() throws Throwable {
AtomixConsistentMap atomixMap = newPrimitive("testCompatibilityFunction");
Serializer rawSerializer = Serializer.using(KryoNamespaces.API, CompatibleValue.class);
Serializer valueSerializer = Serializer.using(KryoNamespaces.BASIC);
// Convert the byte[] value to CompatibleValue<byte[]>
AsyncConsistentMap<String, CompatibleValue<byte[]>> rawMap = DistributedPrimitives.newTranscodingMap(
atomixMap,
key -> HexString.toHexString(rawSerializer.encode(key)),
string -> rawSerializer.decode(HexString.fromHexString(string)),
value -> value == null ? null : rawSerializer.encode(value),
bytes -> rawSerializer.decode(bytes));
// Convert the CompatibleValue<byte[]> value to CompatibleValue<V> using the user-provided serializer.
AsyncConsistentMap<String, CompatibleValue<String>> compatibleMap =
DistributedPrimitives.newTranscodingMap(
rawMap,
key -> key,
key -> key,
value -> value == null ? null :
new CompatibleValue<byte[]>(valueSerializer.encode(value.value()), value.version()),
value -> value == null ? null :
new CompatibleValue<String>(valueSerializer.decode(value.value()), value.version()));
AsyncConsistentMap<String, String> map1 = DistributedPrimitives.newCompatibleMap(
compatibleMap,
(value, version) -> version + ":" + value,
org.onosproject.core.Version.version("1.0.0"));
AsyncConsistentMap<String, String> map2 = DistributedPrimitives.newCompatibleMap(
compatibleMap,
(value, version) -> version + ":" + value,
org.onosproject.core.Version.version("1.0.1"));
map1.put("foo", "Hello world!").join();
assertEquals("Hello world!", map1.get("foo").join().value());
assertEquals("1.0.0:Hello world!", map2.get("foo").join().value());
map2.put("bar", "Hello world again!").join();
assertEquals("Hello world again!", map2.get("bar").join().value());
assertEquals("1.0.1:Hello world again!", map1.get("bar").join().value());
}
private static class TestMapEventListener implements MapEventListener<String, byte[]> { private static class TestMapEventListener implements MapEventListener<String, byte[]> {
private final BlockingQueue<MapEvent<String, byte[]>> queue = new ArrayBlockingQueue<>(1); private final BlockingQueue<MapEvent<String, byte[]>> queue = new ArrayBlockingQueue<>(1);

View File

@ -0,0 +1,82 @@
<!--
~ Copyright 2018-present Open Networking Foundation
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<scenario name="rolling-upgrade" description="ONOS cluster rolling upgrade">
<import file="${ONOS_SCENARIOS}/dist-setup.xml"/>
<dependency name="Distributed-Primitives-Setup"/>
<group name="Upgrade" requires="Distributed-Primitives-Setup">
<step name="Push-Bits" exec="onos-push-bits-through-proxy" if="${OCT}"/>
<group name="Perform-Upgrade">
<sequential var="${OC#}"
starts="Stop-Service-${#}"
ends="Wait-for-Start-${#-1}">
<step name="Stop-Service-${#}"
exec="onos-service ${OC#} stop"/>
<step name="Wait-for-Stop-${#}"
exec="onos-wait-for-stop ${OC#}"
requires="~Stop-Service-${#}"/>
<step name="Uninstall-${#}"
exec="onos-uninstall ${OC#}"
requires="~Wait-for-Stop-${#}"/>
<step name="Push-Bits-${#}"
exec="onos-push-bits ${OC#}"
unless="${OCT}"
requires="~Stop-Service-${#}"/>
<step name="Install-Upgrade-${#}"
exec="onos-install -v ${OC#}"
requires="Push-Bits-${#},Push-Bits,Uninstall-${#}"/>
<step name="Secure-SSH-${#}"
exec="onos-secure-ssh -u ${ONOS_WEB_USER} -p ${ONOS_WEB_PASS} ${OC#}"
requires="~Install-Upgrade-${#}"/>
<step name="Wait-for-Start-${#}"
exec="onos-wait-for-start ${OC#}"
requires="Secure-SSH-${#}"/>
<step name="Distributed-Primitives-Check-Apps-${#}"
exec="onos-check-apps ${OC#} distributedprimitives includes"
requires="Wait-for-Start-${#}"/>
</sequential>
</group>
<group name="Verify-Upgrade" requires="Perform-Upgrade">
<parallel var="${OC#}">
<step name="Check-Nodes-${#}"
exec="onos-check-nodes ${OC#}"
delay="3"/>
<step name="Check-Components-${#}"
exec="onos-check-components ${OC#}"
delay="5"
requires="~Check-Nodes-${#}"/>
<step name="Check-Logs-${#}"
exec="onos-check-logs ${OC#}"
requires="~Check-Components-${#}"/>
<step name="Check-Apps-${#}"
exec="onos-check-apps ${OC#} ${ONOS_APPS},distributedprimitives includes"
requires="~Check-Components-${#}"/>
</parallel>
</group>
</group>
</scenario>