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 2452 : ConfigCass2JsonAdapter::ConfigCass2JsonAdapter(const string &uuid,
62 : ConfigCassandraClient *cassandra_client, const string &obj_type,
63 2452 : const CassColumnKVVec &cdvec) : cassandra_client_(cassandra_client),
64 2452 : uuid_(uuid) {
65 2452 : CreateJsonString(obj_type, cdvec);
66 2452 : }
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 12378 : void ConfigCass2JsonAdapter::AddOneEntry(Value *jsonObject,
90 : const string &obj_type, const JsonAdapterDataType &c,
91 : Document::AllocatorType &a) {
92 :
93 : // Process scalar property values.
94 12378 : if (boost::starts_with(c.key, prop_prefix)) {
95 3637 : string c_value = c.value;
96 3637 : if (c.key == "prop:bgpaas_session_attributes")
97 0 : c_value = "\"\"";
98 3637 : Document prop_document(&a);
99 3637 : prop_document.Parse<0>(c_value.c_str());
100 3637 : CONFIG_PARSE_ASSERT(Property, !prop_document.HasParseError());
101 3637 : Value vk;
102 3637 : jsonObject->AddMember(
103 7274 : vk.SetString(c.key.substr(prop_prefix.size()).c_str(), a),
104 : prop_document, a);
105 3637 : return;
106 3637 : }
107 :
108 : // Process property list and property map values.
109 8741 : bool is_map = false;
110 17277 : if ((is_map = boost::starts_with(c.key, map_prop_prefix)) ||
111 8535 : boost::starts_with(c.key, list_prop_prefix)) {
112 416 : size_t from_front_pos = c.key.find(':');
113 416 : size_t from_back_pos = c.key.rfind(':');
114 : string prop_map = c.key.substr(from_front_pos + 1,
115 416 : from_back_pos - from_front_pos - 1);
116 416 : string wrapper = cassandra_client_->mgr()-> \
117 416 : config_json_parser()->GetWrapperFieldName(type_,
118 416 : prop_map);
119 416 : if (wrapper.empty()) {
120 0 : return;
121 : }
122 416 : if (!jsonObject->HasMember(prop_map.c_str())) {
123 220 : Value v;
124 220 : Value vk;
125 220 : jsonObject->AddMember(
126 : vk.SetString(prop_map.c_str(), a), v.SetObject(), a);
127 220 : Value va;
128 220 : Value vak;
129 220 : (*jsonObject)[prop_map.c_str()].AddMember(
130 : vak.SetString(wrapper.c_str(), a), va.SetArray(), a);
131 220 : }
132 :
133 416 : Document map_document(&a);
134 416 : map_document.Parse<0>(c.value.c_str());
135 416 : if (is_map)
136 206 : CONFIG_PARSE_ASSERT(PropertyMap, !map_document.HasParseError());
137 : else
138 210 : CONFIG_PARSE_ASSERT(PropertyList, !map_document.HasParseError());
139 :
140 416 : (*jsonObject)[prop_map.c_str()][wrapper.c_str()].PushBack(
141 : map_document, a);
142 416 : return;
143 416 : }
144 :
145 8326 : if (boost::starts_with(c.key, ref_prefix)) {
146 942 : size_t from_front_pos = c.key.find(':');
147 942 : size_t from_back_pos = c.key.rfind(':');
148 942 : CONFIG_PARSE_ASSERT(Reference, from_front_pos != string::npos);
149 942 : CONFIG_PARSE_ASSERT(Reference, from_back_pos != string::npos);
150 : string ref_type = c.key.substr(from_front_pos + 1,
151 942 : from_back_pos-from_front_pos - 1);
152 942 : string ref_uuid = c.key.substr(from_back_pos + 1);
153 942 : string fq_name_ref = c.ref_fq_name;
154 942 : string r = ref_type + "_refs";
155 942 : if (!jsonObject->HasMember(r.c_str())) {
156 922 : Value v;
157 922 : Value vk;
158 922 : jsonObject->AddMember(vk.SetString(r.c_str(), a), v.SetArray(), a);
159 922 : }
160 :
161 942 : Value v;
162 942 : v.SetObject();
163 :
164 942 : Value vs1;
165 942 : Value vs2;
166 942 : v.AddMember("to", vs1.SetString(fq_name_ref.c_str(), a), a);
167 942 : v.AddMember("uuid", vs2.SetString(ref_uuid.c_str(), a), a);
168 :
169 : bool link_with_attr =
170 942 : cassandra_client_->mgr()->config_json_parser()->\
171 942 : IsLinkWithAttr(obj_type, ref_type);
172 942 : if (link_with_attr) {
173 382 : Document ref_document(&a);
174 382 : ref_document.Parse<0>(c.value.c_str());
175 382 : CONFIG_PARSE_ASSERT(ReferenceLinkAttributes,
176 : !ref_document.HasParseError());
177 382 : CONFIG_PARSE_ASSERT(ReferenceLinkAttributes,
178 : ref_document.HasMember("attr"));
179 382 : Value &attr_value = ref_document["attr"];
180 382 : if (!attr_value.IsNull()) {
181 378 : v.AddMember("attr", attr_value, a);
182 : } else {
183 4 : Value vm;
184 4 : v.AddMember("attr", vm.SetObject(), a);
185 4 : }
186 382 : } else {
187 560 : Value vm;
188 560 : v.AddMember("attr", vm.SetObject(), a);
189 560 : }
190 942 : (*jsonObject)[r.c_str()].PushBack(v, a);
191 942 : return;
192 942 : }
193 :
194 7384 : if (boost::starts_with(c.key, parent_prefix)) {
195 1410 : size_t pos = c.key.rfind(':');
196 1410 : CONFIG_PARSE_ASSERT(Parent, pos != string::npos);
197 1410 : size_t type_pos = c.key.find(':');
198 1410 : CONFIG_PARSE_ASSERT(Parent, type_pos != string::npos);
199 1410 : Value v;
200 1410 : Value vk;
201 1410 : jsonObject->AddMember(vk.SetString(parent_type_prefix.c_str(), a),
202 1410 : v.SetString(c.key.substr(type_pos + 1,
203 1410 : pos-type_pos - 1).c_str(), a), a);
204 1410 : return;
205 1410 : }
206 :
207 5973 : if (!c.key.compare(fq_name_prefix)) {
208 2452 : Document fq_name_document(&a);
209 2452 : fq_name_document.Parse<0>(c.value.c_str());
210 2452 : CONFIG_PARSE_ASSERT(FqName, !fq_name_document.HasParseError());
211 2452 : Value vk;
212 2452 : jsonObject->AddMember(vk.SetString(c.key.c_str(), a),
213 : fq_name_document, a);
214 2452 : return;
215 2452 : }
216 :
217 3521 : 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 2451 : CONFIG_PARSE_ASSERT(Type, type_ != obj_type);
221 2451 : type_ = c.value;
222 2452 : type_.erase(remove(type_.begin(), type_.end(), '\"'), type_.end());
223 2452 : return;
224 : }
225 : }
226 :
227 2452 : void ConfigCass2JsonAdapter::CreateJsonString(const string &obj_type,
228 : const CassColumnKVVec &cdvec) {
229 2452 : if (cdvec.empty())
230 0 : return;
231 2452 : Document::AllocatorType &a = json_document_.GetAllocator();
232 2452 : Value jsonObject;
233 2452 : 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 2452 : size_t type_index = -1;
238 2452 : size_t i = cdvec.size();
239 6720 : while (i--) {
240 6719 : if (!cdvec[i].key.compare("type")) {
241 2451 : AddOneEntry(&jsonObject, obj_type, cdvec[i], a);
242 2452 : type_index = i;
243 2452 : break;
244 : }
245 : }
246 :
247 2453 : 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 14831 : for (size_t i = 0; i < cdvec.size(); ++i) {
257 12379 : if (i != type_index)
258 9927 : AddOneEntry(&jsonObject, obj_type, cdvec[i], a);
259 : }
260 :
261 2452 : Value vk;
262 2452 : json_document_.SetObject().AddMember(vk.SetString(type_.c_str(), a),
263 : jsonObject, a);
264 2452 : }
|