iSulad/src/services/cri/cni_network_plugin.cc
openeuler-iSula ee41558c0c iSUlad: remove seucrec new
Signed-off-by: openeuler-iSula <isula@huawei.com>
2019-12-29 15:59:28 +08:00

727 lines
23 KiB
C++

/******************************************************************************
* Copyright (c) Huawei Technologies Co., Ltd. 2017-2019. All rights reserved.
* iSulad licensed under the Mulan PSL v1.
* You can use this software according to the terms and conditions of the Mulan PSL v1.
* You may obtain a copy of Mulan PSL v1 at:
* http://license.coscl.org.cn/MulanPSL
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
* PURPOSE.
* See the Mulan PSL v1 for more details.
* Author: tanyifeng
* Create: 2017-11-22
* Description: provide cni network plugin function definition
**********************************************************************************/
#include "cni_network_plugin.h"
#include <iostream>
#include <memory>
#include <algorithm>
#include <vector>
#include <utility>
#include <set>
#include "log.h"
#include "utils.h"
#include "cri_helpers.h"
namespace Network {
static std::string VendorCNIDir(const std::string &prefix, const std::string &pluginType)
{
return prefix + "/opt/" + pluginType + "/bin";
}
static std::unique_ptr<CNINetwork> GetLoNetwork(const std::string &binDir, const std::string &vendorDirPrefix)
{
const std::string loNetConfListJson { "{\"cniVersion\": \"0.3.0\", \"name\": \"cni-loopback\","
"\"plugins\":[{\"type\": \"loopback\" }]}" };
char *cerr { nullptr };
struct cni_network_list_conf *loConf {
nullptr
};
if (cni_conflist_from_bytes(loNetConfListJson.c_str(), &loConf, &cerr) != 0) {
if (cerr != nullptr) {
ERROR("invalid lo config: %s", cerr);
free(cerr);
}
char **traces = get_backtrace();
if (traces != nullptr) {
ERROR("show backtrace: ");
for (char **sym = traces; sym && *sym; sym++) {
ERROR("%s", *sym);
}
util_free_array(traces);
}
sync();
exit(1);
}
auto result = std::unique_ptr<CNINetwork>(new (std::nothrow) CNINetwork("lo", loConf));
if (result == nullptr) {
ERROR("Out of memory");
return nullptr;
}
result->InsertPath(VendorCNIDir(vendorDirPrefix, "loopback"));
result->InsertPath(binDir);
return result;
}
CNINetwork::CNINetwork(const std::string &name, struct cni_network_list_conf *list)
: m_name(name)
, m_networkConfig(list)
{
}
CNINetwork::~CNINetwork()
{
free_cni_network_list_conf(m_networkConfig);
}
char **CNINetwork::GetPaths(Errors &err)
{
char **paths = CRIHelpers::StringVectorToCharArray(m_path);
if (paths == nullptr) {
err.SetError("Get char ** path failed");
}
return paths;
}
void ProbeNetworkPlugins(const std::string &pluginDir, const std::string &binDir,
std::vector<std::shared_ptr<NetworkPlugin>> *plugins)
{
const std::string useBinDir = binDir.empty() ? DEFAULT_CNI_DIR : binDir;
auto plugin = std::make_shared<CniNetworkPlugin>(useBinDir, pluginDir);
plugin->SetLoNetwork(GetLoNetwork(useBinDir, ""));
plugins->push_back(plugin);
}
void CniNetworkPlugin::SetLoNetwork(std::unique_ptr<CNINetwork> lo)
{
if (lo != nullptr) {
m_loNetwork = std::move(lo);
}
}
CniNetworkPlugin::CniNetworkPlugin(const std::string &binDir, const std::string &pluginDir,
const std::string &vendorCNIDirPrefix)
: m_pluginDir(pluginDir)
, m_vendorCNIDirPrefix(vendorCNIDirPrefix)
, m_binDir(binDir)
{
}
CniNetworkPlugin::~CniNetworkPlugin()
{
m_networks.clear();
}
void CniNetworkPlugin::PlatformInit(Errors &error)
{
char *tpath { nullptr };
char *serr { nullptr };
tpath = look_path(const_cast<char *>("nsenter"), &serr);
if (tpath == nullptr) {
error.SetError(serr);
return;
}
m_nsenterPath = tpath;
free(tpath);
return;
}
int CniNetworkPlugin::GetCNIConfFiles(const std::string &pluginDir, std::vector<std::string> &vect_files, Errors &err)
{
int ret { 0 };
std::string usePluginDir { pluginDir };
const char *exts[] { ".conf", ".conflist", ".json" };
char **files { nullptr };
char *serr { nullptr };
if (usePluginDir.empty()) {
usePluginDir = DEFAULT_NET_DIR;
}
ret = cni_conf_files(usePluginDir.c_str(), exts, sizeof(exts) / sizeof(char *), &files, &serr);
if (ret != 0) {
err.Errorf("get conf files: %s", serr);
ret = -1;
goto out;
}
if (util_array_len((const char **)files) == 0) {
err.Errorf("No networks found in %s", usePluginDir.c_str());
ret = -1;
goto out;
}
vect_files = std::vector<std::string>(files, files + util_array_len((const char **)files));
out:
free(serr);
util_free_array(files);
return ret;
}
int CniNetworkPlugin::LoadCNIConfigFileList(const std::string &elem, struct cni_network_list_conf **n_list)
{
int ret { 0 };
std::size_t found = elem.rfind(".conflist");
char *serr { nullptr };
struct cni_network_conf *n_conf {
nullptr
};
if (found != std::string::npos && found + strlen(".conflist") == elem.length()) {
if (cni_conflist_from_file(elem.c_str(), n_list, &serr) != 0) {
WARN("Error loading CNI config list file %s: %s", elem.c_str(), serr);
ret = -1;
goto out;
}
} else {
if (cni_conf_from_file(elem.c_str(), &n_conf, &serr) != 0) {
WARN("Error loading CNI config file %s: %s", elem.c_str(), serr);
ret = -1;
goto out;
}
if (n_conf->type == nullptr || strcmp(n_conf->type, "") == 0) {
WARN("Error loading CNI config file %s: no 'type'; perhaps this is a .conflist?", elem.c_str());
ret = -1;
goto out;
}
if (cni_conflist_from_conf(n_conf, n_list, &serr) != 0) {
WARN("Error converting CNI config file %s to list: %s", elem.c_str(), serr);
ret = -1;
goto out;
}
}
out:
if (n_conf != nullptr) {
free_cni_network_conf(n_conf);
}
free(serr);
return ret;
}
int CniNetworkPlugin::InsertConfNameToAllPanes(struct cni_network_list_conf *n_list, std::set<std::string> &allPanes,
Errors &err)
{
int ret { 0 };
std::string confName { "" };
if (n_list == nullptr) {
err.Errorf("Invalid arguments");
return -1;
}
if (n_list->first_plugin_name != nullptr) {
confName = n_list->first_plugin_name;
}
if (confName.empty() || allPanes.find(confName) != allPanes.end()) {
free_cni_network_list_conf(n_list);
n_list = nullptr;
ret = -1;
ERROR("Invalid cni network name: %s, it may be duplicated or empty.", confName.c_str());
err.Errorf("Invalid cni network name: %s, it may be duplicated or empty.", confName.c_str());
goto out;
}
allPanes.insert(confName);
out:
return ret;
}
int CniNetworkPlugin::InsertNewNetwork(struct cni_network_list_conf *n_list,
std::map<std::string, std::unique_ptr<CNINetwork>> &newNets,
const std::string &binDir, const std::string &vendorCNIDirPrefix, Errors &err)
{
std::string confType { "" };
if (n_list == nullptr) {
err.Errorf("Invalid arguments");
return -1;
}
if (n_list->first_plugin_type != nullptr) {
confType = n_list->first_plugin_type;
}
std::string tpath = VendorCNIDir(vendorCNIDirPrefix, confType);
if (tpath.empty()) {
free_cni_network_list_conf(n_list);
err.SetError("Out of memory");
return -1;
}
std::unique_ptr<CNINetwork> network(new (std::nothrow) CNINetwork(n_list->name, n_list));
if (network == nullptr) {
free_cni_network_list_conf(n_list);
err.SetError("Out of memory");
return -1;
}
network->InsertPath(tpath);
network->InsertPath(binDir);
std::string n_key(network->GetName());
newNets.insert(std::pair<std::string, std::unique_ptr<CNINetwork>>(n_key, std::move(network)));
DEBUG("---parse cni network: %s finish ----", n_key.c_str());
return 0;
}
void CniNetworkPlugin::ResetCNINetwork(std::map<std::string, std::unique_ptr<CNINetwork>> &newNets, Errors &err)
{
std::string pluginNames { "map[" };
m_networks.clear();
for (auto iter = newNets.begin(); iter != newNets.end(); iter++) {
m_networks[iter->first] = std::move(iter->second);
pluginNames += (iter->first + " ");
}
INFO("Loaded cni plugins successfully, all plugins: %s]", pluginNames.substr(0, pluginNames.length() - 1).c_str());
}
void CniNetworkPlugin::GetCNINetwork(const std::string &pluginDir, const std::string &binDir,
const std::string &vendorCNIDirPrefix, Errors &err)
{
std::vector<std::string> files;
std::set<std::string> allPanes;
std::map<std::string, std::unique_ptr<CNINetwork>> newNets;
if (GetCNIConfFiles(pluginDir, files, err) != 0) {
goto free_out;
}
for (auto elem : files) {
struct cni_network_list_conf *n_list = nullptr;
if (LoadCNIConfigFileList(elem, &n_list) != 0) {
continue;
}
if (n_list == nullptr || n_list->plugin_len == 0) {
WARN("CNI config list %s has no networks, skipping", elem.c_str());
free_cni_network_list_conf(n_list);
n_list = nullptr;
continue;
}
if (InsertConfNameToAllPanes(n_list, allPanes, err) != 0) {
goto free_out;
}
if (InsertNewNetwork(n_list, newNets, binDir, vendorCNIDirPrefix, err) != 0) {
goto free_out;
}
}
if (newNets.size() == 0) {
err.Errorf("No valid networks found in %s", pluginDir.c_str());
goto free_out;
}
ResetCNINetwork(newNets, err);
free_out:
newNets.clear();
return;
}
void CniNetworkPlugin::CheckInitialized(Errors &err)
{
RLockNetworkMap(err);
if (err.NotEmpty()) {
ERROR("%s", err.GetCMessage());
return;
}
size_t len = m_networks.size();
UnlockNetworkMap(err);
if (len == 0) {
err.AppendError("cni config uninitialized");
}
}
void CniNetworkPlugin::SyncNetworkConfig()
{
Errors err;
WLockNetworkMap(err);
if (err.NotEmpty()) {
ERROR("%s", err.GetCMessage());
return;
}
GetCNINetwork(m_pluginDir, m_binDir, m_vendorCNIDirPrefix, err);
if (err.NotEmpty()) {
WARN("Unable to update cni config: %s", err.GetCMessage());
}
UnlockNetworkMap(err);
if (err.NotEmpty()) {
ERROR("%s", err.GetCMessage());
}
}
void CniNetworkPlugin::Init(CRIRuntimeServiceImpl *criImpl, const std::string &hairpinMode,
const std::string &nonMasqueradeCIDR, int mtu, Errors &error)
{
UNUSED(hairpinMode);
UNUSED(nonMasqueradeCIDR);
UNUSED(mtu);
if (criImpl == nullptr) {
error.Errorf("Empty runtime service");
return;
}
PlatformInit(error);
if (error.NotEmpty()) {
return;
}
m_criImpl = criImpl;
SyncNetworkConfig();
return;
}
const std::string &CniNetworkPlugin::Name() const
{
return CNI_PLUGIN_NAME;
}
void CniNetworkPlugin::Status(Errors &err)
{
SyncNetworkConfig();
CheckInitialized(err);
}
void CniNetworkPlugin::SetUpPod(const std::string &ns, const std::string &name, const std::string &networkPlane,
const std::string &interfaceName, const std::string &id,
const std::map<std::string, std::string> &annotations, Errors &err)
{
CheckInitialized(err);
if (err.NotEmpty()) {
return;
}
std::string netnsPath = m_criImpl->GetNetNS(id, err);
if (err.NotEmpty()) {
ERROR("CNI failed to retrieve network namespace path: %s", err.GetCMessage());
return;
}
auto iter = annotations.find(CRIHelpers::Constants::POD_CHECKPOINT_KEY);
std::string jsonCheckpoint { "" };
if (iter != annotations.end()) {
jsonCheckpoint = iter->second;
}
DEBUG("add checkpoint: ", jsonCheckpoint.c_str());
struct result *preResult = nullptr;
if (m_loNetwork != nullptr) {
AddToNetwork(m_loNetwork.get(), jsonCheckpoint, name, ns, interfaceName, id, netnsPath, &preResult, err);
free_result(preResult);
preResult = nullptr;
if (err.NotEmpty()) {
ERROR("Error while adding to cni lo network: %s", err.GetCMessage());
return;
}
}
RLockNetworkMap(err);
if (err.NotEmpty()) {
ERROR("%s", err.GetCMessage());
return;
}
auto netIter = m_networks.find(networkPlane);
if (netIter == m_networks.end()) {
ERROR("Can't find cni plugin for network plane %s.", networkPlane.c_str());
err.Errorf("Can't find cni plugin for network plane %s.", networkPlane.c_str());
goto unlock_out;
}
AddToNetwork((netIter->second).get(), jsonCheckpoint, name, ns, interfaceName, id, netnsPath, &preResult, err);
free_result(preResult);
preResult = nullptr;
if (err.NotEmpty()) {
ERROR("Error while adding to cni network: %s", err.GetCMessage());
}
unlock_out:
UnlockNetworkMap(err);
}
void CniNetworkPlugin::TearDownPod(const std::string &ns, const std::string &name, const std::string &networkPlane,
const std::string &interfaceName, const std::string &id,
const std::map<std::string, std::string> &annotations, Errors &err)
{
CheckInitialized(err);
if (err.NotEmpty()) {
return;
}
std::string netnsPath = m_criImpl->GetNetNS(id, err);
if (err.NotEmpty()) {
WARN("CNI failed to retrieve network namespace path: %s", err.GetCMessage());
err.Clear();
}
auto iter = annotations.find(CRIHelpers::Constants::POD_CHECKPOINT_KEY);
std::string jsonCheckpoint = "";
if (iter != annotations.end()) {
jsonCheckpoint = iter->second;
}
DEBUG("delete checkpoint: ", jsonCheckpoint.c_str());
RLockNetworkMap(err);
if (err.NotEmpty()) {
ERROR("%s", err.GetCMessage());
return;
}
auto netIter = m_networks.find(networkPlane);
if (netIter == m_networks.end()) {
ERROR("Can't find cni plugin for network plane %s.", networkPlane.c_str());
err.Errorf("Can't find cni plugin for network plane %s.", networkPlane.c_str());
goto unlock_out;
}
DeleteFromNetwork((netIter->second).get(), jsonCheckpoint, name, ns, interfaceName, id, netnsPath, err);
unlock_out:
UnlockNetworkMap(err);
}
std::map<int, bool> *CniNetworkPlugin::Capabilities()
{
return m_noop.Capabilities();
}
void CniNetworkPlugin::Event(const std::string &name, std::map<std::string, std::string> &details)
{
m_noop.Event(name, details);
}
void CniNetworkPlugin::GetPodNetworkStatus(const std::string &ns, const std::string &name,
const std::string &interfaceName, const std::string &podSandboxID,
PodNetworkStatus &status, Errors &err)
{
std::string netnsPath, ip;
Errors tmpErr;
if (podSandboxID.empty()) {
err.SetError("Empty podsandbox ID");
goto out;
}
netnsPath = m_criImpl->GetNetNS(podSandboxID, tmpErr);
if (tmpErr.NotEmpty()) {
err.Errorf("CNI failed to retrieve network namespace path: %s", tmpErr.GetCMessage());
goto out;
}
if (netnsPath.empty()) {
err.Errorf("Cannot find the network namespace, skipping pod network status for container %s",
podSandboxID.c_str());
goto out;
}
ip = GetPodIP(m_nsenterPath, netnsPath, interfaceName, err);
if (err.NotEmpty()) {
ERROR("GetPodIP failed: %s", err.GetCMessage());
goto out;
}
status.SetIP(ip);
out:
INFO("get_pod_network_status: %s", podSandboxID.c_str());
}
void CniNetworkPlugin::AddToNetwork(CNINetwork *snetwork, const std::string &jsonCheckpoint, const std::string &podName,
const std::string &podNamespace, const std::string &interfaceName,
const std::string &podSandboxID, const std::string &podNetnsPath,
struct result **presult, Errors &err)
{
struct runtime_conf *rc {
nullptr
};
if (snetwork == nullptr || presult == nullptr) {
err.Errorf("Invalid arguments");
ERROR("Invalid arguments");
return;
}
BuildCNIRuntimeConf(podName, jsonCheckpoint, podNamespace, interfaceName, podSandboxID, podNetnsPath, &rc, err);
if (err.NotEmpty()) {
ERROR("Error adding network when building cni runtime conf: %s", err.GetCMessage());
return;
}
INFO("About to add CNI network %s (type=%s)", snetwork->GetName().c_str(), snetwork->GetNetworkType().c_str());
char **paths = snetwork->GetPaths(err);
if (paths == nullptr) {
ERROR("Empty cni bin path");
free_runtime_conf(rc);
return;
}
char *serr = nullptr;
int nret = cni_add_network_list(snetwork->GetNetworkConfigJsonStr().c_str(), rc, paths, presult, &serr);
if (nret != 0) {
ERROR("Error adding network: %s", serr);
err.SetError(serr);
}
util_free_array(paths);
free_runtime_conf(rc);
free(serr);
}
void CniNetworkPlugin::DeleteFromNetwork(CNINetwork *network, const std::string &jsonCheckpoint,
const std::string &podName, const std::string &podNamespace,
const std::string &interfaceName, const std::string &podSandboxID,
const std::string &podNetnsPath, Errors &err)
{
struct runtime_conf *rc {
nullptr
};
if (network == nullptr) {
err.Errorf("Invalid arguments");
ERROR("Invalid arguments");
return;
}
BuildCNIRuntimeConf(podName, jsonCheckpoint, podNamespace, interfaceName, podSandboxID, podNetnsPath, &rc, err);
if (err.NotEmpty()) {
ERROR("Error deleting network when building cni runtime conf: %s", err.GetCMessage());
return;
}
INFO("About to del CNI network %s (type=%s)", network->GetName().c_str(), network->GetNetworkType().c_str());
char **paths = network->GetPaths(err);
if (paths == nullptr) {
free_runtime_conf(rc);
ERROR("Empty cni bin path");
return;
}
char *serr = nullptr;
int nret = cni_del_network_list(network->GetNetworkConfigJsonStr().c_str(), rc, paths, &serr);
if (nret != 0) {
ERROR("Error deleting network: %s", serr);
err.Errorf("Error deleting network: %s", serr);
}
util_free_array(paths);
free_runtime_conf(rc);
free(serr);
}
void CniNetworkPlugin::BuildCNIRuntimeConf(const std::string &podName, const std::string &jsonCheckpoint,
const std::string &podNs, const std::string &interfaceName,
const std::string &podSandboxID, const std::string &podNetnsPath,
struct runtime_conf **cni_rc, Errors &err)
{
std::vector<cri::PortMapping> portMappings;
INFO("Got netns path %s", podNetnsPath.c_str());
INFO("Using podns path %s", podNs.c_str());
if (cni_rc == nullptr) {
err.Errorf("Invalid arguments");
ERROR("Invalid arguments");
return;
}
struct runtime_conf *rt = (struct runtime_conf *)util_common_calloc_s(sizeof(struct runtime_conf));
if (rt == nullptr) {
ERROR("Out of memory");
err.SetError("Out of memory");
return;
}
rt->container_id = util_strdup_s(podSandboxID.c_str());
rt->netns = util_strdup_s(podNetnsPath.c_str());
rt->ifname = util_strdup_s(interfaceName.c_str());
rt->args = (char *(*)[2])util_common_calloc_s(sizeof(char *) * 2 * 4);
if (rt->args == nullptr) {
ERROR("Out of memory");
err.SetError("Out of memory");
goto free_out;
}
rt->args_len = 4;
rt->args[0][0] = util_strdup_s("IgnoreUnknown");
rt->args[0][1] = util_strdup_s("1");
rt->args[1][0] = util_strdup_s("K8S_POD_NAMESPACE");
rt->args[1][1] = util_strdup_s(podNs.c_str());
rt->args[2][0] = util_strdup_s("K8S_POD_NAME");
rt->args[2][1] = util_strdup_s(podName.c_str());
rt->args[3][0] = util_strdup_s("K8S_POD_INFRA_CONTAINER_ID");
rt->args[3][1] = util_strdup_s(podSandboxID.c_str());
if (!jsonCheckpoint.empty()) {
cri::PodSandboxCheckpoint checkpoint;
CRIHelpers::GetCheckpoint(jsonCheckpoint, checkpoint, err);
if (err.NotEmpty() || checkpoint.GetData() == nullptr) {
err.Errorf("could not retrieve port mappings: %s", err.GetCMessage());
goto free_out;
}
std::copy(checkpoint.GetData()->GetPortMappings().begin(), checkpoint.GetData()->GetPortMappings().end(),
std::back_inserter(portMappings));
}
if (portMappings.size() > 0) {
if (portMappings.size() > SIZE_MAX / sizeof(struct cni_port_mapping *)) {
err.SetError("Invalid cni port mapping size");
goto free_out;
}
rt->p_mapping = (struct cni_port_mapping **)util_common_calloc_s(sizeof(struct cni_port_mapping *) *
portMappings.size());
if (rt->p_mapping == nullptr) {
err.SetError("Out of memory");
goto free_out;
}
for (auto iter = portMappings.cbegin(); iter != portMappings.cend(); iter++) {
if (iter->GetHostPort() && *(iter->GetHostPort()) <= 0) {
continue;
}
rt->p_mapping[rt->p_mapping_len] =
(struct cni_port_mapping *)util_common_calloc_s(sizeof(struct cni_port_mapping));
if (rt->p_mapping[rt->p_mapping_len] == nullptr) {
err.SetError("Out of memory");
goto free_out;
}
if (iter->GetHostPort()) {
rt->p_mapping[rt->p_mapping_len]->host_port = *(iter->GetHostPort());
}
if (iter->GetContainerPort()) {
rt->p_mapping[rt->p_mapping_len]->container_port = *(iter->GetContainerPort());
}
if (iter->GetProtocol()) {
rt->p_mapping[rt->p_mapping_len]->protocol = strings_to_lower(iter->GetProtocol()->c_str());
}
// ignore hostip, because GetPodPortMappings() don't set
(rt->p_mapping_len)++;
}
}
*cni_rc = rt;
return;
free_out:
free_runtime_conf(rt);
}
void CniNetworkPlugin::RLockNetworkMap(Errors &error)
{
int ret = pthread_rwlock_rdlock(&m_netsLock);
if (ret != 0) {
error.Errorf("Get read lock failed: %s", strerror(ret));
}
}
void CniNetworkPlugin::WLockNetworkMap(Errors &error)
{
int ret = pthread_rwlock_wrlock(&m_netsLock);
if (ret != 0) {
error.Errorf("Get write lock failed: %s", strerror(ret));
}
}
void CniNetworkPlugin::UnlockNetworkMap(Errors &error)
{
int ret = pthread_rwlock_unlock(&m_netsLock);
if (ret != 0) {
error.Errorf("Unlock failed: %s", strerror(ret));
}
}
} // namespace Network