Files
wlan-cloud-owprov/src/StorageService.cpp
2022-11-08 23:08:14 -08:00

438 lines
21 KiB
C++

//
// License type: BSD 3-Clause License
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
//
// Created by Stephane Bourque on 2021-03-04.
// Arilia Wireless Inc.
//
#include "StorageService.h"
#include "RESTObjects/RESTAPI_ProvObjects.h"
#include "framework/utils.h"
#include "fmt/format.h"
namespace OpenWifi {
int Storage::Start() {
poco_information(Logger(),"Starting...");
std::lock_guard Guard(Mutex_);
StorageClass::Start();
EntityDB_ = std::make_unique<OpenWifi::EntityDB>(dbType_,*Pool_, Logger());
PolicyDB_ = std::make_unique<OpenWifi::PolicyDB>(dbType_, *Pool_, Logger());
VenueDB_ = std::make_unique<OpenWifi::VenueDB>(dbType_, *Pool_, Logger());
LocationDB_ = std::make_unique<OpenWifi::LocationDB>(dbType_, *Pool_, Logger());
ContactDB_ = std::make_unique<OpenWifi::ContactDB>(dbType_, *Pool_, Logger());
InventoryDB_ = std::make_unique<OpenWifi::InventoryDB>(dbType_, *Pool_, Logger());
RolesDB_ = std::make_unique<OpenWifi::ManagementRoleDB>(dbType_, *Pool_, Logger());
ConfigurationDB_ = std::make_unique<OpenWifi::ConfigurationDB>(dbType_, *Pool_, Logger());
TagsDictionaryDB_ = std::make_unique<OpenWifi::TagsDictionaryDB>(dbType_, *Pool_, Logger());
TagsObjectDB_ = std::make_unique<OpenWifi::TagsObjectDB>(dbType_, *Pool_, Logger());
MapDB_ = std::make_unique<OpenWifi::MapDB>(dbType_, *Pool_, Logger());
SignupDB_ = std::make_unique<OpenWifi::SignupDB>(dbType_, *Pool_, Logger());
VariablesDB_ = std::make_unique<OpenWifi::VariablesDB>(dbType_, *Pool_, Logger());
OperatorDB_ = std::make_unique<OpenWifi::OperatorDB>(dbType_, *Pool_, Logger());
ServiceClassDB_ = std::make_unique<OpenWifi::ServiceClassDB>(dbType_, *Pool_, Logger());
SubscriberDeviceDB_ = std::make_unique<OpenWifi::SubscriberDeviceDB>(dbType_, *Pool_, Logger());
OpLocationDB_ = std::make_unique<OpenWifi::OpLocationDB>(dbType_, *Pool_, Logger());
OpContactDB_ = std::make_unique<OpenWifi::OpContactDB>(dbType_, *Pool_, Logger());
OverridesDB_ = std::make_unique<OpenWifi::OverridesDB>(dbType_, *Pool_, Logger());
EntityDB_->Create();
PolicyDB_->Create();
VenueDB_->Create();
LocationDB_->Create();
ContactDB_->Create();
InventoryDB_->Create();
RolesDB_->Create();
ConfigurationDB_->Create();
TagsDictionaryDB_->Create();
TagsObjectDB_->Create();
MapDB_->Create();
SignupDB_->Create();
VariablesDB_->Create();
OperatorDB_->Create();
ServiceClassDB_->Create();
SubscriberDeviceDB_->Create();
OpLocationDB_->Create();
OpContactDB_->Create();
OverridesDB_->Create();
ExistFunc_[EntityDB_->Prefix()] = [=](const char *F, std::string &V) -> bool { return EntityDB_->Exists(F,V); };
ExistFunc_[PolicyDB_->Prefix()] = [=](const char *F, std::string &V) -> bool { return PolicyDB_->Exists(F,V); };
ExistFunc_[VenueDB_->Prefix()] = [=](const char *F, std::string &V) -> bool { return VenueDB_->Exists(F,V); };
ExistFunc_[ContactDB_->Prefix()] = [=](const char *F, std::string &V) -> bool { return ContactDB_->Exists(F,V); };
ExistFunc_[InventoryDB_->Prefix()] = [=](const char *F, std::string &V) -> bool { return InventoryDB_->Exists(F,V); };
ExistFunc_[ConfigurationDB_->Prefix()] = [=](const char *F, std::string &V) -> bool { return ConfigurationDB_->Exists(F,V); };
ExistFunc_[LocationDB_->Prefix()] = [=](const char *F, std::string &V) -> bool { return LocationDB_->Exists(F,V); };
ExistFunc_[RolesDB_->Prefix()] = [=](const char *F, std::string &V) -> bool { return RolesDB_->Exists(F,V); };
ExistFunc_[TagsDictionaryDB_->Prefix()] = [=](const char *F, std::string &V) ->bool { return TagsDictionaryDB_->Exists(F,V); };
ExistFunc_[TagsObjectDB_->Prefix()] = [=](const char *F, std::string &V) ->bool { return TagsObjectDB_->Exists(F,V); };
ExistFunc_[MapDB_->Prefix()] = [=](const char *F, std::string &V) ->bool { return MapDB_->Exists(F,V); };
ExistFunc_[SignupDB_->Prefix()] = [=](const char *F, std::string &V) ->bool { return SignupDB_->Exists(F,V); };
ExistFunc_[VariablesDB_->Prefix()] = [=](const char *F, std::string &V) ->bool { return VariablesDB_->Exists(F,V); };
ExistFunc_[OperatorDB_->Prefix()] = [=](const char *F, std::string &V) ->bool { return OperatorDB_->Exists(F,V); };
ExistFunc_[ServiceClassDB_->Prefix()] = [=](const char *F, std::string &V) ->bool { return ServiceClassDB_->Exists(F,V); };
ExistFunc_[SubscriberDeviceDB_->Prefix()] = [=](const char *F, std::string &V) ->bool { return SubscriberDeviceDB_->Exists(F,V); };
ExistFunc_[OpLocationDB_->Prefix()] = [=](const char *F, std::string &V) ->bool { return OpLocationDB_->Exists(F,V); };
ExistFunc_[OpContactDB_->Prefix()] = [=](const char *F, std::string &V) ->bool { return OpContactDB_->Exists(F,V); };
ExistFunc_[OverridesDB_->Prefix()] = [=](const char *F, std::string &V) ->bool { return OverridesDB_->Exists(F,V); };
ExpandFunc_[EntityDB_->Prefix()] = [=](const char *F, std::string &V, std::string &Name, std::string & Description) -> bool { return EntityDB_->GetNameAndDescription(F,V, Name, Description); };
ExpandFunc_[PolicyDB_->Prefix()] = [=](const char *F, std::string &V, std::string &Name, std::string & Description) -> bool { return PolicyDB_->GetNameAndDescription(F,V, Name, Description); };
ExpandFunc_[VenueDB_->Prefix()] = [=](const char *F, std::string &V, std::string &Name, std::string & Description) -> bool { return VenueDB_->GetNameAndDescription(F,V, Name, Description); };
ExpandFunc_[ContactDB_->Prefix()] = [=](const char *F, std::string &V, std::string &Name, std::string & Description) -> bool { return ContactDB_->GetNameAndDescription(F,V, Name, Description); };
ExpandFunc_[InventoryDB_->Prefix()] = [=](const char *F, std::string &V, std::string &Name, std::string & Description) -> bool { return InventoryDB_->GetNameAndDescription(F,V, Name, Description); };
ExpandFunc_[ConfigurationDB_->Prefix()] = [=](const char *F, std::string &V, std::string &Name, std::string & Description) -> bool { return ConfigurationDB_->GetNameAndDescription(F,V, Name, Description); };
ExpandFunc_[LocationDB_->Prefix()] = [=](const char *F, std::string &V, std::string &Name, std::string & Description) -> bool { return LocationDB_->GetNameAndDescription(F,V, Name, Description); };
ExpandFunc_[RolesDB_->Prefix()] = [=](const char *F, std::string &V, std::string &Name, std::string & Description) -> bool { return RolesDB_->GetNameAndDescription(F,V, Name, Description); };
ExpandFunc_[TagsDictionaryDB_->Prefix()] = [=](const char *F, std::string &V, [[maybe_unused]] std::string &Name, [[maybe_unused]] std::string & Description) ->bool { return TagsDictionaryDB_->Exists(F,V); };
ExpandFunc_[TagsObjectDB_->Prefix()] = [=](const char *F, std::string &V, [[maybe_unused]] std::string &Name, [[maybe_unused]] std::string & Description) ->bool { return TagsObjectDB_->Exists(F,V);; };
ExpandFunc_[MapDB_->Prefix()] = [=](const char *F, std::string &V, [[maybe_unused]] std::string &Name, [[maybe_unused]] std::string & Description) ->bool { return MapDB_->Exists(F,V);; };
ExpandFunc_[SignupDB_->Prefix()] = [=](const char *F, std::string &V, [[maybe_unused]] std::string &Name, [[maybe_unused]] std::string & Description) ->bool { return SignupDB_->Exists(F,V);; };
ExpandFunc_[VariablesDB_->Prefix()] = [=](const char *F, std::string &V, [[maybe_unused]] std::string &Name, [[maybe_unused]] std::string & Description) ->bool { return VariablesDB_->Exists(F,V);; };
ExpandFunc_[OperatorDB_->Prefix()] = [=](const char *F, std::string &V, std::string &Name, std::string & Description) ->bool { return OperatorDB_->GetNameAndDescription(F,V, Name, Description); };
ExpandFunc_[ServiceClassDB_->Prefix()] = [=](const char *F, std::string &V, std::string &Name, std::string & Description) ->bool { return ServiceClassDB_->GetNameAndDescription(F,V, Name, Description); };
ExpandFunc_[SubscriberDeviceDB_->Prefix()] = [=](const char *F, std::string &V, std::string &Name, std::string & Description) ->bool { return SubscriberDeviceDB_->GetNameAndDescription(F,V, Name, Description); };
ExpandFunc_[OpLocationDB_->Prefix()] = [=](const char *F, std::string &V, std::string &Name, std::string & Description) ->bool { return OpLocationDB_->GetNameAndDescription(F,V, Name, Description); };
ExpandFunc_[OpContactDB_->Prefix()] = [=](const char *F, std::string &V, std::string &Name, std::string & Description) ->bool { return OpContactDB_->GetNameAndDescription(F,V, Name, Description); };
ExpandFunc_[OverridesDB_->Prefix()] = [=]( [[maybe_unused]] const char *F, [[maybe_unused]] std::string &V, [[maybe_unused]] std::string &Name, [[maybe_unused]] std::string & Description) ->bool { return false; };
InventoryDB_->InitializeSerialCache();
ConsistencyCheck();
InitializeSystemDBs();
TimerCallback_ = std::make_unique<Poco::TimerCallback<Storage>>(*this,&Storage::onTimer);
Timer_.setStartInterval( 20 * 1000); // first run in 20 seconds
Timer_.setPeriodicInterval(1 * 60 * 60 * 1000); // 1 hours
Timer_.start(*TimerCallback_);
return 0;
}
void Storage::onTimer([[maybe_unused]] Poco::Timer &timer) {
Utils::SetThreadName("strg-janitor");
}
void Storage::Stop() {
poco_information(Logger(),"Stopping...");
Timer_.stop();
poco_information(Logger(),"Stopped...");
}
bool Storage::Validate(const Poco::URI::QueryParameters &P, RESTAPI::Errors::msg &Error) {
for(const auto &i:P) {
auto uuid_parts = Utils::Split(i.second,':');
if(uuid_parts.size()==2) {
auto F = ExistFunc_.find(uuid_parts[0]);
if(F!=ExistFunc_.end()) {
if(!F->second("id", uuid_parts[1])) {
Error = RESTAPI::Errors::UnknownId;
return false;
}
}
}
}
return true;
}
bool Storage::ValidateSingle(const std::string &P, std::string & Error) {
auto uuid_parts = Utils::Split(P,':');
if(uuid_parts.size()==2) {
auto F = ExistFunc_.find(uuid_parts[0]);
if(F!=ExistFunc_.end()) {
if(!F->second("id", uuid_parts[1])) {
Error = "Unknown " + F->first + " UUID:" + uuid_parts[1] ;
return false;
} else {
return true;
}
} else {
return false;
}
} else {
return false;
}
}
bool Storage::Validate(const Types::StringVec &P, std::string &Error) {
for(const auto &i:P) {
if(!ValidateSingle(i,Error))
return false;
}
if(Error.empty())
return true;
return false;
}
bool Storage::Validate(const std::string &P) {
std::string Error;
return ValidateSingle(P,Error);
}
bool Storage::ExpandInUse(const Types::StringVec &UUIDs, ExpandedListMap &Map, std::vector<std::string> & Errors) {
for(const auto &i:UUIDs) {
auto uuid_parts = Utils::Split(i,':');
if(uuid_parts.size()==2) {
auto F = ExpandFunc_.find(uuid_parts[0]);
if(F!=ExpandFunc_.end()) {
std::string Name, Description;
if(!F->second("id", uuid_parts[1], Name, Description)) {
Errors.push_back(i);
} else {
auto Hint = Map.find(uuid_parts[0]);
ProvObjects::ExpandedUseEntry X{.uuid=uuid_parts[1],.name=Name, .description=Description};
if(Hint==Map.end()) {
ProvObjects::ExpandedUseEntryList L;
L.type = uuid_parts[0];
L.entries.push_back(X);
Map[uuid_parts[0]] = L;
} else {
Hint->second.entries.push_back(X);
}
}
}
}
}
return true;
}
void Storage::ConsistencyCheck() {
// check that all inventory in venues and entities actually exists, if not, fix it.
auto FixVenueDevices = [&](const ProvObjects::Venue &V) -> bool {
Types::UUIDvec_t NewDevices;
bool modified=false;
for(const auto &device:V.devices) {
ProvObjects::InventoryTag T;
if(InventoryDB().GetRecord("id", device, T)) {
NewDevices.emplace_back(device);
} else {
modified=true;
}
}
ProvObjects::Venue NewVenue = V;
if(V.deviceRules.rrm=="yes") {
NewVenue.deviceRules.rrm="inherit";
modified=true;
}
if(modified) {
poco_warning(Logger(),fmt::format(" fixing venue: {}", V.info.name));
NewVenue.devices = NewDevices;
VenueDB().UpdateRecord("id", V.info.id, NewVenue);
}
return true;
};
auto FixEntity = [&](const ProvObjects::Entity &E) -> bool {
Types::UUIDvec_t NewDevices;
bool Modified=false;
for(const auto &device:E.devices) {
ProvObjects::InventoryTag T;
if(InventoryDB().GetRecord("id", device, T)) {
NewDevices.emplace_back(device);
} else {
Modified=true;
}
}
Types::UUIDvec_t NewContacts;
for(const auto &contact:E.contacts) {
ProvObjects::Contact C;
if(ContactDB().GetRecord("id", contact, C)) {
NewContacts.emplace_back(contact);
} else {
Modified=true;
}
}
Types::UUIDvec_t NewLocations;
for(const auto &location:E.locations) {
ProvObjects::Location L;
if(LocationDB().GetRecord("id", location, L)) {
NewLocations.emplace_back(location);
} else {
Modified=true;
}
}
Types::UUIDvec_t NewVenues;
for(const auto &venue:E.venues) {
ProvObjects::Venue V;
if(VenueDB().GetRecord("id", venue, V)) {
NewVenues.emplace_back(venue);
} else {
Modified=true;
}
}
Types::UUIDvec_t NewVariables;
for(const auto &variable:E.variables) {
ProvObjects::VariableBlock V;
if(VariablesDB().GetRecord("id", variable, V)) {
NewVariables.emplace_back(variable);
} else {
Modified=true;
}
}
ProvObjects::Entity NewEntity = E;
if(E.deviceRules.rrm=="yes") {
NewEntity.deviceRules.rrm="inherit";
Modified=true;
}
if(Modified)
{
poco_warning(Logger(),fmt::format(" fixing entity: {}",E.info.name));
NewEntity.devices = NewDevices;
NewEntity.contacts = NewContacts;
NewEntity.locations = NewLocations;
NewEntity.venues = NewVenues;
NewEntity.variables = NewVariables;
EntityDB().UpdateRecord("id", E.info.id, NewEntity);
}
return true;
};
auto FixInventory = [&](const ProvObjects::InventoryTag &T) -> bool {
// check the venue/entity for this device.
ProvObjects::InventoryTag NewTag{T};
bool modified=false;
if(!T.venue.empty() && !VenueDB().Exists("id",T.venue)) {
NewTag.venue.clear();
modified=true;
}
if(!T.entity.empty() && !EntityDB().Exists("id",T.entity)) {
NewTag.entity.clear();
modified=true;
}
if(!T.location.empty() && !LocationDB().Exists("id",T.location)) {
NewTag.location.clear();
modified=true;
}
if(!T.contact.empty() && !ContactDB().Exists("id",T.contact)) {
NewTag.contact.clear();
modified=true;
}
if(T.deviceRules.rrm=="yes") {
NewTag.deviceRules.rrm = "inherit";
modified=true;
}
if(modified) {
poco_warning(Logger(),fmt::format(" fixing entity: {}",T.info.name));
InventoryDB().UpdateRecord("id", T.info.id, NewTag);
}
return true;
};
auto FixConfiguration = [&](const ProvObjects::DeviceConfiguration &C) -> bool {
ProvObjects::DeviceConfiguration NewConfig{C};
bool modified = false;
if (C.deviceRules.rrm == "yes") {
NewConfig.deviceRules.rrm = "inherit";
modified = true;
}
if (modified) {
poco_warning(Logger(),fmt::format(" fixing configuration: {}", C.info.name));
ConfigurationDB().UpdateRecord("id", C.info.id, NewConfig);
}
return true;
};
auto FixOperator = [&](const ProvObjects::Operator &O) -> bool {
ProvObjects::Operator NewOp{O};
bool modified = false;
if (O.deviceRules.rrm == "yes") {
NewOp.deviceRules.rrm = "inherit";
modified = true;
}
if (modified) {
poco_warning(Logger(),fmt::format(" fixing operator: {}", O.info.name));
OperatorDB().UpdateRecord("id", O.info.id, NewOp);
}
return true;
};
auto FixSubscriber = [&](const ProvObjects::SubscriberDevice &O) -> bool {
ProvObjects::SubscriberDevice NewSub{O};
bool modified = false;
if (O.deviceRules.rrm == "yes") {
NewSub.deviceRules.rrm = "inherit";
modified = true;
}
if (modified) {
poco_warning(Logger(),fmt::format(" fixing subscriber: {}", O.info.name));
SubscriberDeviceDB().UpdateRecord("id", O.info.id, NewSub);
}
return true;
};
poco_information(Logger(),"Checking DB consistency: venues");
VenueDB().Iterate(FixVenueDevices);
poco_information(Logger(),"Checking DB consistency: entities");
EntityDB().Iterate(FixEntity);
poco_information(Logger(),"Checking DB consistency: inventory");
InventoryDB().Iterate(FixInventory);
poco_information(Logger(),"Checking DB consistency: configurations");
ConfigurationDB().Iterate(FixConfiguration);
poco_information(Logger(),"Checking DB consistency: operators");
OperatorDB().Iterate(FixOperator);
poco_information(Logger(),"Checking DB consistency: subscribers");
SubscriberDeviceDB().Iterate(FixSubscriber);
}
void Storage::InitializeSystemDBs() {
if(!EntityDB().Exists("id",EntityDB::RootUUID())) {
ProvObjects::Entity Root;
Root.info.id = EntityDB::RootUUID();
Root.info.name = "Top Entity";
Root.info.created = Root.info.modified = Utils::Now();
Root.deviceRules.rrm = "off";
EntityDB().CreateRecord(Root);
}
auto OperatorCount = OperatorDB().Count();
if(OperatorCount==0) {
ProvObjects::Operator DefOp;
DefOp.info.id = MicroServiceCreateUUID();
DefOp.info.name = "Default Operator";
DefOp.defaultOperator = true;
DefOp.info.created = DefOp.info.modified = Utils::Now();
DefOp.deviceRules.rrm = "inherit";
OperatorDB_->CreateRecord(DefOp);
ProvObjects::ServiceClass DefSer;
DefSer.info.id = MicroServiceCreateUUID();
DefSer.info.name = "Default Service Class";
DefSer.defaultService = true;
DefSer.info.created = DefSer.info.modified = Utils::Now();
DefSer.operatorId = DefOp.info.id;
DefSer.period = "monthly";
DefSer.billingCode = "basic";
DefSer.currency = "USD";
DefSer.cost = 0.0;
ServiceClassDB_->CreateRecord(DefSer);
}
}
}
// namespace