Line data Source code
1 : /*
2 : * Copyright (c) 2016 Juniper Networks, Inc. All rights reserved.
3 : */
4 :
5 : #include "config_cass2json_adapter.h"
6 :
7 : #include <assert.h>
8 : #include <boost/algorithm/string/predicate.hpp>
9 : #include <boost/assign/list_of.hpp>
10 : #include <string>
11 : #include <iostream>
12 :
13 : #include "base/string_util.h"
14 : #include "config_cassandra_client.h"
15 : #include "config_client_log.h"
16 : #include "config_client_log_types.h"
17 : #include "rapidjson/stringbuffer.h"
18 : #include "rapidjson/writer.h"
19 :
20 : using boost::assign::list_of;
21 : using contrail_rapidjson::Document;
22 : using contrail_rapidjson::StringBuffer;
23 : using contrail_rapidjson::Value;
24 : using contrail_rapidjson::Writer;
25 : using std::cout;
26 : using std::endl;
27 : using std::set;
28 : using std::string;
29 :
30 : const string ConfigCass2JsonAdapter::fq_name_prefix = "fq_name";
31 : const string ConfigCass2JsonAdapter::prop_prefix = "prop:";
32 : const string ConfigCass2JsonAdapter::list_prop_prefix = "propl:";
33 : const string ConfigCass2JsonAdapter::map_prop_prefix = "propm:";
34 : const string ConfigCass2JsonAdapter::ref_prefix = "ref:";
35 : const string ConfigCass2JsonAdapter::parent_prefix = "parent:";
36 : const string ConfigCass2JsonAdapter::parent_type_prefix = "parent_type";
37 :
38 : const set<string> ConfigCass2JsonAdapter::allowed_properties =
39 : list_of(prop_prefix)(map_prop_prefix)
40 : (list_prop_prefix)(ref_prefix)(parent_prefix);
41 :
42 : bool ConfigCass2JsonAdapter::assert_on_parse_error_ =
43 : getenv("CONTRAIL_CONFIG_ASSERT_ON_PARSE_ERROR") != NULL;
44 :
45 : #define CONFIG_PARSE_ASSERT(t, condition) \
46 : do { \
47 : if (condition) \
48 : break; \
49 : CONFIG_CLIENT_WARN_LOG(ConfigurationMalformed ## t ## Warning ## Log, \
50 : Category::CONFIG_CLIENT, c.key, c.value, type_, uuid_); \
51 : CONFIG_CLIENT_TRACE(ConfigurationMalformed ## t ## Warning ## Trace, \
52 : c.key, c.value, type_, uuid_); \
53 : cout << "CONFIG_PARSE_ERROR " << __FILE__ << ":" << __LINE__ << " "; \
54 : cout << type_ << " " << c.key << " " << c.value << " "; \
55 : cout << uuid_ << endl; \
56 : if (assert_on_parse_error_) \
57 : assert(false); \
58 : return; \
59 : } while (false)
60 :
61 2717 : ConfigCass2JsonAdapter::ConfigCass2JsonAdapter(const string &uuid,
62 : ConfigCassandraClient *cassandra_client, const string &obj_type,
63 2717 : const CassColumnKVVec &cdvec) : cassandra_client_(cassandra_client),
64 2717 : uuid_(uuid) {
65 2717 : CreateJsonString(obj_type, cdvec);
66 2717 : }
67 :
68 0 : ConfigCass2JsonAdapter::ConfigCass2JsonAdapter(const string &uuid,
69 0 : const string &type, const Document &doc) : uuid_(uuid) {
70 : /**
71 : * Construct the final Json Document.
72 : */
73 0 : Document::AllocatorType &a = json_document_.GetAllocator();
74 0 : Value v;
75 0 : v.CopyFrom(doc, a);
76 :
77 0 : Value vk;
78 0 : json_document_.SetObject().AddMember(vk.SetString(type.c_str(), a),
79 : v, a);
80 0 : }
81 :
82 0 : string ConfigCass2JsonAdapter::GetJsonString(const Value &attr_value) {
83 0 : StringBuffer buffer;
84 0 : Writer<StringBuffer> writer(buffer);
85 0 : attr_value.Accept(writer);
86 0 : return buffer.GetString();
87 0 : }
88 :
89 15871 : void ConfigCass2JsonAdapter::AddOneEntry(Value *jsonObject,
90 : const string &obj_type, const JsonAdapterDataType &c,
91 : Document::AllocatorType &a) {
92 :
93 : // Process scalar property values.
94 15871 : if (boost::starts_with(c.key, prop_prefix)) {
95 4952 : string c_value = c.value;
96 4951 : if (c.key == "prop:bgpaas_session_attributes")
97 0 : c_value = "\"\"";
98 4951 : Document prop_document(&a);
99 4951 : prop_document.Parse<0>(c_value.c_str());
100 4952 : CONFIG_PARSE_ASSERT(Property, !prop_document.HasParseError());
101 4952 : Value vk;
102 4952 : jsonObject->AddMember(
103 9904 : vk.SetString(c.key.substr(prop_prefix.size()).c_str(), a),
104 : prop_document, a);
105 4952 : return;
106 4952 : }
107 :
108 : // Process property list and property map values.
109 10921 : bool is_map = false;
110 21573 : if ((is_map = boost::starts_with(c.key, map_prop_prefix)) ||
111 10649 : boost::starts_with(c.key, list_prop_prefix)) {
112 528 : size_t from_front_pos = c.key.find(':');
113 528 : size_t from_back_pos = c.key.rfind(':');
114 : string prop_map = c.key.substr(from_front_pos + 1,
115 528 : from_back_pos - from_front_pos - 1);
116 528 : string wrapper = cassandra_client_->mgr()-> \
117 528 : config_json_parser()->GetWrapperFieldName(type_,
118 528 : prop_map);
119 528 : if (wrapper.empty()) {
120 0 : return;
121 : }
122 528 : if (!jsonObject->HasMember(prop_map.c_str())) {
123 280 : Value v;
124 280 : Value vk;
125 280 : jsonObject->AddMember(
126 : vk.SetString(prop_map.c_str(), a), v.SetObject(), a);
127 280 : Value va;
128 280 : Value vak;
129 280 : (*jsonObject)[prop_map.c_str()].AddMember(
130 : vak.SetString(wrapper.c_str(), a), va.SetArray(), a);
131 280 : }
132 :
133 528 : Document map_document(&a);
134 528 : map_document.Parse<0>(c.value.c_str());
135 528 : if (is_map)
136 268 : CONFIG_PARSE_ASSERT(PropertyMap, !map_document.HasParseError());
137 : else
138 260 : CONFIG_PARSE_ASSERT(PropertyList, !map_document.HasParseError());
139 :
140 528 : (*jsonObject)[prop_map.c_str()][wrapper.c_str()].PushBack(
141 : map_document, a);
142 528 : return;
143 528 : }
144 :
145 10392 : if (boost::starts_with(c.key, ref_prefix)) {
146 1205 : size_t from_front_pos = c.key.find(':');
147 1205 : size_t from_back_pos = c.key.rfind(':');
148 1205 : CONFIG_PARSE_ASSERT(Reference, from_front_pos != string::npos);
149 1205 : CONFIG_PARSE_ASSERT(Reference, from_back_pos != string::npos);
150 : string ref_type = c.key.substr(from_front_pos + 1,
151 1205 : from_back_pos-from_front_pos - 1);
152 1205 : string ref_uuid = c.key.substr(from_back_pos + 1);
153 1205 : string fq_name_ref = c.ref_fq_name;
154 1205 : string r = ref_type + "_refs";
155 1205 : if (!jsonObject->HasMember(r.c_str())) {
156 1171 : Value v;
157 1171 : Value vk;
158 1171 : jsonObject->AddMember(vk.SetString(r.c_str(), a), v.SetArray(), a);
159 1171 : }
160 :
161 1205 : Value v;
162 1205 : v.SetObject();
163 :
164 1205 : Value vs1;
165 1205 : Value vs2;
166 1205 : v.AddMember("to", vs1.SetString(fq_name_ref.c_str(), a), a);
167 1205 : v.AddMember("uuid", vs2.SetString(ref_uuid.c_str(), a), a);
168 :
169 : bool link_with_attr =
170 1205 : cassandra_client_->mgr()->config_json_parser()->\
171 1205 : IsLinkWithAttr(obj_type, ref_type);
172 1205 : if (link_with_attr) {
173 478 : Document ref_document(&a);
174 478 : ref_document.Parse<0>(c.value.c_str());
175 478 : CONFIG_PARSE_ASSERT(ReferenceLinkAttributes,
176 : !ref_document.HasParseError());
177 478 : CONFIG_PARSE_ASSERT(ReferenceLinkAttributes,
178 : ref_document.HasMember("attr"));
179 478 : Value &attr_value = ref_document["attr"];
180 478 : if (!attr_value.IsNull()) {
181 472 : v.AddMember("attr", attr_value, a);
182 : } else {
183 6 : Value vm;
184 6 : v.AddMember("attr", vm.SetObject(), a);
185 6 : }
186 478 : } else {
187 727 : Value vm;
188 727 : v.AddMember("attr", vm.SetObject(), a);
189 727 : }
190 1205 : (*jsonObject)[r.c_str()].PushBack(v, a);
191 1205 : return;
192 1205 : }
193 :
194 9187 : if (boost::starts_with(c.key, parent_prefix)) {
195 1614 : size_t pos = c.key.rfind(':');
196 1614 : CONFIG_PARSE_ASSERT(Parent, pos != string::npos);
197 1614 : size_t type_pos = c.key.find(':');
198 1614 : CONFIG_PARSE_ASSERT(Parent, type_pos != string::npos);
199 1614 : Value v;
200 1614 : Value vk;
201 1614 : jsonObject->AddMember(vk.SetString(parent_type_prefix.c_str(), a),
202 1614 : v.SetString(c.key.substr(type_pos + 1,
203 1614 : pos-type_pos - 1).c_str(), a), a);
204 1614 : return;
205 1614 : }
206 :
207 7573 : if (!c.key.compare(fq_name_prefix)) {
208 2717 : Document fq_name_document(&a);
209 2717 : fq_name_document.Parse<0>(c.value.c_str());
210 2717 : CONFIG_PARSE_ASSERT(FqName, !fq_name_document.HasParseError());
211 2717 : Value vk;
212 2717 : jsonObject->AddMember(vk.SetString(c.key.c_str(), a),
213 : fq_name_document, a);
214 2717 : return;
215 2717 : }
216 :
217 4856 : if (!c.key.compare("type")) {
218 : // Prepend the 'type'. This is "our key", with value being the json
219 : // sub-document containing all other columns.
220 2717 : CONFIG_PARSE_ASSERT(Type, type_ != obj_type);
221 2717 : type_ = c.value;
222 2717 : type_.erase(remove(type_.begin(), type_.end(), '\"'), type_.end());
223 2717 : return;
224 : }
225 : }
226 :
227 2716 : void ConfigCass2JsonAdapter::CreateJsonString(const string &obj_type,
228 : const CassColumnKVVec &cdvec) {
229 2716 : if (cdvec.empty())
230 0 : return;
231 2717 : Document::AllocatorType &a = json_document_.GetAllocator();
232 2717 : Value jsonObject;
233 2717 : jsonObject.SetObject();
234 :
235 : // First look for and part "type" field. We usually expect it to be at the
236 : // end as column keys are suppose to be allways sorted lexicographically.
237 2717 : size_t type_index = -1;
238 2717 : size_t i = cdvec.size();
239 7024 : while (i--) {
240 7024 : if (!cdvec[i].key.compare("type")) {
241 2717 : AddOneEntry(&jsonObject, obj_type, cdvec[i], a);
242 2717 : type_index = i;
243 2717 : break;
244 : }
245 : }
246 :
247 2717 : if (type_.empty()) {
248 :
249 0 : CONFIG_CLIENT_WARN_LOG(ConfigurationMissingTypeWarningLog,
250 : Category::CONFIG_CLIENT, uuid_, obj_type);
251 0 : CONFIG_CLIENT_TRACE(ConfigurationMissingTypeWarningTrace, uuid_,
252 : obj_type);
253 0 : return;
254 : }
255 :
256 18589 : for (size_t i = 0; i < cdvec.size(); ++i) {
257 15872 : if (i != type_index)
258 13155 : AddOneEntry(&jsonObject, obj_type, cdvec[i], a);
259 : }
260 :
261 2717 : Value vk;
262 2717 : json_document_.SetObject().AddMember(vk.SetString(type_.c_str(), a),
263 : jsonObject, a);
264 2717 : }
|