Commit 48e724fc authored by Tien-Thinh Nguyen's avatar Tien-Thinh Nguyen

Get NetworkSlice from NSSF (code cleanup)

parent b1e2e4ea
......@@ -87,6 +87,9 @@ amf_app::amf_app(const amf_config& amf_cfg)
throw;
}
// Generate an AMF profile (including NF instance)
generate_amf_profile();
// Register to NRF if needed
if (amf_cfg.support_features.enable_nf_registration) register_to_nrf();
......@@ -604,11 +607,6 @@ void amf_app::handle_itti_message(itti_sbi_n1_message_notification& itti_msg) {
return;
}
// AMF Client response handlers
//------------------------------------------------------------------------------
void amf_app::handle_post_sm_context_response_error_400() {
Logger::amf_app().error("Post SM context response error 400");
}
//------------------------------------------------------------------------------
uint32_t amf_app::generate_tmsi() {
......@@ -820,10 +818,13 @@ void amf_app::generate_amf_profile() {
nf_instance_profile.display();
}
//---------------------------------------------------------------------------------------------
std::string amf_app::get_nf_instance() const {
return amf_instance_id;
}
//---------------------------------------------------------------------------------------------
void amf_app::register_to_nrf() {
// create a NF profile to this instance
generate_amf_profile();
// send request to N11 to send NF registration to NRF
trigger_nf_registration_request();
}
......
......@@ -75,6 +75,22 @@ class amf_app {
util::uint_generator<uint32_t> tmsi_generator;
std::map<long, std::shared_ptr<ue_context>> amf_ue_ngap_id2ue_ctx;
mutable std::shared_mutex m_amf_ue_ngap_id2ue_ctx;
std::map<std::string, std::shared_ptr<ue_context>> ue_ctx_key;
mutable std::shared_mutex m_ue_ctx_key;
std::map<std::string, std::shared_ptr<ue_context>> supi2ue_ctx;
mutable std::shared_mutex m_supi2ue_ctx;
mutable std::shared_mutex m_curl_handle_responses_n2_sm;
std::map<uint32_t, boost::shared_ptr<boost::promise<std::string>>>
curl_handle_responses_n2_sm;
mutable std::shared_mutex m_curl_handle_responses_n11;
std::map<uint32_t, boost::shared_ptr<boost::promise<nlohmann::json>>>
curl_handle_responses_n11;
public:
explicit amf_app(const amf_config& amf_cfg);
amf_app(amf_app const&) = delete;
......@@ -114,9 +130,7 @@ class amf_app {
bool get_pdu_sessions_context(
const string& supi,
std::vector<std::shared_ptr<pdu_session_context>>& sessions_ctx);
// SMF Client response handlers
void handle_post_sm_context_response_error_400();
// others
uint32_t generate_tmsi();
bool generate_5g_guti(
uint32_t ranid, long amfid, std::string& mcc, std::string& mnc,
......@@ -277,22 +291,7 @@ class amf_app {
void trigger_process_response(uint32_t pid, std::string n2_sm);
void trigger_process_response(uint32_t pid, nlohmann::json& json_data);
private:
std::map<long, std::shared_ptr<ue_context>> amf_ue_ngap_id2ue_ctx;
mutable std::shared_mutex m_amf_ue_ngap_id2ue_ctx;
std::map<std::string, std::shared_ptr<ue_context>> ue_ctx_key;
mutable std::shared_mutex m_ue_ctx_key;
std::map<std::string, std::shared_ptr<ue_context>> supi2ue_ctx;
mutable std::shared_mutex m_supi2ue_ctx;
mutable std::shared_mutex m_curl_handle_responses_n2_sm;
std::map<uint32_t, boost::shared_ptr<boost::promise<std::string>>>
curl_handle_responses_n2_sm;
mutable std::shared_mutex m_curl_handle_responses_n11;
std::map<uint32_t, boost::shared_ptr<boost::promise<nlohmann::json>>>
curl_handle_responses_n11;
std::string get_nf_instance() const;
};
} // namespace amf_application
......
......@@ -3718,11 +3718,13 @@ bool amf_n1::reroute_registration_request(std::shared_ptr<nas_context>& nc) {
return false;
}
// Process NS selection to select the appropriate AMF
std::string nf_instance_id = {};
slice_info_for_registration_t slice_info = {};
// TODO: use from OpenAPI
authorized_network_slice_info_t authorized_network_slice_info = {};
oai::amf::model::SliceInfoForRegistration slice_info = {};
if (!get_network_slice_selection(
nf_instance_id, slice_info, authorized_network_slice_info)) {
nc, amf_app_inst->get_nf_instance(), slice_info,
authorized_network_slice_info)) {
return false;
}
std::string target_amf = {};
......@@ -3814,9 +3816,16 @@ bool amf_n1::get_slice_selection_subscription_data_from_conf_file(
//------------------------------------------------------------------------------
bool amf_n1::get_network_slice_selection(
const std::string& nf_instance_id,
slice_info_for_registration_t& slice_info,
authorized_network_slice_info_t& authorized_network_slice_info) const {
std::shared_ptr<nas_context>& nc, const std::string& nf_instance_id,
oai::amf::model::SliceInfoForRegistration& slice_info,
authorized_network_slice_info_t& authorized_network_slice_info) {
std::shared_ptr<ue_context> uc = {};
if (!find_ue_context(
nc.get()->ran_ue_ngap_id, nc.get()->amf_ue_ngap_id, uc)) {
Logger::amf_n1().warn("Cannot find the UE context");
return false;
}
if (amf_cfg.support_features.enable_external_nssf) {
// Get Authorized Network Slice Info from an external NSSF
......@@ -3837,6 +3846,8 @@ bool amf_n1::get_network_slice_selection(
itti_msg->nf_instance_id = nf_instance_id;
itti_msg->slice_info = slice_info;
itti_msg->promise_id = promise_id;
itti_msg->plmn.mcc = uc.get()->cgi.mcc;
itti_msg->plmn.mnc = uc.get()->cgi.mnc;
int ret = itti_inst->send_msg(itti_msg);
if (0 != ret) {
......@@ -3878,7 +3889,7 @@ bool amf_n1::get_network_slice_selection(
//------------------------------------------------------------------------------
bool amf_n1::get_network_slice_selection_from_conf_file(
const std::string& nf_instance_id,
slice_info_for_registration_t& slice_info,
oai::amf::model::SliceInfoForRegistration& slice_info,
authorized_network_slice_info_t& authorized_network_slice_info) const {
// TODO: Get Authorized Network Slice Info from local configuration file
......
......@@ -50,6 +50,7 @@
#include "RegistrationAccept.hpp"
#include "ue_context.hpp"
#include "itti.hpp"
#include "SliceInfoForRegistration.h"
namespace amf_application {
......@@ -227,12 +228,12 @@ class amf_n1 {
bool check_requested_nssai(
const std::shared_ptr<nas_context>& nc, const nssai_t& nssai) const;
bool get_network_slice_selection(
const std::string& nf_instance_id,
slice_info_for_registration_t& slice_info,
authorized_network_slice_info_t& authorized_network_slice_info) const;
std::shared_ptr<nas_context>& nc, const std::string& nf_instance_id,
oai::amf::model::SliceInfoForRegistration& slice_info,
authorized_network_slice_info_t& authorized_network_slice_info);
bool get_network_slice_selection_from_conf_file(
const std::string& nf_instance_id,
slice_info_for_registration_t& slice_info,
oai::amf::model::SliceInfoForRegistration& slice_info,
authorized_network_slice_info_t& authorized_network_slice_info) const;
void send_n1_message_notity(
......
......@@ -34,36 +34,27 @@
#include "3gpp_ts24501.hpp"
#include "3gpp_29.500.h"
#include "amf.hpp"
#include "amf_app.hpp"
#include "amf_config.hpp"
#include "AmfEventReport.h"
#include "amf_n1.hpp"
#include "conversions.hpp"
#include "comUt.hpp"
#include "ue_context.hpp"
#include "fqdn.hpp"
#include "itti.hpp"
#include "itti_msg_amf_app.hpp"
#include "mime_parser.hpp"
#include "nas_context.hpp"
// For smf_client
#include "ApiClient.h"
#include "ApiConfiguration.h"
#include "SMContextsCollectionApi.h"
#include "SmContextCreateData.h"
#include "mime_parser.hpp"
#include "ue_context.hpp"
#include "fqdn.hpp"
#include "conversions.hpp"
#include "comUt.hpp"
#include "AmfEventReport.h"
extern "C" {
#include "dynamic_memory_check.h"
}
using namespace oai::smf::model;
using namespace oai::smf::api;
using namespace web;
using namespace web::http;
// Common features like URIs.
using namespace web::http::client;
using namespace config;
using namespace amf_application;
extern itti_mw* itti_inst;
......@@ -71,7 +62,6 @@ extern amf_config amf_cfg;
extern amf_n11* amf_n11_inst;
extern amf_n1* amf_n1_inst;
extern amf_app* amf_app_inst;
extern statistics stacs;
//------------------------------------------------------------------------------
std::size_t callback(
......@@ -93,8 +83,6 @@ void octet_stream_2_hex_stream(uint8_t* buf, int len, std::string& out) {
printf("n1sm buffer: %s\n", out.c_str());
}
//------------------------------------------------------------------------------
void amf_n11_task(void*);
//------------------------------------------------------------------------------
void amf_n11_task(void*) {
const task_id_t task_id = TASK_AMF_N11;
......@@ -109,6 +97,7 @@ void amf_n11_task(void*) {
dynamic_cast<itti_nsmf_pdusession_create_sm_context*>(msg);
amf_n11_inst->handle_itti_message(ref(*m));
} break;
case NSMF_PDU_SESSION_UPDATE_SM_CTX: {
Logger::amf_n11().info(
"Receive Nsmf_PDUSessionUpdateSMContext, handling ...");
......@@ -116,6 +105,7 @@ void amf_n11_task(void*) {
dynamic_cast<itti_nsmf_pdusession_update_sm_context*>(msg);
amf_n11_inst->handle_itti_message(ref(*m));
} break;
case PDU_SESSION_RESOURCE_SETUP_RESPONSE: {
Logger::amf_n11().info(
"Receive PDU Session Resource Setup response, handling ...");
......@@ -123,6 +113,7 @@ void amf_n11_task(void*) {
dynamic_cast<itti_pdu_session_resource_setup_response*>(msg);
amf_n11_inst->handle_itti_message(ref(*m));
} break;
case N11_REGISTER_NF_INSTANCE_REQUEST: {
Logger::amf_n11().info(
"Receive Register NF Instance Request, handling ...");
......@@ -130,6 +121,7 @@ void amf_n11_task(void*) {
dynamic_cast<itti_n11_register_nf_instance_request*>(msg);
// TODO: Handle ITTI
} break;
case SBI_NOTIFY_SUBSCRIBED_EVENT: {
Logger::amf_n11().info(
"Receive Notify Subscribed Event Request, handling ...");
......@@ -137,6 +129,7 @@ void amf_n11_task(void*) {
dynamic_cast<itti_sbi_notify_subscribed_event*>(msg);
amf_n11_inst->handle_itti_message(ref(*m));
} break;
case N11_SLICE_SELECTION_SUBSCRIPTION_DATA: {
Logger::amf_n11().info(
"Receive Slice Selection Subscription Data Retrieval Request, "
......@@ -184,7 +177,9 @@ amf_n11::~amf_n11() {}
//------------------------------------------------------------------------------
void amf_n11::handle_itti_message(
itti_pdu_session_resource_setup_response& itti_msg) {}
itti_pdu_session_resource_setup_response& itti_msg) {
// TODO:
}
//------------------------------------------------------------------------------
void amf_n11::handle_itti_message(
......@@ -250,13 +245,13 @@ void amf_n11::handle_itti_message(
Logger::amf_n11().debug("SMF URI: %s", remote_uri.c_str());
std::string n2SmMsg = {};
std::string n2sm_msg = {};
nlohmann::json pdu_session_update_request = {};
if (itti_msg.is_n2sm_set) {
pdu_session_update_request["n2SmInfoType"] = itti_msg.n2sm_info_type;
pdu_session_update_request["n2SmInfo"]["contentId"] = "n2msg";
octet_stream_2_hex_stream(
(uint8_t*) bdata(itti_msg.n2sm), blength(itti_msg.n2sm), n2SmMsg);
(uint8_t*) bdata(itti_msg.n2sm), blength(itti_msg.n2sm), n2sm_msg);
}
// For N2 HO
......@@ -274,10 +269,8 @@ void amf_n11::handle_itti_message(
// if (amf_cfg.support_features.use_http2) http_version = 2;
curl_http_client(
remote_uri, json_part, "", n2SmMsg, supi, itti_msg.pdu_session_id,
remote_uri, json_part, "", n2sm_msg, supi, itti_msg.pdu_session_id,
http_version, itti_msg.promise_id);
// stacs.display();
}
//------------------------------------------------------------------------------
......@@ -439,14 +432,15 @@ void amf_n11::send_pdu_session_update_sm_context_request(
pdu_session_update_request["n1SmMsg"]["contentId"] = "n1SmMsg";
std::string json_part = pdu_session_update_request.dump();
std::string n1SmMsg = {};
octet_stream_2_hex_stream((uint8_t*) bdata(sm_msg), blength(sm_msg), n1SmMsg);
std::string n1sm_msg = {};
octet_stream_2_hex_stream(
(uint8_t*) bdata(sm_msg), blength(sm_msg), n1sm_msg);
uint8_t http_version = 1;
if (amf_cfg.support_features.use_http2) http_version = 2;
curl_http_client(
remote_uri, json_part, n1SmMsg, "", supi, psc.get()->pdu_session_id,
remote_uri, json_part, n1sm_msg, "", supi, psc.get()->pdu_session_id,
http_version);
}
......@@ -494,25 +488,24 @@ void amf_n11::handle_pdu_session_initial_request(
Logger::amf_n11().debug("Message body %s", json_part.c_str());
std::string n1SmMsg = {};
octet_stream_2_hex_stream((uint8_t*) bdata(sm_msg), blength(sm_msg), n1SmMsg);
std::string n1sm_msg = {};
octet_stream_2_hex_stream(
(uint8_t*) bdata(sm_msg), blength(sm_msg), n1sm_msg);
uint8_t http_version = 1;
// if (amf_cfg.support_features.use_http2) http_version = 2;
curl_http_client(
remote_uri, json_part, n1SmMsg, "", supi, psc.get()->pdu_session_id,
remote_uri, json_part, n1sm_msg, "", supi, psc.get()->pdu_session_id,
http_version);
}
//------------------------------------------------------------------------------
void amf_n11::handle_itti_message(
itti_nsmf_pdusession_release_sm_context& itti_msg) {
// TODO: Need PDU session ID
uint8_t pdu_session_id = 1; // Hardcoded
std::shared_ptr<pdu_session_context> psc = {};
if (!amf_app_inst->find_pdu_session_context(
itti_msg.supi, pdu_session_id, psc)) {
itti_msg.supi, itti_msg.pdu_session_id, psc)) {
Logger::amf_n11().warn(
"PDU Session context for SUPI %s doesn't exit!", itti_msg.supi.c_str());
return;
......@@ -530,13 +523,15 @@ void amf_n11::handle_itti_message(
string remote_uri = psc.get()->location + "release";
nlohmann::json pdu_session_release_request;
pdu_session_release_request["supi"] = itti_msg.supi.c_str();
pdu_session_release_request["dnn"] = psc.get()->dnn.c_str();
pdu_session_release_request["sNssai"]["sst"] = 1;
pdu_session_release_request["sNssai"]["sd"] = "0";
pdu_session_release_request["pduSessionId"] = psc.get()->pdu_session_id;
pdu_session_release_request["cause"] = "REL_DUE_TO_REACTIVATION";
pdu_session_release_request["ngApCause"] = "radioNetwork";
pdu_session_release_request["supi"] = itti_msg.supi.c_str();
pdu_session_release_request["dnn"] = psc.get()->dnn.c_str();
pdu_session_release_request["sNssai"]["sst"] =
1; // TODO: check hardcoded value
pdu_session_release_request["sNssai"]["sd"] =
"0"; // TODO: check hardcoded value
pdu_session_release_request["pduSessionId"] = psc.get()->pdu_session_id;
pdu_session_release_request["cause"] = "REL_DUE_TO_REACTIVATION";
pdu_session_release_request["ngApCause"] = "radioNetwork";
std::string json_part = pdu_session_release_request.dump();
uint8_t http_version = 1;
if (amf_cfg.support_features.use_http2) http_version = 2;
......@@ -614,61 +609,47 @@ void amf_n11::handle_itti_message(
nlohmann::json response_data = {};
curl_http_client(url, "GET", body, response_data);
/*
curl_global_init(CURL_GLOBAL_ALL);
CURL* curl = curl = curl_easy_init();
std::unique_ptr<std::string> httpData(new std::string());
nlohmann::json response_data = {};
// Notify to the result
if (itti_msg.promise_id > 0) {
amf_app_inst->trigger_process_response(itti_msg.promise_id, response_data);
return;
}
if (curl) {
CURLcode res = {};
struct curl_slist* headers = nullptr;
headers = curl_slist_append(headers, "content-type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT_MS);
curl_easy_setopt(curl, CURLOPT_INTERFACE, amf_cfg.n11.if_name.c_str());
if (itti_msg.http_version == 2) {
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
// we use a self-signed test server, skip verification during debugging
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(
curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE);
}
return;
}
// Response information
long httpCode = {0};
std::unique_ptr<std::string> httpData(new std::string());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, httpData.get());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, body.length());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
res = curl_easy_perform(curl);
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode);
void amf_n11::handle_itti_message(
itti_n11_network_slice_selection_information& itti_msg) {
Logger::amf_n11().debug(
"Send Network Slice Selection Information Request to NSSF (HTTP version "
"%d)",
itti_msg.http_version);
Logger::amf_n11().debug("Response from UDM, HTTP Code: %d", httpCode);
std::string url =
std::string(
inet_ntoa(*((struct in_addr*) &amf_cfg.nssf_addr.ipv4_addr))) +
":" + std::to_string(amf_cfg.nssf_addr.port) + "/nnssf-nsselection/" +
amf_cfg.nssf_addr.api_version + "/network-slice-information";
// slice-info-request-for-registration
nlohmann::json slice_info = {};
to_json(slice_info, itti_msg.slice_info);
// home-plmn-id
nlohmann::json home_plmn_id = {};
home_plmn_id["mcc"] = itti_msg.plmn.mcc;
home_plmn_id["mnc"] = itti_msg.plmn.mnc;
// TODO: tai
std::string parameters = {};
parameters = "?nf-type=AMF?nf-id=" + amf_app_inst->get_nf_instance() +
"?slice-info-request-for-registration=" + slice_info.dump() +
"?home-plmn-id=" + home_plmn_id.dump();
if (static_cast<http_response_codes_e>(httpCode) !=
http_response_codes_e::HTTP_RESPONSE_CODE_200_OK) {
try {
response_data = nlohmann::json::parse(*httpData.get());
} catch (nlohmann::json::exception& e) {
Logger::amf_n11().warn("Could not parse JSON from the UDM response");
}
} else {
Logger::amf_n11().warn("Could not get NSSAI from UDM");
}
Logger::amf_n11().debug(
"Send Slice Selection Subscription Data Retrieval to UDM, URL %s",
url.c_str());
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
*/
nlohmann::json response_data = {};
curl_http_client(url, "GET", "", response_data);
// Notify to the result
if (itti_msg.promise_id > 0) {
......@@ -679,9 +660,6 @@ void amf_n11::handle_itti_message(
return;
}
void amf_n11::handle_itti_message(
itti_n11_network_slice_selection_information& itti_msg) {}
//------------------------------------------------------------------------------
bool amf_n11::smf_selection_from_configuration(
std::string& smf_addr, std::string& smf_api_version) {
......@@ -719,9 +697,6 @@ bool amf_n11::smf_selection_from_configuration(
return false;
}
//------------------------------------------------------------------------------
void amf_n11::handle_post_sm_context_response_error_400() {}
//------------------------------------------------------------------------------
void amf_n11::handle_post_sm_context_response_error(
long code, std::string cause, bstring n1sm, std::string supi,
......@@ -777,6 +752,8 @@ bool amf_n11::discover_smf_from_nsi_info(
"info-request-for-pdu-session=" +
slice_info.dump();
// TODO: use curl_http_client
curl_global_init(CURL_GLOBAL_ALL);
CURL* curl = curl = curl_easy_init();
if (curl) {
......@@ -804,8 +781,6 @@ bool amf_n11::discover_smf_from_nsi_info(
std::unique_ptr<std::string> httpData(new std::string());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, httpData.get());
// curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, body.length());
// curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
res = curl_easy_perform(curl);
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode);
......@@ -893,6 +868,8 @@ bool amf_n11::discover_smf(
amf_cfg.nrf_addr.api_version +
"/nf-instances?target-nf-type=SMF&requester-nf-type=AMF";
// TODO: use curl_http_client
curl_global_init(CURL_GLOBAL_ALL);
CURL* curl = curl = curl_easy_init();
......@@ -1043,6 +1020,8 @@ void amf_n11::register_nf_instance(
uint8_t http_version = 1;
if (amf_cfg.support_features.use_http2) http_version = 2;
// TODO: use curl_http_client
if (curl) {
CURLcode res = {};
struct curl_slist* headers = nullptr;
......@@ -1123,6 +1102,8 @@ bool amf_n11::send_ue_authentication_request(
Logger::amf_n11().debug(
"Send UE Authentication Request to AUSF, msg body: \n %s", body.c_str());
// TODO: use curl_http_client
curl_global_init(CURL_GLOBAL_ALL);
CURL* curl = curl = curl_easy_init();
......@@ -1190,8 +1171,8 @@ bool amf_n11::send_ue_authentication_request(
//------------------------------------------------------------------------------
void amf_n11::curl_http_client(
std::string remote_uri, std::string jsonData, std::string n1SmMsg,
std::string n2SmMsg, std::string supi, uint8_t pdu_session_id,
std::string remote_uri, std::string json_data, std::string n1sm_msg,
std::string n2sm_msg, std::string supi, uint8_t pdu_session_id,
uint8_t http_version, uint32_t promise_id) {
Logger::amf_n11().debug("Call SMF service: %s", remote_uri.c_str());
......@@ -1208,22 +1189,22 @@ void amf_n11::curl_http_client(
return;
}
if ((n1SmMsg.size() > 0) and (n2SmMsg.size() > 0)) {
if ((n1sm_msg.size() > 0) and (n2sm_msg.size() > 0)) {
// prepare the body content for Curl
parser.create_multipart_related_content(
body, jsonData, CURL_MIME_BOUNDARY, n1SmMsg, n2SmMsg);
} else if (n1SmMsg.size() > 0) { // only N1 content
body, json_data, CURL_MIME_BOUNDARY, n1sm_msg, n2sm_msg);
} else if (n1sm_msg.size() > 0) { // only N1 content
// prepare the body content for Curl
parser.create_multipart_related_content(
body, jsonData, CURL_MIME_BOUNDARY, n1SmMsg,
body, json_data, CURL_MIME_BOUNDARY, n1sm_msg,
multipart_related_content_part_e::NAS);
} else if (n2SmMsg.size() > 0) { // only N2 content
} else if (n2sm_msg.size() > 0) { // only N2 content
// prepare the body content for Curl
parser.create_multipart_related_content(
body, jsonData, CURL_MIME_BOUNDARY, n2SmMsg,
body, json_data, CURL_MIME_BOUNDARY, n2sm_msg,
multipart_related_content_part_e::NGAP);
} else {
body = jsonData;
body = json_data;
is_multipart = false;
}
......@@ -1344,7 +1325,7 @@ void amf_n11::curl_http_client(
}
Logger::amf_n11().debug(
"Get response with jsonData: %s", json_data_response.c_str());
"Get response with json_data: %s", json_data_response.c_str());
conv::msg_str_2_msg_hex(n1sm, n1sm_hex);
comUt::print_buffer(
"amf_n11", "Get response with n1sm:", (uint8_t*) bdata(n1sm_hex),
......@@ -1551,12 +1532,21 @@ void amf_n11::curl_http_client(
(static_cast<http_response_codes_e>(httpCode) !=
http_response_codes_e::HTTP_RESPONSE_CODE_204_NO_CONTENT)) {
is_response_ok = false;
response_json = {}; // Do not process the ProblemDetails
if (response.size() < 1) {
Logger::amf_n11().info("There's no content in the response");
response_json = {};
return;
}
Logger::amf_n11().debug("Error with response code %d", httpCode);
// Get the ProblemDetails
try {
response_json = nlohmann::json::parse(response);
} catch (nlohmann::json::exception& e) {
Logger::amf_n11().info("Could not get JSON content from the response");
response_json = {};
}
Logger::amf_n11().debug("Response code %d", httpCode);
return;
}
......@@ -1568,7 +1558,7 @@ void amf_n11::curl_http_client(
}
Logger::amf_n11().info(
"Get response with jsonData: %s", response_json.dump().c_str());
"Get response with Json content: %s", response_json.dump().c_str());
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
......
......@@ -70,7 +70,6 @@ class amf_n11 {
bool smf_selection_from_configuration(
std::string& smf_addr, std::string& smf_api_version);
void handle_post_sm_context_response_error_400();
void handle_post_sm_context_response_error(
long code, std::string cause, bstring n1sm, std::string supi,
uint8_t pdu_session_id);
......
......@@ -29,6 +29,7 @@
#include "bstrlib.h"
#include "itti_msg.hpp"
#include "3gpp_29.531.h"
#include "SliceInfoForRegistration.h"
class itti_msg_n11 : public itti_msg {
public:
......@@ -222,7 +223,8 @@ class itti_n11_network_slice_selection_information : public itti_msg_n11 {
uint8_t http_version;
std::string nf_instance_id;
slice_info_for_registration_t slice_info;
oai::amf::model::SliceInfoForRegistration slice_info;
plmn_t plmn;
uint32_t promise_id;
};
......
......@@ -12,6 +12,9 @@
*/
#include "AllowedNssai.h"
#include "Helpers.h"
#include <sstream>
namespace oai {
namespace amf {
......@@ -19,10 +22,63 @@ namespace model {
AllowedNssai::AllowedNssai() {}
AllowedNssai::~AllowedNssai() {}
void AllowedNssai::validate() const {
std::stringstream msg;
// if (!validate(msg))
// {
// throw oai::nssf_server::helpers::ValidationException(msg.str());
// }
}
bool AllowedNssai::validate(std::stringstream& msg) const {
return validate(msg, "");
}
bool AllowedNssai::validate(
std::stringstream& msg, const std::string& pathPrefix) const {
bool success = true;
const std::string _pathPrefix =
pathPrefix.empty() ? "AllowedNssai" : pathPrefix;
/* AllowedSnssaiList */ {
const std::vector<AllowedSnssai>& value = m_AllowedSnssaiList;
const std::string currentValuePath = _pathPrefix + ".allowedSnssaiList";
if (value.size() < 1) {
success = false;
msg << currentValuePath << ": must have at least 1 elements;";
}
{ // Recursive validation of array elements
const std::string oldValuePath = currentValuePath;
int i = 0;
for (const AllowedSnssai& value : value) {
const std::string currentValuePath =
oldValuePath + "[" + std::to_string(i) + "]";
success =
value.validate(msg, currentValuePath + ".allowedSnssaiList") &&
success;
i++;
}
}
}
void AllowedNssai::validate() {
// TODO: implement validation
return success;
}
bool AllowedNssai::operator==(const AllowedNssai& rhs) const {
return
(getAllowedSnssaiList() == rhs.getAllowedSnssaiList()) // &&
// (getAccessType() == rhs.getAccessType())
;
}
bool AllowedNssai::operator!=(const AllowedNssai& rhs) const {
return !(*this == rhs);
}
void to_json(nlohmann::json& j, const AllowedNssai& o) {
......@@ -36,9 +92,13 @@ void from_json(const nlohmann::json& j, AllowedNssai& o) {
j.at("accessType").get_to(o.m_AccessType);
}
std::vector<AllowedSnssai>& AllowedNssai::getAllowedSnssaiList() {
std::vector<AllowedSnssai> AllowedNssai::getAllowedSnssaiList() const {
return m_AllowedSnssaiList;
}
void AllowedNssai::setAllowedSnssaiList(
std::vector<AllowedSnssai> const& value) {
m_AllowedSnssaiList = value;
}
AccessType AllowedNssai::getAccessType() const {
return m_AccessType;
}
......
......@@ -34,9 +34,28 @@ namespace model {
class AllowedNssai {
public:
AllowedNssai();
virtual ~AllowedNssai();
virtual ~AllowedNssai() = default;
void validate();
/// <summary>
/// Validate the current data in the model. Throws a ValidationException on
/// failure.
/// </summary>
void validate() const;
/// <summary>
/// Validate the current data in the model. Returns false on error and writes
/// an error message into the given stringstream.
/// </summary>
bool validate(std::stringstream& msg) const;
/// <summary>
/// Helper overload for validate. Used when one model stores another model and
/// calls it's validate. Not meant to be called outside that case.
/// </summary>
bool validate(std::stringstream& msg, const std::string& pathPrefix) const;
bool operator==(const AllowedNssai& rhs) const;
bool operator!=(const AllowedNssai& rhs) const;
/////////////////////////////////////////////
/// AllowedNssai members
......@@ -44,7 +63,8 @@ class AllowedNssai {
/// <summary>
///
/// </summary>
std::vector<AllowedSnssai>& getAllowedSnssaiList();
std::vector<AllowedSnssai> getAllowedSnssaiList() const;
void setAllowedSnssaiList(std::vector<AllowedSnssai> const& value);
/// <summary>
///
/// </summary>
......
......@@ -12,6 +12,9 @@
*/
#include "AllowedSnssai.h"
#include "Helpers.h"
#include <sstream>
namespace oai {
namespace amf {
......@@ -22,16 +25,75 @@ AllowedSnssai::AllowedSnssai() {
m_MappedHomeSnssaiIsSet = false;
}
AllowedSnssai::~AllowedSnssai() {}
void AllowedSnssai::validate() const {
std::stringstream msg;
// if (!validate(msg))
// {
// throw oai::nssf_server::helpers::ValidationException(msg.str());
// }
}
bool AllowedSnssai::validate(std::stringstream& msg) const {
return validate(msg, "");
}
bool AllowedSnssai::validate(
std::stringstream& msg, const std::string& pathPrefix) const {
bool success = true;
const std::string _pathPrefix =
pathPrefix.empty() ? "AllowedSnssai" : pathPrefix;
if (nsiInformationListIsSet()) {
const std::vector<NsiInformation>& value = m_NsiInformationList;
const std::string currentValuePath = _pathPrefix + ".nsiInformationList";
if (value.size() < 1) {
success = false;
msg << currentValuePath << ": must have at least 1 elements;";
}
{ // Recursive validation of array elements
const std::string oldValuePath = currentValuePath;
int i = 0;
for (const NsiInformation& value : value) {
const std::string currentValuePath =
oldValuePath + "[" + std::to_string(i) + "]";
success =
value.validate(msg, currentValuePath + ".nsiInformationList") &&
success;
i++;
}
}
}
void AllowedSnssai::validate() {
// TODO: implement validation
return success;
}
bool AllowedSnssai::operator==(const AllowedSnssai& rhs) const {
return
(getAllowedSnssai() == rhs.getAllowedSnssai()) &&
((!nsiInformationListIsSet() && !rhs.nsiInformationListIsSet()) ||
(nsiInformationListIsSet() && rhs.nsiInformationListIsSet() &&
getNsiInformationList() == rhs.getNsiInformationList())) &&
((!mappedHomeSnssaiIsSet() && !rhs.mappedHomeSnssaiIsSet()) ||
(mappedHomeSnssaiIsSet() && rhs.mappedHomeSnssaiIsSet() &&
getMappedHomeSnssai() == rhs.getMappedHomeSnssai()))
;
}
bool AllowedSnssai::operator!=(const AllowedSnssai& rhs) const {
return !(*this == rhs);
}
void to_json(nlohmann::json& j, const AllowedSnssai& o) {
j = nlohmann::json();
j["allowedSnssai"] = o.m_AllowedSnssai;
if (o.nsiInformationListIsSet())
if (o.nsiInformationListIsSet() || !o.m_NsiInformationList.empty())
j["nsiInformationList"] = o.m_NsiInformationList;
if (o.mappedHomeSnssaiIsSet()) j["mappedHomeSnssai"] = o.m_MappedHomeSnssai;
}
......@@ -54,9 +116,14 @@ Snssai AllowedSnssai::getAllowedSnssai() const {
void AllowedSnssai::setAllowedSnssai(Snssai const& value) {
m_AllowedSnssai = value;
}
std::vector<NsiInformation>& AllowedSnssai::getNsiInformationList() {
std::vector<NsiInformation> AllowedSnssai::getNsiInformationList() const {
return m_NsiInformationList;
}
void AllowedSnssai::setNsiInformationList(
std::vector<NsiInformation> const& value) {
m_NsiInformationList = value;
m_NsiInformationListIsSet = true;
}
bool AllowedSnssai::nsiInformationListIsSet() const {
return m_NsiInformationListIsSet;
}
......
......@@ -34,9 +34,28 @@ namespace model {
class AllowedSnssai {
public:
AllowedSnssai();
virtual ~AllowedSnssai();
virtual ~AllowedSnssai() = default;
void validate();
/// <summary>
/// Validate the current data in the model. Throws a ValidationException on
/// failure.
/// </summary>
void validate() const;
/// <summary>
/// Validate the current data in the model. Returns false on error and writes
/// an error message into the given stringstream.
/// </summary>
bool validate(std::stringstream& msg) const;
/// <summary>
/// Helper overload for validate. Used when one model stores another model and
/// calls it's validate. Not meant to be called outside that case.
/// </summary>
bool validate(std::stringstream& msg, const std::string& pathPrefix) const;
bool operator==(const AllowedSnssai& rhs) const;
bool operator!=(const AllowedSnssai& rhs) const;
/////////////////////////////////////////////
/// AllowedSnssai members
......@@ -49,7 +68,8 @@ class AllowedSnssai {
/// <summary>
///
/// </summary>
std::vector<NsiInformation>& getNsiInformationList();
std::vector<NsiInformation> getNsiInformationList() const;
void setNsiInformationList(std::vector<NsiInformation> const& value);
bool nsiInformationListIsSet() const;
void unsetNsiInformationList();
/// <summary>
......
/**
* NSSF NS Selection
* NSSF Network Slice Selection Service. © 2021, 3GPP Organizational Partners
* (ARIB, ATIS, CCSA, ETSI, TSDSI, TTA, TTC). All rights reserved.
*
* The version of the OpenAPI document: 2.1.2
*
*
* NOTE: This class is auto generated by OpenAPI Generator
* (https://openapi-generator.tech). https://openapi-generator.tech Do not edit
* the class manually.
*/
#include "MappingOfSnssai.h"
#include "Helpers.h"
#include <sstream>
namespace oai {
namespace amf {
namespace model {
MappingOfSnssai::MappingOfSnssai() {}
void MappingOfSnssai::validate() const {
std::stringstream msg;
// if (!validate(msg))
// {
// throw oai::nssf_server::helpers::ValidationException(msg.str());
// }
}
bool MappingOfSnssai::validate(std::stringstream& msg) const {
return validate(msg, "");
}
bool MappingOfSnssai::validate(
std::stringstream& msg, const std::string& pathPrefix) const {
bool success = true;
const std::string _pathPrefix =
pathPrefix.empty() ? "MappingOfSnssai" : pathPrefix;
return success;
}
bool MappingOfSnssai::operator==(const MappingOfSnssai& rhs) const {
return
(getServingSnssai() == rhs.getServingSnssai()) &&
(getHomeSnssai() == rhs.getHomeSnssai())
;
}
bool MappingOfSnssai::operator!=(const MappingOfSnssai& rhs) const {
return !(*this == rhs);
}
void to_json(nlohmann::json& j, const MappingOfSnssai& o) {
j = nlohmann::json();
j["servingSnssai"] = o.m_ServingSnssai;
j["homeSnssai"] = o.m_HomeSnssai;
}
void from_json(const nlohmann::json& j, MappingOfSnssai& o) {
j.at("servingSnssai").get_to(o.m_ServingSnssai);
j.at("homeSnssai").get_to(o.m_HomeSnssai);
}
Snssai MappingOfSnssai::getServingSnssai() const {
return m_ServingSnssai;
}
void MappingOfSnssai::setServingSnssai(Snssai const& value) {
m_ServingSnssai = value;
}
Snssai MappingOfSnssai::getHomeSnssai() const {
return m_HomeSnssai;
}
void MappingOfSnssai::setHomeSnssai(Snssai const& value) {
m_HomeSnssai = value;
}
} // namespace model
} // namespace amf
} // namespace oai
/**
* NSSF NS Selection
* NSSF Network Slice Selection Service. © 2021, 3GPP Organizational Partners
* (ARIB, ATIS, CCSA, ETSI, TSDSI, TTA, TTC). All rights reserved.
*
* The version of the OpenAPI document: 2.1.2
*
*
* NOTE: This class is auto generated by OpenAPI Generator
* (https://openapi-generator.tech). https://openapi-generator.tech Do not edit
* the class manually.
*/
/*
* MappingOfSnssai.h
*
*
*/
#ifndef MappingOfSnssai_H_
#define MappingOfSnssai_H_
#include "Snssai.h"
#include <nlohmann/json.hpp>
namespace oai {
namespace amf {
namespace model {
/// <summary>
///
/// </summary>
class MappingOfSnssai {
public:
MappingOfSnssai();
virtual ~MappingOfSnssai() = default;
/// <summary>
/// Validate the current data in the model. Throws a ValidationException on
/// failure.
/// </summary>
void validate() const;
/// <summary>
/// Validate the current data in the model. Returns false on error and writes
/// an error message into the given stringstream.
/// </summary>
bool validate(std::stringstream& msg) const;
/// <summary>
/// Helper overload for validate. Used when one model stores another model and
/// calls it's validate. Not meant to be called outside that case.
/// </summary>
bool validate(std::stringstream& msg, const std::string& pathPrefix) const;
bool operator==(const MappingOfSnssai& rhs) const;
bool operator!=(const MappingOfSnssai& rhs) const;
/////////////////////////////////////////////
/// MappingOfSnssai members
/// <summary>
///
/// </summary>
Snssai getServingSnssai() const;
void setServingSnssai(Snssai const& value);
/// <summary>
///
/// </summary>
Snssai getHomeSnssai() const;
void setHomeSnssai(Snssai const& value);
friend void to_json(nlohmann::json& j, const MappingOfSnssai& o);
friend void from_json(const nlohmann::json& j, MappingOfSnssai& o);
protected:
Snssai m_ServingSnssai;
Snssai m_HomeSnssai;
};
} // namespace model
} // namespace amf
} // namespace oai
#endif /* MappingOfSnssai_H_ */
......@@ -12,27 +12,75 @@
*/
#include "NsiInformation.h"
#include "Helpers.h"
#include <sstream>
namespace oai {
namespace amf {
namespace model {
NsiInformation::NsiInformation() {
m_NrfId = "";
m_NsiId = "";
m_NsiIdIsSet = false;
m_NrfId = "";
m_NsiId = "";
m_NsiIdIsSet = false;
m_NrfNfMgtUri = "";
m_NrfNfMgtUriIsSet = false;
m_NrfAccessTokenUri = "";
m_NrfAccessTokenUriIsSet = false;
}
void NsiInformation::validate() const {
std::stringstream msg;
// if (!validate(msg))
// {
// throw oai::nssf_server::helpers::ValidationException(msg.str());
// }
}
NsiInformation::~NsiInformation() {}
bool NsiInformation::validate(std::stringstream& msg) const {
return validate(msg, "");
}
bool NsiInformation::validate(
std::stringstream& msg, const std::string& pathPrefix) const {
bool success = true;
const std::string _pathPrefix =
pathPrefix.empty() ? "NsiInformation" : pathPrefix;
return success;
}
bool NsiInformation::operator==(const NsiInformation& rhs) const {
return
(getNrfId() == rhs.getNrfId()) &&
((!nsiIdIsSet() && !rhs.nsiIdIsSet()) ||
(nsiIdIsSet() && rhs.nsiIdIsSet() && getNsiId() == rhs.getNsiId())) &&
((!nrfNfMgtUriIsSet() && !rhs.nrfNfMgtUriIsSet()) ||
(nrfNfMgtUriIsSet() && rhs.nrfNfMgtUriIsSet() &&
getNrfNfMgtUri() == rhs.getNrfNfMgtUri())) &&
((!nrfAccessTokenUriIsSet() && !rhs.nrfAccessTokenUriIsSet()) ||
(nrfAccessTokenUriIsSet() && rhs.nrfAccessTokenUriIsSet() &&
getNrfAccessTokenUri() == rhs.getNrfAccessTokenUri()))
void NsiInformation::validate() {
// TODO: implement validation
;
}
bool NsiInformation::operator!=(const NsiInformation& rhs) const {
return !(*this == rhs);
}
void to_json(nlohmann::json& j, const NsiInformation& o) {
j = nlohmann::json();
j["nrfId"] = o.m_NrfId;
if (o.nsiIdIsSet()) j["nsiId"] = o.m_NsiId;
if (o.nrfNfMgtUriIsSet()) j["nrfNfMgtUri"] = o.m_NrfNfMgtUri;
if (o.nrfAccessTokenUriIsSet())
j["nrfAccessTokenUri"] = o.m_NrfAccessTokenUri;
}
void from_json(const nlohmann::json& j, NsiInformation& o) {
......@@ -41,6 +89,14 @@ void from_json(const nlohmann::json& j, NsiInformation& o) {
j.at("nsiId").get_to(o.m_NsiId);
o.m_NsiIdIsSet = true;
}
if (j.find("nrfNfMgtUri") != j.end()) {
j.at("nrfNfMgtUri").get_to(o.m_NrfNfMgtUri);
o.m_NrfNfMgtUriIsSet = true;
}
if (j.find("nrfAccessTokenUri") != j.end()) {
j.at("nrfAccessTokenUri").get_to(o.m_NrfAccessTokenUri);
o.m_NrfAccessTokenUriIsSet = true;
}
}
std::string NsiInformation::getNrfId() const {
......@@ -62,6 +118,32 @@ bool NsiInformation::nsiIdIsSet() const {
void NsiInformation::unsetNsiId() {
m_NsiIdIsSet = false;
}
std::string NsiInformation::getNrfNfMgtUri() const {
return m_NrfNfMgtUri;
}
void NsiInformation::setNrfNfMgtUri(std::string const& value) {
m_NrfNfMgtUri = value;
m_NrfNfMgtUriIsSet = true;
}
bool NsiInformation::nrfNfMgtUriIsSet() const {
return m_NrfNfMgtUriIsSet;
}
void NsiInformation::unsetNrfNfMgtUri() {
m_NrfNfMgtUriIsSet = false;
}
std::string NsiInformation::getNrfAccessTokenUri() const {
return m_NrfAccessTokenUri;
}
void NsiInformation::setNrfAccessTokenUri(std::string const& value) {
m_NrfAccessTokenUri = value;
m_NrfAccessTokenUriIsSet = true;
}
bool NsiInformation::nrfAccessTokenUriIsSet() const {
return m_NrfAccessTokenUriIsSet;
}
void NsiInformation::unsetNrfAccessTokenUri() {
m_NrfAccessTokenUriIsSet = false;
}
} // namespace model
} // namespace amf
......
......@@ -32,9 +32,28 @@ namespace model {
class NsiInformation {
public:
NsiInformation();
virtual ~NsiInformation();
virtual ~NsiInformation() = default;
void validate();
/// <summary>
/// Validate the current data in the model. Throws a ValidationException on
/// failure.
/// </summary>
void validate() const;
/// <summary>
/// Validate the current data in the model. Returns false on error and writes
/// an error message into the given stringstream.
/// </summary>
bool validate(std::stringstream& msg) const;
/// <summary>
/// Helper overload for validate. Used when one model stores another model and
/// calls it's validate. Not meant to be called outside that case.
/// </summary>
bool validate(std::stringstream& msg, const std::string& pathPrefix) const;
bool operator==(const NsiInformation& rhs) const;
bool operator!=(const NsiInformation& rhs) const;
/////////////////////////////////////////////
/// NsiInformation members
......@@ -51,6 +70,20 @@ class NsiInformation {
void setNsiId(std::string const& value);
bool nsiIdIsSet() const;
void unsetNsiId();
/// <summary>
///
/// </summary>
std::string getNrfNfMgtUri() const;
void setNrfNfMgtUri(std::string const& value);
bool nrfNfMgtUriIsSet() const;
void unsetNrfNfMgtUri();
/// <summary>
///
/// </summary>
std::string getNrfAccessTokenUri() const;
void setNrfAccessTokenUri(std::string const& value);
bool nrfAccessTokenUriIsSet() const;
void unsetNrfAccessTokenUri();
friend void to_json(nlohmann::json& j, const NsiInformation& o);
friend void from_json(const nlohmann::json& j, NsiInformation& o);
......@@ -60,6 +93,10 @@ class NsiInformation {
std::string m_NsiId;
bool m_NsiIdIsSet;
std::string m_NrfNfMgtUri;
bool m_NrfNfMgtUriIsSet;
std::string m_NrfAccessTokenUri;
bool m_NrfAccessTokenUriIsSet;
};
} // namespace model
......
/**
* NSSF NS Selection
* NSSF Network Slice Selection Service. © 2021, 3GPP Organizational Partners
* (ARIB, ATIS, CCSA, ETSI, TSDSI, TTA, TTC). All rights reserved.
*
* The version of the OpenAPI document: 2.1.2
*
*
* NOTE: This class is auto generated by OpenAPI Generator
* (https://openapi-generator.tech). https://openapi-generator.tech Do not edit
* the class manually.
*/
#include "SliceInfoForRegistration.h"
#include "Helpers.h"
#include <sstream>
namespace oai {
namespace amf {
namespace model {
SliceInfoForRegistration::SliceInfoForRegistration() {
m_SubscribedNssaiIsSet = false;
m_AllowedNssaiCurrentAccessIsSet = false;
m_AllowedNssaiOtherAccessIsSet = false;
m_SNssaiForMappingIsSet = false;
m_RequestedNssaiIsSet = false;
m_DefaultConfiguredSnssaiInd = false;
m_DefaultConfiguredSnssaiIndIsSet = false;
m_MappingOfNssaiIsSet = false;
m_RequestMapping = false;
m_RequestMappingIsSet = false;
}
void SliceInfoForRegistration::validate() const {
std::stringstream msg;
// if (!validate(msg))
// {
// throw oai::nssf_server::helpers::ValidationException(msg.str());
// }
}
bool SliceInfoForRegistration::validate(std::stringstream& msg) const {
return validate(msg, "");
}
bool SliceInfoForRegistration::validate(
std::stringstream& msg, const std::string& pathPrefix) const {
bool success = true;
const std::string _pathPrefix =
pathPrefix.empty() ? "SliceInfoForRegistration" : pathPrefix;
if (subscribedNssaiIsSet()) {
const std::vector<SubscribedSnssai>& value = m_SubscribedNssai;
const std::string currentValuePath = _pathPrefix + ".subscribedNssai";
if (value.size() < 1) {
success = false;
msg << currentValuePath << ": must have at least 1 elements;";
}
{ // Recursive validation of array elements
const std::string oldValuePath = currentValuePath;
int i = 0;
for (const SubscribedSnssai& value : value) {
const std::string currentValuePath =
oldValuePath + "[" + std::to_string(i) + "]";
success = value.validate(msg, currentValuePath + ".subscribedNssai") &&
success;
i++;
}
}
}
if (sNssaiForMappingIsSet()) {
const std::vector<Snssai>& value = m_SNssaiForMapping;
const std::string currentValuePath = _pathPrefix + ".sNssaiForMapping";
if (value.size() < 1) {
success = false;
msg << currentValuePath << ": must have at least 1 elements;";
}
{ // Recursive validation of array elements
const std::string oldValuePath = currentValuePath;
int i = 0;
for (const Snssai& value : value) {
const std::string currentValuePath =
oldValuePath + "[" + std::to_string(i) + "]";
success = value.validate(msg, currentValuePath + ".sNssaiForMapping") &&
success;
i++;
}
}
}
if (requestedNssaiIsSet()) {
const std::vector<Snssai>& value = m_RequestedNssai;
const std::string currentValuePath = _pathPrefix + ".requestedNssai";
if (value.size() < 1) {
success = false;
msg << currentValuePath << ": must have at least 1 elements;";
}
{ // Recursive validation of array elements
const std::string oldValuePath = currentValuePath;
int i = 0;
for (const Snssai& value : value) {
const std::string currentValuePath =
oldValuePath + "[" + std::to_string(i) + "]";
success = value.validate(msg, currentValuePath + ".requestedNssai") &&
success;
i++;
}
}
}
if (mappingOfNssaiIsSet()) {
const std::vector<MappingOfSnssai>& value = m_MappingOfNssai;
const std::string currentValuePath = _pathPrefix + ".mappingOfNssai";
if (value.size() < 1) {
success = false;
msg << currentValuePath << ": must have at least 1 elements;";
}
{ // Recursive validation of array elements
const std::string oldValuePath = currentValuePath;
int i = 0;
for (const MappingOfSnssai& value : value) {
const std::string currentValuePath =
oldValuePath + "[" + std::to_string(i) + "]";
success = value.validate(msg, currentValuePath + ".mappingOfNssai") &&
success;
i++;
}
}
}
return success;
}
bool SliceInfoForRegistration::operator==(
const SliceInfoForRegistration& rhs) const {
return
((!subscribedNssaiIsSet() && !rhs.subscribedNssaiIsSet()) ||
(subscribedNssaiIsSet() && rhs.subscribedNssaiIsSet() &&
getSubscribedNssai() == rhs.getSubscribedNssai())) &&
((!allowedNssaiCurrentAccessIsSet() &&
!rhs.allowedNssaiCurrentAccessIsSet()) ||
(allowedNssaiCurrentAccessIsSet() &&
rhs.allowedNssaiCurrentAccessIsSet() &&
getAllowedNssaiCurrentAccess() ==
rhs.getAllowedNssaiCurrentAccess())) &&
((!allowedNssaiOtherAccessIsSet() &&
!rhs.allowedNssaiOtherAccessIsSet()) ||
(allowedNssaiOtherAccessIsSet() && rhs.allowedNssaiOtherAccessIsSet() &&
getAllowedNssaiOtherAccess() == rhs.getAllowedNssaiOtherAccess())) &&
((!sNssaiForMappingIsSet() && !rhs.sNssaiForMappingIsSet()) ||
(sNssaiForMappingIsSet() && rhs.sNssaiForMappingIsSet() &&
getSNssaiForMapping() == rhs.getSNssaiForMapping())) &&
((!requestedNssaiIsSet() && !rhs.requestedNssaiIsSet()) ||
(requestedNssaiIsSet() && rhs.requestedNssaiIsSet() &&
getRequestedNssai() == rhs.getRequestedNssai())) &&
((!defaultConfiguredSnssaiIndIsSet() &&
!rhs.defaultConfiguredSnssaiIndIsSet()) ||
(defaultConfiguredSnssaiIndIsSet() &&
rhs.defaultConfiguredSnssaiIndIsSet() &&
isDefaultConfiguredSnssaiInd() ==
rhs.isDefaultConfiguredSnssaiInd())) &&
((!mappingOfNssaiIsSet() && !rhs.mappingOfNssaiIsSet()) ||
(mappingOfNssaiIsSet() && rhs.mappingOfNssaiIsSet() &&
getMappingOfNssai() == rhs.getMappingOfNssai())) &&
((!requestMappingIsSet() && !rhs.requestMappingIsSet()) ||
(requestMappingIsSet() && rhs.requestMappingIsSet() &&
isRequestMapping() == rhs.isRequestMapping()))
;
}
bool SliceInfoForRegistration::operator!=(
const SliceInfoForRegistration& rhs) const {
return !(*this == rhs);
}
void to_json(nlohmann::json& j, const SliceInfoForRegistration& o) {
j = nlohmann::json();
if (o.subscribedNssaiIsSet() || !o.m_SubscribedNssai.empty())
j["subscribedNssai"] = o.m_SubscribedNssai;
if (o.allowedNssaiCurrentAccessIsSet())
j["allowedNssaiCurrentAccess"] = o.m_AllowedNssaiCurrentAccess;
if (o.allowedNssaiOtherAccessIsSet())
j["allowedNssaiOtherAccess"] = o.m_AllowedNssaiOtherAccess;
if (o.sNssaiForMappingIsSet() || !o.m_SNssaiForMapping.empty())
j["sNssaiForMapping"] = o.m_SNssaiForMapping;
if (o.requestedNssaiIsSet() || !o.m_RequestedNssai.empty())
j["requestedNssai"] = o.m_RequestedNssai;
if (o.defaultConfiguredSnssaiIndIsSet())
j["defaultConfiguredSnssaiInd"] = o.m_DefaultConfiguredSnssaiInd;
if (o.mappingOfNssaiIsSet() || !o.m_MappingOfNssai.empty())
j["mappingOfNssai"] = o.m_MappingOfNssai;
if (o.requestMappingIsSet()) j["requestMapping"] = o.m_RequestMapping;
}
void from_json(const nlohmann::json& j, SliceInfoForRegistration& o) {
if (j.find("subscribedNssai") != j.end()) {
j.at("subscribedNssai").get_to(o.m_SubscribedNssai);
o.m_SubscribedNssaiIsSet = true;
}
if (j.find("allowedNssaiCurrentAccess") != j.end()) {
j.at("allowedNssaiCurrentAccess").get_to(o.m_AllowedNssaiCurrentAccess);
o.m_AllowedNssaiCurrentAccessIsSet = true;
}
if (j.find("allowedNssaiOtherAccess") != j.end()) {
j.at("allowedNssaiOtherAccess").get_to(o.m_AllowedNssaiOtherAccess);
o.m_AllowedNssaiOtherAccessIsSet = true;
}
if (j.find("sNssaiForMapping") != j.end()) {
j.at("sNssaiForMapping").get_to(o.m_SNssaiForMapping);
o.m_SNssaiForMappingIsSet = true;
}
if (j.find("requestedNssai") != j.end()) {
j.at("requestedNssai").get_to(o.m_RequestedNssai);
o.m_RequestedNssaiIsSet = true;
}
if (j.find("defaultConfiguredSnssaiInd") != j.end()) {
j.at("defaultConfiguredSnssaiInd").get_to(o.m_DefaultConfiguredSnssaiInd);
o.m_DefaultConfiguredSnssaiIndIsSet = true;
}
if (j.find("mappingOfNssai") != j.end()) {
j.at("mappingOfNssai").get_to(o.m_MappingOfNssai);
o.m_MappingOfNssaiIsSet = true;
}
if (j.find("requestMapping") != j.end()) {
j.at("requestMapping").get_to(o.m_RequestMapping);
o.m_RequestMappingIsSet = true;
}
}
std::vector<SubscribedSnssai> SliceInfoForRegistration::getSubscribedNssai()
const {
return m_SubscribedNssai;
}
void SliceInfoForRegistration::setSubscribedNssai(
std::vector<SubscribedSnssai> const& value) {
m_SubscribedNssai = value;
m_SubscribedNssaiIsSet = true;
}
bool SliceInfoForRegistration::subscribedNssaiIsSet() const {
return m_SubscribedNssaiIsSet;
}
void SliceInfoForRegistration::unsetSubscribedNssai() {
m_SubscribedNssaiIsSet = false;
}
AllowedNssai SliceInfoForRegistration::getAllowedNssaiCurrentAccess() const {
return m_AllowedNssaiCurrentAccess;
}
void SliceInfoForRegistration::setAllowedNssaiCurrentAccess(
AllowedNssai const& value) {
m_AllowedNssaiCurrentAccess = value;
m_AllowedNssaiCurrentAccessIsSet = true;
}
bool SliceInfoForRegistration::allowedNssaiCurrentAccessIsSet() const {
return m_AllowedNssaiCurrentAccessIsSet;
}
void SliceInfoForRegistration::unsetAllowedNssaiCurrentAccess() {
m_AllowedNssaiCurrentAccessIsSet = false;
}
AllowedNssai SliceInfoForRegistration::getAllowedNssaiOtherAccess() const {
return m_AllowedNssaiOtherAccess;
}
void SliceInfoForRegistration::setAllowedNssaiOtherAccess(
AllowedNssai const& value) {
m_AllowedNssaiOtherAccess = value;
m_AllowedNssaiOtherAccessIsSet = true;
}
bool SliceInfoForRegistration::allowedNssaiOtherAccessIsSet() const {
return m_AllowedNssaiOtherAccessIsSet;
}
void SliceInfoForRegistration::unsetAllowedNssaiOtherAccess() {
m_AllowedNssaiOtherAccessIsSet = false;
}
std::vector<Snssai> SliceInfoForRegistration::getSNssaiForMapping() const {
return m_SNssaiForMapping;
}
void SliceInfoForRegistration::setSNssaiForMapping(
std::vector<Snssai> const& value) {
m_SNssaiForMapping = value;
m_SNssaiForMappingIsSet = true;
}
bool SliceInfoForRegistration::sNssaiForMappingIsSet() const {
return m_SNssaiForMappingIsSet;
}
void SliceInfoForRegistration::unsetSNssaiForMapping() {
m_SNssaiForMappingIsSet = false;
}
std::vector<Snssai> SliceInfoForRegistration::getRequestedNssai() const {
return m_RequestedNssai;
}
void SliceInfoForRegistration::setRequestedNssai(
std::vector<Snssai> const& value) {
m_RequestedNssai = value;
m_RequestedNssaiIsSet = true;
}
bool SliceInfoForRegistration::requestedNssaiIsSet() const {
return m_RequestedNssaiIsSet;
}
void SliceInfoForRegistration::unsetRequestedNssai() {
m_RequestedNssaiIsSet = false;
}
bool SliceInfoForRegistration::isDefaultConfiguredSnssaiInd() const {
return m_DefaultConfiguredSnssaiInd;
}
void SliceInfoForRegistration::setDefaultConfiguredSnssaiInd(bool const value) {
m_DefaultConfiguredSnssaiInd = value;
m_DefaultConfiguredSnssaiIndIsSet = true;
}
bool SliceInfoForRegistration::defaultConfiguredSnssaiIndIsSet() const {
return m_DefaultConfiguredSnssaiIndIsSet;
}
void SliceInfoForRegistration::unsetDefaultConfiguredSnssaiInd() {
m_DefaultConfiguredSnssaiIndIsSet = false;
}
std::vector<MappingOfSnssai> SliceInfoForRegistration::getMappingOfNssai()
const {
return m_MappingOfNssai;
}
void SliceInfoForRegistration::setMappingOfNssai(
std::vector<MappingOfSnssai> const& value) {
m_MappingOfNssai = value;
m_MappingOfNssaiIsSet = true;
}
bool SliceInfoForRegistration::mappingOfNssaiIsSet() const {
return m_MappingOfNssaiIsSet;
}
void SliceInfoForRegistration::unsetMappingOfNssai() {
m_MappingOfNssaiIsSet = false;
}
bool SliceInfoForRegistration::isRequestMapping() const {
return m_RequestMapping;
}
void SliceInfoForRegistration::setRequestMapping(bool const value) {
m_RequestMapping = value;
m_RequestMappingIsSet = true;
}
bool SliceInfoForRegistration::requestMappingIsSet() const {
return m_RequestMappingIsSet;
}
void SliceInfoForRegistration::unsetRequestMapping() {
m_RequestMappingIsSet = false;
}
} // namespace model
} // namespace amf
} // namespace oai
/**
* NSSF NS Selection
* NSSF Network Slice Selection Service. © 2021, 3GPP Organizational Partners
* (ARIB, ATIS, CCSA, ETSI, TSDSI, TTA, TTC). All rights reserved.
*
* The version of the OpenAPI document: 2.1.2
*
*
* NOTE: This class is auto generated by OpenAPI Generator
* (https://openapi-generator.tech). https://openapi-generator.tech Do not edit
* the class manually.
*/
/*
* SliceInfoForRegistration.h
*
*
*/
#ifndef SliceInfoForRegistration_H_
#define SliceInfoForRegistration_H_
#include "AllowedNssai.h"
#include "MappingOfSnssai.h"
#include "Snssai.h"
#include "SubscribedSnssai.h"
#include <nlohmann/json.hpp>
#include <vector>
namespace oai {
namespace amf {
namespace model {
/// <summary>
///
/// </summary>
class SliceInfoForRegistration {
public:
SliceInfoForRegistration();
virtual ~SliceInfoForRegistration() = default;
/// <summary>
/// Validate the current data in the model. Throws a ValidationException on
/// failure.
/// </summary>
void validate() const;
/// <summary>
/// Validate the current data in the model. Returns false on error and writes
/// an error message into the given stringstream.
/// </summary>
bool validate(std::stringstream& msg) const;
/// <summary>
/// Helper overload for validate. Used when one model stores another model and
/// calls it's validate. Not meant to be called outside that case.
/// </summary>
bool validate(std::stringstream& msg, const std::string& pathPrefix) const;
bool operator==(const SliceInfoForRegistration& rhs) const;
bool operator!=(const SliceInfoForRegistration& rhs) const;
/////////////////////////////////////////////
/// SliceInfoForRegistration members
/// <summary>
///
/// </summary>
std::vector<SubscribedSnssai> getSubscribedNssai() const;
void setSubscribedNssai(std::vector<SubscribedSnssai> const& value);
bool subscribedNssaiIsSet() const;
void unsetSubscribedNssai();
/// <summary>
///
/// </summary>
AllowedNssai getAllowedNssaiCurrentAccess() const;
void setAllowedNssaiCurrentAccess(AllowedNssai const& value);
bool allowedNssaiCurrentAccessIsSet() const;
void unsetAllowedNssaiCurrentAccess();
/// <summary>
///
/// </summary>
AllowedNssai getAllowedNssaiOtherAccess() const;
void setAllowedNssaiOtherAccess(AllowedNssai const& value);
bool allowedNssaiOtherAccessIsSet() const;
void unsetAllowedNssaiOtherAccess();
/// <summary>
///
/// </summary>
std::vector<Snssai> getSNssaiForMapping() const;
void setSNssaiForMapping(std::vector<Snssai> const& value);
bool sNssaiForMappingIsSet() const;
void unsetSNssaiForMapping();
/// <summary>
///
/// </summary>
std::vector<Snssai> getRequestedNssai() const;
void setRequestedNssai(std::vector<Snssai> const& value);
bool requestedNssaiIsSet() const;
void unsetRequestedNssai();
/// <summary>
///
/// </summary>
bool isDefaultConfiguredSnssaiInd() const;
void setDefaultConfiguredSnssaiInd(bool const value);
bool defaultConfiguredSnssaiIndIsSet() const;
void unsetDefaultConfiguredSnssaiInd();
/// <summary>
///
/// </summary>
std::vector<MappingOfSnssai> getMappingOfNssai() const;
void setMappingOfNssai(std::vector<MappingOfSnssai> const& value);
bool mappingOfNssaiIsSet() const;
void unsetMappingOfNssai();
/// <summary>
///
/// </summary>
bool isRequestMapping() const;
void setRequestMapping(bool const value);
bool requestMappingIsSet() const;
void unsetRequestMapping();
friend void to_json(nlohmann::json& j, const SliceInfoForRegistration& o);
friend void from_json(const nlohmann::json& j, SliceInfoForRegistration& o);
protected:
std::vector<SubscribedSnssai> m_SubscribedNssai;
bool m_SubscribedNssaiIsSet;
AllowedNssai m_AllowedNssaiCurrentAccess;
bool m_AllowedNssaiCurrentAccessIsSet;
AllowedNssai m_AllowedNssaiOtherAccess;
bool m_AllowedNssaiOtherAccessIsSet;
std::vector<Snssai> m_SNssaiForMapping;
bool m_SNssaiForMappingIsSet;
std::vector<Snssai> m_RequestedNssai;
bool m_RequestedNssaiIsSet;
bool m_DefaultConfiguredSnssaiInd;
bool m_DefaultConfiguredSnssaiIndIsSet;
std::vector<MappingOfSnssai> m_MappingOfNssai;
bool m_MappingOfNssaiIsSet;
bool m_RequestMapping;
bool m_RequestMappingIsSet;
};
} // namespace model
} // namespace amf
} // namespace oai
#endif /* SliceInfoForRegistration_H_ */
......@@ -12,6 +12,9 @@
*/
#include "Snssai.h"
#include "Helpers.h"
#include <sstream>
namespace oai {
namespace amf {
......@@ -23,10 +26,58 @@ Snssai::Snssai() {
m_SdIsSet = false;
}
Snssai::~Snssai() {}
void Snssai::validate() const {
std::stringstream msg;
// if (!validate(msg))
// {
// throw oai::nssf_server::helpers::ValidationException(msg.str());
// }
}
bool Snssai::validate(std::stringstream& msg) const {
return validate(msg, "");
}
bool Snssai::validate(
std::stringstream& msg, const std::string& pathPrefix) const {
bool success = true;
const std::string _pathPrefix = pathPrefix.empty() ? "Snssai" : pathPrefix;
/* Sst */ {
const int32_t& value = m_Sst;
const std::string currentValuePath = _pathPrefix + ".sst";
if (value < 0) {
success = false;
msg << currentValuePath << ": must be greater than or equal to 0;";
}
if (value > 255) {
success = false;
msg << currentValuePath << ": must be less than or equal to 255;";
}
}
if (sdIsSet()) {
const std::string& value = m_Sd;
const std::string currentValuePath = _pathPrefix + ".sd";
}
return success;
}
bool Snssai::operator==(const Snssai& rhs) const {
return
(getSst() == rhs.getSst()) &&
((!sdIsSet() && !rhs.sdIsSet()) ||
(sdIsSet() && rhs.sdIsSet() && getSd() == rhs.getSd()))
;
}
void Snssai::validate() {
// TODO: implement validation
bool Snssai::operator!=(const Snssai& rhs) const {
return !(*this == rhs);
}
void to_json(nlohmann::json& j, const Snssai& o) {
......
......@@ -32,9 +32,28 @@ namespace model {
class Snssai {
public:
Snssai();
virtual ~Snssai();
virtual ~Snssai() = default;
void validate();
/// <summary>
/// Validate the current data in the model. Throws a ValidationException on
/// failure.
/// </summary>
void validate() const;
/// <summary>
/// Validate the current data in the model. Returns false on error and writes
/// an error message into the given stringstream.
/// </summary>
bool validate(std::stringstream& msg) const;
/// <summary>
/// Helper overload for validate. Used when one model stores another model and
/// calls it's validate. Not meant to be called outside that case.
/// </summary>
bool validate(std::stringstream& msg, const std::string& pathPrefix) const;
bool operator==(const Snssai& rhs) const;
bool operator!=(const Snssai& rhs) const;
/////////////////////////////////////////////
/// Snssai members
......
/**
* NSSF NS Selection
* NSSF Network Slice Selection Service. © 2021, 3GPP Organizational Partners
* (ARIB, ATIS, CCSA, ETSI, TSDSI, TTA, TTC). All rights reserved.
*
* The version of the OpenAPI document: 2.1.2
*
*
* NOTE: This class is auto generated by OpenAPI Generator
* (https://openapi-generator.tech). https://openapi-generator.tech Do not edit
* the class manually.
*/
#include "SubscribedSnssai.h"
#include "Helpers.h"
#include <sstream>
namespace oai {
namespace amf {
namespace model {
SubscribedSnssai::SubscribedSnssai() {
m_DefaultIndication = false;
m_DefaultIndicationIsSet = false;
}
void SubscribedSnssai::validate() const {
std::stringstream msg;
// if (!validate(msg))
// {
// throw oai::nssf_server::helpers::ValidationException(msg.str());
// }
}
bool SubscribedSnssai::validate(std::stringstream& msg) const {
return validate(msg, "");
}
bool SubscribedSnssai::validate(
std::stringstream& msg, const std::string& pathPrefix) const {
bool success = true;
const std::string _pathPrefix =
pathPrefix.empty() ? "SubscribedSnssai" : pathPrefix;
return success;
}
bool SubscribedSnssai::operator==(const SubscribedSnssai& rhs) const {
return
(getSubscribedSnssai() == rhs.getSubscribedSnssai()) &&
((!defaultIndicationIsSet() && !rhs.defaultIndicationIsSet()) ||
(defaultIndicationIsSet() && rhs.defaultIndicationIsSet() &&
isDefaultIndication() == rhs.isDefaultIndication()))
;
}
bool SubscribedSnssai::operator!=(const SubscribedSnssai& rhs) const {
return !(*this == rhs);
}
void to_json(nlohmann::json& j, const SubscribedSnssai& o) {
j = nlohmann::json();
j["subscribedSnssai"] = o.m_SubscribedSnssai;
if (o.defaultIndicationIsSet())
j["defaultIndication"] = o.m_DefaultIndication;
}
void from_json(const nlohmann::json& j, SubscribedSnssai& o) {
j.at("subscribedSnssai").get_to(o.m_SubscribedSnssai);
if (j.find("defaultIndication") != j.end()) {
j.at("defaultIndication").get_to(o.m_DefaultIndication);
o.m_DefaultIndicationIsSet = true;
}
}
Snssai SubscribedSnssai::getSubscribedSnssai() const {
return m_SubscribedSnssai;
}
void SubscribedSnssai::setSubscribedSnssai(Snssai const& value) {
m_SubscribedSnssai = value;
}
bool SubscribedSnssai::isDefaultIndication() const {
return m_DefaultIndication;
}
void SubscribedSnssai::setDefaultIndication(bool const value) {
m_DefaultIndication = value;
m_DefaultIndicationIsSet = true;
}
bool SubscribedSnssai::defaultIndicationIsSet() const {
return m_DefaultIndicationIsSet;
}
void SubscribedSnssai::unsetDefaultIndication() {
m_DefaultIndicationIsSet = false;
}
} // namespace model
} // namespace amf
} // namespace oai
/**
* NSSF NS Selection
* NSSF Network Slice Selection Service. © 2021, 3GPP Organizational Partners
* (ARIB, ATIS, CCSA, ETSI, TSDSI, TTA, TTC). All rights reserved.
*
* The version of the OpenAPI document: 2.1.2
*
*
* NOTE: This class is auto generated by OpenAPI Generator
* (https://openapi-generator.tech). https://openapi-generator.tech Do not edit
* the class manually.
*/
/*
* SubscribedSnssai.h
*
*
*/
#ifndef SubscribedSnssai_H_
#define SubscribedSnssai_H_
#include "Snssai.h"
#include <nlohmann/json.hpp>
namespace oai {
namespace amf {
namespace model {
/// <summary>
///
/// </summary>
class SubscribedSnssai {
public:
SubscribedSnssai();
virtual ~SubscribedSnssai() = default;
/// <summary>
/// Validate the current data in the model. Throws a ValidationException on
/// failure.
/// </summary>
void validate() const;
/// <summary>
/// Validate the current data in the model. Returns false on error and writes
/// an error message into the given stringstream.
/// </summary>
bool validate(std::stringstream& msg) const;
/// <summary>
/// Helper overload for validate. Used when one model stores another model and
/// calls it's validate. Not meant to be called outside that case.
/// </summary>
bool validate(std::stringstream& msg, const std::string& pathPrefix) const;
bool operator==(const SubscribedSnssai& rhs) const;
bool operator!=(const SubscribedSnssai& rhs) const;
/////////////////////////////////////////////
/// SubscribedSnssai members
/// <summary>
///
/// </summary>
Snssai getSubscribedSnssai() const;
void setSubscribedSnssai(Snssai const& value);
/// <summary>
///
/// </summary>
bool isDefaultIndication() const;
void setDefaultIndication(bool const value);
bool defaultIndicationIsSet() const;
void unsetDefaultIndication();
friend void to_json(nlohmann::json& j, const SubscribedSnssai& o);
friend void from_json(const nlohmann::json& j, SubscribedSnssai& o);
protected:
Snssai m_SubscribedSnssai;
bool m_DefaultIndication;
bool m_DefaultIndicationIsSet;
};
} // namespace model
} // namespace amf
} // namespace oai
#endif /* SubscribedSnssai_H_ */
......@@ -168,7 +168,7 @@ SMContextsCollectionApi::postSmContexts(
// 4xx - client error : not OK
// 5xx - client error : not OK
if (localVarResponse.status_code() >= 400) {
amf_n11_inst->handle_post_sm_context_response_error_400();
// amf_n11_inst->handle_post_sm_context_response_error_400();
throw ApiException(
localVarResponse.status_code(),
utility::conversions::to_string_t(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment