[Feature] Add destroyNetdev, createBridge, CreateNetdevVethTest, CreateNetdevMacvlanTest
[Cause] N/A
[Solution] Netlink interface
[Verification] Build, run test
Change-Id: Iba17d864158d35d71d2fae83742ef13d729d5a7f
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;
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
}
},
{
+ "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",
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,
mCreateNetdevPhysCallback = callback;
}
+void HostConnection::setDestroyNetdevCallback(const DestroyNetdevCallback& callback)
+{
+ mDestroyNetdevCallback = callback;
+}
+
void HostConnection::setDeclareFileCallback(const DeclareFileCallback& callback)
{
mDeclareFileCallback = callback;
}
}
+ 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;
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,
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);
CreateNetdevVethCallback mCreateNetdevVethCallback;
CreateNetdevMacvlanCallback mCreateNetdevMacvlanCallback;
CreateNetdevPhysCallback mCreateNetdevPhysCallback;
+ DestroyNetdevCallback mDestroyNetdevCallback;
DeclareFileCallback mDeclareFileCallback;
DeclareMountCallback mDeclareMountCallback;
DeclareLinkCallback mDeclareLinkCallback;
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";
" <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'/>"
#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;
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,
{
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)
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
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
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");
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);
mAdmin->moveNetdev(devId);
}
+void Zone::destroyNetdev(const std::string& devId)
+{
+ Lock lock(mReconnectMutex);
+ mAdmin->destroyNetdev(devId);
+}
+
void Zone::goForeground()
{
Lock lock(mReconnectMutex);
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);
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));
}
}
+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,
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,
#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 {
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)
{
""));
}
+ 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
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()