Add destroyNetdev, createBridge and tests for netdev 68/36268/10
authorMateusz Malicki <m.malicki2@samsung.com>
Tue, 3 Mar 2015 15:01:44 +0000 (16:01 +0100)
committerMateusz Malicki <m.malicki2@samsung.com>
Wed, 25 Mar 2015 09:06:19 +0000 (10:06 +0100)
[Feature]       Add destroyNetdev, createBridge, CreateNetdevVethTest, CreateNetdevMacvlanTest
[Cause]         N/A
[Solution]      Netlink interface
[Verification]  Build, run test

Change-Id: Iba17d864158d35d71d2fae83742ef13d729d5a7f

16 files changed:
cli/command-line-interface.cpp
cli/command-line-interface.hpp
cli/main.cpp
client/vasum-client-impl.cpp
server/host-connection.cpp
server/host-connection.hpp
server/host-dbus-definitions.hpp
server/netdev.cpp
server/netdev.hpp
server/zone-admin.cpp
server/zone-admin.hpp
server/zone.cpp
server/zone.hpp
server/zones-manager.cpp
server/zones-manager.hpp
tests/unit_tests/server/ut-zone.cpp

index ecf6b9c..100aaa1 100644 (file)
@@ -397,6 +397,19 @@ void create_netdev_phys(int pos, int argc, const char** argv)
                   argv[pos + 2]));
 }
 
