diff --git a/src/api-server/model/QosData.cpp b/src/api-server/model/QosData.cpp index 8261a6c34c9d829fbc648ce56c26d596f499de80..ced3f5604f0f878b3030ea55b7529dbe1ebd5a0b 100644 --- a/src/api-server/model/QosData.cpp +++ b/src/api-server/model/QosData.cpp @@ -62,8 +62,7 @@ QosData::QosData() { void QosData::validate() const { std::stringstream msg; if (!validate(msg)) { - // throw - // org::openapitools::server::helpers::ValidationException(msg.str()); + throw oai::model::common::helpers::ValidationException(msg.str()); } } @@ -89,27 +88,35 @@ bool QosData::validate( msg << currentValuePath << ": must be less than or equal to 255;"; } } - /* + if (maxbrUlIsSet()) { const std::string& value = m_MaxbrUl; const std::string currentValuePath = _pathPrefix + ".maxbrUl"; + success &= helpers::validate_regex( + BANDWIDTH_VALIDATION_REGEX, value, msg, currentValuePath); } if (maxbrDlIsSet()) { const std::string& value = m_MaxbrDl; const std::string currentValuePath = _pathPrefix + ".maxbrDl"; + success &= helpers::validate_regex( + BANDWIDTH_VALIDATION_REGEX, value, msg, currentValuePath); } if (gbrUlIsSet()) { const std::string& value = m_GbrUl; const std::string currentValuePath = _pathPrefix + ".gbrUl"; + success &= helpers::validate_regex( + BANDWIDTH_VALIDATION_REGEX, value, msg, currentValuePath); } if (gbrDlIsSet()) { const std::string& value = m_GbrDl; const std::string currentValuePath = _pathPrefix + ".gbrDl"; + success &= helpers::validate_regex( + BANDWIDTH_VALIDATION_REGEX, value, msg, currentValuePath); } - */ + if (priorityLevelIsSet()) { const int32_t& value = m_PriorityLevel; const std::string currentValuePath = _pathPrefix + ".priorityLevel"; @@ -299,7 +306,9 @@ void to_json(nlohmann::json& j, const QosData& o) { } void from_json(const nlohmann::json& j, QosData& o) { - j.at("qosId").get_to(o.m_QosId); + if (j.find("qosId") != j.end()) { + j.at("qosId").get_to(o.m_QosId); + } if (j.find("5qi") != j.end()) { j.at("5qi").get_to(o.m_r_5qi); o.m_r_5qiIsSet = true; diff --git a/src/api-server/model/QosData.h b/src/api-server/model/QosData.h index b14369a4fd2290fc48aae25892c9905707bd1c07..81e9409e00101284754cb40e31f8d52b398e14b1 100644 --- a/src/api-server/model/QosData.h +++ b/src/api-server/model/QosData.h @@ -31,6 +31,9 @@ namespace model { /// /// </summary> class QosData { + const std::string BANDWIDTH_VALIDATION_REGEX = + R"(^\d+(\.\d+)? (bps|Kbps|Mbps|Gbps|Tbps)$)"; + public: QosData(); virtual ~QosData() = default; diff --git a/src/oai-cn5g-common-src b/src/oai-cn5g-common-src index 42e49ae9764b228fe571d8a331f560f2f1c5729f..3e64f08aac80e71337c78f0af73a37f722d98309 160000 --- a/src/oai-cn5g-common-src +++ b/src/oai-cn5g-common-src @@ -1 +1 @@ -Subproject commit 42e49ae9764b228fe571d8a331f560f2f1c5729f +Subproject commit 3e64f08aac80e71337c78f0af73a37f722d98309 diff --git a/src/oai_pcf/CMakeLists.txt b/src/oai_pcf/CMakeLists.txt index a6921e38fa69aaaa100f5393f7a6d0f5c0224e43..70d90bd0dde109e6cda2cf4f04afd74767fc563d 100644 --- a/src/oai_pcf/CMakeLists.txt +++ b/src/oai_pcf/CMakeLists.txt @@ -293,6 +293,7 @@ add_executable(pcf include(${BUILD_TOP_DIR}/pcf/used_common_files.cmake) include(${SRC_TOP_DIR}/${MOUNTED_COMMON}/logger/logger.cmake) include(${SRC_TOP_DIR}/${MOUNTED_COMMON}/config/config.cmake) +include_directories(${SRC_TOP_DIR}/${MOUNTED_COMMON}/utils) IF(STATIC_LINKING) SET(CMAKE_EXE_LINKER_FLAGS "-static") diff --git a/src/pcf_app/pcf_config.cpp b/src/pcf_app/pcf_config.cpp index 02e78b7c64080f78a802e5100d9a8110d85bf26a..642d5818917b2ec1ce6325be7c3bf1eadf560921 100644 --- a/src/pcf_app/pcf_config.cpp +++ b/src/pcf_app/pcf_config.cpp @@ -45,7 +45,7 @@ oai::config::pcf::pcf_config::pcf_config( sbi_interface("SBI", "oai-pcf", 80, "v1", "eth0"), policy_config( DEFAULT_POLICY_DECISIONS_PATH, DEFAULT_PCC_RULES_PATH, - DEFAULT_TRAFFIC_RULES_PATH)); + DEFAULT_TRAFFIC_RULES_PATH, DEFAULT_QOS_DATA_PATH)); auto nrf = std::make_shared<nf>( NRF_CONFIG_NAME, "oai-nrf", diff --git a/src/pcf_app/pcf_config.hpp b/src/pcf_app/pcf_config.hpp index 95a2db9b878f16cb4fec64e2daba2ad4836ad817..74e57e1dadea14a2de390c584007fc40915ea7f8 100644 --- a/src/pcf_app/pcf_config.hpp +++ b/src/pcf_app/pcf_config.hpp @@ -39,6 +39,7 @@ const std::string DEFAULT_TRAFFIC_RULES_PATH = "/openair-pcf/policies/traffic_rules"; const std::string DEFAULT_POLICY_DECISIONS_PATH = "/openair-pcf/policies/policy_decisions"; +const std::string DEFAULT_QOS_DATA_PATH = "/openair-pcf/policies/qos_data"; class pcf_config : public oai::config::config { public: diff --git a/src/pcf_app/pcf_config_types.cpp b/src/pcf_app/pcf_config_types.cpp index 9c0cf59108010d156a30a2894a0e80a5c85d2123..6331b2086ad61c4b1df0d1c5a144aa67023220b1 100644 --- a/src/pcf_app/pcf_config_types.cpp +++ b/src/pcf_app/pcf_config_types.cpp @@ -37,14 +37,15 @@ using namespace oai::config::pcf; policy_config::policy_config( const std::string& policy_decisions_path, const std::string& pcc_rules_path, - const std::string& traffic_rules_path) { + const std::string& traffic_rules_path, const std::string& qos_data_path) { m_config_name = "Policy"; m_traffic_rules_path = string_config_value("Traffic Rules", traffic_rules_path); m_pcc_rules_path = string_config_value("PCC Rules", pcc_rules_path); m_policy_decisions_path = string_config_value("Policy Decisions", policy_decisions_path); - m_set = true; + m_qos_data_path = string_config_value("QoS Data", qos_data_path); + m_set = true; } void policy_config::from_yaml(const YAML::Node& node) { @@ -57,23 +58,31 @@ void policy_config::from_yaml(const YAML::Node& node) { if (node["traffic_rules_path"]) { m_traffic_rules_path.from_yaml(node["traffic_rules_path"]); } + if (node["qos_data_path"]) { + m_qos_data_path.from_yaml(node["qos_data_path"]); + } } std::string policy_config::to_string(const std::string& indent) const { if (!m_set) return ""; std::string out; - unsigned int inner_width = get_inner_width(indent.length()); - out.append(m_config_name).append("\n"); + std::string title_fmt = get_title_formatter(0); + std::string value_fmt = get_value_formatter(1); + + out.append(indent).append(fmt::format(title_fmt, m_config_name)); out.append(indent).append(fmt::format( - BASE_FORMATTER, OUTER_LIST_ELEM, - m_policy_decisions_path.get_config_name(), inner_width, + value_fmt, m_policy_decisions_path.get_config_name(), m_policy_decisions_path.get_value())); out.append(indent).append(fmt::format( - BASE_FORMATTER, OUTER_LIST_ELEM, m_pcc_rules_path.get_config_name(), - inner_width, m_pcc_rules_path.get_value())); + value_fmt, m_pcc_rules_path.get_config_name(), + m_pcc_rules_path.get_value())); + out.append(indent).append(fmt::format( + value_fmt, m_traffic_rules_path.get_config_name(), + m_traffic_rules_path.get_value())); out.append(indent).append(fmt::format( - BASE_FORMATTER, OUTER_LIST_ELEM, m_traffic_rules_path.get_config_name(), - inner_width, m_traffic_rules_path.get_value())); + value_fmt, m_qos_data_path.get_config_name(), + m_qos_data_path.get_value())); + return out; } @@ -89,6 +98,10 @@ const std::string& policy_config::get_traffic_rules_path() const { return m_traffic_rules_path.get_value(); } +const std::string& policy_config::get_qos_data_path() const { + return m_qos_data_path.get_value(); +} + pcf_config_type::pcf_config_type( const std::string& name, const std::string& host, const sbi_interface& sbi, const policy_config& policy) diff --git a/src/pcf_app/pcf_config_types.hpp b/src/pcf_app/pcf_config_types.hpp index fd8b8378b6c7a900903d78c076a30afa089c93d8..f6c28b33f1eec4ea6aac552c3d89991ef0217469 100644 --- a/src/pcf_app/pcf_config_types.hpp +++ b/src/pcf_app/pcf_config_types.hpp @@ -37,11 +37,13 @@ class policy_config : public config_type { string_config_value m_pcc_rules_path; string_config_value m_policy_decisions_path; string_config_value m_traffic_rules_path; + string_config_value m_qos_data_path; public: explicit policy_config( const std::string& policy_decisions_path, - const std::string& pcc_rules_path, const std::string& traffic_rules_path); + const std::string& pcc_rules_path, const std::string& traffic_rules_path, + const std::string& qos_data_path); void from_yaml(const YAML::Node& node) override; @@ -49,6 +51,7 @@ class policy_config : public config_type { [[nodiscard]] const std::string& get_pcc_rules_path() const; [[nodiscard]] const std::string& get_policy_decisions_path() const; [[nodiscard]] const std::string& get_traffic_rules_path() const; + [[nodiscard]] const std::string& get_qos_data_path() const; }; class pcf_config_type : public nf { diff --git a/src/pcf_app/sm_policy/policy_provisioning_file.cpp b/src/pcf_app/sm_policy/policy_provisioning_file.cpp index e1b1869e789d8e06cd27187484e71c39f8ed51e5..ed80bfa85c38abddac3f192e303ef395e44d2f42 100644 --- a/src/pcf_app/sm_policy/policy_provisioning_file.cpp +++ b/src/pcf_app/sm_policy/policy_provisioning_file.cpp @@ -37,6 +37,7 @@ #include "pcf_config.hpp" #include "logger.hpp" #include "Snssai.h" +#include "conv.hpp" using namespace oai::pcf::app; using namespace oai::config::pcf; @@ -52,6 +53,7 @@ bool policy_provisioning_file::read_all_policy_files() { std::vector<YAML::Node> traffic_controls; std::vector<YAML::Node> pcc_rules; std::vector<YAML::Node> policy_decisions; + std::vector<YAML::Node> qos_data; if (!read_all_files_in_dir( pcf_cfg->get_pcf_policy().get_traffic_rules_path(), @@ -70,50 +72,55 @@ bool policy_provisioning_file::read_all_policy_files() { "Could not load mandatory policy decisions configuration"); return false; } + if (!read_all_files_in_dir( + pcf_cfg->get_pcf_policy().get_qos_data_path(), qos_data)) { + Logger::pcf_app().warn("Could not load QoS Data files"); + } auto traffic_control_objects = convert_yaml_to_model<TrafficControlData>(traffic_controls); auto pcc_rule_objects = convert_yaml_to_model<PccRule>(pcc_rules); + auto qos_data_objects = convert_yaml_to_model<QosData>(qos_data); if (traffic_control_objects.empty()) { Logger::pcf_app().warn("No traffic control descriptions are loaded"); } + if (qos_data_objects.empty()) { + Logger::pcf_app().warn("No QoS data descriptions are loaded"); + } if (pcc_rule_objects.empty()) { Logger::pcf_app().warn("No mandatory PCC rules are loaded"); return false; } // we need to manually adjust the IDs of the PCC rules and traffic controls - for (auto traffic : traffic_control_objects) { + for (auto& traffic : traffic_control_objects) { traffic.second.setTcId(traffic.first); - traffic_control_objects[traffic.first] = traffic.second; } - for (auto pcc : pcc_rule_objects) { + for (auto& qos : qos_data_objects) { + qos.second.setQosId(qos.first); + } + for (auto& pcc : pcc_rule_objects) { pcc.second.setPccRuleId(pcc.first); - // check if traffic control data exists - auto reftc = pcc.second.getRefTcData(); - auto traffic_it = reftc.begin(); - while (traffic_it != reftc.end()) { - auto traffic_exists = traffic_control_objects.find(*traffic_it); - if (traffic_exists == traffic_control_objects.end()) { - Logger::pcf_app().warn( - "You have referenced Traffic Control ID %s in PCC Rule %s, but it " - "does not exist. It is removed from this PCC rule", - (*traffic_it).c_str(), pcc.first.c_str()); - traffic_it = reftc.erase(traffic_it); - } else { - ++traffic_it; - } - } + auto reftc = pcc.second.getRefTcData(); + remove_ids_not_in_map<TrafficControlData>( + traffic_control_objects, reftc, "Traffic Rules", pcc.first); + + auto refqos = pcc.second.getRefQosData(); + remove_ids_not_in_map<QosData>( + qos_data_objects, refqos, "QoS Data", pcc.first); + pcc.second.setRefTcData(reftc); + pcc.second.setRefQosData(refqos); } // Now parse the decisions (manually as it is not a model) for (auto node : policy_decisions) { for (auto it = node.begin(); it != node.end(); ++it) { SmPolicyDecision decision = decision_from_rules( - it->second, pcc_rule_objects, traffic_control_objects); + it->second, pcc_rule_objects, traffic_control_objects, + qos_data_objects); if (!decision.pccRulesIsSet()) { Logger::pcf_app().warn( "Decision %s could not be parsed. It is ignored", @@ -156,7 +163,8 @@ bool policy_provisioning_file::read_all_policy_files() { SmPolicyDecision policy_provisioning_file::decision_from_rules( const YAML::Node& node, const std::map<std::string, PccRule>& pcc_rules, - const std::map<std::string, TrafficControlData>& traffic_control) { + const std::map<std::string, TrafficControlData>& traffic_control, + const std::map<std::string, QosData>& qos_data) { SmPolicyDecision decision = {}; std::map<std::string, PccRule> used_rules; @@ -187,7 +195,9 @@ SmPolicyDecision policy_provisioning_file::decision_from_rules( return {}; } std::map<std::string, TrafficControlData> used_traffic_control; - // now add the TrafficControlDescriptions + std::map<std::string, QosData> used_qos_data; + + // now add the TrafficControlDescriptions and QosData for (const auto& rule : used_rules) { for (const auto& traffic : rule.second.getRefTcData()) { auto found = traffic_control.find(traffic); @@ -195,63 +205,84 @@ SmPolicyDecision policy_provisioning_file::decision_from_rules( used_traffic_control.insert(std::make_pair(traffic, found->second)); } } + for (const auto& qos : rule.second.getRefQosData()) { + auto found = qos_data.find(qos); + if (found != qos_data.end()) { + used_qos_data.insert(std::make_pair(qos, found->second)); + } + } } if (!used_rules.empty()) { decision.setPccRules(used_rules); decision.setTraffContDecs(used_traffic_control); + decision.setQosDecs(used_qos_data); return decision; } return {}; } -void policy_provisioning_file::replace_json_string_with_int(nlohmann::json& j) { - for (const auto& elem : j.items()) { - if (elem.value().is_primitive()) { - try { - std::string e = elem.value(); - if (e == "true") j[elem.key()] = true; - if (e == "false") j[elem.key()] = false; - int val = std::stoi(e); - j[elem.key()] = val; // replace with int - } catch (std::invalid_argument& ex) { - } - } else { - replace_json_string_with_int(elem.value()); - } - } -} - template<class T> std::map<std::string, T> policy_provisioning_file::convert_yaml_to_model( const std::vector<YAML::Node>& nodes) { std::map<std::string, T> objects_map; - // here we convert YAML to json so we can use the already existing JSON parser - // https://stackoverflow.com/questions/43902941/emitting-json-with-yaml-cpp for (const auto& node : nodes) { - YAML::Emitter emitter; - emitter << YAML::DoubleQuoted << YAML::Flow << YAML::BeginSeq << node; - std::string json_string(emitter.c_str() + 1); - nlohmann::json j = nlohmann::json::parse(json_string); - // this is a bit hacky but the problem is that YAML emits ints as strings - replace_json_string_with_int(j); + auto j = oai::utils::conversions::yaml_to_json(node); for (const auto& elem : j.items()) { T obj; - from_json(elem.value(), obj); - std::stringstream stream; - if (obj.validate(stream)) { - objects_map.insert(std::make_pair(elem.key(), obj)); - Logger::pcf_app().debug( - "Rule %s successfully parsed.", elem.key().c_str()); - } else { + try { + from_json(elem.value(), obj); + std::stringstream stream; + if (obj.validate(stream)) { + objects_map.insert(std::make_pair(elem.key(), obj)); + Logger::pcf_app().debug( + "Rule %s successfully parsed.", elem.key().c_str()); + } else { + Logger::pcf_app().warn( + "Error while parsing rule %s: %s", elem.key(), + stream.str().c_str()); + } + } catch (nlohmann::json::exception& e) { Logger::pcf_app().warn( - "Error while parsing rules: %s", stream.str().c_str()); + "Error while parsing rule %s: %s", elem.key(), e.what()); } } } return objects_map; } +template<class T> +void policy_provisioning_file::remove_ids_not_in_map( + const std::map<std::string, T>& map, std::vector<std::string>& ids, + const std::string& error_msg_type, const std::string& pcc_id) { + auto it = ids.begin(); + while (it != ids.end()) { + auto traffic_exists = map.find(*it); + if (traffic_exists == map.end()) { + Logger::pcf_app().warn( + "You have referenced %s ID %s in PCC Rule %s, but it " + "does not exist. It is removed from this PCC rule", + error_msg_type, *it, pcc_id); + it = ids.erase(it); + } else { + ++it; + } + } +} + +template<class T> +std::map<std::string, T> policy_provisioning_file::extract_existing_ids( + const std::map<std::string, T>& map, const std::vector<std::string>& ids) { + std::map<std::string, T> used_elements; + for (const auto& id : ids) { + auto found = map.find(id); + if (found != map.end()) { + used_elements.insert(std::make_pair(id, found->second)); + } + } + return used_elements; +} + bool policy_provisioning_file::read_all_files_in_dir( const std::string& dir_path, std::vector<YAML::Node>& yaml_output) { if (!exists(dir_path)) { diff --git a/src/pcf_app/sm_policy/policy_provisioning_file.hpp b/src/pcf_app/sm_policy/policy_provisioning_file.hpp index 912f8c9d828fd466c8a5611b287860fcc93c58b3..bc349cf58a85d4c8c6b433d2cd63892174ae249f 100644 --- a/src/pcf_app/sm_policy/policy_provisioning_file.hpp +++ b/src/pcf_app/sm_policy/policy_provisioning_file.hpp @@ -57,13 +57,21 @@ class policy_provisioning_file { const YAML::Node& node, const std::map<std::string, oai::pcf::model::PccRule>& pcc_rules, const std::map<std::string, oai::pcf::model::TrafficControlData>& - traffic_control); + traffic_control, + const std::map<std::string, oai::pcf::model::QosData>& qos_data); template<class T> static std::map<std::string, T> convert_yaml_to_model( const std::vector<YAML::Node>& nodes); - static void replace_json_string_with_int(nlohmann::json& j); + template<class T> + static void remove_ids_not_in_map( + const std::map<std::string, T>& map, std::vector<std::string>& ids, + const std::string& error_msg_type, const std::string& pcc_id); + + template<class T> + static std::map<std::string, T> extract_existing_ids( + const std::map<std::string, T>& map, const std::vector<std::string>& ids); std::shared_ptr<oai::pcf::app::sm_policy::policy_storage> m_policy_storage; };