// // 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(dbType_,*Pool_, Logger()); PolicyDB_ = std::make_unique(dbType_, *Pool_, Logger()); VenueDB_ = std::make_unique(dbType_, *Pool_, Logger()); LocationDB_ = std::make_unique(dbType_, *Pool_, Logger()); ContactDB_ = std::make_unique(dbType_, *Pool_, Logger()); InventoryDB_ = std::make_unique(dbType_, *Pool_, Logger()); RolesDB_ = std::make_unique(dbType_, *Pool_, Logger()); ConfigurationDB_ = std::make_unique(dbType_, *Pool_, Logger()); TagsDictionaryDB_ = std::make_unique(dbType_, *Pool_, Logger()); TagsObjectDB_ = std::make_unique(dbType_, *Pool_, Logger()); MapDB_ = std::make_unique(dbType_, *Pool_, Logger()); SignupDB_ = std::make_unique(dbType_, *Pool_, Logger()); VariablesDB_ = std::make_unique(dbType_, *Pool_, Logger()); OperatorDB_ = std::make_unique(dbType_, *Pool_, Logger()); ServiceClassDB_ = std::make_unique(dbType_, *Pool_, Logger()); SubscriberDeviceDB_ = std::make_unique(dbType_, *Pool_, Logger()); OpLocationDB_ = std::make_unique(dbType_, *Pool_, Logger()); OpContactDB_ = std::make_unique(dbType_, *Pool_, Logger()); OverridesDB_ = std::make_unique(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>(*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 & 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