1
+ // Copyright (c) 2015-2024, Lawrence Livermore National Security, LLC.
2
+ // See top-level LICENSE file for details.
3
+
4
+ // LCNodeInfo.cpp
5
+ // A Caliper service that collects information from the /etc/node_info.json
6
+ // file on TOSS4 systems
7
+
8
+ #include " caliper/CaliperService.h"
9
+
10
+ #include " Services.h"
11
+
12
+ #include " caliper/Caliper.h"
13
+
14
+ #include " caliper/common/Log.h"
15
+ #include " caliper/common/RuntimeConfig.h"
16
+ #include " caliper/common/StringConverter.h"
17
+ #include " caliper/common/Variant.h"
18
+
19
+ #include < fstream>
20
+ #include < iostream>
21
+ #include < string>
22
+ #include < vector>
23
+
24
+ using namespace cali ;
25
+
26
+ namespace
27
+ {
28
+
29
+ const char * lcnodeinfo_service_spec = R"json(
30
+ {
31
+ "name" : "lcnodeinfo",
32
+ "description" : "Read node information from /etc/node_info.json",
33
+ "config" :
34
+ [
35
+ { "name" : "filename",
36
+ "description" : "The JSON file to read",
37
+ "type" : "string",
38
+ "value" : "/etc/node_info.json"
39
+ },
40
+ { "name" : "keys",
41
+ "description" : "List of JSON dict keys to read",
42
+ "type" : "string",
43
+ "value" : "host.name,host.cluster,host.os"
44
+ }
45
+ ]
46
+ }
47
+ )json" ;
48
+
49
+ std::pair<bool , StringConverter>
50
+ find_key (const std::vector<std::string>& path, const std::map<std::string, StringConverter>& dict)
51
+ {
52
+ if (path.empty ())
53
+ return std::make_pair (false , StringConverter ());
54
+
55
+ auto it = dict.find (path.front ());
56
+ if (it == dict.end ())
57
+ return std::make_pair (false , StringConverter ());
58
+
59
+ if (path.size () == 1 )
60
+ return std::make_pair (true , it->second );
61
+
62
+ StringConverter ret = it->second ;
63
+ for (auto path_it = path.begin ()+1 ; path_it != path.end (); ++path_it) {
64
+ auto sub_dict = ret.rec_dict ();
65
+ it = sub_dict.find (*path_it);
66
+ if (it == sub_dict.end ())
67
+ return std::make_pair (false , StringConverter ());
68
+ ret = it->second ;
69
+ }
70
+
71
+ return std::make_pair (true , ret);
72
+ }
73
+
74
+ void
75
+ add_entry (Caliper* c, Channel* channel, const std::string& name, const std::string& val)
76
+ {
77
+ Attribute attr =
78
+ c->create_attribute (name.c_str (), CALI_TYPE_STRING,
79
+ CALI_ATTR_UNALIGNED | CALI_ATTR_GLOBAL | CALI_ATTR_SKIP_EVENTS);
80
+
81
+ c->set (channel, attr, Variant (CALI_TYPE_STRING, val.data (), val.size ()));
82
+ }
83
+
84
+ unsigned
85
+ add_entries (Caliper* c, Channel* channel, const std::string& key, const StringConverter& val)
86
+ {
87
+ unsigned num_entries = 0 ;
88
+
89
+ std::string name (" nodeinfo." );
90
+ name.append (key);
91
+
92
+ bool is_dict = false ;
93
+ auto dict = val.rec_dict (&is_dict);
94
+
95
+ if (is_dict) {
96
+ std::string prefix = name.append (" ." );
97
+ for (const auto & p : dict) {
98
+ name = prefix;
99
+ name.append (p.first );
100
+ add_entry (c, channel, name, p.second .to_string ());
101
+ ++num_entries;
102
+ }
103
+ } else {
104
+ add_entry (c, channel, name, val.to_string ());
105
+ ++num_entries;
106
+ }
107
+
108
+ return num_entries;
109
+ }
110
+
111
+ void
112
+ read_nodeinfo (Caliper* c, Channel* channel, const std::vector<std::string>& keys, const std::string& filename)
113
+ {
114
+ std::ifstream is (filename.c_str (), std::ios::ate);
115
+
116
+ if (!is) {
117
+ Log (1 ).stream () << channel->name () << " : lcnodeinfo: Cannot open "
118
+ << filename << " , quitting\n " ;
119
+ return ;
120
+ }
121
+
122
+ auto size = is.tellg ();
123
+ std::string str (size, ' \0 ' );
124
+ is.seekg (0 );
125
+ if (!is.read (&str[0 ], size)) {
126
+ Log (0 ).stream () << channel->name () << " : lcnodeinfo: Cannot read "
127
+ << filename << " , quitting\n " ;
128
+ return ;
129
+ }
130
+
131
+ if (Log::verbosity () >= 2 ) {
132
+ Log (2 ).stream () << channel->name () << " : lcnodeinfo: "
133
+ << size << " bytes read from " << filename << std::endl;
134
+ }
135
+
136
+ bool ok = false ;
137
+ auto top = StringConverter (str).rec_dict (&ok);
138
+
139
+ if (!ok) {
140
+ Log (0 ).stream () << channel->name () << " : lcnodeinfo: Cannot parse top-level dict in "
141
+ << filename << " , quitting\n " ;
142
+ return ;
143
+ }
144
+
145
+ unsigned num_entries = 0 ;
146
+
147
+ for (const std::string& key : keys) {
148
+ std::vector<std::string> path = StringConverter (key).to_stringlist (" ." );
149
+ auto ret = find_key (path, top);
150
+ if (ret.first )
151
+ num_entries += add_entries (c, channel, key, ret.second );
152
+ else
153
+ Log (1 ).stream () << channel->name () << " : lcnodeinfo: Key "
154
+ << key << " not found\n " ;
155
+ }
156
+
157
+ Log (1 ).stream () << channel->name () << " : lcnodeinfo: Added "
158
+ << num_entries << " entries\n " ;
159
+ }
160
+
161
+ void
162
+ lcnodeinfo_register (Caliper*, Channel* channel)
163
+ {
164
+ ConfigSet cfg =
165
+ services::init_config_from_spec (channel->config (), lcnodeinfo_service_spec);
166
+
167
+ std::vector<std::string> keys = cfg.get (" keys" ).to_stringlist ();
168
+ std::string filename = cfg.get (" filename" ).to_string ();
169
+
170
+ if (keys.empty ()) {
171
+ Log (1 ).stream () << channel->name () << " : lcnodeinfo: "
172
+ << " No keys provided, quitting\n " ;
173
+ return ;
174
+ }
175
+
176
+ channel->events ().post_init_evt .connect ([keys,filename](Caliper* c, Channel* channel){
177
+ read_nodeinfo (c, channel, keys, filename);
178
+ });
179
+ }
180
+
181
+ }
182
+
183
+ namespace cali
184
+ {
185
+
186
+ CaliperService lcnodeinfo_service { ::lcnodeinfo_service_spec, ::lcnodeinfo_register };
187
+
188
+ }
0 commit comments