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