+void destroy_netdev(int pos, int argc, const char** argv)
+{
+    using namespace std::placeholders;
+
+    if (argc <= pos + 2) {
+        throw runtime_error("Not enough parameters");
+    }
+    one_shot(bind(vsm_destroy_netdev,
+                  _1,
+                  argv[pos + 1],
+                  argv[pos + 2]));
+}
+
 void zone_get_netdevs(int pos, int argc, const char** argv)
 {
     using namespace std::placeholders;
index eb33428..006396c 100644 (file)
@@ -209,6 +209,13 @@ void create_netdev_macvlan(int pos, int argc, const char** argv);
 void create_netdev_phys(int pos, int argc, const char** argv);
 
 /**
+ * Parses command line arguments and call vsm_destroy_netdev
+ *
+ * @see vsm_destroy_netdev
+ */
+void destroy_netdev(int pos, int argc, const char** argv);
+
+/**
  * Parses command line arguments and prints result of vsm_zone_get_netdevs
  *
  * @see vsm_zone_get_netdevs
index a30146e..c915415 100644 (file)
@@ -173,6 +173,15 @@ std::map<std::string, CommandLineInterface> commands = {
         }
     },
     {
+        "destroy_netdev", {
+            destroy_netdev,
+            "destroy_netdev zone_id devId",
+            "Destroy netdev in zone",
+            {{"zone_id", "id zone name"},
+             {"devId", "network device id"}}
+        }
+    },
+    {
         "zone_get_netdevs", {
             zone_get_netdevs,
             "zone_get_netdevs zone_id",
index 1ac4c3c..fb1b2ec 100644 (file)
@@ -768,10 +768,10 @@ VsmStatus Client::vsm_lookup_netdev_by_name(const char*, const char*, VsmNetdev*
     return vsm_get_status();
 }
 
-VsmStatus Client::vsm_destroy_netdev(const char*, const char*) noexcept
+VsmStatus Client::vsm_destroy_netdev(const char* zone, const char* devId) noexcept
 {
-    mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented");
-    return vsm_get_status();
+    GVariant* args_in = g_variant_new("(ss)", zone, devId);
+    return callMethod(HOST_INTERFACE, api::host::METHOD_DESTROY_NETDEV, args_in);
 }
 
 VsmStatus Client::vsm_declare_file(const char* zone,
index a1a7448..d0b612a 100644 (file)
@@ -162,6 +162,11 @@ void HostConnection::setCreateNetdevPhysCallback(const CreateNetdevPhysCallback&
     mCreateNetdevPhysCallback = callback;
 }
 
+void HostConnection::setDestroyNetdevCallback(const DestroyNetdevCallback& callback)
+{
+    mDestroyNetdevCallback = callback;
+}
+
 void HostConnection::setDeclareFileCallback(const DeclareFileCallback& callback)
 {
     mDeclareFileCallback = callback;
@@ -393,6 +398,16 @@ void HostConnection::onMessageCall(const std::string& objectPath,
         }
     }
 
+    if (methodName == api::host::METHOD_DESTROY_NETDEV) {
+        const gchar* id = NULL;
+        const gchar* devId = NULL;
+        g_variant_get(parameters, "(&s&s)", &id, &devId);
+        if (mDestroyNetdevCallback) {
+            auto rb = std::make_shared<api::DbusMethodResultBuilder<api::Void>>(result);
+            mDestroyNetdevCallback(id, devId, rb);
+        }
+    }
+
     if (methodName == api::host::METHOD_DECLARE_FILE) {
         const gchar* zone;
         int32_t type;
index 3ef8cdf..0ef9585 100644 (file)
@@ -90,6 +90,10 @@ public:
                                const std::string& devId,
                                api::MethodResultBuilder::Pointer result
                               )> CreateNetdevPhysCallback;
+    typedef std::function<void(const std::string& id,
+                               const std::string& devId,
+                               api::MethodResultBuilder::Pointer result
+                              )> DestroyNetdevCallback;
     typedef std::function<void(const std::string& zone,
                                const int32_t& type,
                                const std::string& path,
@@ -210,6 +214,11 @@ public:
     void setCreateNetdevPhysCallback(const CreateNetdevPhysCallback& callback);
 
     /**
+     * Register a callback called to destroy netdev
+     */
+    void setDestroyNetdevCallback(const DestroyNetdevCallback& callback);
+
+    /**
      * Register a callback called to declare file
      */
     void setDeclareFileCallback(const DeclareFileCallback& callback);
@@ -306,6 +315,7 @@ private:
     CreateNetdevVethCallback mCreateNetdevVethCallback;
     CreateNetdevMacvlanCallback mCreateNetdevMacvlanCallback;
     CreateNetdevPhysCallback mCreateNetdevPhysCallback;
+    DestroyNetdevCallback mDestroyNetdevCallback;
     DeclareFileCallback mDeclareFileCallback;
     DeclareMountCallback mDeclareMountCallback;
     DeclareLinkCallback mDeclareLinkCallback;
index 34d64eb..2ac5745 100644 (file)
@@ -48,6 +48,7 @@ const std::string METHOD_GET_NETDEV_LIST       = "GetNetdevList";
 const std::string METHOD_CREATE_NETDEV_VETH    = "CreateNetdevVeth";
 const std::string METHOD_CREATE_NETDEV_MACVLAN = "CreateNetdevMacvlan";
 const std::string METHOD_CREATE_NETDEV_PHYS    = "CreateNetdevPhys";
+const std::string METHOD_DESTROY_NETDEV        = "DestroyNetdev";
 const std::string METHOD_DECLARE_FILE          = "DeclareFile";
 const std::string METHOD_DECLARE_MOUNT         = "DeclareMount";
 const std::string METHOD_DECLARE_LINK          = "DeclareLink";
@@ -120,6 +121,10 @@ const std::string DEFINITION =
     "      <arg type='s' name='id' direction='in'/>"
     "      <arg type='s' name='devId' direction='in'/>"
     "    </method>"
+    "    <method name='" + METHOD_DESTROY_NETDEV + "'>"
+    "      <arg type='s' name='id' direction='in'/>"
+    "      <arg type='s' name='devId' direction='in'/>"
+    "    </method>"
     "    <method name='" + METHOD_DECLARE_FILE + "'>"
     "      <arg type='s' name='zone' direction='in'/>"
     "      <arg type='i' name='type' direction='in'/>"
index f8f9264..e17ecda 100644 (file)
 #include <linux/sockios.h>
 #include <linux/if_link.h>
 #include <linux/rtnetlink.h>
+#include <linux/in6.h>
+#include <linux/if_bridge.h>
+
+//IFLA_BRIDGE_FLAGS and BRIDGE_FLAGS_MASTER
+//should be defined in linux/if_bridge.h since kernel v3.7
+#ifndef IFLA_BRIDGE_FLAGS
+#define IFLA_BRIDGE_FLAGS 0
+#endif
+#ifndef BRIDGE_FLAGS_MASTER
+#define BRIDGE_FLAGS_MASTER 1
+#endif
 
 using namespace std;
 using namespace vasum;
@@ -205,10 +216,18 @@ void createVeth(const pid_t& nsPid, const string& nsDev, const string& hostDev)
     string hostVeth = getUniqueVethName();
     LOGT("Creating veth: bridge: " << hostDev << ", port: " << hostVeth << ", zone: " << nsDev);
     createPipedNetdev(nsDev, hostVeth);
-    //TODO: clean up if following instructions fail
-    attachToBridge(hostDev, hostVeth);
-    up(hostVeth);
-    moveToNS(nsDev, nsPid);
+    try {
+        attachToBridge(hostDev, hostVeth);
+        up(hostVeth);
+        moveToNS(nsDev, nsPid);
+    } catch(const exception& ex) {
+        try {
+            destroyNetdev(hostVeth);
+        } catch (const exception& ex) {
+            LOGE("Can't destroy netdev pipe: " << hostVeth << ", " << nsDev);
+        }
+        throw;
+    }
 }
 
 void createMacvlan(const pid_t& nsPid,
@@ -218,9 +237,17 @@ void createMacvlan(const pid_t& nsPid,
 {
     LOGT("Creating macvlan: host: " << hostDev << ", zone: " << nsDev << ", mode: " << mode);
     createMacvlan(hostDev, nsDev, mode);
-    //TODO: clean up if following instructions fail
-    up(nsDev);
-    moveToNS(nsDev, nsPid);
+    try {
+        up(nsDev);
+        moveToNS(nsDev, nsPid);
+    } catch(const exception& ex) {
+        try {
+            destroyNetdev(nsDev);
+        } catch (const exception& ex) {
+            LOGE("Can't destroy netdev: " << nsDev);
+        }
+        throw;
+    }
 }
 
 void movePhys(const pid_t& nsPid, const string& devId)
@@ -247,6 +274,43 @@ std::vector<std::string> listNetdev(const pid_t& nsPid)
     return interfaces;
 }
 
+void destroyNetdev(const string& netdev, const pid_t pid)
+{
+    LOGT("Destroying netdev: " << netdev);
+    validateNetdevName(netdev);
+
+    NetlinkMessage nlm(RTM_DELLINK, NLM_F_REQUEST|NLM_F_ACK);
+    ifinfomsg infopeer = utils::make_clean<ifinfomsg>();
+    infopeer.ifi_family = AF_UNSPEC;
+    infopeer.ifi_change = 0xFFFFFFFF;
+    nlm.put(infopeer)
+        .put(IFLA_IFNAME, netdev);
+    send(nlm, pid);
+}
+
+void createBridge(const string& netdev)
+{
+    LOGT("Creating bridge: " << netdev);
+    validateNetdevName(netdev);
+
+    NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK);
+    ifinfomsg infoPeer = utils::make_clean<ifinfomsg>();
+    infoPeer.ifi_family = AF_UNSPEC;
+    infoPeer.ifi_change = 0xFFFFFFFF;
+    nlm.put(infoPeer)
+        .beginNested(IFLA_LINKINFO)
+            .put(IFLA_INFO_KIND, "bridge")
+            .beginNested(IFLA_INFO_DATA)
+                .beginNested(IFLA_AF_SPEC)
+                    .put<uint32_t>(IFLA_BRIDGE_FLAGS, BRIDGE_FLAGS_MASTER)
+                .endNested()
+            .endNested()
+        .endNested()
+        .put(IFLA_IFNAME, netdev);
+    send(nlm);
+}
+
+
 } //namespace netdev
 } //namespace vasum
 
