in syncd/ComparisonLogic.cpp [1165:1757]
bool ComparisonLogic::performObjectSetTransition(
_In_ AsicView ¤tView,
_In_ AsicView &temporaryView,
_In_ const std::shared_ptr<SaiObj> currentBestMatch,
_In_ std::shared_ptr<SaiObj> temporaryObj,
_In_ bool performTransition)
{
SWSS_LOG_ENTER();
/*
* All parents (if any) are in final state here, we could now search for
* best match in current view that some of the objects in temp final state
* could been created so they should exist in current view but without
* actual RID since all creation and RID mapping is done after all
* matching, so it may cause problems for finding RID for compare.
*/
/*
* When we have best match we need to determine whether current object can
* be updated to "this temporary" object or whether current needs to be
* destroyed and recreated according to temporary.
*/
std::set<sai_attr_id_t> processedAttributes;
/*
* Matched objects can have different attributes so we need to mark in
* processed attributes which one were processed so if current object has
* more attributes then we need to bring them back to default values if
* possible.
*/
/*
* Depending on performTransition flag this method used in first pass will
* determine whether current object can be updated to temporary one. If
* first pass was successful second pass when flag is set to true, actual
* SET operations will be generated and current view will be modified. No
* actual ASIC operations will be performed, all ASIC changes will be done
* after all object will be moved to final state.
*/
/*
* XXX If objects are matched (same vid/rid on object id) then this
* function must return true, just skip all create only attributes.
*/
for (auto &at: temporaryObj->getAllAttributes())
{
auto &temporaryAttr = at.second;
SWSS_LOG_INFO("first pass (temp): attr %s", temporaryAttr->getStrAttrId().c_str());
const auto meta = temporaryAttr->getAttrMetadata();
const sai_attribute_t &attr = *temporaryAttr->getSaiAttr();
processedAttributes.insert(attr.id); // mark attr id as processed
if (currentBestMatch->hasAttr(attr.id))
{
/*
* Same attribute exists on current and temp view, check if it's
* the same. Previously we used hasEqualAttribute method to find
* best match but now we are looking for different attribute
* values.
*/
auto currentAttr = currentBestMatch->getSaiAttr(attr.id);
SWSS_LOG_INFO("compare attr value curr %s vs temp %s",
currentBestMatch->getSaiAttr(attr.id)->getStrAttrValue().c_str(),
temporaryObj->getSaiAttr(attr.id)->getStrAttrValue().c_str());
if (BestCandidateFinder::hasEqualAttribute(currentView, temporaryView, currentBestMatch, temporaryObj, attr.id))
{
/*
* Attributes are equal so go for next attribute
*/
continue;
}
/*
* Now we know that attribute values are different.
*/
/*
* Here we don't need to check if attribute is mandatory on create
* or conditional since attribute is present on both objects. If
* attribute is CREATE_AND_SET that means we can update attribute
* value on current best match object.
*/
if (SAI_HAS_FLAG_CREATE_AND_SET(meta->flags))
{
SWSS_LOG_DEBUG("Attr %s can be updated from %s to %s",
meta->attridname,
currentAttr->getStrAttrValue().c_str(),
temporaryAttr->getStrAttrValue().c_str());
/*
* Generate action and update current view in second pass
* and continue for next attribute.
*/
if (performTransition)
{
setAttributeOnCurrentObject(currentView, temporaryView, currentBestMatch, temporaryAttr);
}
continue;
}
/*
* In this place we know attribute is CREATE_ONLY and it's value is
* different on both objects. Object must be destroyed and new
* object must be created. In this case does not matter whether
* attribute is mandatory on create or conditional since attribute
* is present on both objects.
*/
if (currentBestMatch->getObjectStatus() == SAI_OBJECT_STATUS_MATCHED)
{
/*
* This should not happen, since this mean, that attribute is
* create only, object is matched, and attribute value is
* different! DB is broken?
*/
SWSS_LOG_THROW("Attr %s CAN'T be updated from %s to %s on VID %s when MATCHED and CREATE_ONLY, FATAL",
meta->attridname,
currentAttr->getStrAttrValue().c_str(),
temporaryAttr->getStrAttrValue().c_str(),
temporaryObj->m_str_object_id.c_str());
}
SWSS_LOG_WARN("Attr %s CAN'T be updated from %s to %s since it's CREATE_ONLY",
meta->attridname,
currentAttr->getStrAttrValue().c_str(),
temporaryAttr->getStrAttrValue().c_str());
/*
* We return false since object can't be updated. Object creation
* is in different place when current best match is not found.
*/
return false;
}
/*
* In this case attribute exists only on temporary object. Because of
* different flags, and conditions this maybe not easy task to
* determine what should happen.
*
* Depends on attribute order processing, we may process mandatory on
* create conditional attribute first, before finding out that
* condition attribute is different, but if condition would be the same
* then this conditional attribute would be also present on current
* best match.
*
* There are also default values here that come into play. We can
* expand this logic in the future.
*/
bool conditional = meta->isconditional;
/*
* If attribute is CREATE_AND_SET and not conditional then it's
* safe to make SET operation.
*
* XXX previously we had (meta->flags == SAI_ATTR_FLAGS_CREATE_AND_SET)
* If it's not conditional current SAI_HAS_FLAG should not matter. But it
* can also be mandatory on create but this does not matter since if
* it's mandatory on create then current object already exists co we can
* still perform update on this attribute because it was passed during
* creation.
*/
if (SAI_HAS_FLAG_CREATE_AND_SET(meta->flags) && !conditional)
{
SWSS_LOG_INFO("Missing current attr %s can be set to %s",
meta->attridname,
temporaryAttr->getStrAttrValue().c_str());
/*
* There is another case here, if this attribute exists only in
* temporary view and it has default value, and SET value is the
* same as default, then there is no need for ASIC operation.
*
* NOTE: This can lead to not put attributes with default VALUE to
* redis database and could be confusing when debugging.
*/
const auto defaultValueAttr = BestCandidateFinder::getSaiAttrFromDefaultValue(currentView, m_switch, *meta);
if (defaultValueAttr != nullptr)
{
std::string defStr = sai_serialize_attr_value(*meta, *defaultValueAttr->getSaiAttr());
if (defStr == temporaryAttr->getStrAttrValue())
{
SWSS_LOG_NOTICE("explicit %s:%s is the same as default, no need for ASIC SET action",
meta->attridname, defStr.c_str());
continue;
}
}
/*
* Generate action and update current view in second pass
* and continue for next attribute.
*/
if (performTransition)
{
setAttributeOnCurrentObject(currentView, temporaryView, currentBestMatch, temporaryAttr);
}
continue;
}
if (currentBestMatch->getObjectStatus() == SAI_OBJECT_STATUS_MATCHED)
{
if (SAI_HAS_FLAG_CREATE_ONLY(meta->flags))
{
/*
* Attribute is create only attribute on matched object. This
* can happen when we are have create only attributes in asic
* view, those attributes were put by snoop logic. Since we
* skipping only read-only attributes then we snoop create-only
* also, but on "existing" objects this will cause problem and
* during apply logic we need to skip this attribute since we
* won't be able to SET it anyway on matched object, and value
* is the same as current object.
*/
SWSS_LOG_INFO("Skipping create only attr on matched object: %s:%s",
meta->attridname,
temporaryAttr->getStrAttrValue().c_str());
continue;
}
}
/*
* This is the most interesting case, we currently leave it here and we
* will support it later. Some other cases here also can be considered
* as success, for example if default value is the same as current
* value.
*/
SWSS_LOG_WARN("Missing current attr %s (conditional: %d) CAN'T be set to %s, flags: 0x%x, FIXME",
meta->attridname,
conditional,
temporaryAttr->getStrAttrValue().c_str(),
meta->flags);
/*
* We can't continue with update in that case, so return false.
*/
return false;
}
const bool beginTempSizeZero = temporaryObj->getAllAttributes().size() == 0;
/*
* Current best match can have more attributes than temporary object.
* let see if we can bring them to default value if possible.
*/
for (auto &ac: currentBestMatch->getAllAttributes())
{
auto ¤tAttr = ac.second;
const auto &meta = currentAttr->getAttrMetadata();
const sai_attribute_t &attr = *currentAttr->getSaiAttr();
if (processedAttributes.find(attr.id) != processedAttributes.end())
{
/*
* This attribute was processed in previous temporary attributes processing so skip it here.
*/
continue;
}
SWSS_LOG_INFO("first pass (curr): attr %s", currentAttr->getStrAttrId().c_str());
/*
* Check if we are bringing one of the default created objects to
* default value, since for that we will need dependency TREE. Most of
* the time user should just query configuration and just set new
* values based on get results, so this will not be needed.
*
* And even if we will have dependency tree, those values may not be
* synced because of remove etc, so we will need to check if default
* values actually exists.
*/
if (currentBestMatch->isOidObject())
{
sai_object_id_t vid = currentBestMatch->getVid();
/*
* Current best match may be created, check if vid/rid exist.
*/
if (currentView.hasVid(vid))
{
sai_object_id_t rid = currentView.m_vidToRid.at(vid);
if (m_switch->isDiscoveredRid(rid))
{
SWSS_LOG_INFO("performing default on existing object VID %s: %s: %s, we need default dependency TREE, FIXME",
sai_serialize_object_id(vid).c_str(),
meta->attridname,
currentAttr->getStrAttrValue().c_str());
}
}
}
/*
* We should not have MANDATORY_ON_CREATE attributes here since all
* mandatory on create (even conditional) should be present in in
* previous loop and they are matching, so we should get here
* CREATE_ONLY or CREATE_AND_SET attributes only. So we should not get
* conditional attributes here also, but lets take extra care about that
* just as sanity check.
*/
bool conditional = meta->isconditional;
if (conditional || SAI_HAS_FLAG_MANDATORY_ON_CREATE(meta->flags))
{
if (currentBestMatch->getObjectStatus() == SAI_OBJECT_STATUS_MATCHED &&
SAI_HAS_FLAG_CREATE_AND_SET(meta->flags))
{
if (meta->objecttype == SAI_OBJECT_TYPE_PORT &&
meta->attrid == SAI_PORT_ATTR_SPEED)
{
/*
* NOTE: for SPEED we could query each port at start and
* save it's speed to recover here, or even we could query
* each attribute on existing object during discovery
* process.
*/
SWSS_LOG_WARN("No previous value specified on %s (VID), can't bring to default, leaving attr unchanged: %s:%s",
sai_serialize_object_id(currentBestMatch->getVid()).c_str(),
meta->attridname,
currentAttr->getStrAttrValue().c_str());
continue;
}
if (meta->objecttype == SAI_OBJECT_TYPE_SCHEDULER_GROUP &&
meta->attrid == SAI_SCHEDULER_GROUP_ATTR_SCHEDULER_PROFILE_ID)
{
/*
* This attribute can hold reference to user created
* objects which maybe required to be destroyed, that's why
* we need to bring real value. What if real value were
* removed?
*/
sai_object_id_t def = SAI_NULL_OBJECT_ID;
sai_object_id_t vid = currentBestMatch->getVid();
if (currentView.hasVid(vid))
{
// scheduler_group RID
sai_object_id_t rid = currentView.m_vidToRid.at(vid);
rid = m_switch->getDefaultValueForOidAttr(rid, SAI_SCHEDULER_GROUP_ATTR_SCHEDULER_PROFILE_ID);
if (rid != SAI_NULL_OBJECT_ID && currentView.hasRid(rid))
{
/*
* We found default value
*/
SWSS_LOG_DEBUG("found default rid %s, vid %s for %s",
sai_serialize_object_id(rid).c_str(),
sai_serialize_object_id(vid).c_str(),
meta->attridname);
def = currentView.m_ridToVid.at(rid);
}
}
sai_attribute_t defattr;
defattr.id = meta->attrid;
defattr.value.oid = def;
std::string str_attr_value = sai_serialize_attr_value(*meta, defattr, false);
auto defaultValueAttr = std::make_shared<SaiAttr>(meta->attridname, str_attr_value);
if (performTransition)
{
setAttributeOnCurrentObject(currentView, temporaryView, currentBestMatch, defaultValueAttr);
}
continue;
}
// current best match is MATCHED
//auto vid = currentBestMatch->getVid();
// TODO don't transfer oid attributes, we don't know how to handle this yet
// If OA did GET on any attributes, snoop in syncd should catch that and write
// to database so we would have some attributes here.
if (beginTempSizeZero && !meta->isoidattribute)
{
SWSS_LOG_WARN("current attr is MoC|CaS and object is MATCHED: %s transferring %s:%s to temp object (was empty)",
currentBestMatch->m_str_object_id.c_str(),
meta->attridname,
currentAttr->getStrAttrValue().c_str());
std::shared_ptr<SaiAttr> transferedAttr = std::make_shared<SaiAttr>(
currentAttr->getStrAttrId(),
currentAttr->getStrAttrValue());
temporaryObj->setAttr(transferedAttr);
continue;
}
// SAI_QUEUE_ATTR_PARENT_SCHEDULER_NODE
// SAI_SCHEDULER_GROUP_ATTR_SCHEDULER_PROFILE_ID*
// SAI_SCHEDULER_GROUP_ATTR_PARENT_NODE
// SAI_BRIDGE_PORT_ATTR_BRIDGE_ID
//
// TODO matched by ID (MATCHED state) should always be updatable
// except those 4 above (at least for those above since they can have
// default value present after switch creation
// TODO SAI_SCHEDULER_GROUP_ATTR_SCHEDULER_PROFILE_ID is mandatory on create but also SET
// if attribute is set we and object is in MATCHED state then that means we are able to
// bring this attribute to default state not for all attributes!
// *SAI_SCHEDULER_GROUP_ATTR_SCHEDULER_PROFILE_ID - is not any more mandatory on create, so default should be NULL
SWSS_LOG_ERROR("current attribute is mandatory on create, crate and set, and object MATCHED, FIXME %s %s:%s",
currentBestMatch->m_str_object_id.c_str(),
meta->attridname,
currentAttr->getStrAttrValue().c_str());
return false;
}
if (currentBestMatch->getObjectStatus() == SAI_OBJECT_STATUS_MATCHED)
{
if (SAI_HAS_FLAG_CREATE_ONLY(meta->flags))
{
/*
* Attribute is create only attribute on matched object. This
* can happen when we are have create only attributes in asic
* view, those attributes were put by snoop logic. Since we
* skipping only read-only attributes then we snoop create-only
* also, but on "existing" objects this will cause problem and
* during apply logic we need to skip this attribute since we
* won't be able to SET it anyway on matched object, and value
* is the same as current object.
*/
SWSS_LOG_INFO("Skipping create only attr on matched object: %s:%s",
meta->attridname,
currentAttr->getStrAttrValue().c_str());
// don't produce too much noise for queues
if (currentAttr->getStrAttrId() != "SAI_QUEUE_ATTR_TYPE")
{
SWSS_LOG_WARN("current attr is CREATE_ONLY and object is MATCHED: %s transferring %s:%s to temp object",
currentBestMatch->m_str_object_id.c_str(),
meta->attridname,
currentAttr->getStrAttrValue().c_str());
}
std::shared_ptr<SaiAttr> transferedAttr = std::make_shared<SaiAttr>(
currentAttr->getStrAttrId(),
currentAttr->getStrAttrValue());
temporaryObj->setAttr(transferedAttr);
continue;
}
}
SWSS_LOG_ERROR("Present current attr %s:%s is conditional or MANDATORY_ON_CREATE, we don't expect this here, FIXME",
meta->attridname,
currentAttr->getStrAttrValue().c_str());
/*
* We don't expect conditional or mandatory on create attributes,
* since in previous loop all attributes were matching they also
* had to match. If we hit this case we need to take a closer look
* since it will mean we have a bug somewhere.
*/
return false;
}
/*
* If attribute is CREATE_AND_SET or CREATE_ONLY, they may have a
* default value, for create and set we maybe able to set it and for
* create only we just need to make sure its expected value, if not
* then it can't be updated and we need to return false.
*
* TODO Currently we will support limited default value types.
*
* Later this comparison of default value we need to extract to
* separate functions. Maybe create SaiAttr from default value or
* nullptr if it's not supported yet.
*/
if (meta->flags == SAI_ATTR_FLAGS_CREATE_AND_SET || meta->flags == SAI_ATTR_FLAGS_CREATE_ONLY)
{
// TODO default value for existing objects needs dependency tree
const auto defaultValueAttr = BestCandidateFinder::getSaiAttrFromDefaultValue(currentView, m_switch, *meta);
if (defaultValueAttr == nullptr)
{
SWSS_LOG_WARN("Can't get default value for present current attr %s:%s, FIXME",
meta->attridname,
currentAttr->getStrAttrValue().c_str());
/*
* If we can't get default value then we can't do set, because
* we don't know with what, so we need to destroy current
* object and recreate new one from temporary.
*/
return false;
}
if (currentAttr->getStrAttrValue() == defaultValueAttr->getStrAttrValue())
{
SWSS_LOG_INFO("Present current attr %s value %s is the same as default value, no action needed",
meta->attridname,
currentAttr->getStrAttrValue().c_str());
continue;
}
if (meta->flags == SAI_ATTR_FLAGS_CREATE_ONLY)
{
SWSS_LOG_WARN("Present current attr %s:%s has default that CAN'T be set to %s since it's CREATE_ONLY",
meta->attridname,
currentAttr->getStrAttrValue().c_str(),
defaultValueAttr->getStrAttrValue().c_str());
return false;
}
SWSS_LOG_INFO("Present current attr %s:%s has default that can be set to %s",
meta->attridname,
currentAttr->getStrAttrValue().c_str(),
defaultValueAttr->getStrAttrValue().c_str());
/*
* Generate action and update current view in second pass
* and continue for next attribute.
*/
if (performTransition)
{
setAttributeOnCurrentObject(currentView, temporaryView, currentBestMatch, defaultValueAttr);
}
continue;
}
SWSS_LOG_THROW("we should not get here, we have a bug, current present attribute %s:%s has some wrong flags 0x%x",
meta->attridname,
currentAttr->getStrAttrValue().c_str(),
meta->flags);
}
/*
* All attributes were processed, and ether no changes are required or all
* changes can be performed or some missing attributes has exact value as
* default value.
*/
return true;
}