You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by ji...@apache.org on 2015/07/25 01:37:59 UTC

[06/12] mesos git commit: Moved containerizer related tests under src/tests/containerizer.

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/routing_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/routing_tests.cpp b/src/tests/containerizer/routing_tests.cpp
new file mode 100644
index 0000000..e4f1bcf
--- /dev/null
+++ b/src/tests/containerizer/routing_tests.cpp
@@ -0,0 +1,1416 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+#include <signal.h>
+#include <unistd.h>
+
+#include <linux/version.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <gtest/gtest.h>
+
+#include <process/clock.hpp>
+#include <process/gtest.hpp>
+
+#include <stout/foreach.hpp>
+#include <stout/gtest.hpp>
+#include <stout/hashmap.hpp>
+#include <stout/ip.hpp>
+#include <stout/mac.hpp>
+#include <stout/net.hpp>
+#include <stout/stringify.hpp>
+
+#include "linux/routing/handle.hpp"
+#include "linux/routing/route.hpp"
+#include "linux/routing/utils.hpp"
+
+#include "linux/routing/diagnosis/diagnosis.hpp"
+
+#include "linux/routing/filter/basic.hpp"
+#include "linux/routing/filter/handle.hpp"
+#include "linux/routing/filter/icmp.hpp"
+#include "linux/routing/filter/ip.hpp"
+
+#include "linux/routing/link/link.hpp"
+
+#include "linux/routing/queueing/fq_codel.hpp"
+#include "linux/routing/queueing/htb.hpp"
+#include "linux/routing/queueing/ingress.hpp"
+#include "linux/routing/queueing/statistics.hpp"
+
+using namespace process;
+
+using namespace routing;
+using namespace routing::filter;
+using namespace routing::queueing;
+
+using std::endl;
+using std::set;
+using std::string;
+using std::vector;
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+
+static const string TEST_VETH_LINK = "veth-test";
+static const string TEST_PEER_LINK = "veth-peer";
+
+
+class RoutingTest : public ::testing::Test
+{
+protected:
+  virtual void SetUp()
+  {
+    ASSERT_SOME(routing::check())
+      << "-------------------------------------------------------------\n"
+      << "We cannot run any routing tests because either your libnl\n"
+      << "library or kernel is not new enough. You can either upgrade,\n"
+      << "or disable this test case\n"
+      << "-------------------------------------------------------------";
+  }
+};
+
+
+// Tests that require setting up virtual ethernet on host.
+class RoutingVethTest : public RoutingTest
+{
+protected:
+  virtual void SetUp()
+  {
+    RoutingTest::SetUp();
+
+    // Clean up the test links, in case it wasn't cleaned up properly
+    // from previous tests.
+    link::remove(TEST_VETH_LINK);
+
+    ASSERT_SOME_FALSE(link::exists(TEST_VETH_LINK));
+    ASSERT_SOME_FALSE(link::exists(TEST_PEER_LINK));
+  }
+
+  virtual void TearDown()
+  {
+    link::remove(TEST_VETH_LINK);
+  }
+};
+
+
+TEST_F(RoutingTest, PortRange)
+{
+  Try<ip::PortRange> ports = ip::PortRange::fromBeginEnd(1, 0);
+  EXPECT_ERROR(ports);
+
+  ports = ip::PortRange::fromBeginEnd(4, 11);
+  EXPECT_ERROR(ports);
+
+  ports = ip::PortRange::fromBeginEnd(4, 7);
+  ASSERT_SOME(ports);
+  EXPECT_EQ(4u, ports.get().begin());
+  EXPECT_EQ(7u, ports.get().end());
+  EXPECT_EQ(0xfffc, ports.get().mask());
+  EXPECT_EQ("[4,7]", stringify(ports.get()));
+
+  ports = ip::PortRange::fromBeginEnd(10, 10);
+  ASSERT_SOME(ports);
+  EXPECT_EQ(10u, ports.get().begin());
+  EXPECT_EQ(10u, ports.get().end());
+  EXPECT_EQ(0xffff, ports.get().mask());
+  EXPECT_EQ("[10,10]", stringify(ports.get()));
+
+  ports = ip::PortRange::fromBeginMask(20, 0xffff);
+  ASSERT_SOME(ports);
+  EXPECT_EQ(20u, ports.get().begin());
+  EXPECT_EQ(20u, ports.get().end());
+  EXPECT_EQ(0xffff, ports.get().mask());
+  EXPECT_EQ("[20,20]", stringify(ports.get()));
+
+  ports = ip::PortRange::fromBeginMask(1024, 0xfff8);
+  ASSERT_SOME(ports);
+  EXPECT_EQ(1024u, ports.get().begin());
+  EXPECT_EQ(1031u, ports.get().end());
+  EXPECT_EQ(0xfff8, ports.get().mask());
+  EXPECT_EQ("[1024,1031]", stringify(ports.get()));
+}
+
+
+TEST_F(RoutingTest, RouteTable)
+{
+  Try<vector<route::Rule> > table = route::table();
+  EXPECT_SOME(table);
+
+  Result<net::IP> gateway = route::defaultGateway();
+  EXPECT_FALSE(gateway.isError());
+}
+
+
+TEST_F(RoutingTest, LinkIndex)
+{
+  Try<set<string> > links = net::links();
+  ASSERT_SOME(links);
+
+  foreach (const string& link, links.get()) {
+    EXPECT_SOME_NE(0, link::index(link));
+  }
+
+  EXPECT_NONE(link::index("not-exist"));
+}
+
+
+TEST_F(RoutingTest, LinkName)
+{
+  Try<set<string> > links = net::links();
+  ASSERT_SOME(links);
+
+  foreach (const string& link, links.get()) {
+    EXPECT_SOME_NE(0, link::index(link));
+    EXPECT_SOME_EQ(link, link::name(link::index(link).get()));
+  }
+}
+
+
+TEST_F(RoutingTest, LinkStatistics)
+{
+  Try<set<string> > links = net::links();
+  ASSERT_SOME(links);
+
+  foreach (const string& link, links.get()) {
+    Result<hashmap<string, uint64_t> > statistics = link::statistics(link);
+
+    ASSERT_SOME(statistics);
+    EXPECT_TRUE(statistics.get().contains("rx_packets"));
+    EXPECT_TRUE(statistics.get().contains("rx_bytes"));
+    EXPECT_TRUE(statistics.get().contains("tx_packets"));
+    EXPECT_TRUE(statistics.get().contains("tx_bytes"));
+  }
+
+  EXPECT_NONE(link::statistics("not-exist"));
+}
+
+
+TEST_F(RoutingTest, LinkExists)
+{
+  Try<set<string> > links = net::links();
+  ASSERT_SOME(links);
+
+  foreach (const string& link, links.get()) {
+    EXPECT_SOME_TRUE(link::exists(link));
+  }
+
+  EXPECT_SOME_FALSE(link::exists("not-exist"));
+}
+
+
+TEST_F(RoutingTest, Eth0)
+{
+  Result<string> eth0 = link::eth0();
+  EXPECT_FALSE(eth0.isError());
+
+  if (eth0.isSome()) {
+    ASSERT_SOME_TRUE(link::exists(eth0.get()));
+  }
+}
+
+
+TEST_F(RoutingTest, Lo)
+{
+  Result<string> lo = link::lo();
+  EXPECT_FALSE(lo.isError());
+
+  if (lo.isSome()) {
+    ASSERT_SOME_TRUE(link::exists(lo.get()));
+  }
+}
+
+
+TEST_F(RoutingTest, INETSockets)
+{
+  Try<vector<diagnosis::socket::Info> > infos =
+    diagnosis::socket::infos(AF_INET, diagnosis::socket::state::ALL);
+
+  EXPECT_SOME(infos);
+
+  foreach (const diagnosis::socket::Info& info, infos.get()) {
+    // Both source and destination IPs should be present since
+    // 'AF_INET' is asked for.
+    EXPECT_SOME(info.sourceIP);
+    EXPECT_SOME(info.destinationIP);
+  }
+}
+
+
+TEST_F(RoutingVethTest, ROOT_LinkCreate)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  EXPECT_SOME_NE(0, link::index(TEST_VETH_LINK));
+  EXPECT_SOME_NE(0, link::index(TEST_PEER_LINK));
+
+  // Test the case where the veth (with the same name) already exists.
+  EXPECT_SOME_FALSE(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_LinkRemove)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::remove(TEST_VETH_LINK));
+  EXPECT_SOME_FALSE(link::remove(TEST_VETH_LINK));
+  EXPECT_SOME_FALSE(link::remove(TEST_PEER_LINK));
+}
+
+
+// An old glibc might not have this symbol.
+#ifndef CLONE_NEWNET
+#define CLONE_NEWNET 0x40000000
+#endif
+
+
+// Entry point of the child process (used in clone()).
+static int child(void*)
+{
+  // Wait to be killed.
+  while (true) {
+    sleep(1);
+  }
+
+  // Should not reach here.
+  ABORT("Child process should not reach here");
+}
+
+
+TEST_F(RoutingVethTest, ROOT_LinkCreatePid)
+{
+  // Stack used in the child process.
+  unsigned long long stack[32];
+
+  pid_t pid = ::clone(child, &stack[31], CLONE_NEWNET | SIGCHLD, NULL);
+  ASSERT_NE(-1, pid);
+
+  // In parent process.
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, pid));
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+
+  // The peer should not exist in parent network namespace.
+  EXPECT_SOME_FALSE(link::exists(TEST_PEER_LINK));
+
+  // TODO(jieyu): Enter the child network namespace and make sure that
+  // the TEST_PEER_LINK is there.
+
+  EXPECT_SOME_NE(0, link::index(TEST_VETH_LINK));
+
+  // Kill the child process.
+  ASSERT_NE(-1, kill(pid, SIGKILL));
+
+  // Wait for the child process.
+  int status;
+  EXPECT_NE(-1, waitpid((pid_t) -1, &status, 0));
+  ASSERT_TRUE(WIFSIGNALED(status));
+  EXPECT_EQ(SIGKILL, WTERMSIG(status));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_LinkWait)
+{
+  AWAIT_READY(link::removed(TEST_VETH_LINK));
+
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  Future<Nothing> removed = link::removed(TEST_VETH_LINK);
+  EXPECT_TRUE(removed.isPending());
+
+  ASSERT_SOME_TRUE(link::remove(TEST_VETH_LINK));
+  AWAIT_READY(removed);
+}
+
+
+TEST_F(RoutingVethTest, ROOT_LinkSetUp)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  EXPECT_SOME_FALSE(link::isUp(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::setUp(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::isUp(TEST_VETH_LINK));
+
+  EXPECT_SOME_FALSE(link::isUp(TEST_PEER_LINK));
+  EXPECT_SOME_TRUE(link::setUp(TEST_PEER_LINK));
+  EXPECT_SOME_TRUE(link::isUp(TEST_PEER_LINK));
+
+  EXPECT_NONE(link::isUp("non-exist"));
+  EXPECT_SOME_FALSE(link::setUp("non-exist"));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_LinkSetMAC)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  uint8_t bytes[6] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc};
+
+  EXPECT_SOME_TRUE(link::setMAC(TEST_VETH_LINK, net::MAC(bytes)));
+  EXPECT_SOME_TRUE(link::setMAC(TEST_PEER_LINK, net::MAC(bytes)));
+
+  Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
+
+  ASSERT_SOME(mac);
+  EXPECT_EQ(mac.get(), net::MAC(bytes));
+
+  mac = net::mac(TEST_PEER_LINK);
+
+  ASSERT_SOME(mac);
+  EXPECT_EQ(mac.get(), net::MAC(bytes));
+
+  EXPECT_SOME_FALSE(link::setMAC("non-exist", net::MAC(bytes)));
+
+  // Kernel will reject a multicast MAC address.
+  uint8_t multicast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+  EXPECT_ERROR(link::setMAC(TEST_VETH_LINK, net::MAC(multicast)));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_LinkMTU)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  EXPECT_SOME_TRUE(link::setMTU(TEST_VETH_LINK, 10000));
+
+  Result<unsigned int> mtu = link::mtu(TEST_VETH_LINK);
+  ASSERT_SOME(mtu);
+  EXPECT_EQ(10000u, mtu.get());
+
+  EXPECT_NONE(link::mtu("not-exist"));
+  EXPECT_SOME_FALSE(link::setMTU("not-exist", 1500));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_IngressQdisc)
+{
+  // Test for a qdisc on a nonexistent interface should fail.
+  EXPECT_SOME_FALSE(ingress::exists("noSuchInterface"));
+
+  EXPECT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  // Interface exists but does not have an ingress qdisc.
+  EXPECT_SOME_FALSE(ingress::exists(TEST_VETH_LINK));
+  EXPECT_SOME_FALSE(ingress::exists(TEST_PEER_LINK));
+
+  // Interfaces without qdisc established no data.
+  EXPECT_NONE(ingress::statistics(TEST_VETH_LINK));
+  EXPECT_NONE(ingress::statistics(TEST_PEER_LINK));
+
+  // Try to create an ingress qdisc on a nonexistent interface.
+  EXPECT_ERROR(ingress::create("noSuchInterface"));
+
+  // Create an ingress qdisc on an existing interface.
+  EXPECT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  // Interface exists and has an ingress qdisc.
+  EXPECT_SOME_TRUE(ingress::exists(TEST_VETH_LINK));
+  EXPECT_SOME_FALSE(ingress::exists(TEST_PEER_LINK));
+
+  // Interfaces which exist return at least the core statisitcs.
+  Result<hashmap<string, uint64_t>> stats = ingress::statistics(TEST_VETH_LINK);
+  ASSERT_SOME(stats);
+  EXPECT_TRUE(stats.get().contains(statistics::PACKETS));
+  EXPECT_TRUE(stats.get().contains(statistics::BYTES));
+  EXPECT_TRUE(stats.get().contains(statistics::RATE_BPS));
+  EXPECT_TRUE(stats.get().contains(statistics::RATE_PPS));
+  EXPECT_TRUE(stats.get().contains(statistics::QLEN));
+  EXPECT_TRUE(stats.get().contains(statistics::BACKLOG));
+  EXPECT_TRUE(stats.get().contains(statistics::DROPS));
+  EXPECT_TRUE(stats.get().contains(statistics::REQUEUES));
+  EXPECT_TRUE(stats.get().contains(statistics::OVERLIMITS));
+
+  // Interface without qdisc returns no data.
+  EXPECT_NONE(ingress::statistics(TEST_PEER_LINK));
+
+  // Try to create a second ingress qdisc on an existing interface.
+  EXPECT_SOME_FALSE(ingress::create(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(ingress::exists(TEST_VETH_LINK));
+  EXPECT_SOME_FALSE(ingress::exists(TEST_PEER_LINK));
+
+  // Remove the ingress qdisc.
+  EXPECT_SOME_TRUE(ingress::remove(TEST_VETH_LINK));
+  EXPECT_SOME_FALSE(ingress::exists(TEST_VETH_LINK));
+  EXPECT_SOME_FALSE(ingress::exists(TEST_PEER_LINK));
+
+  // Try to remove it from a nonexistent interface.
+  EXPECT_SOME_FALSE(ingress::remove("noSuchInterface"));
+
+  // Remove the ingress qdisc when it does not exist.
+  EXPECT_SOME_FALSE(ingress::remove(TEST_VETH_LINK));
+  EXPECT_SOME_FALSE(ingress::exists(TEST_VETH_LINK));
+  EXPECT_SOME_FALSE(ingress::exists(TEST_PEER_LINK));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_HTBQdisc)
+{
+  // Test for a qdisc on a nonexistent interface should fail.
+  EXPECT_SOME_FALSE(htb::exists("noSuchInterface", EGRESS_ROOT));
+
+  EXPECT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  // This test uses a common handle throughout
+  const Handle handle = Handle(1, 0);
+
+  // Interface exists but does not have an htb qdisc.
+  EXPECT_SOME_FALSE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Interfaces without qdisc established no data.
+  EXPECT_NONE(htb::statistics(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_NONE(htb::statistics(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Try to create an htb qdisc on a nonexistent interface.
+  EXPECT_ERROR(htb::create("noSuchInterface", EGRESS_ROOT, handle));
+
+  // Create an htb qdisc on an existing interface.
+  EXPECT_SOME_TRUE(htb::create(TEST_VETH_LINK, EGRESS_ROOT, handle));
+
+  // Interface exists and has an htb qdisc.
+  EXPECT_SOME_TRUE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Interfaces which exist return at least the core statisitcs.
+  Result<hashmap<string, uint64_t>> stats =
+      htb::statistics(TEST_VETH_LINK, EGRESS_ROOT);
+  ASSERT_SOME(stats);
+  EXPECT_TRUE(stats.get().contains(statistics::PACKETS));
+  EXPECT_TRUE(stats.get().contains(statistics::BYTES));
+  EXPECT_TRUE(stats.get().contains(statistics::RATE_BPS));
+  EXPECT_TRUE(stats.get().contains(statistics::RATE_PPS));
+  EXPECT_TRUE(stats.get().contains(statistics::QLEN));
+  EXPECT_TRUE(stats.get().contains(statistics::BACKLOG));
+  EXPECT_TRUE(stats.get().contains(statistics::DROPS));
+  EXPECT_TRUE(stats.get().contains(statistics::REQUEUES));
+  EXPECT_TRUE(stats.get().contains(statistics::OVERLIMITS));
+
+  // Interface without htb qdisc returns no data.
+  EXPECT_NONE(htb::statistics(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Try to create a second htb qdisc on an existing interface.
+  EXPECT_SOME_FALSE(htb::create(TEST_VETH_LINK, EGRESS_ROOT, handle));
+  EXPECT_SOME_TRUE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Remove the htb qdisc.
+  EXPECT_SOME_TRUE(htb::remove(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Try to remove it from a nonexistent interface.
+  EXPECT_SOME_FALSE(htb::remove("noSuchInterface", EGRESS_ROOT));
+
+  // Remove the htb qdisc when it does not exist.
+  EXPECT_SOME_FALSE(htb::remove(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Try to create an htb qdisc on a nonexistent interface and
+  // default handle.
+  EXPECT_ERROR(htb::create("noSuchInterface", EGRESS_ROOT, None()));
+
+  // Create an htb qdisc on an existing interface.
+  EXPECT_SOME_TRUE(htb::create(TEST_VETH_LINK, EGRESS_ROOT, None()));
+
+  // Interface exists and has an htb qdisc.
+  EXPECT_SOME_TRUE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Remove the htb qdisc.
+  EXPECT_SOME_TRUE(htb::remove(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_FqCodeQdisc)
+{
+  // Test for a qdisc on a nonexistent interface should fail.
+  EXPECT_SOME_FALSE(fq_codel::exists("noSuchInterface", EGRESS_ROOT));
+
+  EXPECT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  // This test uses a common handle throughout
+  const Handle handle = Handle(1, 0);
+
+  // Interface exists but does not have an fq_codel qdisc.
+  EXPECT_SOME_FALSE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Interfaces without qdisc established no data.
+  EXPECT_NONE(fq_codel::statistics(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_NONE(fq_codel::statistics(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Try to create an fq_codel qdisc on a nonexistent interface.
+  EXPECT_ERROR(fq_codel::create("noSuchInterface", EGRESS_ROOT, handle));
+
+  // Create an fq_codel qdisc on an existing interface.
+  EXPECT_SOME_TRUE(fq_codel::create(TEST_VETH_LINK, EGRESS_ROOT, handle));
+
+  // Interface exists and has an fq_codel qdisc.
+  EXPECT_SOME_TRUE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Interfaces which exist return at least the core statisitcs.
+  Result<hashmap<string, uint64_t>> stats =
+      fq_codel::statistics(TEST_VETH_LINK, EGRESS_ROOT);
+  ASSERT_SOME(stats);
+  EXPECT_TRUE(stats.get().contains(statistics::PACKETS));
+  EXPECT_TRUE(stats.get().contains(statistics::BYTES));
+  EXPECT_TRUE(stats.get().contains(statistics::RATE_BPS));
+  EXPECT_TRUE(stats.get().contains(statistics::RATE_PPS));
+  EXPECT_TRUE(stats.get().contains(statistics::QLEN));
+  EXPECT_TRUE(stats.get().contains(statistics::BACKLOG));
+  EXPECT_TRUE(stats.get().contains(statistics::DROPS));
+  EXPECT_TRUE(stats.get().contains(statistics::REQUEUES));
+  EXPECT_TRUE(stats.get().contains(statistics::OVERLIMITS));
+
+  // Interface without fq_codel qdisc returns no data.
+  EXPECT_NONE(fq_codel::statistics(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Try to create a second fq_codel qdisc on an existing interface.
+  EXPECT_SOME_FALSE(fq_codel::create(TEST_VETH_LINK, EGRESS_ROOT, handle));
+  EXPECT_SOME_TRUE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Remove the fq_codel qdisc.
+  EXPECT_SOME_TRUE(fq_codel::remove(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Try to remove it from a nonexistent interface.
+  EXPECT_SOME_FALSE(fq_codel::remove("noSuchInterface", EGRESS_ROOT));
+
+  // Remove the fq_codel qdisc when it does not exist.
+  EXPECT_SOME_FALSE(fq_codel::remove(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Try to create an fq_codel qdisc on a nonexistent interface and
+  // default handle.
+  EXPECT_ERROR(fq_codel::create("noSuchInterface", EGRESS_ROOT, None()));
+
+  // Create an fq_codel qdisc on an existing interface.
+  EXPECT_SOME_TRUE(fq_codel::create(TEST_VETH_LINK, EGRESS_ROOT, None()));
+
+  // Interface exists and has an fq_codel qdisc.
+  EXPECT_SOME_TRUE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Remove the fq_codel qdisc.
+  EXPECT_SOME_TRUE(fq_codel::remove(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_FqCodelClassifier)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  const Handle handle = Handle(1, 0);
+  ASSERT_SOME_TRUE(fq_codel::create(TEST_VETH_LINK, EGRESS_ROOT, handle));
+
+  EXPECT_SOME_TRUE(basic::create(
+      TEST_VETH_LINK,
+      handle,
+      ETH_P_ALL,
+      None(),
+      Handle(handle, 0)));
+
+  EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, handle, ETH_P_ALL));
+
+  EXPECT_SOME_TRUE(basic::create(
+      TEST_VETH_LINK,
+      handle,
+      ETH_P_ARP,
+      None(),
+      Handle(handle, 0)));
+
+  // There is a kernel bug which could cause this test fail. Please
+  // make sure your kernel, if newer than 3.14, has commit:
+  // b057df24a7536cce6c372efe9d0e3d1558afedf4
+  // (https://git.kernel.org/cgit/linux/kernel/git/davem/net.git).
+  // Please fix your kernel if you see this failure.
+  EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, handle, ETH_P_ARP));
+
+  EXPECT_SOME_TRUE(icmp::create(
+      TEST_VETH_LINK,
+      handle,
+      icmp::Classifier(None()),
+      None(),
+      Handle(handle, 0)));
+
+  EXPECT_SOME_TRUE(icmp::exists(
+      TEST_VETH_LINK,
+      handle,
+      icmp::Classifier(None())));
+
+  Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
+  ASSERT_SOME(mac);
+
+  net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+  Try<ip::PortRange> sourcePorts =
+    ip::PortRange::fromBeginEnd(1024, 1027);
+  ASSERT_SOME(sourcePorts);
+
+  Try<ip::PortRange> destinationPorts =
+    ip::PortRange::fromBeginEnd(2000, 2000);
+  ASSERT_SOME(destinationPorts);
+
+  ip::Classifier classifier =
+    ip::Classifier(
+        mac.get(),
+        ip,
+        sourcePorts.get(),
+        destinationPorts.get());
+
+  EXPECT_SOME_TRUE(ip::create(
+      TEST_VETH_LINK,
+      handle,
+      classifier,
+      None(),
+      Handle(handle, 1)));
+
+  EXPECT_SOME_TRUE(ip::exists(TEST_VETH_LINK, handle, classifier));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ARPFilterCreate)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  EXPECT_SOME_TRUE(basic::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      ETH_P_ARP,
+      None(),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ARPFilterCreateDuplicated)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  set<string> links;
+  links.insert(TEST_PEER_LINK);
+
+  EXPECT_SOME_TRUE(basic::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      ETH_P_ARP,
+      None(),
+      action::Mirror(links)));
+
+  EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
+
+  EXPECT_SOME_FALSE(basic::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      ETH_P_ARP,
+      None(),
+      action::Redirect(TEST_PEER_LINK)));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ARPFilterRemove)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  set<string> links;
+  links.insert(TEST_PEER_LINK);
+
+  EXPECT_SOME_TRUE(basic::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      ETH_P_ARP,
+      None(),
+      action::Mirror(links)));
+
+  EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
+  EXPECT_SOME_TRUE(basic::remove(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
+  EXPECT_SOME_FALSE(basic::exists(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ARPFilterUpdate)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  set<string> links;
+  links.insert(TEST_PEER_LINK);
+
+  EXPECT_SOME_FALSE(basic::update(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      ETH_P_ARP,
+      action::Mirror(links)));
+
+  EXPECT_SOME_TRUE(basic::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      ETH_P_ARP,
+      None(),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
+
+  EXPECT_SOME_TRUE(basic::update(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      ETH_P_ARP,
+      action::Mirror(links)));
+
+  EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ICMPFilterCreate)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+  EXPECT_SOME_TRUE(icmp::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(ip),
+      None(),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(icmp::exists(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(ip)));
+
+  Result<vector<icmp::Classifier> > classifiers =
+    icmp::classifiers(TEST_VETH_LINK, ingress::HANDLE);
+
+  ASSERT_SOME(classifiers);
+  ASSERT_EQ(1u, classifiers.get().size());
+  EXPECT_SOME_EQ(ip, classifiers.get().front().destinationIP);
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ICMPFilterCreateDuplicated)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  set<string> links;
+  links.insert(TEST_PEER_LINK);
+
+  EXPECT_SOME_TRUE(icmp::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None()),
+      None(),
+      action::Mirror(links)));
+
+  EXPECT_SOME_TRUE(icmp::exists(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None())));
+
+  EXPECT_SOME_FALSE(icmp::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None()),
+      None(),
+      action::Mirror(links)));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ICMPFilterCreateMultiple)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  net::IP ip1 = net::IP(0x01020304); // 1.2.3.4
+  net::IP ip2 = net::IP(0x05060708); // 5.6.7.8
+
+  EXPECT_SOME_TRUE(icmp::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(ip1),
+      Priority(1, 1),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(icmp::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(ip2),
+      Priority(1, 2),
+      action::Redirect(TEST_PEER_LINK)));
+
+  Result<vector<icmp::Classifier> > classifiers =
+    icmp::classifiers(TEST_VETH_LINK, ingress::HANDLE);
+
+  ASSERT_SOME(classifiers);
+  ASSERT_EQ(2u, classifiers.get().size());
+  EXPECT_SOME_EQ(ip1, classifiers.get().front().destinationIP);
+  EXPECT_SOME_EQ(ip2, classifiers.get().back().destinationIP);
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ICMPFilterRemove)
+{
+  ASSERT_SOME(link::create(
+      TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  EXPECT_SOME_TRUE(icmp::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None()),
+      None(),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(icmp::exists(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None())));
+
+  EXPECT_SOME_TRUE(icmp::remove(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None())));
+
+  EXPECT_SOME_FALSE(icmp::exists(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None())));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ICMPFilterUpdate)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+  set<string> links;
+  links.insert(TEST_PEER_LINK);
+
+  EXPECT_SOME_FALSE(icmp::update(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None()),
+      action::Mirror(links)));
+
+  EXPECT_SOME_TRUE(icmp::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None()),
+      None(),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(icmp::exists(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None())));
+
+  EXPECT_SOME_FALSE(icmp::update(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(ip),
+      action::Mirror(links)));
+
+  EXPECT_SOME_TRUE(icmp::update(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None()),
+      action::Mirror(links)));
+
+  EXPECT_SOME_TRUE(icmp::exists(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None())));
+
+  EXPECT_SOME_FALSE(icmp::exists(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(ip)));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_IPFilterCreate)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
+  ASSERT_SOME(mac);
+
+  net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+  Try<ip::PortRange> sourcePorts =
+    ip::PortRange::fromBeginEnd(1024, 1027);
+
+  ASSERT_SOME(sourcePorts);
+
+  Try<ip::PortRange> destinationPorts =
+    ip::PortRange::fromBeginEnd(2000, 2000);
+
+  ASSERT_SOME(destinationPorts);
+
+  ip::Classifier classifier =
+    ip::Classifier(
+        mac.get(),
+        ip,
+        sourcePorts.get(),
+        destinationPorts.get());
+
+  EXPECT_SOME_TRUE(ip::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      classifier,
+      None(),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(ip::exists(TEST_VETH_LINK, ingress::HANDLE, classifier));
+
+  Result<vector<ip::Classifier> > classifiers =
+    ip::classifiers(TEST_VETH_LINK, ingress::HANDLE);
+
+  ASSERT_SOME(classifiers);
+  ASSERT_EQ(1u, classifiers.get().size());
+  EXPECT_SOME_EQ(mac.get(), classifiers.get().front().destinationMAC);
+  EXPECT_SOME_EQ(ip, classifiers.get().front().destinationIP);
+
+  EXPECT_SOME_EQ(
+      sourcePorts.get(),
+      classifiers.get().front().sourcePorts);
+
+  EXPECT_SOME_EQ(
+      destinationPorts.get(),
+      classifiers.get().front().destinationPorts);
+}
+
+
+TEST_F(RoutingVethTest, ROOT_IPFilterCreate2)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  net::IP ip(0x12345678);
+
+  EXPECT_SOME_TRUE(ip::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      ip::Classifier(None(), ip, None(), None()),
+      None(),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(ip::exists(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      ip::Classifier(None(), ip, None(), None())));
+
+  Result<vector<ip::Classifier> > classifiers =
+    ip::classifiers(TEST_VETH_LINK, ingress::HANDLE);
+
+  ASSERT_SOME(classifiers);
+  ASSERT_EQ(1u, classifiers.get().size());
+  EXPECT_NONE(classifiers.get().front().destinationMAC);
+  EXPECT_SOME_EQ(ip, classifiers.get().front().destinationIP);
+  EXPECT_NONE(classifiers.get().front().sourcePorts);
+  EXPECT_NONE(classifiers.get().front().destinationPorts);
+}
+
+
+TEST_F(RoutingVethTest, ROOT_IPFilterCreateDuplicated)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
+  ASSERT_SOME(mac);
+
+  net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+  Try<ip::PortRange> sourcePorts =
+    ip::PortRange::fromBeginEnd(1024, 1027);
+
+  ASSERT_SOME(sourcePorts);
+
+  Try<ip::PortRange> destinationPorts =
+    ip::PortRange::fromBeginEnd(2000, 2000);
+
+  ASSERT_SOME(destinationPorts);
+
+  ip::Classifier classifier =
+    ip::Classifier(
+        mac.get(),
+        ip,
+        sourcePorts.get(),
+        destinationPorts.get());
+
+  EXPECT_SOME_TRUE(ip::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      classifier,
+      None(),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(ip::exists(TEST_VETH_LINK, ingress::HANDLE, classifier));
+
+  EXPECT_SOME_FALSE(ip::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      classifier,
+      None(),
+      action::Redirect(TEST_PEER_LINK)));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_IPFilterCreateMultiple)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
+  ASSERT_SOME(mac);
+
+  net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+  Try<ip::PortRange> sourcePorts1 =
+    ip::PortRange::fromBeginEnd(1024, 1027);
+
+  ASSERT_SOME(sourcePorts1);
+
+  Try<ip::PortRange> destinationPorts1 =
+    ip::PortRange::fromBeginEnd(2000, 2000);
+
+  ASSERT_SOME(destinationPorts1);
+
+  Try<ip::PortRange> sourcePorts2 =
+    ip::PortRange::fromBeginEnd(3024, 3025);
+
+  ASSERT_SOME(sourcePorts2);
+
+  Try<ip::PortRange> destinationPorts2 =
+    ip::PortRange::fromBeginEnd(4000, 4003);
+
+  ASSERT_SOME(destinationPorts2);
+
+  ip::Classifier classifier1 =
+    ip::Classifier(
+        mac.get(),
+        ip,
+        sourcePorts1.get(),
+        destinationPorts1.get());
+
+  ip::Classifier classifier2 =
+    ip::Classifier(
+        mac.get(),
+        ip,
+        sourcePorts2.get(),
+        destinationPorts2.get());
+
+  EXPECT_SOME_TRUE(ip::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      classifier1,
+      Priority(2, 1),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(ip::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      classifier2,
+      Priority(2, 2),
+      action::Redirect(TEST_PEER_LINK)));
+
+  Result<vector<ip::Classifier> > classifiers =
+    ip::classifiers(TEST_VETH_LINK, ingress::HANDLE);
+
+  ASSERT_SOME(classifiers);
+  ASSERT_EQ(2u, classifiers.get().size());
+
+  EXPECT_SOME_EQ(mac.get(), classifiers.get().front().destinationMAC);
+  EXPECT_SOME_EQ(ip, classifiers.get().front().destinationIP);
+
+  EXPECT_SOME_EQ(
+      sourcePorts1.get(),
+      classifiers.get().front().sourcePorts);
+
+  EXPECT_SOME_EQ(
+      destinationPorts1.get(),
+      classifiers.get().front().destinationPorts);
+
+  EXPECT_SOME_EQ(mac.get(), classifiers.get().back().destinationMAC);
+  EXPECT_SOME_EQ(ip, classifiers.get().back().destinationIP);
+
+  EXPECT_SOME_EQ(
+      sourcePorts2.get(),
+      classifiers.get().back().sourcePorts);
+
+  EXPECT_SOME_EQ(
+      destinationPorts2.get(),
+      classifiers.get().back().destinationPorts);
+}
+
+
+TEST_F(RoutingVethTest, ROOT_IPFilterRemove)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
+  ASSERT_SOME(mac);
+
+  net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+  Try<ip::PortRange> sourcePorts1 =
+    ip::PortRange::fromBeginEnd(1024, 1027);
+
+  ASSERT_SOME(sourcePorts1);
+
+  Try<ip::PortRange> destinationPorts1 =
+    ip::PortRange::fromBeginEnd(2000, 2000);
+
+  ASSERT_SOME(destinationPorts1);
+
+  Try<ip::PortRange> sourcePorts2 =
+    ip::PortRange::fromBeginEnd(3024, 3025);
+
+  ASSERT_SOME(sourcePorts2);
+
+  Try<ip::PortRange> destinationPorts2 =
+    ip::PortRange::fromBeginEnd(4000, 4003);
+
+  ASSERT_SOME(destinationPorts2);
+
+  ip::Classifier classifier1 =
+    ip::Classifier(
+        mac.get(),
+        ip,
+        sourcePorts1.get(),
+        destinationPorts1.get());
+
+  ip::Classifier classifier2 =
+    ip::Classifier(
+        mac.get(),
+        ip,
+        sourcePorts2.get(),
+        destinationPorts2.get());
+
+  EXPECT_SOME_TRUE(ip::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      classifier1,
+      None(),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(ip::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      classifier2,
+      None(),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(ip::remove(TEST_VETH_LINK, ingress::HANDLE, classifier1));
+  EXPECT_SOME_FALSE(ip::exists(TEST_VETH_LINK, ingress::HANDLE, classifier1));
+
+  EXPECT_SOME_TRUE(ip::remove(TEST_VETH_LINK, ingress::HANDLE, classifier2));
+  EXPECT_SOME_FALSE(ip::exists(TEST_VETH_LINK, ingress::HANDLE, classifier2));
+
+  Result<vector<ip::Classifier> > classifiers =
+    ip::classifiers(TEST_VETH_LINK, ingress::HANDLE);
+
+  ASSERT_SOME(classifiers);
+  EXPECT_EQ(0u, classifiers.get().size());
+}
+
+
+// Test the workaround introduced for MESOS-1617.
+TEST_F(RoutingVethTest, ROOT_HandleGeneration)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
+  ASSERT_SOME(mac);
+
+  net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+  Try<ip::PortRange> sourcePorts1 =
+    ip::PortRange::fromBeginEnd(1024, 1027);
+
+  ASSERT_SOME(sourcePorts1);
+
+  Try<ip::PortRange> destinationPorts1 =
+    ip::PortRange::fromBeginEnd(2000, 2000);
+
+  ASSERT_SOME(destinationPorts1);
+
+  Try<ip::PortRange> sourcePorts2 =
+    ip::PortRange::fromBeginEnd(3024, 3025);
+
+  ASSERT_SOME(sourcePorts2);
+
+  Try<ip::PortRange> destinationPorts2 =
+    ip::PortRange::fromBeginEnd(4000, 4003);
+
+  ASSERT_SOME(destinationPorts2);
+
+  ip::Classifier classifier1 =
+    ip::Classifier(
+        mac.get(),
+        ip,
+        sourcePorts1.get(),
+        destinationPorts1.get());
+
+  ip::Classifier classifier2 =
+    ip::Classifier(
+        mac.get(),
+        ip,
+        sourcePorts2.get(),
+        destinationPorts2.get());
+
+  // Use handle 800:00:fff for the first filter.
+  EXPECT_SOME_TRUE(ip::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      classifier1,
+      Priority(2, 1),
+      U32Handle(0x800, 0x0, 0xfff),
+      action::Redirect(TEST_PEER_LINK)));
+
+  // With the workaround, this filter should be assigned a handle
+  // different than 800:00:fff.
+  EXPECT_SOME_TRUE(ip::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      classifier2,
+      Priority(2, 1),
+      action::Redirect(TEST_PEER_LINK)));
+
+  // Try to remove the second filter. If we don't have the workaround,
+  // removing the second filter will return false since the kernel
+  // will find the handle matches the first filter.
+  EXPECT_SOME_TRUE(ip::remove(TEST_VETH_LINK, ingress::HANDLE, classifier2));
+}
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/sched_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/sched_tests.cpp b/src/tests/containerizer/sched_tests.cpp
new file mode 100644
index 0000000..00723d0
--- /dev/null
+++ b/src/tests/containerizer/sched_tests.cpp
@@ -0,0 +1,96 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+#include "linux/sched.hpp"
+
+#include <process/gtest.hpp>
+#include <process/reap.hpp>
+
+#include <gtest/gtest.h>
+
+#include <stout/gtest.hpp>
+
+using sched::Policy;
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+// TODO(idownes): Test the priority and preemption behavior for
+// running competing SCHED_OTHER and SCHED_IDLE tasks.
+
+TEST(SchedTest, ROOT_PolicySelf)
+{
+  Try<Policy> original = sched::policy::get();
+  ASSERT_SOME(original);
+
+  Policy different = (original.get() == Policy::OTHER ? Policy::IDLE
+                                                      : Policy::OTHER);
+
+  // Change our own scheduling policy.
+  EXPECT_SOME(sched::policy::set(different));
+  EXPECT_SOME_EQ(different, sched::policy::get());
+
+  // Change it back.
+  EXPECT_SOME(sched::policy::set(original.get()));
+  EXPECT_SOME_EQ(original.get(), sched::policy::get());
+}
+
+
+// Change the scheduling policy of a different process (our child).
+TEST(SchedTest, ROOT_PolicyChild)
+{
+  Try<Policy> original = sched::policy::get();
+  ASSERT_SOME(original);
+
+  Policy different = (original.get() == Policy::OTHER ? Policy::IDLE
+                                                      : Policy::OTHER);
+
+  pid_t pid = ::fork();
+  ASSERT_NE(-1, pid);
+
+  if (pid == 0) {
+    // Child.
+    sleep(10);
+
+    ABORT("Child process should not reach here");
+  }
+
+  // Continue in parent.
+  // Check the child has inherited our policy.
+  EXPECT_SOME_EQ(original.get(), sched::policy::get(pid));
+
+  // Check we can change the child's policy.
+  EXPECT_SOME(sched::policy::set(different, pid));
+  EXPECT_SOME_EQ(different, sched::policy::get(pid));
+
+  process::Future<Option<int>> status = process::reap(pid);
+
+  // Kill the child process.
+  ASSERT_NE(-1, ::kill(pid, SIGKILL));
+
+  // Wait for the child process.
+  AWAIT_READY(status);
+  ASSERT_SOME(status.get());
+  EXPECT_TRUE(WIFSIGNALED(status.get().get()));
+  EXPECT_EQ(SIGKILL, WTERMSIG(status.get().get()));
+}
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/setns_test_helper.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/setns_test_helper.cpp b/src/tests/containerizer/setns_test_helper.cpp
new file mode 100644
index 0000000..ec68b08
--- /dev/null
+++ b/src/tests/containerizer/setns_test_helper.cpp
@@ -0,0 +1,63 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+#include <set>
+#include <string>
+
+#include <stout/foreach.hpp>
+#include <stout/subcommand.hpp>
+#include <stout/try.hpp>
+
+#include "linux/ns.hpp"
+
+#include "tests/containerizer/setns_test_helper.hpp"
+
+using std::set;
+using std::string;
+
+const char SetnsTestHelper::NAME[] = "SetnsTestHelper";
+
+int SetnsTestHelper::execute()
+{
+  // Get all the available namespaces.
+  set<string> namespaces = ns::namespaces();
+
+  // Note: /proc has not been remounted so we can look up pid 1's
+  // namespaces, even if we're in a separate pid namespace.
+  foreach (const string& ns, namespaces) {
+    if (ns == "pid") {
+      // ns::setns() does not (currently) support pid namespaces so
+      // this should return an error.
+      Try<Nothing> setns = ns::setns(1, ns);
+      if (!setns.isError()) {
+        return 1;
+      }
+    } else if (ns == "user") {
+      // ns::setns() will also fail with user namespaces, so we skip
+      // for now. See MESOS-3083.
+      continue;
+    } else {
+      Try<Nothing> setns = ns::setns(1, ns);
+      if (!setns.isSome()) {
+        return 1;
+      }
+    }
+  }
+
+  return 0;
+}

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/setns_test_helper.hpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/setns_test_helper.hpp b/src/tests/containerizer/setns_test_helper.hpp
new file mode 100644
index 0000000..51d6378
--- /dev/null
+++ b/src/tests/containerizer/setns_test_helper.hpp
@@ -0,0 +1,35 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef __SETNS_TEST_HELPER_HPP__
+#define __SETNS_TEST_HELPER_HPP__
+
+#include <stout/subcommand.hpp>
+
+class SetnsTestHelper : public Subcommand
+{
+public:
+  static const char NAME[];
+
+  SetnsTestHelper() : Subcommand(NAME) {}
+
+protected:
+  virtual int execute();
+};
+
+#endif // __SETNS_TEST_HELPER_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/setns_test_helper_main.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/setns_test_helper_main.cpp b/src/tests/containerizer/setns_test_helper_main.cpp
new file mode 100644
index 0000000..c8e270a
--- /dev/null
+++ b/src/tests/containerizer/setns_test_helper_main.cpp
@@ -0,0 +1,30 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+*/
+
+#include <stout/subcommand.hpp>
+
+#include "tests/containerizer/setns_test_helper.hpp"
+
+int main(int argc, char** argv)
+{
+  return Subcommand::dispatch(
+      None(),
+      argc,
+      argv,
+      new SetnsTestHelper());
+}

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer_tests.cpp b/src/tests/containerizer_tests.cpp
deleted file mode 100644
index 9508613..0000000
--- a/src/tests/containerizer_tests.cpp
+++ /dev/null
@@ -1,731 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- */
-
-#include <list>
-#include <map>
-#include <string>
-#include <vector>
-
-#include <gmock/gmock.h>
-
-#include <mesos/mesos.hpp>
-
-#include <mesos/slave/isolator.hpp>
-
-#include <process/future.hpp>
-#include <process/owned.hpp>
-
-#include <stout/strings.hpp>
-
-#include "slave/flags.hpp"
-
-#include "slave/containerizer/fetcher.hpp"
-#include "slave/containerizer/launcher.hpp"
-
-#include "slave/containerizer/mesos/containerizer.hpp"
-
-#include "tests/flags.hpp"
-#include "tests/isolator.hpp"
-#include "tests/launcher.hpp"
-#include "tests/mesos.hpp"
-#include "tests/utils.hpp"
-
-using namespace process;
-
-using mesos::internal::master::Master;
-
-using mesos::internal::slave::Fetcher;
-using mesos::internal::slave::Launcher;
-using mesos::internal::slave::MesosContainerizer;
-using mesos::internal::slave::MesosContainerizerProcess;
-using mesos::internal::slave::PosixLauncher;
-using mesos::internal::slave::Provisioner;
-using mesos::internal::slave::Slave;
-
-using mesos::internal::slave::state::ExecutorState;
-using mesos::internal::slave::state::FrameworkState;
-using mesos::internal::slave::state::RunState;
-using mesos::internal::slave::state::SlaveState;
-
-using mesos::slave::ExecutorLimitation;
-using mesos::slave::ExecutorRunState;
-using mesos::slave::Isolator;
-using mesos::slave::IsolatorProcess;
-
-using std::list;
-using std::map;
-using std::string;
-using std::vector;
-
-using testing::_;
-using testing::DoAll;
-using testing::Invoke;
-using testing::Return;
-
-namespace mesos {
-namespace internal {
-namespace tests {
-
-class MesosContainerizerIsolatorPreparationTest :
-  public TemporaryDirectoryTest
-{
-public:
-  // Construct a MesosContainerizer with TestIsolator(s) which use the provided
-  // 'prepare' command(s).
-  Try<MesosContainerizer*> CreateContainerizer(
-      Fetcher* fetcher,
-      const vector<Option<CommandInfo>>& prepares)
-  {
-    vector<Owned<Isolator>> isolators;
-
-    foreach (const Option<CommandInfo>& prepare, prepares) {
-      Try<Isolator*> isolator = TestIsolatorProcess::create(prepare);
-      if (isolator.isError()) {
-        return Error(isolator.error());
-      }
-
-      isolators.push_back(Owned<Isolator>(isolator.get()));
-    }
-
-    slave::Flags flags;
-    flags.launcher_dir = path::join(tests::flags.build_dir, "src");
-
-    Try<Launcher*> launcher = PosixLauncher::create(flags);
-    if (launcher.isError()) {
-      return Error(launcher.error());
-    }
-
-    return new MesosContainerizer(
-        flags,
-        false,
-        fetcher,
-        Owned<Launcher>(launcher.get()),
-        isolators,
-        hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>());
-  }
-
-  Try<MesosContainerizer*> CreateContainerizer(
-      Fetcher* fetcher,
-      const Option<CommandInfo>& prepare)
-  {
-    vector<Option<CommandInfo>> prepares;
-    prepares.push_back(prepare);
-
-    return CreateContainerizer(fetcher, prepares);
-  }
-};
-
-
-// The isolator has a prepare command that succeeds.
-TEST_F(MesosContainerizerIsolatorPreparationTest, ScriptSucceeds)
-{
-  string directory = os::getcwd(); // We're inside a temporary sandbox.
-  string file = path::join(directory, "child.script.executed");
-
-  Fetcher fetcher;
-
-  Try<MesosContainerizer*> containerizer = CreateContainerizer(
-      &fetcher,
-      CREATE_COMMAND_INFO("touch " + file));
-
-  CHECK_SOME(containerizer);
-
-  ContainerID containerId;
-  containerId.set_value("test_container");
-
-  Future<bool> launch = containerizer.get()->launch(
-      containerId,
-      CREATE_EXECUTOR_INFO("executor", "exit 0"),
-      directory,
-      None(),
-      SlaveID(),
-      PID<Slave>(),
-      false);
-
-  // Wait until the launch completes.
-  AWAIT_READY(launch);
-
-  // Wait for the child (preparation script + executor) to complete.
-  Future<containerizer::Termination> wait =
-    containerizer.get()->wait(containerId);
-
-  AWAIT_READY(wait);
-
-  // Check the child exited correctly.
-  EXPECT_TRUE(wait.get().has_status());
-  EXPECT_EQ(0, wait.get().status());
-
-  // Check the preparation script actually ran.
-  EXPECT_TRUE(os::exists(file));
-
-  // Destroy the container.
-  containerizer.get()->destroy(containerId);
-
-  delete containerizer.get();
-}
-
-
-// The isolator has a prepare command that fails.
-TEST_F(MesosContainerizerIsolatorPreparationTest, ScriptFails)
-{
-  string directory = os::getcwd(); // We're inside a temporary sandbox.
-  string file = path::join(directory, "child.script.executed");
-
-  Fetcher fetcher;
-
-  Try<MesosContainerizer*> containerizer = CreateContainerizer(
-      &fetcher,
-      CREATE_COMMAND_INFO("touch " + file + " && exit 1"));
-
-  CHECK_SOME(containerizer);
-
-  ContainerID containerId;
-  containerId.set_value("test_container");
-
-  Future<bool> launch = containerizer.get()->launch(
-      containerId,
-      CREATE_EXECUTOR_INFO("executor", "exit 0"),
-      directory,
-      None(),
-      SlaveID(),
-      PID<Slave>(),
-      false);
-
-  // Wait until the launch completes.
-  AWAIT_READY(launch);
-
-  // Wait for the child (preparation script + executor) to complete.
-  Future<containerizer::Termination> wait =
-    containerizer.get()->wait(containerId);
-
-  AWAIT_READY(wait);
-
-  // Check the child failed to exit correctly.
-  EXPECT_TRUE(wait.get().has_status());
-  EXPECT_NE(0, wait.get().status());
-
-  // Check the preparation script actually ran.
-  EXPECT_TRUE(os::exists(file));
-
-  // Destroy the container.
-  containerizer.get()->destroy(containerId);
-
-  delete containerizer.get();
-}
-
-
-// There are two isolators, one with a prepare command that succeeds
-// and another that fails. The execution order is not defined but the
-// launch should fail from the failing prepare command.
-TEST_F(MesosContainerizerIsolatorPreparationTest, MultipleScripts)
-{
-  string directory = os::getcwd(); // We're inside a temporary sandbox.
-  string file1 = path::join(directory, "child.script.executed.1");
-  string file2 = path::join(directory, "child.script.executed.2");
-
-  vector<Option<CommandInfo>> prepares;
-
-  // This isolator prepare command one will succeed if called first,
-  // otherwise it won't get run.
-  prepares.push_back(CREATE_COMMAND_INFO("touch " + file1 + " && exit 0"));
-
-  // This will fail, either first or after the successful command.
-  prepares.push_back(CREATE_COMMAND_INFO("touch " + file2 + " && exit 1"));
-
-  Fetcher fetcher;
-
-  Try<MesosContainerizer*> containerizer =
-    CreateContainerizer(&fetcher, prepares);
-
-  CHECK_SOME(containerizer);
-
-  ContainerID containerId;
-  containerId.set_value("test_container");
-
-  Future<bool> launch = containerizer.get()->launch(
-      containerId,
-      CREATE_EXECUTOR_INFO("executor", "exit 0"),
-      directory,
-      None(),
-      SlaveID(),
-      PID<Slave>(),
-      false);
-
-  // Wait until the launch completes.
-  AWAIT_READY(launch);
-
-  // Wait for the child (preparation script(s) + executor) to complete.
-  Future<containerizer::Termination> wait =
-    containerizer.get()->wait(containerId);
-  AWAIT_READY(wait);
-
-  // Check the child failed to exit correctly.
-  EXPECT_TRUE(wait.get().has_status());
-  EXPECT_NE(0, wait.get().status());
-
-  // Check the failing preparation script has actually ran.
-  EXPECT_TRUE(os::exists(file2));
-
-  // Destroy the container.
-  containerizer.get()->destroy(containerId);
-
-  delete containerizer.get();
-}
-
-
-class MesosContainerizerExecuteTest : public TemporaryDirectoryTest {};
-
-
-TEST_F(MesosContainerizerExecuteTest, IoRedirection)
-{
-  string directory = os::getcwd(); // We're inside a temporary sandbox.
-
-  slave::Flags flags;
-  flags.launcher_dir = path::join(tests::flags.build_dir, "src");
-
-  Fetcher fetcher;
-
-  // Use local=false so std{err,out} are redirected to files.
-  Try<MesosContainerizer*> containerizer =
-    MesosContainerizer::create(flags, false, &fetcher);
-
-  ASSERT_SOME(containerizer);
-
-  ContainerID containerId;
-  containerId.set_value("test_container");
-
-  string errMsg = "this is stderr";
-  string outMsg = "this is stdout";
-  string command =
-    "(echo '" + errMsg + "' 1>&2) && echo '" + outMsg + "'";
-
-  Future<bool> launch = containerizer.get()->launch(
-      containerId,
-      CREATE_EXECUTOR_INFO("executor", command),
-      directory,
-      None(),
-      SlaveID(),
-      PID<Slave>(),
-      false);
-
-  // Wait for the launch to complete.
-  AWAIT_READY(launch);
-
-  // Wait on the container.
-  Future<containerizer::Termination> wait =
-    containerizer.get()->wait(containerId);
-
-  AWAIT_READY(wait);
-
-  // Check the executor exited correctly.
-  EXPECT_TRUE(wait.get().has_status());
-  EXPECT_EQ(0, wait.get().status());
-
-  // Check that std{err, out} was redirected.
-  // NOTE: Fetcher uses GLOG, which outputs extra information to
-  // stderr.
-  Try<string> stderr = os::read(path::join(directory, "stderr"));
-  ASSERT_SOME(stderr);
-  EXPECT_TRUE(strings::contains(stderr.get(), errMsg));
-
-  EXPECT_SOME_EQ(outMsg + "\n", os::read(path::join(directory, "stdout")));
-
-  delete containerizer.get();
-}
-
-
-class MesosContainerizerDestroyTest : public MesosTest {};
-
-
-class MockMesosContainerizerProcess : public MesosContainerizerProcess
-{
-public:
-  MockMesosContainerizerProcess(
-      const slave::Flags& flags,
-      bool local,
-      Fetcher* fetcher,
-      const Owned<Launcher>& launcher,
-      const vector<Owned<Isolator>>& isolators,
-      const hashmap<ContainerInfo::Image::Type,
-                    Owned<Provisioner>>& provisioners)
-    : MesosContainerizerProcess(
-          flags,
-          local,
-          fetcher,
-          launcher,
-          isolators,
-          provisioners)
-  {
-    // NOTE: See TestContainerizer::setup for why we use
-    // 'EXPECT_CALL' and 'WillRepeatedly' here instead of
-    // 'ON_CALL' and 'WillByDefault'.
-    EXPECT_CALL(*this, exec(_, _))
-      .WillRepeatedly(Invoke(this, &MockMesosContainerizerProcess::_exec));
-  }
-
-  MOCK_METHOD2(
-      exec,
-      Future<bool>(
-          const ContainerID& containerId,
-          int pipeWrite));
-
-  Future<bool> _exec(
-      const ContainerID& containerId,
-      int pipeWrite)
-  {
-    return MesosContainerizerProcess::exec(
-        containerId,
-        pipeWrite);
-  }
-};
-
-
-class MockIsolator : public mesos::slave::Isolator
-{
-public:
-  MockIsolator()
-  {
-    EXPECT_CALL(*this, watch(_))
-      .WillRepeatedly(Return(watchPromise.future()));
-
-    EXPECT_CALL(*this, isolate(_, _))
-      .WillRepeatedly(Return(Nothing()));
-
-    EXPECT_CALL(*this, cleanup(_))
-      .WillRepeatedly(Return(Nothing()));
-
-    EXPECT_CALL(*this, prepare(_, _, _, _, _))
-      .WillRepeatedly(Invoke(this, &MockIsolator::_prepare));
-  }
-
-  MOCK_METHOD2(
-      recover,
-      Future<Nothing>(
-          const list<ExecutorRunState>&,
-          const hashset<ContainerID>&));
-
-  MOCK_METHOD5(
-      prepare,
-      Future<Option<CommandInfo>>(
-          const ContainerID&,
-          const ExecutorInfo&,
-          const string&,
-          const Option<string>&,
-          const Option<string>&));
-
-  virtual Future<Option<CommandInfo>> _prepare(
-      const ContainerID& containerId,
-      const ExecutorInfo& executorInfo,
-      const string& directory,
-      const Option<string>& rootfs,
-      const Option<string>& user)
-  {
-    return None();
-  }
-
-  MOCK_METHOD2(
-      isolate,
-      Future<Nothing>(const ContainerID&, pid_t));
-
-  MOCK_METHOD1(
-      watch,
-      Future<mesos::slave::ExecutorLimitation>(const ContainerID&));
-
-  MOCK_METHOD2(
-      update,
-      Future<Nothing>(const ContainerID&, const Resources&));
-
-  MOCK_METHOD1(
-      usage,
-      Future<ResourceStatistics>(const ContainerID&));
-
-  MOCK_METHOD1(
-      cleanup,
-      Future<Nothing>(const ContainerID&));
-
-  Promise<mesos::slave::ExecutorLimitation> watchPromise;
-};
-
-
-// Destroying a mesos containerizer while it is fetching should
-// complete without waiting for the fetching to finish.
-TEST_F(MesosContainerizerDestroyTest, DestroyWhileFetching)
-{
-  slave::Flags flags = CreateSlaveFlags();
-
-  Try<Launcher*> launcher = PosixLauncher::create(flags);
-  ASSERT_SOME(launcher);
-
-  Fetcher fetcher;
-
-  MockMesosContainerizerProcess* process = new MockMesosContainerizerProcess(
-      flags,
-      true,
-      &fetcher,
-      Owned<Launcher>(launcher.get()),
-      vector<Owned<Isolator>>(),
-      hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>());
-
-  Future<Nothing> exec;
-  Promise<bool> promise;
-
-  // Letting exec hang to simulate a long fetch.
-  EXPECT_CALL(*process, exec(_, _))
-    .WillOnce(DoAll(FutureSatisfy(&exec),
-                    Return(promise.future())));
-
-  MesosContainerizer containerizer((Owned<MesosContainerizerProcess>(process)));
-
-  ContainerID containerId;
-  containerId.set_value("test_container");
-
-  TaskInfo taskInfo;
-  CommandInfo commandInfo;
-  taskInfo.mutable_command()->MergeFrom(commandInfo);
-
-  containerizer.launch(
-      containerId,
-      taskInfo,
-      CREATE_EXECUTOR_INFO("executor", "exit 0"),
-      os::getcwd(),
-      None(),
-      SlaveID(),
-      PID<Slave>(),
-      false);
-
-  Future<containerizer::Termination> wait = containerizer.wait(containerId);
-
-  AWAIT_READY(exec);
-
-  containerizer.destroy(containerId);
-
-  // The container should still exit even if fetch didn't complete.
-  AWAIT_READY(wait);
-}
-
-
-// Destroying a mesos containerizer while it is preparing should wait
-// until isolators are finished preparing before destroying.
-TEST_F(MesosContainerizerDestroyTest, DestroyWhilePreparing)
-{
-  slave::Flags flags = CreateSlaveFlags();
-
-  Try<Launcher*> launcher = PosixLauncher::create(flags);
-  ASSERT_SOME(launcher);
-
-  MockIsolator* isolator = new MockIsolator();
-
-  Future<Nothing> prepare;
-  Promise<Option<CommandInfo>> promise;
-
-  // Simulate a long prepare from the isolator.
-  EXPECT_CALL(*isolator, prepare(_, _, _, _, _))
-    .WillOnce(DoAll(FutureSatisfy(&prepare),
-                    Return(promise.future())));
-
-  Fetcher fetcher;
-
-  MockMesosContainerizerProcess* process = new MockMesosContainerizerProcess(
-      flags,
-      true,
-      &fetcher,
-      Owned<Launcher>(launcher.get()),
-      {Owned<Isolator>(isolator)},
-      hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>());
-
-  MesosContainerizer containerizer((Owned<MesosContainerizerProcess>(process)));
-
-  ContainerID containerId;
-  containerId.set_value("test_container");
-
-  TaskInfo taskInfo;
-  CommandInfo commandInfo;
-  taskInfo.mutable_command()->MergeFrom(commandInfo);
-
-  containerizer.launch(
-      containerId,
-      taskInfo,
-      CREATE_EXECUTOR_INFO("executor", "exit 0"),
-      os::getcwd(),
-      None(),
-      SlaveID(),
-      PID<Slave>(),
-      false);
-
-  Future<containerizer::Termination> wait = containerizer.wait(containerId);
-
-  AWAIT_READY(prepare);
-
-  containerizer.destroy(containerId);
-
-  // The container should not exit until prepare is complete.
-  ASSERT_TRUE(wait.isPending());
-
-  // Need to help the compiler to disambiguate between overloads.
-  Option<CommandInfo> option = commandInfo;
-  promise.set(option);
-
-  AWAIT_READY(wait);
-
-  containerizer::Termination termination = wait.get();
-
-  EXPECT_EQ(
-      "Container destroyed while preparing isolators",
-      termination.message());
-
-  EXPECT_TRUE(termination.killed());
-  EXPECT_FALSE(termination.has_status());
-}
-
-
-// This action destroys the container using the real launcher and
-// waits until the destroy is complete.
-ACTION_P(InvokeDestroyAndWait, launcher)
-{
-  Future<Nothing> destroy = launcher->real->destroy(arg0);
-  AWAIT_READY(destroy);
-}
-
-
-// This test verifies that when a container destruction fails the
-// 'container_destroy_errors' metric is updated.
-TEST_F(MesosContainerizerDestroyTest, LauncherDestroyFailure)
-{
-  // Create a TestLauncher backed by PosixLauncher.
-  slave::Flags flags = CreateSlaveFlags();
-
-  Try<Launcher*> launcher_ = PosixLauncher::create(flags);
-  ASSERT_SOME(launcher_);
-
-  TestLauncher* launcher = new TestLauncher(Owned<Launcher>(launcher_.get()));
-
-  Fetcher fetcher;
-
-  MesosContainerizerProcess* process = new MesosContainerizerProcess(
-      flags,
-      true,
-      &fetcher,
-      Owned<Launcher>(launcher),
-      vector<Owned<Isolator>>(),
-      hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>());
-
-  MesosContainerizer containerizer((Owned<MesosContainerizerProcess>(process)));
-
-  ContainerID containerId;
-  containerId.set_value("test_container");
-
-  TaskInfo taskInfo;
-  CommandInfo commandInfo;
-  taskInfo.mutable_command()->MergeFrom(commandInfo);
-
-  // Destroy the container using the PosixLauncher but return a failed
-  // future to the containerizer.
-  EXPECT_CALL(*launcher, destroy(_))
-    .WillOnce(DoAll(InvokeDestroyAndWait(launcher),
-                    Return(Failure("Destroy failure"))));
-
-  Future<bool> launch = containerizer.launch(
-      containerId,
-      taskInfo,
-      CREATE_EXECUTOR_INFO("executor", "sleep 1000"),
-      os::getcwd(),
-      None(),
-      SlaveID(),
-      PID<Slave>(),
-      false);
-
-  AWAIT_READY(launch);
-
-  Future<containerizer::Termination> wait = containerizer.wait(containerId);
-
-  containerizer.destroy(containerId);
-
-  // The container destroy should fail.
-  AWAIT_FAILED(wait);
-
-  // We settle the clock here to ensure that the processing of
-  // 'MesosContainerizerProcess::__destroy()' is complete and the
-  // metric is updated.
-  Clock::pause();
-  Clock::settle();
-  Clock::resume();
-
-  // Ensure that the metric is updated.
-  JSON::Object metrics = Metrics();
-  ASSERT_EQ(
-      1u,
-      metrics.values.count("containerizer/mesos/container_destroy_errors"));
-  ASSERT_EQ(
-      1u,
-      metrics.values["containerizer/mesos/container_destroy_errors"]);
-}
-
-
-class MesosContainerizerRecoverTest : public MesosTest {};
-
-
-// This test checks that MesosContainerizer doesn't recover executors
-// that were started by another containerizer (e.g: Docker).
-TEST_F(MesosContainerizerRecoverTest, SkipRecoverNonMesosContainers)
-{
-  slave::Flags flags = CreateSlaveFlags();
-  Fetcher fetcher;
-
-  Try<MesosContainerizer*> containerizer =
-    MesosContainerizer::create(flags, true, &fetcher);
-
-  ASSERT_SOME(containerizer);
-
-  ExecutorID executorId;
-  executorId.set_value(UUID::random().toString());
-
-  ContainerID containerId;
-  containerId.set_value(UUID::random().toString());
-
-  ExecutorInfo executorInfo;
-  executorInfo.mutable_container()->set_type(ContainerInfo::DOCKER);
-
-  ExecutorState executorState;
-  executorState.info = executorInfo;
-  executorState.latest = containerId;
-
-  RunState runState;
-  runState.id = containerId;
-  executorState.runs.put(containerId, runState);
-
-  FrameworkState frameworkState;
-  frameworkState.executors.put(executorId, executorState);
-
-  SlaveState slaveState;
-  FrameworkID frameworkId;
-  frameworkId.set_value(UUID::random().toString());
-  slaveState.frameworks.put(frameworkId, frameworkState);
-
-  Future<Nothing> recover = containerizer.get()->recover(slaveState);
-  AWAIT_READY(recover);
-
-  Future<hashset<ContainerID>> containers = containerizer.get()->containers();
-  AWAIT_READY(containers);
-  EXPECT_EQ(0u, containers.get().size());
-
-  delete containerizer.get();
-}
-
-} // namespace tests {
-} // namespace internal {
-} // namespace mesos {