index b3d658a..0e9bfde 100644 (file)
@@ -39,7 +39,17 @@ void createMacvlan(const pid_t& nsPid,
                    const std::string& hostDev,
                    const macvlan_mode& mode);
 void movePhys(const pid_t& nsPid, const std::string& devId);
-std::vector<std::string> listNetdev(const pid_t& nsPid);
+std::vector<std::string> listNetdev(const pid_t& nsPid = 0);
+void destroyNetdev(const std::string& netdev, const pid_t pid = 0);
+
+/**
+ * Create bridge
+ *
+ * Bridge are in BRIDGE_MODE_VEB (loopback) mode and it is software bridge (BRIDGE_FLAGS_MASTER)
+ *
+ * @param netdev bridge name
+ */
+void createBridge(const std::string& netdev);
 
 } //namespace netdev
 } //namespace vasum
index 64092a8..29577f8 100644 (file)
@@ -302,6 +302,11 @@ void ZoneAdmin::moveNetdev(const std::string& devId)
     netdev::movePhys(mZone.getInitPid(), devId);
 }
 
+void ZoneAdmin::destroyNetdev(const std::string& devId)
+{
+    netdev::destroyNetdev(devId, mZone.getInitPid());
+}
+
 void ZoneAdmin::setNetdevAttrs(const std::string& /* netdev */, const NetdevAttrs& /* attrs */)
 {
     throw ZoneOperationException("Not implemented");
index ade4585..c6d26f0 100644 (file)
@@ -150,6 +150,11 @@ public:
     void moveNetdev(const std::string& devId);
 
     /**
+     * Destroy network device in zone
+     */
+    void destroyNetdev(const std::string& devId);
+
+    /**
      * Set network device attributes
      */
     void setNetdevAttrs(const std::string& netdev, const NetdevAttrs& attrs);
index 181cc9e..b0c3c28 100644 (file)
@@ -278,6 +278,12 @@ void Zone::moveNetdev(const std::string& devId)
     mAdmin->moveNetdev(devId);
 }
 
