void ProducerStateTable::set(string key, vector<FieldValueTuple> &values, string op /*= SET_COMMAND*/, string prefix) { // Assembly redis command args into a string vector vector<string> args; args.push_back("EVALSHA"); args.push_back(m_shaSet); args.push_back(to_string(values.size() + 2)); args.push_back(getChannelName()); args.push_back(getKeySetName()); args.insert(args.end(), values.size(), getKeyName(key)); args.push_back("G"); args.push_back(key); for (auto& iv: values) { args.push_back(fvField(iv)); args.push_back(fvValue(iv)); } // Transform data structure vector<const char *> args1; transform(args.begin(), args.end(), back_inserter(args1), [](const string &s) { return s.c_str(); } ); // Invoke redis command RedisCommand command; command.formatArgv((int)args1.size(), &args1[0], NULL); m_pipe->push(command, REDIS_REPLY_NIL); if (!m_buffered) { m_pipe->flush(); } }
void swss::NotificationConsumer::pop(std::string &op, std::string &data, std::vector<FieldValueTuple> &values) { JSon::readJson(m_msg, values); FieldValueTuple fvt = values.at(0); op = fvField(fvt); data = fvValue(fvt); values.erase(values.begin()); }
[[ noreturn ]] void Logger::settingThread() { Select select; DBConnector db(LOGLEVEL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); std::vector<std::shared_ptr<ConsumerStateTable>> selectables(m_settingChangeObservers.size()); for (const auto& i : m_settingChangeObservers) { std::shared_ptr<ConsumerStateTable> table = std::make_shared<ConsumerStateTable>(&db, i.first); selectables.push_back(table); select.addSelectable(table.get()); } while(true) { Selectable *selectable = nullptr; int ret = select.select(&selectable); if (ret == Select::ERROR) { SWSS_LOG_NOTICE("%s select error %s", __PRETTY_FUNCTION__, strerror(errno)); continue; } KeyOpFieldsValuesTuple koValues; dynamic_cast<ConsumerStateTable *>(selectable)->pop(koValues); std::string key = kfvKey(koValues), op = kfvOp(koValues); if ((op != SET_COMMAND) || (m_settingChangeObservers.find(key) == m_settingChangeObservers.end())) { continue; } auto values = kfvFieldsValues(koValues); for (const auto& i : values) { const std::string &field = fvField(i), &value = fvValue(i); if ((field == DAEMON_LOGLEVEL) && (value != m_currentPrios[key])) { m_currentPrios[key] = value; m_settingChangeObservers[key].first(key, value); } else if ((field == DAEMON_LOGOUTPUT) && (value != m_currentOutputs[key])) { m_currentOutputs[key] = value; m_settingChangeObservers[key].second(key, value); } break; } } }
void handleUnittestChannelOp( _In_ const std::string &op, _In_ const std::string &key, _In_ const std::vector<swss::FieldValueTuple> &values) { MUTEX(); SWSS_LOG_ENTER(); /* * Since we will access and modify DB we need to be under mutex. * * NOTE: since this unittest channel is handled in thread, then that means * there is a DELAY from producer and consumer thread in VS, so if user * will set value on the specific READ_ONLY value he should wait for some * time until that value will be propagated to virtual switch. */ SWSS_LOG_NOTICE("op = %s, key = %s", op.c_str(), key.c_str()); for (const auto &v: values) { SWSS_LOG_DEBUG("attr: %s: %s", fvField(v).c_str(), fvValue(v).c_str()); } if (op == SAI_VS_UNITTEST_ENABLE_UNITTESTS) { channelOpEnableUnittests(key, values); } else if (op == SAI_VS_UNITTEST_SET_RO_OP) { channelOpSetReadOnlyAttribute(key, values); } else if (op == SAI_VS_UNITTEST_SET_STATS_OP) { channelOpSetStats(key, values); } else { SWSS_LOG_ERROR("unknown unittest operation: %s", op.c_str()); } }
void swss::NotificationConsumer::pop(std::string &op, std::string &data, std::vector<FieldValueTuple> &values) { SWSS_LOG_ENTER(); if (m_queue.size() == 0) { SWSS_LOG_ERROR("notification queue is empty, can't pop"); throw std::runtime_error("notification queue is empty, can't pop"); } std::string msg = m_queue.front(); m_queue.pop(); JSon::readJson(msg, values); FieldValueTuple fvt = values.at(0); op = fvField(fvt); data = fvValue(fvt); values.erase(values.begin()); }
sai_status_t internal_redis_bulk_generic_remove( _In_ sai_object_type_t object_type, _In_ const std::vector<std::string> &serialized_object_ids, _Out_ sai_status_t *object_statuses) /* array */ { SWSS_LOG_ENTER(); std::string str_object_type = sai_serialize_object_type(object_type); std::vector<swss::FieldValueTuple> entries; /* * We are recording all entries and their statuses, but we send to sairedis * only those that succeeded metadata check, since only those will be * executed on syncd, so there is no need with bothering decoding statuses * on syncd side. */ for (size_t idx = 0; idx < serialized_object_ids.size(); ++idx) { std::string str_attr = ""; swss::FieldValueTuple fvtNoStatus(serialized_object_ids[idx], str_attr); entries.push_back(fvtNoStatus); } /* * We are adding number of entries to actually add ':' to be compatible * with previous */ if (g_record) { std::string joined; for (const auto &e: entries) { // ||obj_id|attr=val|attr=val||obj_id|attr=val|attr=val joined += "||" + fvField(e) + "|" + fvValue(e); } /* * Capital 'C' stands for bulk CREATE operation. */ recordLine("C|" + str_object_type + joined); } // key: object_type:count // field: object_id // value: object_attrs std::string key = str_object_type + ":" + std::to_string(entries.size()); if (entries.size()) { g_asicState->set(key, entries, "bulkremove"); } return SAI_STATUS_SUCCESS; }
void TunnelDecapOrch::doTask(Consumer& consumer) { SWSS_LOG_ENTER(); if (!gPortsOrch->isInitDone()) { return; } auto it = consumer.m_toSync.begin(); while (it != consumer.m_toSync.end()) { KeyOpFieldsValuesTuple t = it->second; string key = kfvKey(t); string op = kfvOp(t); IpAddresses ip_addresses; IpAddress src_ip; string tunnel_type; string dscp_mode; string ecn_mode; string ttl_mode; bool valid = true; // checking to see if the tunnel already exists bool exists = (tunnelTable.find(key) != tunnelTable.end()); if (op == SET_COMMAND) { for (auto i : kfvFieldsValues(t)) { if (fvField(i) == "tunnel_type") { tunnel_type = fvValue(i); if (tunnel_type != "IPINIP") { SWSS_LOG_ERROR("Invalid tunnel type %s", tunnel_type.c_str()); valid = false; break; } } else if (fvField(i) == "dst_ip") { try { ip_addresses = IpAddresses(fvValue(i)); } catch (const std::invalid_argument &e) { SWSS_LOG_ERROR("%s", e.what()); valid = false; break; } if (exists) { setIpAttribute(key, ip_addresses, tunnelTable.find(key)->second.tunnel_id); } } else if (fvField(i) == "src_ip") { try { src_ip = IpAddress(fvValue(i)); } catch (const std::invalid_argument &e) { SWSS_LOG_ERROR("%s", e.what()); valid = false; break; } if (exists) { SWSS_LOG_ERROR("cannot modify src ip for existing tunnel"); } } else if (fvField(i) == "dscp_mode") { dscp_mode = fvValue(i); if (dscp_mode != "uniform" && dscp_mode != "pipe") { SWSS_LOG_ERROR("Invalid dscp mode %s\n", dscp_mode.c_str()); valid = false; break; } if (exists) { setTunnelAttribute(fvField(i), dscp_mode, tunnelTable.find(key)->second.tunnel_id); } } else if (fvField(i) == "ecn_mode") { ecn_mode = fvValue(i); if (ecn_mode != "copy_from_outer" && ecn_mode != "standard") { SWSS_LOG_ERROR("Invalid ecn mode %s\n", ecn_mode.c_str()); valid = false; break; } if (exists) { setTunnelAttribute(fvField(i), ecn_mode, tunnelTable.find(key)->second.tunnel_id); } } else if (fvField(i) == "ttl_mode") { ttl_mode = fvValue(i); if (ttl_mode != "uniform" && ttl_mode != "pipe") { SWSS_LOG_ERROR("Invalid ttl mode %s\n", ttl_mode.c_str()); valid = false; break; } if (exists) { setTunnelAttribute(fvField(i), ttl_mode, tunnelTable.find(key)->second.tunnel_id); } } } // create new tunnel if it doesn't exists already if (valid && !exists) { if (addDecapTunnel(key, tunnel_type, ip_addresses, src_ip, dscp_mode, ecn_mode, ttl_mode)) { SWSS_LOG_NOTICE("Tunnel(s) added to ASIC_DB."); } else { SWSS_LOG_ERROR("Failed to add tunnels to ASIC_DB."); } } } if (op == DEL_COMMAND) { if (exists) { removeDecapTunnel(key); } else { SWSS_LOG_ERROR("Tunnel cannot be removed since it doesn't exist."); } } it = consumer.m_toSync.erase(it); } }
void channelOpSetReadOnlyAttribute( _In_ const std::string &key, _In_ const std::vector<swss::FieldValueTuple> &values) { SWSS_LOG_ENTER(); for (const auto &v: values) { SWSS_LOG_DEBUG("attr: %s: %s", fvField(v).c_str(), fvValue(v).c_str()); } if (values.size() != 1) { SWSS_LOG_ERROR("expected 1 value only, but given: %zu", values.size()); return; } const std::string &str_object_type = key.substr(0, key.find(":")); const std::string &str_object_id = key.substr(key.find(":") + 1); sai_object_type_t object_type; sai_deserialize_object_type(str_object_type, object_type); if (object_type == SAI_OBJECT_TYPE_NULL || object_type >= SAI_OBJECT_TYPE_EXTENSIONS_MAX) { SWSS_LOG_ERROR("invalid object type: %d", object_type); return; } auto info = sai_metadata_get_object_type_info(object_type); if (info->isnonobjectid) { SWSS_LOG_ERROR("non object id %s is not supported yet", str_object_type.c_str()); return; } sai_object_id_t object_id; sai_deserialize_object_id(str_object_id, object_id); sai_object_type_t ot = sai_object_type_query(object_id); if (ot != object_type) { SWSS_LOG_ERROR("object type is differnt than provided %s, but oid is %s", str_object_type.c_str(), sai_serialize_object_type(ot).c_str()); return; } sai_object_id_t switch_id = sai_switch_id_query(object_id); if (switch_id == SAI_NULL_OBJECT_ID) { SWSS_LOG_ERROR("failed to find switch id for oid %s", str_object_id.c_str()); return; } // oid is validated and we got switch id const std::string &str_attr_id = fvField(values.at(0)); const std::string &str_attr_value = fvValue(values.at(0)); auto meta = sai_metadata_get_attr_metadata_by_attr_id_name(str_attr_id.c_str()); if (meta == NULL) { SWSS_LOG_ERROR("failed to find attr %s", str_attr_id.c_str()); return; } if (meta->objecttype != ot) { SWSS_LOG_ERROR("attr %s belongs to differnt object type than oid: %s", str_attr_id.c_str(), sai_serialize_object_type(ot).c_str()); return; } // we got attr metadata sai_attribute_t attr; attr.id = meta->attrid; sai_deserialize_attr_value(str_attr_value, *meta, attr); SWSS_LOG_NOTICE("switch id is %s", sai_serialize_object_id(switch_id).c_str()); sai_status_t status = meta_unittests_allow_readonly_set_once(meta->objecttype, meta->attrid); if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("failed to enable SET readonly attribute once: %s", sai_serialize_status(status).c_str()); return; } sai_object_meta_key_t meta_key = { .objecttype = ot, .objectkey = { .key = { .object_id = object_id } } }; status = info->set(&meta_key, &attr); if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("failed to set %s to %s on %s", str_attr_id.c_str(), str_attr_value.c_str(), str_object_id.c_str()); } else { SWSS_LOG_NOTICE("SUCCESS to set %s to %s on %s", str_attr_id.c_str(), str_attr_value.c_str(), str_object_id.c_str()); } sai_deserialize_free_attribute_value(meta->attrvaluetype, attr); }
void channelOpSetStats( _In_ const std::string &key, _In_ const std::vector<swss::FieldValueTuple> &values) { SWSS_LOG_ENTER(); // NOTE: we need to find stats for specific object, later SAI already have // this feature and this search could be optimized here: // https://github.com/opencomputeproject/SAI/commit/acc83933ff21c68e8ef10c9826de45807fdc0438 sai_object_id_t oid; sai_deserialize_object_id(key, oid); sai_object_type_t ot = sai_object_type_query(oid); if (ot == SAI_OBJECT_TYPE_NULL) { SWSS_LOG_ERROR("invalid object id: %s", key.c_str()); return; } sai_object_id_t switch_id = sai_switch_id_query(oid); if (switch_id == SAI_NULL_OBJECT_ID) { SWSS_LOG_ERROR("unable to get switch_id from oid: %s", key.c_str()); return; } /* * Check if object for statistics was created and exists on switch. */ auto &objectHash = g_switch_state_map.at(switch_id)->objectHash.at(ot); auto it = objectHash.find(key.c_str()); if (it == objectHash.end()) { SWSS_LOG_ERROR("object not found: %s", key.c_str()); return; } /* * Check if object for statistics have statistic map created, if not * create empty map. */ auto &countersMap = g_switch_state_map.at(switch_id)->countersMap; auto mapit = countersMap.find(key); if (mapit == countersMap.end()) countersMap[key] = std::map<int,uint64_t>(); /* * Find stats enum based on object type. In new metadata we have enum on * object type, but here we need to find it manually enum is in format * "sai_" + object_type + "_stat_t" */ std::string lower_ot = sai_serialize_object_type(ot).substr(16); // 16 = skip "SAI_OBJECT_TYPE_" std::transform(lower_ot.begin(), lower_ot.end(), lower_ot.begin(), ::tolower); std::string stat_enum_name = "sai_" + lower_ot + "_stat_t"; const sai_enum_metadata_t* statenum = NULL; for (size_t i = 0; i < sai_metadata_all_enums_count; ++i) { if (sai_metadata_all_enums[i]->name == stat_enum_name) { SWSS_LOG_INFO("found enum %s", stat_enum_name.c_str()); // found statenum = sai_metadata_all_enums[i]; break; } } if (statenum == NULL) { SWSS_LOG_ERROR("failed to find stat enum: %s", stat_enum_name.c_str()); return; } for (auto v: values) { // value format: stat_enum_name:uint64 auto name = fvField(v); uint64_t value; if (sscanf(fvValue(v).c_str(), "%lu", &value) != 1) { SWSS_LOG_ERROR("failed to deserialize %s as couner value uint64_t", fvValue(v).c_str()); } // linear search int enumvalue = -1; for (size_t i = 0; i < statenum->valuescount; ++i) { if (statenum->valuesnames[i] == name) { enumvalue = statenum->values[i]; break; } } if (enumvalue == -1) { SWSS_LOG_ERROR("failed to find enum value: %s", name.c_str()); continue; } SWSS_LOG_DEBUG("writting %s = %lu on %s", name.c_str(), value, key.c_str()); countersMap.at(key)[enumvalue] = value; } }
void handleUnittestChannelOp( _In_ const std::string &op, _In_ const std::string &key, _In_ const std::vector<swss::FieldValueTuple> &values) { /* * Since we will access and modify DB we need to be under mutex. * * NOTE: since this unittest channel is handled in thread, then that means * there is a DELAY from producer and consumer thread in VS, so if user * will set value on the specific READ_ONLY value he should wait for some * time until that value will be propagated to virtual switch. */ MUTEX(); SWSS_LOG_ENTER(); SWSS_LOG_NOTICE("read only SET: op = %s, key = %s", op.c_str(), key.c_str()); if (op == SAI_VS_UNITTEST_ENABLE_UNITTESTS) { bool enable = (key == "true"); meta_unittests_enable(enable); } else if (op == SAI_VS_UNITTEST_SET_RO_OP) { for (const auto &v: values) { SWSS_LOG_DEBUG("attr: %s: %s", fvField(v).c_str(), fvValue(v).c_str()); } if (values.size() != 1) { SWSS_LOG_ERROR("expected 1 value only, but given: %zu", values.size()); return; } const std::string &str_object_type = key.substr(0, key.find(":")); const std::string &str_object_id = key.substr(key.find(":") + 1); sai_object_type_t object_type; sai_deserialize_object_type(str_object_type, object_type); if (object_type == SAI_OBJECT_TYPE_NULL || object_type >= SAI_OBJECT_TYPE_MAX) { SWSS_LOG_ERROR("invalid object type: %d", object_type); return; } auto info = sai_metadata_get_object_type_info(object_type); if (info->isnonobjectid) { SWSS_LOG_ERROR("non object id %s is not supported yet", str_object_type.c_str()); return; } sai_object_id_t object_id; sai_deserialize_object_id(str_object_id, object_id); sai_object_type_t ot = sai_object_type_query(object_id); if (ot != object_type) { SWSS_LOG_ERROR("object type is differnt than provided %s, but oid is %s", str_object_type.c_str(), sai_serialize_object_type(ot).c_str()); return; } sai_object_id_t switch_id = sai_switch_id_query(object_id); if (switch_id == SAI_NULL_OBJECT_ID) { SWSS_LOG_ERROR("failed to find switch id for oid %s", str_object_id.c_str()); return; } // oid is validated and we got switch id const std::string &str_attr_id = fvField(values.at(0)); const std::string &str_attr_value = fvValue(values.at(0)); auto meta = sai_metadata_get_attr_metadata_by_attr_id_name(str_attr_id.c_str()); if (meta == NULL) { SWSS_LOG_ERROR("failed to find attr %s", str_attr_id.c_str()); return; } if (meta->objecttype != ot) { SWSS_LOG_ERROR("attr %s belongs to differnt object type than oid: %s", str_attr_id.c_str(), sai_serialize_object_type(ot).c_str()); return; } // we got attr metadata sai_attribute_t attr; attr.id = meta->attrid; sai_deserialize_attr_value(str_attr_value, *meta, attr); SWSS_LOG_NOTICE("switch id is %s", sai_serialize_object_id(switch_id).c_str()); sai_status_t status = meta_unittests_allow_readonly_set_once(meta->objecttype, meta->attrid); if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("failed to enable SET readonly attribute once: %s", sai_serialize_status(status).c_str()); return; } sai_object_meta_key_t meta_key = { .objecttype = ot, .objectkey = { .key = { .object_id = object_id } } }; status = info->set(&meta_key, &attr); if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("failed to set %s to %s on %s", str_attr_id.c_str(), str_attr_value.c_str(), str_object_id.c_str()); } else { SWSS_LOG_NOTICE("SUCCESS to set %s to %s on %s", str_attr_id.c_str(), str_attr_value.c_str(), str_object_id.c_str()); } sai_deserialize_free_attribute_value(meta->attrvaluetype, attr); } else {
void ConsumerStateTable::pops(std::deque<KeyOpFieldsValuesTuple> &vkco, std::string /*prefix*/) { static std::string luaScript = "local ret = {}\n" "local keys = redis.call('SPOP', KEYS[1], ARGV[1])\n" "local n = table.getn(keys)\n" "for i = 1, n do\n" "local key = keys[i]\n" "local values = redis.call('HGETALL', KEYS[2] .. key)\n" "table.insert(ret, {key, values})\n" "end\n" "return ret\n"; static std::string sha = loadRedisScript(m_db, luaScript); RedisCommand command; command.format( "EVALSHA %s 2 %s %s: %d ''", sha.c_str(), getKeySetName().c_str(), getTableName().c_str(), POP_BATCH_SIZE); RedisReply r(m_db, command); auto ctx0 = r.getContext(); vkco.clear(); // if the set is empty, return an empty kco object if (ctx0->type == REDIS_REPLY_NIL) { return; } assert(ctx0->type == REDIS_REPLY_ARRAY); size_t n = ctx0->elements; vkco.resize(n); for (size_t ie = 0; ie < n; ie++) { auto& kco = vkco[ie]; auto& values = kfvFieldsValues(kco); assert(values.empty()); auto& ctx = ctx0->element[ie]; assert(ctx->elements == 2); assert(ctx->element[0]->type == REDIS_REPLY_STRING); std::string key = ctx->element[0]->str; kfvKey(kco) = key; assert(ctx->element[1]->type == REDIS_REPLY_ARRAY); auto ctx1 = ctx->element[1]; for (size_t i = 0; i < ctx1->elements / 2; i++) { FieldValueTuple e; fvField(e) = ctx1->element[i * 2]->str; fvValue(e) = ctx1->element[i * 2 + 1]->str; values.push_back(e); } // if there is no field-value pair, the key is already deleted if (values.empty()) { kfvOp(kco) = DEL_COMMAND; } else { kfvOp(kco) = SET_COMMAND; } } }