blob: fd36eef17ba510ec5328cc250d624510768ca118 [file] [log] [blame]
Matt Spinler852db672019-03-06 13:46:14 -06001#include "association_manager.hpp"
2
Matt Spinler99e66a02019-03-06 14:11:22 -06003#include <filesystem>
4#include <fstream>
5#include <nlohmann/json.hpp>
6#include <phosphor-logging/log.hpp>
7
Matt Spinler852db672019-03-06 13:46:14 -06008namespace phosphor
9{
10namespace inventory
11{
12namespace manager
13{
14namespace associations
15{
Matt Spinler99e66a02019-03-06 14:11:22 -060016using namespace phosphor::logging;
17using sdbusplus::exception::SdBusError;
Matt Spinler852db672019-03-06 13:46:14 -060018
19Manager::Manager(sdbusplus::bus::bus& bus, const std::string& jsonPath) :
20 _bus(bus), _jsonFile(jsonPath)
21{
Matt Spinler99e66a02019-03-06 14:11:22 -060022 load();
23}
24
25/**
26 * @brief Throws an exception if 'num' is zero. Used for JSON
27 * sanity checking.
28 *
29 * @param[in] num - the number to check
30 */
31void throwIfZero(int num)
32{
33 if (!num)
34 {
35 throw std::invalid_argument("Invalid empty field in JSON");
36 }
37}
38
39void Manager::load()
40{
41 // Load the contents of _jsonFile into _associations and throw
42 // an exception on any problem.
43
44 std::ifstream file{_jsonFile};
45
46 auto json = nlohmann::json::parse(file, nullptr, true);
47
48 const std::string root{INVENTORY_ROOT};
49
50 for (const auto& jsonAssoc : json)
51 {
52 // Only add the slash if necessary
53 std::string path = jsonAssoc.at("path");
54 throwIfZero(path.size());
55 if (path.front() != '/')
56 {
57 path = root + "/" + path;
58 }
59 else
60 {
61 path = root + path;
62 }
63
64 auto& assocEndpoints = _associations[path];
65
66 for (const auto& endpoint : jsonAssoc.at("endpoints"))
67 {
68 std::string ftype = endpoint.at("types").at("fType");
69 std::string rtype = endpoint.at("types").at("rType");
70 throwIfZero(ftype.size());
71 throwIfZero(rtype.size());
72 Types types{std::move(ftype), std::move(rtype)};
73
74 Paths paths = endpoint.at("paths");
75 throwIfZero(paths.size());
76 assocEndpoints.emplace_back(std::move(types), std::move(paths));
77 }
78 }
Matt Spinler852db672019-03-06 13:46:14 -060079}
80
81void Manager::createAssociations(const std::string& objectPath)
82{
Matt Spinlerc47ca582019-03-06 14:37:42 -060083 auto endpoints = _associations.find(objectPath);
84 if (endpoints == _associations.end())
85 {
86 return;
87 }
88
89 if (std::find(_handled.begin(), _handled.end(), objectPath) !=
90 _handled.end())
91 {
92 return;
93 }
94
95 _handled.push_back(objectPath);
96
97 for (const auto& endpoint : endpoints->second)
98 {
99 const auto& types = std::get<typesPos>(endpoint);
100 const auto& paths = std::get<pathsPos>(endpoint);
101
102 for (const auto& endpointPath : paths)
103 {
104 const auto& forwardType = std::get<forwardTypePos>(types);
105 const auto& reverseType = std::get<reverseTypePos>(types);
106
107 createAssociation(objectPath, forwardType, endpointPath,
108 reverseType);
109 }
110 }
111}
112
113void Manager::createAssociation(const std::string& forwardPath,
114 const std::string& forwardType,
115 const std::string& reversePath,
116 const std::string& reverseType)
117{
118 auto object = _associationIfaces.find(forwardPath);
119 if (object == _associationIfaces.end())
120 {
121 auto a = std::make_unique<AssociationObject>(_bus, forwardPath.c_str(),
122 true);
123
124 using AssociationProperty =
125 std::vector<std::tuple<std::string, std::string, std::string>>;
126 AssociationProperty prop;
127
128 prop.emplace_back(forwardType, reverseType, reversePath);
129 a->associations(std::move(prop));
130 a->emit_object_added();
131 _associationIfaces.emplace(forwardPath, std::move(a));
132 }
133 else
134 {
135 // Interface exists, just update the property
136 auto prop = object->second->associations();
137 prop.emplace_back(forwardType, reverseType, reversePath);
138 object->second->associations(std::move(prop));
139 }
Matt Spinler852db672019-03-06 13:46:14 -0600140}
141} // namespace associations
142} // namespace manager
143} // namespace inventory
144} // namespace phosphor