+void Zone::destroyNetdev(const std::string& devId)
+{
+    Lock lock(mReconnectMutex);
+    mAdmin->destroyNetdev(devId);
+}
+
 void Zone::goForeground()
 {
     Lock lock(mReconnectMutex);
index 45bacbd..b981e9a 100644 (file)
@@ -292,6 +292,11 @@ public:
     void moveNetdev(const std::string& devId);
 
     /**
+     * Destroy network device in zone
+     */
+    void destroyNetdev(const std::string& devId);
+
+    /**
      * Set network device attributes
      */
     void setNetdevAttrs(const std::string& netdev, const ZoneAdmin::NetdevAttrs& attrs);
index 2dcfaa0..4266820 100644 (file)
@@ -160,6 +160,9 @@ ZonesManager::ZonesManager(const std::string& configPath)
     mHostConnection.setCreateNetdevPhysCallback(bind(&ZonesManager::handleCreateNetdevPhysCall,
                                                      this, _1, _2, _3));
 
+    mHostConnection.setDestroyNetdevCallback(bind(&ZonesManager::handleDestroyNetdevCall,
+                                                  this, _1, _2, _3));
+
     mHostConnection.setDeclareFileCallback(bind(&ZonesManager::handleDeclareFileCall,
                                                 this, _1, _2, _3, _4, _5, _6));
 
@@ -927,6 +930,25 @@ void ZonesManager::handleCreateNetdevPhysCall(const std::string& zone,
     }
 }
 
