blob: 73af6fc027e750f2a7d92708621b60a217e6c199 [file] [log] [blame]
Václav Kubernát96344a12018-05-28 16:33:39 +02001/*
2 * Copyright (C) 2018 CESNET, https://photonics.cesnet.cz/
3 * Copyright (C) 2018 FIT CVUT, https://fit.cvut.cz/
4 *
5 * Written by Václav Kubernát <kubervac@fit.cvut.cz>
6 *
7*/
8
Václav Kubernát5b8a8f32020-05-20 00:57:22 +02009#include <boost/algorithm/string/predicate.hpp>
Václav Kubernát509ce652019-05-29 19:46:44 +020010#include <boost/mpl/for_each.hpp>
Václav Kubernátb61336d2018-05-28 17:35:03 +020011#include <iostream>
Václav Kubernát9cfcd872020-02-18 12:34:02 +010012#include <sstream>
Václav Kubernáte066fe22022-04-06 00:32:26 +020013#include "UniqueResource.hpp"
Václav Kubernát6415b822018-08-22 17:40:01 +020014#include "datastore_access.hpp"
Václav Kubernát96344a12018-05-28 16:33:39 +020015#include "interpreter.hpp"
Václav Kubernát509ce652019-05-29 19:46:44 +020016#include "utils.hpp"
Václav Kubernát96344a12018-05-28 16:33:39 +020017
Václav Kubernát4c3d22f2020-06-12 16:05:01 +020018struct pathToStringVisitor : boost::static_visitor<std::string> {
19 std::string operator()(const module_& path) const
20 {
21 using namespace std::string_literals;
22 return "/"s + boost::get<module_>(path).m_name + ":*";
23 }
24 std::string operator()(const schemaPath_& path) const
25 {
26 return pathToSchemaString(path, Prefixes::WhenNeeded);
27 }
28 std::string operator()(const dataPath_& path) const
29 {
30 return pathToDataString(path, Prefixes::WhenNeeded);
31 }
32};
33
Václav Kubernáte7248b22020-06-26 15:38:59 +020034namespace {
35void printTree(const DatastoreAccess::Tree tree)
36{
37 for (auto it = tree.begin(); it != tree.end(); it++) {
38 auto [path, value] = *it;
39 if (value.type() == typeid(special_) && boost::get<special_>(value).m_value == SpecialValue::LeafList) {
40 auto leafListPrefix = path;
41 std::cout << path << " = " << leafDataToString(value) << std::endl;
42
43 while (it + 1 != tree.end() && boost::starts_with((it + 1)->first, leafListPrefix)) {
44 ++it;
45 std::cout << stripLeafListValueFromPath(it->first) << " = " << leafDataToString(it->second) << std::endl;
46 }
47 } else {
48 std::cout << path << " = " << leafDataToString(value) << std::endl;
49 }
50 }
51}
52}
53
Václav Kubernát4c3d22f2020-06-12 16:05:01 +020054template <typename PathType>
55std::string pathToString(const PathType& path)
56{
57 return boost::apply_visitor(pathToStringVisitor(), path);
58}
59
aleskucera@cesnet.cz87480ef2023-10-27 13:13:59 +020060void Interpreter::checkRpcPath(const dataPath_& commandPath) const
61{
62 if (auto inputPath = m_datastore.inputDatastorePath()) {
63 dataPath_ newPath = realPath(m_parser.currentPath(), commandPath);
64 if (!pathToDataString(newPath, Prefixes::WhenNeeded).starts_with(*inputPath)) {
65 throw std::runtime_error("Can't execute `cd` outside of the `prepare` context.");
66 }
67 }
68}
69
Václav Kubernát812ee282018-08-30 17:10:03 +020070void Interpreter::operator()(const commit_&) const
71{
72 m_datastore.commitChanges();
73}
74
Václav Kubernát6d791432018-10-25 16:00:35 +020075void Interpreter::operator()(const discard_&) const
76{
77 m_datastore.discardChanges();
78}
79
Václav Kubernát07204242018-06-04 18:12:09 +020080void Interpreter::operator()(const set_& set) const
81{
Václav Kubernáteeb38842019-03-20 19:46:05 +010082 auto data = set.m_data;
83
84 // If the user didn't supply a module prefix for identityref, we need to add it ourselves
85 if (data.type() == typeid(identityRef_)) {
86 auto identityRef = boost::get<identityRef_>(data);
87 if (!identityRef.m_prefix) {
88 identityRef.m_prefix = set.m_path.m_nodes.front().m_prefix.value();
89 data = identityRef;
90 }
91 }
Václav Kubernát4c3d22f2020-06-12 16:05:01 +020092 m_datastore.setLeaf(pathToString(toCanonicalPath(set.m_path)), data);
Václav Kubernát07204242018-06-04 18:12:09 +020093}
94
Václav Kubernátb6ff0b62018-08-30 16:14:53 +020095void Interpreter::operator()(const get_& get) const
96{
Václav Kubernáte066fe22022-04-06 00:32:26 +020097 auto targetSwitcher = make_unique_resource([] {}, [this, oldTarget = m_datastore.target()] {
98 m_datastore.setTarget(oldTarget);
99 });
100
101 if (get.m_dsTarget) {
102 m_datastore.setTarget(*get.m_dsTarget);
103 }
104
Václav Kubernát4c3d22f2020-06-12 16:05:01 +0200105 auto items = m_datastore.getItems(pathToString(toCanonicalPath(get.m_path)));
Václav Kubernáte7248b22020-06-26 15:38:59 +0200106 printTree(items);
Václav Kubernátb6ff0b62018-08-30 16:14:53 +0200107}
108
Václav Kubernát96344a12018-05-28 16:33:39 +0200109void Interpreter::operator()(const cd_& cd) const
110{
aleskucera@cesnet.cz87480ef2023-10-27 13:13:59 +0200111 checkRpcPath(cd.m_path);
Václav Kubernát96344a12018-05-28 16:33:39 +0200112 m_parser.changeNode(cd.m_path);
113}
114
Václav Kubernátb61336d2018-05-28 17:35:03 +0200115void Interpreter::operator()(const create_& create) const
116{
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200117 m_datastore.createItem(pathToString(toCanonicalPath(create.m_path)));
Václav Kubernátb61336d2018-05-28 17:35:03 +0200118}
119
120void Interpreter::operator()(const delete_& delet) const
121{
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200122 m_datastore.deleteItem(pathToString(toCanonicalPath(delet.m_path)));
Václav Kubernátb61336d2018-05-28 17:35:03 +0200123}
124
Václav Kubernát11afac72018-07-18 14:59:53 +0200125void Interpreter::operator()(const ls_& ls) const
126{
127 std::cout << "Possible nodes:" << std::endl;
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200128 auto recursion{Recursion::NonRecursive};
129 for (auto it : ls.m_options) {
Václav Kubernát3a433232020-07-08 17:52:50 +0200130 if (it == LsOption::Recursive) {
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200131 recursion = Recursion::Recursive;
Václav Kubernát3a433232020-07-08 17:52:50 +0200132 }
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200133 }
Václav Kubernát11afac72018-07-18 14:59:53 +0200134
Václav Kubernát4c3d22f2020-06-12 16:05:01 +0200135 auto toPrint = m_datastore.schema()->availableNodes(toCanonicalPath(ls.m_path), recursion);
Václav Kubernát82086872020-04-29 01:09:50 +0200136
Václav Kubernát95b08872020-04-28 01:04:17 +0200137 for (const auto& it : toPrint) {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100138 std::cout << (it.first ? *it.first + ":" : "") + it.second << std::endl;
Václav Kubernát95b08872020-04-28 01:04:17 +0200139 }
Václav Kubernát11afac72018-07-18 14:59:53 +0200140}
141
Václav Kubernát7160a132020-04-03 02:11:01 +0200142void Interpreter::operator()(const copy_& copy) const
143{
144 m_datastore.copyConfig(copy.m_source, copy.m_destination);
145}
146
Václav Kubernát9cfcd872020-02-18 12:34:02 +0100147std::string Interpreter::buildTypeInfo(const std::string& path) const
148{
149 std::ostringstream ss;
Václav Kubernát1ae24f42020-12-01 02:32:04 +0100150 std::string typeDescription;
Václav Kubernát9cfcd872020-02-18 12:34:02 +0100151 switch (m_datastore.schema()->nodeType(path)) {
152 case yang::NodeTypes::Container:
153 ss << "container";
154 break;
155 case yang::NodeTypes::PresenceContainer:
156 ss << "presence container";
157 break;
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100158 case yang::NodeTypes::Leaf: {
Václav Kubernát9cfcd872020-02-18 12:34:02 +0100159 auto leafType = m_datastore.schema()->leafType(path);
160 auto typedefName = m_datastore.schema()->leafTypeName(path);
161 std::string baseTypeStr;
Václav Kubernát13b23d72020-04-16 21:49:51 +0200162 if (std::holds_alternative<yang::LeafRef>(leafType.m_type)) {
Václav Kubernát9cfcd872020-02-18 12:34:02 +0100163 ss << "-> ";
164 ss << m_datastore.schema()->leafrefPath(path) << " ";
Václav Kubernát13b23d72020-04-16 21:49:51 +0200165 baseTypeStr = leafDataTypeToString(std::get<yang::LeafRef>(leafType.m_type).m_targetType->m_type);
Václav Kubernát9cfcd872020-02-18 12:34:02 +0100166 } else {
Václav Kubernát13b23d72020-04-16 21:49:51 +0200167 baseTypeStr = leafDataTypeToString(leafType.m_type);
Václav Kubernát9cfcd872020-02-18 12:34:02 +0100168 }
169
170 if (typedefName) {
171 ss << *typedefName << " (" << baseTypeStr << ")";
172 } else {
173 ss << baseTypeStr;
174 }
175
Václav Kubernát13b23d72020-04-16 21:49:51 +0200176 if (leafType.m_units) {
177 ss << " [" + *leafType.m_units + "]";
Václav Kubernát9cfcd872020-02-18 12:34:02 +0100178 }
179
Václav Kubernát1ae24f42020-12-01 02:32:04 +0100180 if (leafType.m_description) {
181 typeDescription = "\nType description: " + *leafType.m_description;
182 }
183
Václav Kubernát9cfcd872020-02-18 12:34:02 +0100184 if (m_datastore.schema()->leafIsKey(path)) {
185 ss << " (key)";
186 }
Václav Kubernátb1a75c62020-04-21 15:20:16 +0200187
188 if (auto defaultValue = m_datastore.schema()->defaultValue(path)) {
189 ss << " default: " << leafDataToString(*defaultValue);
190 }
Václav Kubernát9cfcd872020-02-18 12:34:02 +0100191 break;
192 }
193 case yang::NodeTypes::List:
194 ss << "list";
195 break;
Václav Kubernáte7248b22020-06-26 15:38:59 +0200196 case yang::NodeTypes::Rpc:
197 ss << "RPC";
198 break;
Václav Kubernátaaafeae2020-05-05 15:41:45 +0200199 case yang::NodeTypes::LeafList:
Václav Kubernát160e5a22020-12-01 01:11:28 +0100200 ss << "leaf-list";
201 break;
202 case yang::NodeTypes::Action:
203 ss << "action";
204 break;
205 case yang::NodeTypes::AnyXml:
206 throw std::logic_error("Sorry, anyxml isn't supported yet.");
Václav Kubernátaaafeae2020-05-05 15:41:45 +0200207 case yang::NodeTypes::Notification:
Václav Kubernát160e5a22020-12-01 01:11:28 +0100208 throw std::logic_error("Sorry, notifications aren't supported yet.");
Václav Kubernát9cfcd872020-02-18 12:34:02 +0100209 }
Václav Kubernát0599e9f2020-04-21 09:51:33 +0200210
211 if (!m_datastore.schema()->isConfig(path)) {
Václav Kubernát1ae24f42020-12-01 02:32:04 +0100212 ss << " (ro)\n";
Václav Kubernát0599e9f2020-04-21 09:51:33 +0200213 }
214
Václav Kubernáta1c4c9e2020-04-22 00:37:52 +0200215 auto status = m_datastore.schema()->status(path);
216 auto statusStr = status == yang::Status::Deprecated ? " (deprecated)" :
217 status == yang::Status::Obsolete ? " (obsolete)" :
218 "";
219
Václav Kubernát1ae24f42020-12-01 02:32:04 +0100220 ss << statusStr;
221
Václav Kubernát9cfcd872020-02-18 12:34:02 +0100222 if (auto description = m_datastore.schema()->description(path)) {
Václav Kubernát1ae24f42020-12-01 02:32:04 +0100223 ss << std::endl << *description << std::endl;
Václav Kubernát9cfcd872020-02-18 12:34:02 +0100224 }
Václav Kubernát1ae24f42020-12-01 02:32:04 +0100225
226 ss << typeDescription;
227 return ss.str();
228}
229
230void Interpreter::operator()(const describe_& describe) const
231{
232 auto fullPath = pathToString(toCanonicalPath(describe.m_path));
233
234 std::cout << pathToString(describe.m_path) << ": " << buildTypeInfo(fullPath) << std::endl;
Václav Kubernát9cfcd872020-02-18 12:34:02 +0100235}
236
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200237void Interpreter::operator()(const move_& move) const
238{
239 m_datastore.moveItem(pathToDataString(move.m_source, Prefixes::WhenNeeded), move.m_destination);
240}
241
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200242void Interpreter::operator()(const dump_& dump) const
243{
244 std::cout << m_datastore.dump(dump.m_format) << "\n";
245}
246
Václav Kubernátaa4250a2020-07-22 00:02:23 +0200247void Interpreter::operator()(const prepare_& prepare) const
Václav Kubernáte7248b22020-06-26 15:38:59 +0200248{
Václav Kubernátb3960f82020-12-01 03:21:48 +0100249 m_datastore.initiate(pathToString(toCanonicalPath(prepare.m_path)));
Václav Kubernátaa4250a2020-07-22 00:02:23 +0200250 m_parser.changeNode(prepare.m_path);
Václav Kubernáte7248b22020-06-26 15:38:59 +0200251}
252
Václav Kubernátd8408e02020-12-02 05:13:27 +0100253void Interpreter::operator()(const exec_& exec) const
Václav Kubernáte7248b22020-06-26 15:38:59 +0200254{
255 m_parser.changeNode({Scope::Absolute, {}});
Václav Kubernátd8408e02020-12-02 05:13:27 +0100256 if (exec.m_path) {
257 m_datastore.initiate(pathToString(toCanonicalPath(*exec.m_path)));
258 }
Václav Kubernátaa4250a2020-07-22 00:02:23 +0200259 auto output = m_datastore.execute();
260 std::cout << "RPC/action output:\n";
Václav Kubernáte7248b22020-06-26 15:38:59 +0200261 printTree(output);
262}
263
264void Interpreter::operator()(const cancel_&) const
265{
266 m_parser.changeNode({Scope::Absolute, {}});
Václav Kubernátaa4250a2020-07-22 00:02:23 +0200267 m_datastore.cancel();
Václav Kubernáte7248b22020-06-26 15:38:59 +0200268}
269
Václav Kubernát162165e2021-02-22 09:46:53 +0100270void Interpreter::operator()(const switch_& switch_cmd) const
271{
272 m_datastore.setTarget(switch_cmd.m_target);
273}
274
Václav Kubernát054cc992019-02-21 14:23:52 +0100275struct commandLongHelpVisitor : boost::static_visitor<const char*> {
276 template <typename T>
277 auto constexpr operator()(boost::type<T>) const
278 {
279 return T::longHelp;
280 }
281};
282
283struct commandShortHelpVisitor : boost::static_visitor<const char*> {
284 template <typename T>
285 auto constexpr operator()(boost::type<T>) const
286 {
287 return T::shortHelp;
288 }
289};
290
291void Interpreter::operator()(const help_& help) const
292{
Václav Kubernát3a433232020-07-08 17:52:50 +0200293 if (help.m_cmd) {
Václav Kubernát054cc992019-02-21 14:23:52 +0100294 std::cout << boost::apply_visitor(commandLongHelpVisitor(), help.m_cmd.get()) << std::endl;
Václav Kubernát3a433232020-07-08 17:52:50 +0200295 } else {
Václav Kubernát054cc992019-02-21 14:23:52 +0100296 boost::mpl::for_each<CommandTypes, boost::type<boost::mpl::_>>([](auto cmd) {
297 std::cout << commandShortHelpVisitor()(cmd) << std::endl;
298 });
Václav Kubernát3a433232020-07-08 17:52:50 +0200299 }
Václav Kubernát054cc992019-02-21 14:23:52 +0100300}
301
Petr Gotthard1c76dea2022-04-13 17:56:58 +0200302void Interpreter::operator()(const quit_&) const
303{
304 // no operation
305}
306
Václav Kubernát7e167692020-06-12 09:53:01 +0200307template <typename PathType>
Václav Kubernát4c3d22f2020-06-12 16:05:01 +0200308boost::variant<dataPath_, schemaPath_, module_> Interpreter::toCanonicalPath(const boost::optional<PathType>& optPath) const
Václav Kubernát6415b822018-08-22 17:40:01 +0200309{
Václav Kubernát7e167692020-06-12 09:53:01 +0200310 if (!optPath) {
Václav Kubernát4c3d22f2020-06-12 16:05:01 +0200311 return m_parser.currentPath();
Václav Kubernát7e167692020-06-12 09:53:01 +0200312 }
313 return toCanonicalPath(*optPath);
Václav Kubernát6415b822018-08-22 17:40:01 +0200314}
315
Václav Kubernát7e167692020-06-12 09:53:01 +0200316struct impl_toCanonicalPath {
317 const dataPath_& m_parserPath;
318
Václav Kubernát4c3d22f2020-06-12 16:05:01 +0200319 using ReturnType = boost::variant<dataPath_, schemaPath_, module_>;
320
Václav Kubernát7e167692020-06-12 09:53:01 +0200321 impl_toCanonicalPath(const dataPath_& parserPath)
322 : m_parserPath(parserPath)
323 {
324 }
Václav Kubernát4c3d22f2020-06-12 16:05:01 +0200325 ReturnType operator()(const module_& path) const
Václav Kubernátd6247992020-05-27 00:17:56 +0200326 {
Václav Kubernát4c3d22f2020-06-12 16:05:01 +0200327 return path;
Václav Kubernátd6247992020-05-27 00:17:56 +0200328 }
Václav Kubernát4c3d22f2020-06-12 16:05:01 +0200329 ReturnType operator()(const schemaPath_& path) const
Václav Kubernát5c75b252018-10-10 18:33:47 +0200330 {
Václav Kubernát4c3d22f2020-06-12 16:05:01 +0200331 return impl(path);
Václav Kubernát5c75b252018-10-10 18:33:47 +0200332 }
Václav Kubernát4c3d22f2020-06-12 16:05:01 +0200333 ReturnType operator()(const dataPath_& path) const
Václav Kubernát5c75b252018-10-10 18:33:47 +0200334 {
Václav Kubernát4c3d22f2020-06-12 16:05:01 +0200335 return impl(path);
Václav Kubernát5c75b252018-10-10 18:33:47 +0200336 }
Václav Kubernát5c75b252018-10-10 18:33:47 +0200337
Václav Kubernát7e167692020-06-12 09:53:01 +0200338private:
339 template <typename PathType>
Václav Kubernát59e4ee42020-07-08 17:32:45 +0200340 [[nodiscard]] ReturnType impl(const PathType& suffix) const
Václav Kubernátd6247992020-05-27 00:17:56 +0200341 {
Václav Kubernát7e167692020-06-12 09:53:01 +0200342 PathType res = [this] {
343 if constexpr (std::is_same<PathType, schemaPath_>()) {
344 return dataPathToSchemaPath(m_parserPath);
345 } else {
346 return m_parserPath;
347 }
348 }();
Václav Kubernátd6247992020-05-27 00:17:56 +0200349
Václav Kubernát7e167692020-06-12 09:53:01 +0200350 if (suffix.m_scope == Scope::Absolute) {
Václav Kubernát59be0de2020-06-15 13:58:45 +0200351 res = {Scope::Absolute, {}};
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200352 }
Václav Kubernátb6ff0b62018-08-30 16:14:53 +0200353
Václav Kubernát7e167692020-06-12 09:53:01 +0200354 for (const auto& fragment : suffix.m_nodes) {
Václav Kubernáte781b902020-06-15 14:35:11 +0200355 res.pushFragment(fragment);
Václav Kubernát7e167692020-06-12 09:53:01 +0200356 }
Václav Kubernát4c3d22f2020-06-12 16:05:01 +0200357
Václav Kubernát7e167692020-06-12 09:53:01 +0200358 return res;
359 }
360};
361
362template <typename PathType>
Václav Kubernát4c3d22f2020-06-12 16:05:01 +0200363boost::variant<dataPath_, schemaPath_, module_> Interpreter::toCanonicalPath(const PathType& path) const
Václav Kubernát9cfcd872020-02-18 12:34:02 +0100364{
Václav Kubernát7e167692020-06-12 09:53:01 +0200365 if constexpr (std::is_same<PathType, dataPath_>()) {
366 return impl_toCanonicalPath(m_parser.currentPath())(path);
367 } else {
368 return boost::apply_visitor(impl_toCanonicalPath(m_parser.currentPath()), path);
369 }
Václav Kubernát9cfcd872020-02-18 12:34:02 +0100370}
371
Václav Kubernát48e9dfa2020-07-08 10:55:12 +0200372Interpreter::Interpreter(Parser& parser, ProxyDatastore& datastore)
Václav Kubernát812ee282018-08-30 17:10:03 +0200373 : m_parser(parser)
374 , m_datastore(datastore)
Václav Kubernát96344a12018-05-28 16:33:39 +0200375{
376}