+void ZonesManager::handleDestroyNetdevCall(const std::string& zone,
+                                           const std::string& devId,
+                                           api::MethodResultBuilder::Pointer result)
+{
+    LOGI("DestroyNetdev call");
+    try {
+        Lock lock(mMutex);
+
+        getZone(zone).destroyNetdev(devId);
+        result->setVoid();
+    } catch (const InvalidZoneIdException&) {
+        LOGE("No zone with id=" << zone);
+        result->setError(api::ERROR_INVALID_ID, "No such zone id");
+    } catch (const VasumException& ex) {
+        LOGE("Can't create netdev: " << ex.what());
+        result->setError(api::ERROR_INTERNAL, ex.what());
+    }
+}
+
 void ZonesManager::handleDeclareFileCall(const std::string& zone,
                                          const int32_t& type,
                                          const std::string& path,
index 825b1c8..deb9b9d 100644 (file)
@@ -189,6 +189,9 @@ private:
     void handleCreateNetdevPhysCall(const std::string& zone,
                                     const std::string& devId,
                                     api::MethodResultBuilder::Pointer result);
+    void handleDestroyNetdevCall(const std::string& zone,
+                                 const std::string& devId,
+                                 api::MethodResultBuilder::Pointer result);
     void handleDeclareFileCall(const std::string& zone,
                                const int32_t& type,
                                const std::string& path,
index a8b608f..91d0540 100644 (file)
@@ -27,6 +27,7 @@
 #include "ut.hpp"
 
 #include "zone.hpp"
+#include "netdev.hpp"
 #include "exception.hpp"
 
 #include "utils/exception.hpp"
 #include <string>
 #include <thread>
 #include <chrono>
+#include <linux/in6.h>
+#include <linux/if_bridge.h>
 
 using namespace vasum;
+using namespace vasum::netdev;
 using namespace config;
 
 namespace {
@@ -53,15 +57,28 @@ const std::string MISSING_CONFIG_PATH = TEMPLATES_DIR + "/missing.conf";
 const std::string ZONES_PATH = "/tmp/ut-zones";
 const std::string LXC_TEMPLATES_PATH = VSM_TEST_LXC_TEMPLATES_INSTALL_DIR;
 const std::string DB_PATH = ZONES_PATH + "/vasum.db";
+const std::string BRIDGE_NAME = "brtest01";
+const std::string ZONE_NETDEV = "netdevtest01";
 
 struct Fixture {
     utils::ScopedGlibLoop mLoop;
     utils::ScopedDir mZonesPathGuard;
     utils::ScopedDir mRunGuard;
+    std::string mBridgeName;
 
     Fixture()
         : mZonesPathGuard(ZONES_PATH)
     {}
+    ~Fixture()
+    {
+        if (!mBridgeName.empty()) {
+            try {
+                destroyNetdev(mBridgeName);
+            } catch (std::exception& ex) {
+                BOOST_MESSAGE("Can't destroy bridge: " + std::string(ex.what()));
+            }
+        }
+    }
 
     std::unique_ptr<Zone> create(const std::string& configPath)
     {
@@ -74,11 +91,23 @@ struct Fixture {
                                               ""));
     }
 
+    void setupBridge(const std::string& name)
+    {
+        createBridge(name);
+        mBridgeName = name;
+    }
+
+
     void ensureStarted()
     {
         // wait for zones init to fully start
         std::this_thread::sleep_for(std::chrono::milliseconds(200));
     }
+    void ensureStop()
+    {
+        // wait for fully stop
+        std::this_thread::sleep_for(std::chrono::milliseconds(200));
+    }
 };
 
 } // namespace
@@ -144,4 +173,40 @@ BOOST_AUTO_TEST_CASE(ListNetdevTest)
     c->stop(false);
 }
 
+BOOST_AUTO_TEST_CASE(CreateNetdevVethTest)
+{
+    typedef std::vector<std::string> NetdevList;
+
+    setupBridge(BRIDGE_NAME);
+    auto c = create(TEST_CONFIG_PATH);
+    c->start();
+    ensureStarted();
+    c->createNetdevVeth(ZONE_NETDEV, BRIDGE_NAME);
+    NetdevList netdevs = c->getNetdevList();
+    BOOST_CHECK(find(netdevs.begin(), netdevs.end(), ZONE_NETDEV) != netdevs.end());
+    c->stop(false);
+    ensureStop();
+
+    //Check clean up
+    NetdevList hostNetdevsInit = listNetdev();
+    BOOST_REQUIRE_THROW(c->createNetdevVeth(ZONE_NETDEV, BRIDGE_NAME), VasumException);
+    NetdevList hostNetdevsThrow = listNetdev();
+    BOOST_CHECK_EQUAL_COLLECTIONS(hostNetdevsInit.begin(), hostNetdevsInit.end(),
+                                  hostNetdevsThrow.begin(), hostNetdevsThrow.end());
+}
+
+BOOST_AUTO_TEST_CASE(CreateNetdevMacvlanTest)
+{
+    typedef std::vector<std::string> NetdevList;
+
+    setupBridge(BRIDGE_NAME);
+    auto c = create(TEST_CONFIG_PATH);
+    c->start();
+    ensureStarted();
+    c->createNetdevVeth(ZONE_NETDEV, BRIDGE_NAME);
+    NetdevList netdevs = c->getNetdevList();
+    BOOST_CHECK(find(netdevs.begin(), netdevs.end(), ZONE_NETDEV) != netdevs.end());
+    c->stop(false);
+}
+
 BOOST_AUTO_TEST_SUITE_END()