meta/saisanitycheck.c (4,012 lines of code) (raw):
/**
* Copyright (c) 2014 Microsoft Open Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT
* LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS
* FOR A PARTICULAR PURPOSE, MERCHANTABILITY OR NON-INFRINGEMENT.
*
* See the Apache Version 2.0 License for specific language governing
* permissions and limitations under the License.
*
* Microsoft would like to thank the following companies for their review and
* assistance with these files: Intel Corporation, Mellanox Technologies Ltd,
* Dell Products, L.P., Facebook, Inc., Marvell International Ltd.
*
* @file saisanitycheck.c
*
* @brief Defines SAI metadata sanity check
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <alloca.h>
#include <sai.h>
#include <saiversion.h>
#include "saimetadatautils.h"
#include "saimetadata.h"
#include "saimetadatalogger.h"
typedef struct _defined_attr_t {
const sai_attr_metadata_t* metadata;
const struct _defined_attr_t* next;
} defined_attr_t;
bool debug = false;
defined_attr_t* defined_attributes = NULL;
#define META_LOG_DEBUG(format, ...)\
if (debug) { printf("DEBUG: " format "\n", ##__VA_ARGS__); }
#define META_LOG_WARN(format, ...)\
fprintf(stderr, "WARN: " format "\n", ##__VA_ARGS__);
#define META_LOG_INFO(format, ...)\
fprintf(stderr, "INFO: " format "\n", ##__VA_ARGS__);
#define META_LOG_ENTER() \
META_LOG_DEBUG(":> %s", __FUNCTION__);
#define META_ENUM_LOG_WARN(emd, format, ...)\
META_LOG_WARN("%s: " format, emd->name, ##__VA_ARGS__);
#define META_MD_LOG_DEBUG(md, format, ...)\
META_LOG_DEBUG("%s: " format, md->attridname, ##__VA_ARGS__);
#define META_MD_LOG_WARN(md, format, ...)\
META_LOG_WARN("%s: " format, md->attridname, ##__VA_ARGS__);
#define META_ASSERT_FAIL(format, ...) \
{ \
fprintf(stderr, \
" ASSERT FAILED (on line %d): " format "\n", \
__LINE__, ##__VA_ARGS__); \
exit(1); \
}
#define META_MD_ASSERT_FAIL(md, format, ...)\
META_ASSERT_FAIL("%s: " format, md->attridname, ##__VA_ARGS__);
#define META_ENUM_ASSERT_FAIL(emd, format, ...)\
META_ASSERT_FAIL("%s: " format, emd->name, ##__VA_ARGS__);
#define META_ASSERT_NOT_NULL(x)\
if ((x) == NULL) { META_ASSERT_FAIL("not expected NULL: " #x); }
#define META_ASSERT_NULL(x)\
if ((x) != NULL) { META_ASSERT_FAIL("expected NULL: " #x); }
#define META_ASSERT_TRUE(x, format, ...)\
if ((x) == false) { META_ASSERT_FAIL("expected true '" #x "': " format, ##__VA_ARGS__); }
#define META_ASSERT_FALSE(x, format, ...)\
if ((x) == true) { META_ASSERT_FAIL("expected false '" #x "': " format, ##__VA_ARGS__); }
/* custom ranges start are the same for all objects */
#define CUSTOM_ATTR_RANGE_START SAI_PORT_ATTR_CUSTOM_RANGE_START
#define EXTENSION_RANGE_START (0x20000000)
#define EXTENSION_OBJECT_TYPE_COUNT (SAI_OBJECT_TYPE_EXTENSIONS_RANGE_END - SAI_OBJECT_TYPE_EXTENSIONS_RANGE_START)
#define TOTAL_OBJECT_TYPE_COUNT (EXTENSION_OBJECT_TYPE_COUNT + SAI_OBJECT_TYPE_MAX)
bool is_extensions_enum(
_In_ const sai_enum_metadata_t* emd)
{
META_LOG_ENTER();
return strstr(emd->name, "_extensions_t") != NULL;
}
void check_all_enums_name_pointers()
{
META_LOG_ENTER();
size_t i = 0;
META_ASSERT_TRUE(sai_metadata_all_enums_count > 300, "we need to have some enums");
for (; i < sai_metadata_all_enums_count; ++i)
{
const sai_enum_metadata_t* emd = sai_metadata_all_enums[i];
META_ASSERT_NOT_NULL(emd);
META_LOG_DEBUG("enum: %s", emd->name);
META_ASSERT_NOT_NULL(emd->name);
META_ASSERT_NOT_NULL(emd->values);
META_ASSERT_NOT_NULL(emd->valuesnames);
META_ASSERT_NOT_NULL(emd->valuesshortnames);
if (is_extensions_enum(emd))
{
/* allow empty extensions enums */
if (emd->valuescount == 0)
META_LOG_WARN("enum %s has no values", emd->name);
}
else
{
META_ASSERT_TRUE(emd->valuescount > 0, "enum must have some values");
}
size_t j = 0;
for (; j < emd->valuescount; ++j)
{
META_LOG_DEBUG(" value: %s", emd->valuesnames[j]);
META_ASSERT_NOT_NULL(emd->valuesnames[j]);
META_ASSERT_NOT_NULL(emd->valuesshortnames[j]);
}
META_ASSERT_TRUE(emd->valuesnames[j] == NULL, "missing null pointer after enum names");
META_ASSERT_TRUE(emd->valuesshortnames[j] == NULL, "missing null pointer after enum short names");
}
}
bool is_flag_enum(const sai_enum_metadata_t* emd)
{
META_LOG_ENTER();
return emd->containsflags;
}
void check_all_enums_values()
{
META_LOG_ENTER();
size_t i = 0;
for (; i < sai_metadata_all_enums_count; ++i)
{
const sai_enum_metadata_t* emd = sai_metadata_all_enums[i];
META_LOG_DEBUG("enum: %s", emd->name);
bool flags = false;
size_t j = 0;
int last = -1;
if (strcmp(emd->name, "sai_status_t") == 0)
{
/* status values are negative */
continue;
}
for (; j < emd->valuescount; ++j)
{
META_LOG_DEBUG(" value: %s", emd->valuesnames[j]);
int value = emd->values[j];
META_ASSERT_FALSE(value < 0, "enum values are negative");
META_ASSERT_TRUE(last < value, "enum values are not increasing");
if (j == 0)
{
if (value != 0)
{
flags = true;
if (is_flag_enum(emd))
{
/* ok, flags not need zero enum */
}
else
{
META_ENUM_ASSERT_FAIL(emd, "first enum should start with zero");
}
}
}
if (value != last + 1)
{
if (value == CUSTOM_ATTR_RANGE_START)
{
/*
* Object contains custom attributes in custom range, they
* still needs to be increasing by 1 but since those are
* not flags, attribute value can't be used as array index.
*/
META_ENUM_LOG_WARN(emd, "contains custom range attributes");
}
else if (value == EXTENSION_RANGE_START)
{
/*
* Object contains extensions attributes, they still needs
* to be increasing by 1 but since those are not flags,
* attribute value can't be used as array index.
*/
META_ENUM_LOG_WARN(emd, "contains extensions range attributes");
}
else
{
flags = true;
if (is_flag_enum(emd))
{
/* flags, ok */
}
else
{
META_ENUM_ASSERT_FAIL(emd, "values are not increasing by 1: last: %d current: %d, should be marked as @flags?", last, value);
}
}
}
last = emd->values[j];
if (value >= CUSTOM_ATTR_RANGE_START && value < EXTENSION_RANGE_START)
{
/* value is in custom range */
}
else if (value >= EXTENSION_RANGE_START)
{
/* value is in extensions range */
}
else
{
META_ASSERT_TRUE(value < 0x10000, "enum value is too big, range?");
}
}
META_ASSERT_TRUE(emd->values[j] == -1, "missing guard at the end of enum");
if (emd->valuescount > 0 && flags != emd->containsflags)
{
META_ENUM_ASSERT_FAIL(emd, "enum flags: %d but declared as %d", flags, emd->containsflags);
}
}
}
void check_enums_ignore_values()
{
META_LOG_ENTER();
size_t i = 0;
for (; i < sai_metadata_all_enums_count; ++i)
{
const sai_enum_metadata_t* emd = sai_metadata_all_enums[i];
META_LOG_DEBUG("enum: %s", emd->name);
if (emd->ignorevalues == NULL)
{
META_ASSERT_TRUE(emd->ignorevaluesnames == NULL, "names must be NULL when values are NULL");
continue;
}
META_ASSERT_TRUE(emd->ignorevaluesnames != NULL, "names must be NOT NULL when values are defined");
size_t j = 0;
for (; emd->ignorevalues[j] != -1; ++j)
{
META_LOG_DEBUG(" value: %s", emd->ignorevaluesnames[j]);
META_ASSERT_TRUE(emd->ignorevaluesnames[j] != NULL, "names must be NOT NULL when values are defined");
int value = emd->ignorevalues[j];
META_ASSERT_FALSE(value < 0, "enum values are negative");
bool contains = false;
size_t k = 0;
for (; k < emd->valuescount; k++)
{
if (emd->values[k] == value)
{
contains = true;
break;
}
}
META_ASSERT_TRUE(contains, "ignored enum value must be defined in enum values");
}
META_ASSERT_TRUE(emd->ignorevalues[j] == -1, "missing guard at the end of enum");
META_ASSERT_TRUE(emd->ignorevaluesnames[j] == NULL, "missing guard at the end of enum");
}
}
void check_sai_status()
{
META_LOG_ENTER();
META_ASSERT_TRUE(SAI_STATUS_SUCCESS == 0, "success must be zero");
META_ASSERT_TRUE(sai_metadata_enum_sai_status_t.valuescount > 1, "there must be error codes");
size_t i = 0;
int last = 1;
for (; i < sai_metadata_enum_sai_status_t.valuescount; ++i)
{
META_LOG_DEBUG("status: %s", sai_metadata_enum_sai_status_t.valuesnames[i]);
int value = sai_metadata_enum_sai_status_t.values[i];
if (i == 0)
{
META_ASSERT_TRUE(value == 0, "first must be status success");
}
else
{
META_ASSERT_TRUE(last > value, "status codes are not decreasing");
}
last = value;
}
META_ASSERT_TRUE(sai_metadata_enum_sai_status_t.containsflags, "sai_status_t must be marked as containsflags");
}
void check_object_type()
{
META_LOG_ENTER();
META_ASSERT_TRUE(SAI_OBJECT_TYPE_NULL == 0, "sai object type null must be zero");
size_t i = 0;
int last = -1; /* will enforce NULL be first */
for (; i < sai_metadata_enum_sai_object_type_t.valuescount; ++i)
{
META_LOG_DEBUG("object_type: %s", sai_metadata_enum_sai_object_type_t.valuesnames[i]);
int value = sai_metadata_enum_sai_object_type_t.values[i];
if (last < value && value == EXTENSION_RANGE_START)
{
/* ok, but object type can't be used as array index any more */
}
else
{
META_ASSERT_TRUE(value == last + 1, "object type values must be consecutive numbers");
}
last = value;
}
/*
* In long distance future this could be relaxed, but it will have impact
* on sonic and vendors. As currently object type is encoded on a single
* byte, and with this extensions change it will be encoded on 9 bits in
* sonic:
* - 8 bits for object type under SAI_OBJECT_TYPE_MAX) and extensions bit equal to 0
* - 8 bits for extension object types reduced by 0x20000000 and extension bit seq to 1
* This approach will allow to encode 255 object type for each range.
*/
META_ASSERT_TRUE(SAI_OBJECT_TYPE_MAX < 256, "object types must be able to encode on 1 byte");
i = SAI_OBJECT_TYPE_NULL;
for (; i < SAI_OBJECT_TYPE_MAX; i++)
{
int value = sai_metadata_enum_sai_object_type_t.values[i];
META_ASSERT_TRUE(value == (int)i, "values from SAI_OBJECT_TYPE_NULL to SAI_OBJECT_TYPE_MAX must increase by 1");
}
}
void check_attr_by_object_type()
{
META_LOG_ENTER();
/*
* Extensions object types for now should be minimum, since it could be
* encoded on 1 byte in OID, but this could be later on relaxed.
*/
META_ASSERT_TRUE(EXTENSION_OBJECT_TYPE_COUNT < 64, "too many experimental object types");
META_ASSERT_TRUE(sai_metadata_attr_by_object_type_count == (EXTENSION_OBJECT_TYPE_COUNT + SAI_OBJECT_TYPE_MAX), "invalid object type count in metadata");
size_t idx = 1;
for (; sai_metadata_all_object_type_infos[idx]; idx++)
{
META_LOG_DEBUG("processing %zu, %s", idx, sai_metadata_get_object_type_name(sai_metadata_all_object_type_infos[idx]->objecttype));
const sai_attr_metadata_t * const* const ot = sai_metadata_all_object_type_infos[idx]->attrmetadata;
size_t index = 0;
while (ot[index] != NULL)
{
sai_object_type_t current = ot[index]->objecttype;
META_ASSERT_TRUE(current == sai_metadata_all_object_type_infos[idx]->objecttype, "object type must be equal on object type list");
/*
* For Switch Attribute we have crossed > 300 with Vendor extension
* for SAI v1.8.0 so increasing threshold.
*/
META_ASSERT_TRUE(index < 300, "object defines > 300 attributes, metadata bug?");
META_ASSERT_TRUE(current > SAI_OBJECT_TYPE_NULL, "object type must be > NULL");
if (current > SAI_OBJECT_TYPE_NULL && current < SAI_OBJECT_TYPE_MAX)
{
/* ok */
}
else if (current >= (int)SAI_OBJECT_TYPE_EXTENSIONS_RANGE_START && current < (int)SAI_OBJECT_TYPE_EXTENSIONS_RANGE_END)
{
/* ok */
}
else
{
META_ASSERT_FAIL("invalid object type number: %d", current);
}
/* META_LOG_DEBUG("processing indexer %lu", index); */
index++;
}
META_LOG_DEBUG("attr index %zu for %s", index, sai_metadata_get_object_type_name(sai_metadata_all_object_type_infos[idx]->objecttype));
}
META_ASSERT_NULL(sai_metadata_all_object_type_infos[idx]);
}
bool is_valid_object_type(
_In_ sai_object_type_t ot)
{
META_LOG_ENTER();
/* possible later to add custom range, iterate over all OT */
if (ot > SAI_OBJECT_TYPE_NULL && ot < SAI_OBJECT_TYPE_MAX)
return true;
if (ot >= (int)SAI_OBJECT_TYPE_EXTENSIONS_RANGE_START && ot < (int)SAI_OBJECT_TYPE_EXTENSIONS_RANGE_END)
return true;
return false;
}
void check_attr_object_type(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
if (!is_valid_object_type(md->objecttype))
{
META_MD_ASSERT_FAIL(md, "invalid object type value %d", md->objecttype);
}
}
void check_attr_value_type_range(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
META_ASSERT_NOT_NULL(sai_metadata_get_enum_value_name(&sai_metadata_enum_sai_attr_value_type_t, md->attrvaluetype));
}
bool sai_metadata_is_acl_field_or_action(
_In_ const sai_attr_metadata_t* metadata)
{
if (metadata == NULL)
{
return false;
}
if (metadata->objecttype == SAI_OBJECT_TYPE_ACL_ENTRY)
{
if (metadata->attrid >= SAI_ACL_ENTRY_ATTR_FIELD_START &&
metadata->attrid <= SAI_ACL_ENTRY_ATTR_FIELD_END)
{
return true;
}
if (metadata->attrid >= SAI_ACL_ENTRY_ATTR_ACTION_START &&
metadata->attrid <= SAI_ACL_ENTRY_ATTR_ACTION_END)
{
return true;
}
if (metadata->isextensionattr)
{
return true;
}
}
if (metadata->objecttype == SAI_OBJECT_TYPE_UDF_MATCH)
{
if (metadata->attrid <= SAI_UDF_MATCH_ATTR_GRE_TYPE)
{
return true;
}
if (metadata->attrid == SAI_UDF_MATCH_ATTR_L4_DST_PORT_TYPE)
{
return true;
}
}
return false;
}
void check_attr_flags(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
switch ((int)md->flags)
{
case SAI_ATTR_FLAGS_MANDATORY_ON_CREATE | SAI_ATTR_FLAGS_CREATE_ONLY | SAI_ATTR_FLAGS_KEY:
case SAI_ATTR_FLAGS_MANDATORY_ON_CREATE | SAI_ATTR_FLAGS_CREATE_ONLY:
case SAI_ATTR_FLAGS_MANDATORY_ON_CREATE | SAI_ATTR_FLAGS_CREATE_AND_SET:
if (md->validonlytype != SAI_ATTR_CONDITION_TYPE_NONE)
{
META_MD_ASSERT_FAIL(md, "valid only attribute can't be mandatory on create, use condition");
}
/*
* Currently we don't use RANGE type so this check can be disabled.
* Range idea was introduces so attribute can be in specific range
* of other MIN/MAX attributes. This is not supported yet.
*
* if (md->attrvaluetype == SAI_ATTR_VALUE_TYPE_UINT32 &&
* md->defaultvaluetype == SAI_DEFAULT_VALUE_TYPE_ATTR_RANGE)
* {
* break;
* }
*/
if (md->defaultvaluetype != SAI_DEFAULT_VALUE_TYPE_NONE)
{
META_MD_ASSERT_FAIL(md, "no default value expected, but type provided: %s",
sai_metadata_get_default_value_type_name(md->defaultvaluetype));
}
break;
case SAI_ATTR_FLAGS_CREATE_ONLY:
case SAI_ATTR_FLAGS_CREATE_AND_SET:
if (md->defaultvaluetype == SAI_DEFAULT_VALUE_TYPE_NONE)
{
/*
* Default value of object id can be internal or NULL and it
* needs to be specified explicitly.
*/
/*
* Default value for pointer must be specified and must be NULL.
*/
/*
* Default value for acl field or action is provided and is
* disabled by default.
*/
META_MD_ASSERT_FAIL(md, "expected default value, but none provided");
}
break;
case SAI_ATTR_FLAGS_READ_ONLY:
if (md->conditiontype != SAI_ATTR_CONDITION_TYPE_NONE)
{
META_MD_ASSERT_FAIL(md, "read only attribute can't be conditional");
}
if (md->validonlytype != SAI_ATTR_CONDITION_TYPE_NONE)
{
META_MD_ASSERT_FAIL(md, "read only attribute can't be valid only");
}
if (md->defaultvaluetype == SAI_DEFAULT_VALUE_TYPE_SWITCH_INTERNAL)
{
if (md->attrvaluetype == SAI_ATTR_VALUE_TYPE_OBJECT_ID ||
md->attrvaluetype == SAI_ATTR_VALUE_TYPE_OBJECT_LIST)
{
/*
* Read only object id/list can be marked as internal like
* default virtual router, cpu port id, default queues on
* ports, etc.
*/
break;
}
}
if (md->defaultvaluetype != SAI_DEFAULT_VALUE_TYPE_NONE)
{
META_MD_ASSERT_FAIL(md, "no default value expected, but type provided: %s",
sai_metadata_get_default_value_type_name(md->defaultvaluetype));
}
break;
default:
META_MD_ASSERT_FAIL(md, "invalid creation flags: 0x%x", md->flags);
}
META_ASSERT_TRUE(SAI_HAS_FLAG_MANDATORY_ON_CREATE(md->flags) == md->ismandatoryoncreate, "wrong ismandatoryoncreate");
META_ASSERT_TRUE(SAI_HAS_FLAG_CREATE_ONLY(md->flags) == md->iscreateonly, "wrong iscreateonly");
META_ASSERT_TRUE(SAI_HAS_FLAG_CREATE_AND_SET(md->flags) == md->iscreateandset, "wrong iscreateandset");
META_ASSERT_TRUE(SAI_HAS_FLAG_READ_ONLY(md->flags) == md->isreadonly, "wrong isreadonly");
META_ASSERT_TRUE(SAI_HAS_FLAG_KEY(md->flags) == md->iskey, "wrong iskey");
}
void check_attr_object_id_allownull(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
if (md->attrvaluetype != SAI_ATTR_VALUE_TYPE_OBJECT_ID)
{
/*
* We don't care about ACL entry data/field, in that when we set NULL
* as object id on acl data/field it should mean as disable, or it
* should be not allowed at all.
*/
return;
}
/* attribute is object id type */
switch ((int)md->flags)
{
case SAI_ATTR_FLAGS_CREATE_ONLY:
case SAI_ATTR_FLAGS_CREATE_AND_SET:
/* default value is required */
switch (md->defaultvaluetype)
{
case SAI_DEFAULT_VALUE_TYPE_CONST:
if (md->allownullobjectid == false)
{
/*
* since attribute require default value and default value is
* set to SAI_NULL_OBJECT_ID then allownull should be true
*/
META_MD_ASSERT_FAIL(md, "allow null object id should be set to true since default value is required");
}
break;
case SAI_DEFAULT_VALUE_TYPE_ATTR_VALUE:
/* default attr value from another attr may not support null */
break;
default:
META_MD_ASSERT_FAIL(md, "invalid default value type on object id when default is required");
break;
}
break;
default:
break;
}
}
void check_attr_object_type_provided(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
switch (md->attrvaluetype)
{
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_LIST:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_ID:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_LIST:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_ID:
case SAI_ATTR_VALUE_TYPE_OBJECT_LIST:
case SAI_ATTR_VALUE_TYPE_OBJECT_ID:
if (md->allowedobjecttypes == NULL)
{
META_MD_ASSERT_FAIL(md, "object types list is required but it's empty");
}
break;
case SAI_ATTR_VALUE_TYPE_BOOL:
case SAI_ATTR_VALUE_TYPE_INT8:
case SAI_ATTR_VALUE_TYPE_INT32:
case SAI_ATTR_VALUE_TYPE_INT8_LIST:
case SAI_ATTR_VALUE_TYPE_UINT8_LIST:
case SAI_ATTR_VALUE_TYPE_UINT16_LIST:
case SAI_ATTR_VALUE_TYPE_INT32_LIST:
case SAI_ATTR_VALUE_TYPE_UINT8:
case SAI_ATTR_VALUE_TYPE_UINT16:
case SAI_ATTR_VALUE_TYPE_INT16:
case SAI_ATTR_VALUE_TYPE_VLAN_LIST:
case SAI_ATTR_VALUE_TYPE_UINT32:
case SAI_ATTR_VALUE_TYPE_UINT64:
case SAI_ATTR_VALUE_TYPE_MAC:
case SAI_ATTR_VALUE_TYPE_POINTER:
case SAI_ATTR_VALUE_TYPE_IP_ADDRESS:
case SAI_ATTR_VALUE_TYPE_IP_PREFIX:
case SAI_ATTR_VALUE_TYPE_PRBS_RX_STATE:
case SAI_ATTR_VALUE_TYPE_CHARDATA:
case SAI_ATTR_VALUE_TYPE_UINT32_RANGE:
case SAI_ATTR_VALUE_TYPE_UINT16_RANGE_LIST:
case SAI_ATTR_VALUE_TYPE_UINT32_LIST:
case SAI_ATTR_VALUE_TYPE_QOS_MAP_LIST:
case SAI_ATTR_VALUE_TYPE_MAP_LIST:
case SAI_ATTR_VALUE_TYPE_ACL_CAPABILITY:
case SAI_ATTR_VALUE_TYPE_ACL_RESOURCE_LIST:
case SAI_ATTR_VALUE_TYPE_TLV_LIST:
case SAI_ATTR_VALUE_TYPE_SEGMENT_LIST:
case SAI_ATTR_VALUE_TYPE_IP_ADDRESS_LIST:
case SAI_ATTR_VALUE_TYPE_PORT_EYE_VALUES_LIST:
case SAI_ATTR_VALUE_TYPE_PORT_FREQUENCY_OFFSET_PPM_LIST:
case SAI_ATTR_VALUE_TYPE_PORT_SNR_LIST:
case SAI_ATTR_VALUE_TYPE_LATCH_STATUS:
case SAI_ATTR_VALUE_TYPE_PORT_LANE_LATCH_STATUS_LIST:
case SAI_ATTR_VALUE_TYPE_TIMESPEC:
case SAI_ATTR_VALUE_TYPE_JSON:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_BOOL:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT8:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_INT8:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT16:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_INT16:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_INT32:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT32:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT64:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_MAC:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_IPV4:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_IPV6:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT8_LIST:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_BOOL:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_UINT8:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_INT8:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_UINT16:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_INT16:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_UINT32:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_INT32:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_MAC:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_IPV4:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_IPV6:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_IP_ADDRESS:
case SAI_ATTR_VALUE_TYPE_IPV4:
case SAI_ATTR_VALUE_TYPE_IPV6:
case SAI_ATTR_VALUE_TYPE_ENCRYPT_KEY:
case SAI_ATTR_VALUE_TYPE_AUTH_KEY:
case SAI_ATTR_VALUE_TYPE_MACSEC_SAK:
case SAI_ATTR_VALUE_TYPE_MACSEC_AUTH_KEY:
case SAI_ATTR_VALUE_TYPE_MACSEC_SALT:
case SAI_ATTR_VALUE_TYPE_SYSTEM_PORT_CONFIG:
case SAI_ATTR_VALUE_TYPE_SYSTEM_PORT_CONFIG_LIST:
case SAI_ATTR_VALUE_TYPE_FABRIC_PORT_REACHABILITY:
case SAI_ATTR_VALUE_TYPE_PORT_ERR_STATUS_LIST:
case SAI_ATTR_VALUE_TYPE_IP_PREFIX_LIST:
case SAI_ATTR_VALUE_TYPE_ACL_CHAIN_LIST:
case SAI_ATTR_VALUE_TYPE_POE_PORT_POWER_CONSUMPTION:
if (md->allowedobjecttypes != NULL)
{
META_MD_ASSERT_FAIL(md, "allowed object types defined for non object type");
}
break;
default:
META_MD_ASSERT_FAIL(md, "attr value type is not supported, FIXME");
}
}
void check_attr_allowed_object_types(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
if (md->allowedobjecttypeslength != 0 && md->allowedobjecttypes == NULL)
{
META_MD_ASSERT_FAIL(md, "allowed object type len is specified but pointer is NULL");
}
if (md->allowedobjecttypeslength == 0 && md->allowedobjecttypes != NULL)
{
META_MD_ASSERT_FAIL(md, "allowed object type len zero, but but pointer to objects is specified");
}
if (md->allowedobjecttypes == NULL)
{
return;
}
if (md->isoidattribute)
{
META_ASSERT_TRUE(md->allowedobjecttypeslength > 0, "object len should be at least 1");
}
else
{
META_ASSERT_TRUE(md->allowedobjecttypeslength == 0, "object len should be 0");
}
switch (md->attrvaluetype)
{
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_LIST:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_ID:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_LIST:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_ID:
case SAI_ATTR_VALUE_TYPE_OBJECT_LIST:
case SAI_ATTR_VALUE_TYPE_OBJECT_ID:
break;
default:
META_MD_ASSERT_FAIL(md, "allowed object types should be empty on this attr value type");
}
/*
* check if allowed object types are in range
* they may repeat, but we can also check that
*/
size_t i = 0;
for (; i < md->allowedobjecttypeslength; ++i)
{
sai_object_type_t ot = md->allowedobjecttypes[i];
if (!is_valid_object_type(ot))
{
META_MD_ASSERT_FAIL(md, "invalid allowed object type: %d", ot);
}
const sai_object_type_info_t* info = sai_metadata_get_object_type_info(ot);
META_ASSERT_NOT_NULL(info);
if (info->isnonobjectid)
{
META_MD_ASSERT_FAIL(md, "non object id can't be used as object id: %d", ot);
}
if (ot == SAI_OBJECT_TYPE_SWITCH ||
ot == SAI_OBJECT_TYPE_FDB_FLUSH ||
ot == SAI_OBJECT_TYPE_HOSTIF_PACKET)
{
/* switch object type is meant to be used only in non object id struct types */
META_MD_ASSERT_FAIL(md, "switch object type can't be used as object type in any attribute");
}
}
}
void check_attr_default_required(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
bool requiredefault = (!SAI_HAS_FLAG_MANDATORY_ON_CREATE(md->flags)) &&
(SAI_HAS_FLAG_CREATE_ONLY(md->flags) || SAI_HAS_FLAG_CREATE_AND_SET(md->flags));
if (requiredefault == false)
{
return;
}
if (md->defaultvaluetype == SAI_DEFAULT_VALUE_TYPE_NONE)
{
/*
* If default value type is NONE, then default value must be NULL.
*/
META_ASSERT_NULL(md->defaultvalue);
/*
* By default we assume that default acl field or action is
* disabled and default value is not provided.
*/
META_MD_ASSERT_FAIL(md, "expected default value, but none provided");
}
switch (md->defaultvaluetype)
{
case SAI_DEFAULT_VALUE_TYPE_CONST:
if (md->objecttype == SAI_OBJECT_TYPE_UDF && md->attrid == SAI_UDF_ATTR_HASH_MASK)
{
if (md->attrvaluetype == SAI_ATTR_VALUE_TYPE_UINT8_LIST)
{
/*
* Const on list, this is exception for UDF object since
* it's default value is 2 bytes 0xFF,0xFF and it's special
* case.
*/
break;
}
}
if ((md->objecttype == SAI_OBJECT_TYPE_PORT) || (md->objecttype == SAI_OBJECT_TYPE_PORT_SERDES))
{
/*
* Allow PORT non object list attributes to be set to internal switch values.
*/
break;
}
if (md->defaultvalue == NULL)
{
META_MD_ASSERT_FAIL(md, "default value type is provided, but default value pointer is NULL");
}
break;
case SAI_DEFAULT_VALUE_TYPE_ATTR_VALUE:
case SAI_DEFAULT_VALUE_TYPE_ATTR_RANGE:
case SAI_DEFAULT_VALUE_TYPE_SWITCH_INTERNAL:
case SAI_DEFAULT_VALUE_TYPE_VENDOR_SPECIFIC:
case SAI_DEFAULT_VALUE_TYPE_EMPTY_LIST:
if (md->defaultvalue != NULL)
{
META_MD_ASSERT_FAIL(md, "default value type is provided, but default value pointer is not NULL");
}
break;
default:
META_MD_ASSERT_FAIL(md, "unknown default value type %d", md->defaultvaluetype);
}
/* default value is required */
switch (md->attrvaluetype)
{
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_BOOL:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT8:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_INT8:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT16:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_INT16:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_INT32:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT32:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT64:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_MAC:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_IPV4:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_IPV6:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_ID:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_BOOL:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_UINT8:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_INT8:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_UINT16:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_INT16:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_UINT32:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_INT32:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_MAC:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_IPV4:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_IPV6:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_IP_ADDRESS:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_ID:
if (md->defaultvaluetype == SAI_DEFAULT_VALUE_TYPE_CONST)
{
break;
}
META_MD_ASSERT_FAIL(md, "default value on acl field/action must be const/disabled");
case SAI_ATTR_VALUE_TYPE_OBJECT_ID:
case SAI_ATTR_VALUE_TYPE_BOOL:
case SAI_ATTR_VALUE_TYPE_INT32:
case SAI_ATTR_VALUE_TYPE_UINT8:
case SAI_ATTR_VALUE_TYPE_UINT16:
case SAI_ATTR_VALUE_TYPE_UINT32:
case SAI_ATTR_VALUE_TYPE_UINT64:
case SAI_ATTR_VALUE_TYPE_MAC:
case SAI_ATTR_VALUE_TYPE_IP_ADDRESS:
case SAI_ATTR_VALUE_TYPE_IP_PREFIX:
case SAI_ATTR_VALUE_TYPE_PRBS_RX_STATE:
case SAI_ATTR_VALUE_TYPE_TIMESPEC:
case SAI_ATTR_VALUE_TYPE_IPV4:
case SAI_ATTR_VALUE_TYPE_SYSTEM_PORT_CONFIG:
case SAI_ATTR_VALUE_TYPE_IPV6:
break;
case SAI_ATTR_VALUE_TYPE_CHARDATA:
if (md->defaultvaluetype == SAI_DEFAULT_VALUE_TYPE_CONST)
{
break;
}
META_MD_ASSERT_FAIL(md, "default value on chardata const");
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_LIST:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_LIST:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT8_LIST:
/* even if this is list, on acl field/action we require disabled */
if (md->defaultvaluetype == SAI_DEFAULT_VALUE_TYPE_CONST)
{
break;
}
META_MD_ASSERT_FAIL(md, "default value on acl field/action must be const/disabled");
case SAI_ATTR_VALUE_TYPE_INT8_LIST:
case SAI_ATTR_VALUE_TYPE_UINT32_LIST:
case SAI_ATTR_VALUE_TYPE_INT32_LIST:
case SAI_ATTR_VALUE_TYPE_OBJECT_LIST:
case SAI_ATTR_VALUE_TYPE_TLV_LIST:
case SAI_ATTR_VALUE_TYPE_SEGMENT_LIST:
case SAI_ATTR_VALUE_TYPE_MAP_LIST:
case SAI_ATTR_VALUE_TYPE_IP_ADDRESS_LIST:
case SAI_ATTR_VALUE_TYPE_PORT_EYE_VALUES_LIST:
case SAI_ATTR_VALUE_TYPE_PORT_FREQUENCY_OFFSET_PPM_LIST:
case SAI_ATTR_VALUE_TYPE_PORT_SNR_LIST:
case SAI_ATTR_VALUE_TYPE_PORT_LANE_LATCH_STATUS_LIST:
case SAI_ATTR_VALUE_TYPE_SYSTEM_PORT_CONFIG_LIST:
case SAI_ATTR_VALUE_TYPE_IP_PREFIX_LIST:
case SAI_ATTR_VALUE_TYPE_ACL_CHAIN_LIST:
if (((md->objecttype == SAI_OBJECT_TYPE_PORT) || (md->objecttype == SAI_OBJECT_TYPE_PORT_SERDES))
&& md->defaultvaluetype == SAI_DEFAULT_VALUE_TYPE_SWITCH_INTERNAL)
{
/*
* Allow non object lists on PORT to be set to internal default value.
*/
break;
}
if (md->defaultvaluetype == SAI_DEFAULT_VALUE_TYPE_EMPTY_LIST)
{
break;
}
META_MD_ASSERT_FAIL(md, "default value list is needed on this attr value type but list is NULL");
case SAI_ATTR_VALUE_TYPE_UINT8_LIST:
if (md->defaultvaluetype == SAI_DEFAULT_VALUE_TYPE_EMPTY_LIST)
{
break;
}
if (md->objecttype == SAI_OBJECT_TYPE_UDF && md->attrid == SAI_UDF_ATTR_HASH_MASK)
{
if (md->defaultvaluetype == SAI_DEFAULT_VALUE_TYPE_CONST)
{
/*
* Again this is exception only for this one UDF attribute
* to support CONST on list.
*/
break;
}
}
META_MD_ASSERT_FAIL(md, "default value list is needed on this attr value type but list is NULL");
case SAI_ATTR_VALUE_TYPE_UINT16_LIST:
if (md->defaultvaluetype == SAI_DEFAULT_VALUE_TYPE_EMPTY_LIST)
{
break;
}
META_MD_ASSERT_FAIL(md, "default value list is needed on this attr value type but list is NULL");
case SAI_ATTR_VALUE_TYPE_POINTER:
/*
* Gearbox exception for mandatory pointer attribute
* to support CONST on list.
*/
break;
case SAI_ATTR_VALUE_TYPE_JSON:
break;
default:
META_MD_ASSERT_FAIL(md, "default value is required but this attr value type is not supported yet");
}
}
void check_attr_enums(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
if (md->isenum)
{
switch (md->attrvaluetype)
{
case SAI_ATTR_VALUE_TYPE_INT32:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_INT32:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_INT32:
break;
default:
META_MD_ASSERT_FAIL(md, "attribute is marked as enum, but attr value type is not enum compatible");
}
}
if (md->isenum && md->isenumlist)
{
META_MD_ASSERT_FAIL(md, "attribute can't be marked as enum and enum list");
}
if ((md->isenum || md->isenumlist) && md->enummetadata == NULL)
{
META_MD_ASSERT_FAIL(md, "is marked enum but missing enum metadata");
}
if (!(md->isenum || md->isenumlist) && md->enummetadata != NULL)
{
META_MD_ASSERT_FAIL(md, "is not marked enum but has defined enum type string");
}
if ((md->isenum || md->isenumlist) && md->enummetadata->valuescount == 0)
{
META_MD_ASSERT_FAIL(md, "is marked enum but missing enum allowed values");
}
bool requiredefault = (!SAI_HAS_FLAG_MANDATORY_ON_CREATE(md->flags)) &&
(SAI_HAS_FLAG_CREATE_ONLY(md->flags) || SAI_HAS_FLAG_CREATE_AND_SET(md->flags));
if (requiredefault && md->isenum)
{
if (md->defaultvalue == NULL)
{
META_MD_ASSERT_FAIL(md, "marked as enum, and require default, but not provided");
}
if (sai_metadata_is_acl_field_or_action(md))
{
/*
* Default value for acl action is disabled, so enum value can't be
* compared since it's not there.
*/
return;
}
int32_t enumdefault = md->defaultvalue->s32;
if (sai_metadata_get_enum_value_name(md->enummetadata, enumdefault) == NULL)
{
META_MD_ASSERT_FAIL(md, "default enum value %d is not present on enum allowed values (%s)", enumdefault, md->enummetadata->name);
}
}
if (requiredefault && md->isenumlist)
{
if (md->defaultvalue != NULL)
{
META_MD_ASSERT_FAIL(md, "default values on enum list are not supported yet");
}
}
}
void check_attr_default_value_type(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
switch (md->defaultvaluetype)
{
case SAI_DEFAULT_VALUE_TYPE_NONE:
case SAI_DEFAULT_VALUE_TYPE_CONST:
/* check conditions/creation flags? */
break;
case SAI_DEFAULT_VALUE_TYPE_ATTR_VALUE:
{
const sai_attr_metadata_t* def = sai_metadata_get_attr_metadata(md->defaultvalueobjecttype, md->defaultvalueattrid);
if (def == NULL)
{
META_MD_ASSERT_FAIL(md, "attr value can't be found");
}
if (md->attrvaluetype != def->attrvaluetype)
{
META_MD_ASSERT_FAIL(md, "default attr value type is different");
}
break;
}
case SAI_DEFAULT_VALUE_TYPE_ATTR_RANGE:
{
const sai_attr_metadata_t* def = sai_metadata_get_attr_metadata(md->defaultvalueobjecttype, md->defaultvalueattrid);
if (def == NULL)
{
META_MD_ASSERT_FAIL(md, "attr range can't be found");
}
META_MD_ASSERT_FAIL(md, "attr value attribute value range not supported yet");
break;
}
case SAI_DEFAULT_VALUE_TYPE_EMPTY_LIST:
switch (md->attrvaluetype)
{
case SAI_ATTR_VALUE_TYPE_UINT32_LIST:
case SAI_ATTR_VALUE_TYPE_INT32_LIST:
case SAI_ATTR_VALUE_TYPE_UINT8_LIST:
case SAI_ATTR_VALUE_TYPE_UINT16_LIST:
case SAI_ATTR_VALUE_TYPE_INT8_LIST:
case SAI_ATTR_VALUE_TYPE_OBJECT_LIST:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_LIST:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_LIST:
case SAI_ATTR_VALUE_TYPE_TLV_LIST:
case SAI_ATTR_VALUE_TYPE_SEGMENT_LIST:
case SAI_ATTR_VALUE_TYPE_MAP_LIST:
case SAI_ATTR_VALUE_TYPE_IP_ADDRESS_LIST:
case SAI_ATTR_VALUE_TYPE_PORT_EYE_VALUES_LIST:
case SAI_ATTR_VALUE_TYPE_PORT_FREQUENCY_OFFSET_PPM_LIST:
case SAI_ATTR_VALUE_TYPE_PORT_SNR_LIST:
case SAI_ATTR_VALUE_TYPE_PORT_LANE_LATCH_STATUS_LIST:
case SAI_ATTR_VALUE_TYPE_SYSTEM_PORT_CONFIG_LIST:
case SAI_ATTR_VALUE_TYPE_IP_PREFIX_LIST:
case SAI_ATTR_VALUE_TYPE_ACL_CHAIN_LIST:
break;
default:
META_MD_ASSERT_FAIL(md, "default empty list specified, but attribute is not list");
}
break;
case SAI_DEFAULT_VALUE_TYPE_VENDOR_SPECIFIC:
switch (md->attrvaluetype)
{
case SAI_ATTR_VALUE_TYPE_MAC:
case SAI_ATTR_VALUE_TYPE_UINT64:
case SAI_ATTR_VALUE_TYPE_JSON:
break;
default:
/*
* Vendor specific attribute should be used only on
* primitive types and not on object id types (OIDs).
*/
META_MD_ASSERT_FAIL(md, "vendor specific not allowed on this type");
}
break;
case SAI_DEFAULT_VALUE_TYPE_SWITCH_INTERNAL:
if ((md->objecttype == SAI_OBJECT_TYPE_PORT) ||
(md->objecttype == SAI_OBJECT_TYPE_PORT_SERDES) ||
(md->objecttype == SAI_OBJECT_TYPE_SAMPLEPACKET) ||
(md->objecttype == SAI_OBJECT_TYPE_NEIGHBOR_ENTRY))
{
/*
* Allow PORT, NEIGHBOR attribute list's to be set to internal.
*/
break;
}
if (md->flags != SAI_ATTR_FLAGS_READ_ONLY)
{
META_MD_ASSERT_FAIL(md, "default internal currently can be set only on read only objects");
}
if (md->objecttype != SAI_OBJECT_TYPE_SWITCH)
{
/*
* This can be later relaxed to be set on ports since they have
* by default queues created.
*/
META_MD_ASSERT_FAIL(md, "default internal can be only set on switch object type");
}
switch (md->attrvaluetype)
{
case SAI_ATTR_VALUE_TYPE_OBJECT_ID:
case SAI_ATTR_VALUE_TYPE_OBJECT_LIST:
break;
default:
META_MD_ASSERT_FAIL(md, "invalid attribute value type specified: %d", md->attrvaluetype);
}
break;
default:
META_MD_ASSERT_FAIL(md, "invalid default value type specified: %d", md->defaultvaluetype);
}
}
void check_attr_conditions(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
switch (md->conditiontype)
{
case SAI_ATTR_CONDITION_TYPE_NONE:
case SAI_ATTR_CONDITION_TYPE_OR:
case SAI_ATTR_CONDITION_TYPE_AND:
case SAI_ATTR_CONDITION_TYPE_MIXED:
break;
default:
META_MD_ASSERT_FAIL(md, "invalid condition type specified: %d", md->conditiontype);
}
bool conditional = md->conditiontype != SAI_ATTR_CONDITION_TYPE_NONE;
if (!conditional && md->conditions != NULL)
{
META_MD_ASSERT_FAIL(md, "not conditional but conditions specified");
}
if (!conditional)
{
META_ASSERT_FALSE(md->isconditional, "marked conditional but is not");
return;
}
META_ASSERT_TRUE(md->isconditional, "marked not conditional but is");
if (md->conditions == NULL)
{
META_MD_ASSERT_FAIL(md, "marked as conditional but no conditions specified");
}
switch ((int)md->flags)
{
case SAI_ATTR_FLAGS_MANDATORY_ON_CREATE | SAI_ATTR_FLAGS_CREATE_AND_SET:
case SAI_ATTR_FLAGS_MANDATORY_ON_CREATE | SAI_ATTR_FLAGS_CREATE_ONLY:
/*
* If attribute is marked as conditional then it must have flags
* mandatory on create, otherwise use validonly condition.
*/
break;
default:
META_MD_ASSERT_FAIL(md, "marked as conditional, but invalid creation flags: 0x%x", md->flags);
}
/* condition must be the same object type as attribute we check */
size_t index = 0;
for (; index < md->conditionslength; ++index)
{
const sai_attr_condition_t* c = md->conditions[index];
if (c->attrid == md->attrid)
{
META_MD_ASSERT_FAIL(md, "conditional attr id %d is the same as condition attribute", c->attrid);
}
if (md->conditiontype == SAI_ATTR_CONDITION_TYPE_MIXED && c->attrid == SAI_INVALID_ATTRIBUTE_ID)
{
switch (c->type)
{
case SAI_ATTR_CONDITION_TYPE_OR:
case SAI_ATTR_CONDITION_TYPE_AND:
break;
default:
META_MD_ASSERT_FAIL(md, "conditionwrong sub condition type: %d (expected AND/OR)", c->type);
}
continue;
}
const sai_attr_metadata_t* cmd = sai_metadata_get_attr_metadata(md->objecttype, c->attrid);
if (cmd == NULL)
{
META_MD_ASSERT_FAIL(md, "conditional attribute id %d was not defined yet in metadata", c->attrid);
}
switch (cmd->attrvaluetype)
{
case SAI_ATTR_VALUE_TYPE_BOOL:
META_LOG_DEBUG("attr id: %d cond.bool: %d", c->attrid, c->condition.booldata);
break;
case SAI_ATTR_VALUE_TYPE_INT32:
/*
* Currently force conditional int32 attributes to be enum.
* This can be relaxed later when needed.
*/
if (!cmd->isenum)
{
META_MD_ASSERT_FAIL(md, "conditional attribute %s is not enum type", cmd->attridname);
}
if (cmd->isenum)
{
/* condition value can be a number or enum */
META_LOG_DEBUG("attr id: %d cond.s32: %d ", c->attrid, c->condition.s32);
/* check if condition enum is in condition attribute range */
if (sai_metadata_get_enum_value_name(cmd->enummetadata, c->condition.s32) == NULL)
{
META_MD_ASSERT_FAIL(md, "condition enum %d not found on condition attribute enum range", c->condition.s32);
}
}
break;
case SAI_ATTR_VALUE_TYPE_INT8:
case SAI_ATTR_VALUE_TYPE_INT16:
case SAI_ATTR_VALUE_TYPE_INT64:
case SAI_ATTR_VALUE_TYPE_UINT8:
case SAI_ATTR_VALUE_TYPE_UINT16:
case SAI_ATTR_VALUE_TYPE_UINT32:
case SAI_ATTR_VALUE_TYPE_UINT64:
/* number conditions */
break;
default:
META_MD_ASSERT_FAIL(md, "attr value type %d of conditional attribute is not supported yet", cmd->attrvaluetype);
}
if (cmd->conditiontype != SAI_ATTR_CONDITION_TYPE_NONE)
{
META_MD_ASSERT_FAIL(md, "conditional attribute is also conditional, not allowed");
}
switch ((int)cmd->flags)
{
case SAI_ATTR_FLAGS_MANDATORY_ON_CREATE | SAI_ATTR_FLAGS_CREATE_ONLY | SAI_ATTR_FLAGS_KEY:
case SAI_ATTR_FLAGS_MANDATORY_ON_CREATE | SAI_ATTR_FLAGS_CREATE_ONLY:
case SAI_ATTR_FLAGS_CREATE_ONLY:
/*
* Condition attribute must be create only since if it could
* change then other object may be required to pass on creation
* time that was not passed.
*/
break;
default:
META_MD_ASSERT_FAIL(cmd, "attribute must be create only since used in condition for %s", md->attridname);
}
}
}
void check_attr_validonly(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
switch (md->validonlytype)
{
case SAI_ATTR_CONDITION_TYPE_NONE:
case SAI_ATTR_CONDITION_TYPE_OR:
case SAI_ATTR_CONDITION_TYPE_AND:
case SAI_ATTR_CONDITION_TYPE_MIXED:
break;
default:
META_MD_ASSERT_FAIL(md, "invalid validonly type specified: %d", md->validonlytype);
}
bool conditional = md->validonlytype != SAI_ATTR_CONDITION_TYPE_NONE;
if (!conditional && md->validonly != NULL)
{
META_MD_ASSERT_FAIL(md, "not validonly but validonly specified");
META_ASSERT_FALSE(md->isvalidonly, "marked validonly but is not");
}
if (!conditional)
{
return;
}
if (md->validonly == NULL)
{
META_MD_ASSERT_FAIL(md, "marked as validonly but no validonly specified");
}
META_ASSERT_TRUE(md->isvalidonly, "marked not validonly but is");
switch ((int)md->flags)
{
case SAI_ATTR_FLAGS_MANDATORY_ON_CREATE | SAI_ATTR_FLAGS_CREATE_ONLY | SAI_ATTR_FLAGS_KEY:
case SAI_ATTR_FLAGS_MANDATORY_ON_CREATE | SAI_ATTR_FLAGS_CREATE_ONLY:
case SAI_ATTR_FLAGS_MANDATORY_ON_CREATE | SAI_ATTR_FLAGS_CREATE_AND_SET:
META_MD_ASSERT_FAIL(md, "valid only attribute can't be mandatory on create, use condition");
break;
case SAI_ATTR_FLAGS_CREATE_ONLY:
/*
* In general valid only attribute should be used only on
* CREATE_AND_SET flags, since when attribute is CREATE_ONLY it has
* default value and it can't be changed anyway, and entire purpose
* of valid only attribute is to allow change during runtime.
*
* When attribute CREATE_ONLY is marked as valid only is more like
* indication that this value will be used in that specific case
* but you won't be able to change it anyway.
*/
META_MD_LOG_DEBUG(md, "marked as valid only, on flags CREATE_ONLY, default value is present, should this be CREATE_AND_SET?");
/* intentional fall through */
case SAI_ATTR_FLAGS_CREATE_AND_SET:
/* ok */
break;
case SAI_ATTR_FLAGS_READ_ONLY:
META_MD_ASSERT_FAIL(md, "read only attribute can't be valid only");
break;
default:
META_MD_ASSERT_FAIL(md, "marked as validonly, but invalid creation flags: 0x%x", md->flags);
}
if (md->defaultvaluetype == SAI_DEFAULT_VALUE_TYPE_NONE)
{
/*
* In struct defaultvalue member can be NULL for some other default
* value types like empty list or internal etc. Default value is
* provided for CONST only.
*/
META_MD_ASSERT_FAIL(md, "expected default value on valid only attribute, but none provided");
}
/* condition must be the same object type as attribute we check */
size_t index = 0;
for (; index < md->validonlylength; ++index)
{
const sai_attr_condition_t* c = md->validonly[index];
if (c->attrid == md->attrid)
{
META_MD_ASSERT_FAIL(md, "validonly attr id %d is the same as validonly attribute", c->attrid);
}
if (md->validonlytype == SAI_ATTR_CONDITION_TYPE_MIXED && c->attrid == SAI_INVALID_ATTRIBUTE_ID)
{
switch (c->type)
{
case SAI_ATTR_CONDITION_TYPE_OR:
case SAI_ATTR_CONDITION_TYPE_AND:
break;
default:
META_MD_ASSERT_FAIL(md, "validonly wrong sub condition type: %d (expected AND/OR)", c->type);
}
continue;
}
const sai_attr_metadata_t* cmd = sai_metadata_get_attr_metadata(md->objecttype, c->attrid);
if (cmd == NULL)
{
META_MD_ASSERT_FAIL(md, "validonly attribute id %d was not defined yet in metadata", c->attrid);
}
switch (cmd->attrvaluetype)
{
case SAI_ATTR_VALUE_TYPE_BOOL:
META_LOG_DEBUG("attr id: %d cond.bool: %d", c->attrid, c->condition.booldata);
break;
case SAI_ATTR_VALUE_TYPE_INT32:
/*
* Currently force conditional int32 attributes to be enum.
* This can be relaxed later when needed.
*/
if (!cmd->isenum)
{
META_MD_ASSERT_FAIL(md, "validonly attribute %s is not enum type", cmd->attridname);
}
if (cmd->isenum)
{
/* condition value can be a number or enum */
META_LOG_DEBUG("attr id: %d cond.s32: %d ", c->attrid, c->condition.s32);
/* check if condition enum is in condition attribute range */
if (sai_metadata_get_enum_value_name(cmd->enummetadata, c->condition.s32) == NULL)
{
META_MD_ASSERT_FAIL(md, "validonly enum %d not found on validonly attribute enum range", c->condition.s32);
}
}
break;
case SAI_ATTR_VALUE_TYPE_INT8:
case SAI_ATTR_VALUE_TYPE_INT16:
case SAI_ATTR_VALUE_TYPE_INT64:
case SAI_ATTR_VALUE_TYPE_UINT8:
case SAI_ATTR_VALUE_TYPE_UINT16:
case SAI_ATTR_VALUE_TYPE_UINT32:
case SAI_ATTR_VALUE_TYPE_UINT64:
break;
default:
META_MD_ASSERT_FAIL(md, "attr value type %d of validonly attribute is not supported yet", cmd->attrvaluetype);
}
/*
* TODO can validonly attribute depend on condition attribute which is not provided?
* TODO can validonly depend on other validonly?
*/
if (cmd->validonlytype != SAI_ATTR_CONDITION_TYPE_NONE)
{
if (md->objecttype == SAI_OBJECT_TYPE_MIRROR_SESSION &&
(md->attrid == SAI_MIRROR_SESSION_ATTR_VLAN_TPID || md->attrid == SAI_MIRROR_SESSION_ATTR_VLAN_ID ||
md->attrid == SAI_MIRROR_SESSION_ATTR_VLAN_PRI || md->attrid == SAI_MIRROR_SESSION_ATTR_VLAN_CFI))
{
/*
* Vlan header attributes are depending on VLAN_HEADER_VALID which is
* also valid only for ERSPAN.
*/
}
else if (md->objecttype == SAI_OBJECT_TYPE_NEXT_HOP &&
(md->attrid == SAI_NEXT_HOP_ATTR_OUTSEG_TTL_MODE || md->attrid == SAI_NEXT_HOP_ATTR_OUTSEG_TTL_VALUE ||
md->attrid == SAI_NEXT_HOP_ATTR_OUTSEG_EXP_MODE || md->attrid == SAI_NEXT_HOP_ATTR_OUTSEG_EXP_VALUE ||
md->attrid == SAI_NEXT_HOP_ATTR_QOS_TC_AND_COLOR_TO_MPLS_EXP_MAP))
{
/*
* MPLS out segment attributes are required for ingress node and valid only for MPLS next hop.
*/
}
else if (md->objecttype == SAI_OBJECT_TYPE_TWAMP_SESSION &&
(md->attrid == SAI_TWAMP_SESSION_ATTR_TX_PKT_CNT || md->attrid == SAI_TWAMP_SESSION_ATTR_TX_PKT_PERIOD ||
md->attrid == SAI_TWAMP_SESSION_ATTR_TUNNEL_OUTER_VLAN_ID || md->attrid == SAI_TWAMP_SESSION_ATTR_TUNNEL_OUTER_VLAN_PRI ||
md->attrid == SAI_TWAMP_SESSION_ATTR_TUNNEL_OUTER_VLAN_CFI || md->attrid == SAI_TWAMP_SESSION_ATTR_VLAN_ID ||
md->attrid == SAI_TWAMP_SESSION_ATTR_VLAN_PRI || md->attrid == SAI_TWAMP_SESSION_ATTR_VLAN_CFI))
{
/*
* TWAMP packet tx mode attributes are depending on TWAMP_PKT_TX_MODE.
*/
}
else
{
META_MD_ASSERT_FAIL(md, "validonly attribute is also validonly attribute, not allowed");
}
}
if (cmd->conditiontype != SAI_ATTR_CONDITION_TYPE_NONE)
{
META_MD_ASSERT_FAIL(md, "conditional attribute is also conditional, not allowed");
}
switch ((int)cmd->flags)
{
case SAI_ATTR_FLAGS_MANDATORY_ON_CREATE | SAI_ATTR_FLAGS_CREATE_ONLY | SAI_ATTR_FLAGS_KEY:
case SAI_ATTR_FLAGS_MANDATORY_ON_CREATE | SAI_ATTR_FLAGS_CREATE_ONLY:
case SAI_ATTR_FLAGS_MANDATORY_ON_CREATE | SAI_ATTR_FLAGS_CREATE_AND_SET:
case SAI_ATTR_FLAGS_CREATE_ONLY:
case SAI_ATTR_FLAGS_CREATE_AND_SET:
/*
* Valid only attribute can be create_only or create_and_set
* conditional attribute can change during runtime and it may
* have impact on valid only attribute (it may or may not be
* used).
*/
break;
default:
META_MD_ASSERT_FAIL(cmd, "valid only condition attribute has invalid flags");
}
}
if ((md->conditiontype != SAI_ATTR_CONDITION_TYPE_NONE ) &&
(md->validonlytype != SAI_ATTR_CONDITION_TYPE_NONE ))
{
META_MD_ASSERT_FAIL(md, "attribute is conditional and valid only, not supported");
}
}
void check_attr_enum_list_condition(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
if (md->isenumlist)
{
if (md->attrvaluetype != SAI_ATTR_VALUE_TYPE_INT32_LIST)
{
META_MD_ASSERT_FAIL(md, "marked as enum list but wrong attr value type");
}
if (md->conditiontype != SAI_ATTR_CONDITION_TYPE_NONE)
{
META_MD_ASSERT_FAIL(md, "conditional enum list not supported yet");
}
}
}
void check_attr_enum_list_validonly(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
if (md->isenumlist)
{
if (md->attrvaluetype != SAI_ATTR_VALUE_TYPE_INT32_LIST)
{
META_MD_ASSERT_FAIL(md, "marked as enum list but wrong attr value type");
}
}
}
void check_attr_allow_flags(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
if (md->allownullobjectid)
{
switch (md->attrvaluetype)
{
/* there may be other types in acl field/data that accept object id */
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_ID:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_ID:
case SAI_ATTR_VALUE_TYPE_OBJECT_ID:
break;
default:
META_MD_ASSERT_FAIL(md, "allow null object is set but attr value type is wrong");
}
/*
* Object SAI_ATTR_VALUE_TYPE_POINTER should be allowed null pointer by
* default pointers received from SAI should be only via query api.
*/
if (md->allowedobjecttypeslength == 0)
{
META_MD_ASSERT_FAIL(md, "allow null object is set but allowed object types is empty");
}
}
if (md->allowedobjecttypeslength != 0)
{
META_ASSERT_NOT_NULL(md->allowedobjecttypes);
}
size_t index = 0;
for (; index < md->allowedobjecttypeslength; ++index)
{
sai_object_type_t ot = md->allowedobjecttypes[index];
if (is_valid_object_type(ot))
{
continue;
}
META_MD_ASSERT_FAIL(md, "not allowed object type %d on list", ot);
}
/* allow empty list can point to any list, not only object id list */
if (md->allowemptylist)
{
switch (md->attrvaluetype)
{
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_LIST:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_LIST:
case SAI_ATTR_VALUE_TYPE_OBJECT_LIST:
break;
case SAI_ATTR_VALUE_TYPE_INT8_LIST:
case SAI_ATTR_VALUE_TYPE_UINT8_LIST:
case SAI_ATTR_VALUE_TYPE_UINT16_LIST:
case SAI_ATTR_VALUE_TYPE_INT32_LIST:
case SAI_ATTR_VALUE_TYPE_VLAN_LIST:
case SAI_ATTR_VALUE_TYPE_UINT32_LIST:
case SAI_ATTR_VALUE_TYPE_QOS_MAP_LIST:
case SAI_ATTR_VALUE_TYPE_MAP_LIST:
case SAI_ATTR_VALUE_TYPE_ACL_RESOURCE_LIST:
case SAI_ATTR_VALUE_TYPE_TLV_LIST:
case SAI_ATTR_VALUE_TYPE_SEGMENT_LIST:
case SAI_ATTR_VALUE_TYPE_IP_ADDRESS_LIST:
case SAI_ATTR_VALUE_TYPE_PORT_EYE_VALUES_LIST:
case SAI_ATTR_VALUE_TYPE_PORT_FREQUENCY_OFFSET_PPM_LIST:
case SAI_ATTR_VALUE_TYPE_PORT_SNR_LIST:
case SAI_ATTR_VALUE_TYPE_PORT_LANE_LATCH_STATUS_LIST:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT8_LIST:
case SAI_ATTR_VALUE_TYPE_SYSTEM_PORT_CONFIG_LIST:
case SAI_ATTR_VALUE_TYPE_PORT_ERR_STATUS_LIST:
case SAI_ATTR_VALUE_TYPE_IP_PREFIX_LIST:
case SAI_ATTR_VALUE_TYPE_ACL_CHAIN_LIST:
break;
default:
META_MD_ASSERT_FAIL(md, "allow empty list is set but attr value type is not list");
}
}
if (md->allowrepetitiononlist || md->allowmixedobjecttypes)
{
switch (md->attrvaluetype)
{
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_LIST:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_LIST:
case SAI_ATTR_VALUE_TYPE_OBJECT_LIST:
break;
default:
META_MD_ASSERT_FAIL(md, "allow null object is set but attr value type is wrong");
}
}
}
void check_attr_get_save(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
if (md->getsave)
{
switch (md->attrvaluetype)
{
case SAI_ATTR_VALUE_TYPE_INT32:
break;
default:
META_MD_ASSERT_FAIL(md, "get save not supported on %s", md->attridname);
}
}
}
void check_attr_key(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
if (SAI_HAS_FLAG_KEY(md->flags))
{
switch (md->attrvaluetype)
{
case SAI_ATTR_VALUE_TYPE_UINT32_LIST:
if (md->objecttype == SAI_OBJECT_TYPE_PORT && md->attrid == SAI_PORT_ATTR_HW_LANE_LIST)
{
/*
* This is special case when HW_LANE_LIST is actual KEY for
* port, and it's more complicated because order don't
* matter and same lane can't be used on different port if
* some ports are splitted.
*/
break;
}
META_MD_ASSERT_FAIL(md, "marked as key, but have invalid attr value type (list)");
case SAI_ATTR_VALUE_TYPE_OBJECT_ID:
if ((md->objecttype == SAI_OBJECT_TYPE_QUEUE && md->attrid == SAI_QUEUE_ATTR_PORT) ||
(md->objecttype == SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP && md->attrid == SAI_INGRESS_PRIORITY_GROUP_ATTR_PORT) ||
(md->objecttype == SAI_OBJECT_TYPE_PORT_CONNECTOR && md->attrid == SAI_PORT_CONNECTOR_ATTR_SYSTEM_SIDE_PORT_ID) ||
(md->objecttype == SAI_OBJECT_TYPE_PORT_CONNECTOR && md->attrid == SAI_PORT_CONNECTOR_ATTR_LINE_SIDE_PORT_ID))
{
/*
* This is also special case, OBJECT_ID at should not be a
* KEY in any attribute, this is TODO action to get rid of
* this kind of dependency.
*/
break;
}
META_MD_ASSERT_FAIL(md, "marked as key, but have invalid attr value type (object id)");
case SAI_ATTR_VALUE_TYPE_INT32:
case SAI_ATTR_VALUE_TYPE_UINT32:
case SAI_ATTR_VALUE_TYPE_UINT8:
case SAI_ATTR_VALUE_TYPE_UINT16:
break;
default:
META_MD_ASSERT_FAIL(md, "marked as key, but have invalid attr value type");
}
}
}
void check_attr_acl_fields(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
switch (md->attrvaluetype)
{
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT8:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_INT8:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT16:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_INT16:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_INT32:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT32:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT64:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_MAC:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_IPV4:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_IPV6:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_ID:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_LIST:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT8_LIST:
if (md->objecttype == SAI_OBJECT_TYPE_ACL_ENTRY &&
md->attrid >= SAI_ACL_ENTRY_ATTR_FIELD_START &&
md->attrid <= SAI_ACL_ENTRY_ATTR_FIELD_END)
{
break;
}
if (md->objecttype == SAI_OBJECT_TYPE_ACL_TABLE &&
md->attrid >= SAI_ACL_TABLE_ATTR_FIELD_VALID_BITS_START &&
md->attrid <= SAI_ACL_TABLE_ATTR_FIELD_VALID_BITS_END)
{
break;
}
if (md->objecttype == SAI_OBJECT_TYPE_UDF_MATCH)
{
/*
* This is special case, object for UDF MATCH can use acl field
* attribute values since it's easier to maintain since this
* match also need a mask parameter. But restriction is that
* only primitive types can be used, no object id;
*/
switch (md->attrvaluetype)
{
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT8:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_INT8:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT16:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_INT16:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_INT32:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT32:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT64:
break;
default:
META_MD_ASSERT_FAIL(md, "acl field data used on udf match can be only primitive type");
}
break;
}
if (md->objecttype == SAI_OBJECT_TYPE_DTEL &&
md->attrid == SAI_DTEL_ATTR_INT_L4_DSCP &&
md->attrvaluetype == SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT8)
{
break;
}
META_MD_ASSERT_FAIL(md, "acl field may only be set on acl field and udf match");
break;
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_UINT8:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_INT8:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_UINT16:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_INT16:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_UINT32:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_INT32:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_MAC:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_IPV4:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_IPV6:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_IP_ADDRESS:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_ID:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_LIST:
if (md->objecttype == SAI_OBJECT_TYPE_ACL_ENTRY && md->isextensionattr)
{
break;
}
if (md->objecttype != SAI_OBJECT_TYPE_ACL_ENTRY ||
md->attrid < SAI_ACL_ENTRY_ATTR_ACTION_START ||
md->attrid > SAI_ACL_ENTRY_ATTR_ACTION_END)
{
META_MD_ASSERT_FAIL(md, "acl action may only be set on acl action");
}
break;
default:
break;
}
if (md->objecttype == SAI_OBJECT_TYPE_ACL_ENTRY)
{
if (md->attrid >= SAI_ACL_ENTRY_ATTR_FIELD_START &&
md->attrid <= SAI_ACL_ENTRY_ATTR_FIELD_END)
{
switch (md->attrvaluetype)
{
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_BOOL:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT8:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_INT8:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT16:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_INT16:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_INT32:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT32:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT64:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_MAC:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_IPV4:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_IPV6:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_ID:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_LIST:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT8_LIST:
break;
default:
META_MD_ASSERT_FAIL(md, "invalid attr value type for acl field");
}
}
if (md->attrid >= SAI_ACL_ENTRY_ATTR_ACTION_START &&
md->attrid <= SAI_ACL_ENTRY_ATTR_ACTION_END)
{
switch (md->attrvaluetype)
{
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_BOOL:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_UINT8:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_INT8:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_UINT16:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_INT16:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_INT32:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_UINT32:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_MAC:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_IPV4:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_IPV6:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_IP_ADDRESS:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_ID:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_LIST:
break;
default:
META_MD_ASSERT_FAIL(md, "invalid attr value type for acl action");
}
}
}
}
void check_attr_vlan(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
if (md->isvlan)
{
if (md->attrvaluetype != SAI_ATTR_VALUE_TYPE_UINT16 &&
md->attrvaluetype != SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT16 &&
md->attrvaluetype != SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_UINT16)
{
META_MD_ASSERT_FAIL(md, "marked as vlan, but has wrong attr value type");
}
}
}
void check_condition_in_range(
_In_ const sai_attr_metadata_t* md,
_In_ size_t length,
_In_ const sai_attr_condition_t * const* const conditions,
_In_ sai_attr_id_t start,
_In_ sai_attr_id_t end)
{
META_LOG_ENTER();
size_t index = 0;
for (; index < length; ++index)
{
const sai_attr_condition_t* c = conditions[index];
if (c->attrid < start || c->attrid > end)
{
continue;
}
META_MD_ASSERT_FAIL(md, "has condition depending on acl field / action, not allowed");
}
}
void check_attr_acl_conditions(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
/*
* Purpose of this check is to find out if there is any condition in
* attributes that depends on acl entry object actions since such
* dependency has no sense.
*/
if (md->objecttype == SAI_OBJECT_TYPE_ACL_TABLE)
{
check_condition_in_range(md, md->conditionslength, md->conditions,
SAI_ACL_TABLE_ATTR_FIELD_START, SAI_ACL_TABLE_ATTR_FIELD_END);
if (md->attrid >= SAI_ACL_TABLE_ATTR_FIELD_START &&
md->attrid >= SAI_ACL_TABLE_ATTR_FIELD_END)
{
if (md->conditionslength != 0)
{
META_MD_ASSERT_FAIL(md, "acl table field has conditions, not allowed");
}
}
}
if (md->objecttype == SAI_OBJECT_TYPE_ACL_ENTRY)
{
check_condition_in_range(md, md->validonlylength, md->validonly,
SAI_ACL_ENTRY_ATTR_FIELD_START, SAI_ACL_ENTRY_ATTR_FIELD_END);
check_condition_in_range(md, md->conditionslength, md->conditions,
SAI_ACL_ENTRY_ATTR_FIELD_START, SAI_ACL_ENTRY_ATTR_FIELD_END);
check_condition_in_range(md, md->validonlylength, md->validonly,
SAI_ACL_ENTRY_ATTR_ACTION_START, SAI_ACL_ENTRY_ATTR_ACTION_END);
check_condition_in_range(md, md->conditionslength, md->conditions,
SAI_ACL_ENTRY_ATTR_ACTION_START, SAI_ACL_ENTRY_ATTR_ACTION_END);
if (md->attrid >= SAI_ACL_ENTRY_ATTR_FIELD_START &&
md->attrid >= SAI_ACL_ENTRY_ATTR_FIELD_END)
{
if (md->conditionslength != 0 || md->validonlylength != 0)
{
META_MD_ASSERT_FAIL(md, "acl entry field has conditions, not allowed");
}
}
if (md->attrid >= SAI_ACL_ENTRY_ATTR_ACTION_START &&
md->attrid >= SAI_ACL_ENTRY_ATTR_ACTION_END)
{
if (md->conditionslength != 0 || md->validonlylength != 0)
{
META_MD_ASSERT_FAIL(md, "acl entry action has conditions, not allowed");
}
}
}
}
void check_attr_reverse_graph(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
/*
* Purpose of this method is to check whether any defined attribute with is
* object id type is defined in reverse graph correctly. Read only objects
* are also included.
*/
size_t index = 0;
for (; index < md->allowedobjecttypeslength; index++)
{
/*
* for each defined object id on that list we need to check
* if its defined correctly in reverse graph
*/
sai_object_type_t depobjecttype = md->allowedobjecttypes[index];
const sai_object_type_info_t *oi = sai_metadata_get_object_type_info(depobjecttype);
META_ASSERT_NOT_NULL(oi->revgraphmembers);
size_t revidx = 0;
bool defined = false;
for (; oi->revgraphmembers[revidx] != NULL; revidx++)
{
/*
* now let's search for graph member which defines
* this object and the same attribute value
*/
const sai_rev_graph_member_t *rm = oi->revgraphmembers[revidx];
META_ASSERT_TRUE(rm->objecttype == depobjecttype, "invalid objecttype definition");
if (rm->depobjecttype != md->objecttype)
{
/*
* this is not the member we are looking for
*/
continue;
}
if (rm->attrmetadata == NULL)
{
META_ASSERT_NOT_NULL(rm->structmember);
/*
* object is defined on non object id,
* this will require different method to check
*/
META_MD_ASSERT_FAIL(md, "This is attribute, it can't be defined in struct member");
}
else
{
/*
* object is attribute
*/
META_ASSERT_NOT_NULL(rm->attrmetadata);
META_ASSERT_NULL(rm->structmember);
size_t i = 0;
for (; i < rm->attrmetadata->allowedobjecttypeslength; i++)
{
/*
* Object type of graph member must match and also
* attribute id must match.
*/
if (rm->attrmetadata->allowedobjecttypes[i] == depobjecttype &&
rm->attrmetadata->attrid == md->attrid)
{
META_LOG_DEBUG("dep %s ot %s attr %s\n",
sai_metadata_get_object_type_name(depobjecttype),
sai_metadata_get_object_type_name(md->objecttype),
md->attridname);
defined = true;
break;
}
}
if (defined)
{
break;
}
}
}
META_ASSERT_TRUE(defined, "reverse graph object is not defined anywhere");
}
}
void check_if_attr_was_already_defined(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
const defined_attr_t *p = defined_attributes;
while (p)
{
if (p->metadata != NULL)
{
if (p->metadata->objecttype == md->objecttype &&
p->metadata->attrid == md->attrid)
{
META_MD_ASSERT_FAIL(md, "attribute was already declared");
}
}
p = p->next;
}
}
void check_attr_acl_capability(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
if (md->attrvaluetype != SAI_ATTR_VALUE_TYPE_ACL_CAPABILITY)
{
return;
}
if (md->flags != SAI_ATTR_FLAGS_READ_ONLY)
{
META_MD_ASSERT_FAIL(md, "attribute marked as acl capability should be READ_ONLY");
}
}
void define_attr(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
defined_attr_t *p = (defined_attr_t*)malloc(sizeof(defined_attr_t));
p->metadata = md;
p->next = defined_attributes;
defined_attributes = p;
}
void check_attr_acl_field_or_action(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
/*
* Purpose of this test is to check if respective flags are set to true on
* acl field or action.
*/
if ((md->isaclfield || md->isaclaction) != sai_metadata_is_acl_field_or_action(md))
{
META_MD_ASSERT_FAIL(md, "isaclfield or isaclaction don't match utils method");
}
if (md->isaclfield)
{
META_ASSERT_FALSE(md->defaultvalue->aclfield.enable, "enable should be false");
}
if (md->isaclaction)
{
META_ASSERT_FALSE(md->defaultvalue->aclaction.enable, "enable should be false");
}
if (md->objecttype != SAI_OBJECT_TYPE_ACL_ENTRY && md->objecttype != SAI_OBJECT_TYPE_UDF_MATCH)
{
META_ASSERT_FALSE(md->isaclfield, "field should be not marked as acl field");
META_ASSERT_FALSE(md->isaclaction, "field should be not marked as acl action");
return;
}
if (md->objecttype == SAI_OBJECT_TYPE_ACL_ENTRY)
{
if (md->attrid >= SAI_ACL_ENTRY_ATTR_FIELD_START &&
md->attrid <= SAI_ACL_ENTRY_ATTR_FIELD_END)
{
META_ASSERT_TRUE(md->isaclfield, "field should be marked as acl field");
META_ASSERT_FALSE(md->isaclaction, "field should be not marked as acl action");
}
if (md->attrid >= SAI_ACL_ENTRY_ATTR_ACTION_START &&
md->attrid <= SAI_ACL_ENTRY_ATTR_ACTION_END)
{
META_ASSERT_FALSE(md->isaclfield, "field should not be marked as acl field");
META_ASSERT_TRUE(md->isaclaction, "field should be marked as acl action");
}
}
if (md->objecttype == SAI_OBJECT_TYPE_UDF_MATCH)
{
if (md->attrid <= SAI_UDF_MATCH_ATTR_GRE_TYPE)
{
META_ASSERT_TRUE(md->isaclfield, "field should be marked as acl field");
META_ASSERT_FALSE(md->isaclaction, "field should be not marked as acl action");
}
if (md->attrid == SAI_UDF_MATCH_ATTR_L4_DST_PORT_TYPE)
{
META_ASSERT_TRUE(md->isaclfield, "field should be marked as acl field");
META_ASSERT_FALSE(md->isaclaction, "field should be not marked as acl action");
}
}
}
void check_attr_acl_mask(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
/*
* Field acl mask reuses existing attribute acl field mask key, then we
* need to have some conditions. Since acl field have enable mask and data
* fields, then this mask alone is not compatible.
*/
if (md->isaclmask)
{
META_ASSERT_FALSE(md->isaclfield, "aclmask can't be mark as alcfield");
META_ASSERT_FALSE(md->isaclaction, "aclmask can't be marked as aclaction");
META_ASSERT_TRUE(md->objecttype == SAI_OBJECT_TYPE_ACL_TABLE, "object type for acl mask must be acl table");
META_ASSERT_TRUE((md->attrvaluetype >= SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_BOOL &&
md->attrvaluetype <= SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT8_LIST),
"aclmask attribute attr id must be in acl field start/end range");
}
}
void check_attr_existing_objects(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
/*
* Purpose of this test it to find attributes on objects existing already
* on the switch with attributes that are mandatory on create and create
* and set. Those attributes can be changed by user from previous value,
* and this causes problem for comparison logic to bring those objects to
* default value. We need to store those initial values of created objects
* somewhere.
*
* Worth notice, that this is only helper, since metadata on attributes
* where default value for oid attribute is SAI_NULL_OBJECT_ID, but maybe
* on the switch vendor actually assigned some value, so default value will
* not be NULL after creation.
*/
if (sai_metadata_get_object_type_info(md->objecttype)->isnonobjectid)
{
if (md->storedefaultvalue)
{
/*
* Currently disabled since we need more complicated logic in parser
* and we assume non object id's are not created at the switch by
* internal components.
*
* META_MD_ASSERT_FAIL(md, "store default val should be not present on non object id");
*/
}
return;
}
if (md->defaultvaluetype == SAI_DEFAULT_VALUE_TYPE_VENDOR_SPECIFIC ||
md->defaultvaluetype == SAI_DEFAULT_VALUE_TYPE_ATTR_VALUE)
{
/*
* For attr value we can make restriction that value also needs to be
* CREATE_AND_SET, since some of those values are read only.
*/
if (!md->storedefaultvalue)
{
META_MD_ASSERT_FAIL(md, "vendor/attrvalue specific values needs to be stored");
}
META_LOG_DEBUG("vendor/attrvalue specific values needs to be stored %s", md->attridname);
return;
}
if (!SAI_HAS_FLAG_MANDATORY_ON_CREATE(md->flags) || !SAI_HAS_FLAG_CREATE_AND_SET(md->flags))
{
return;
}
if (!md->storedefaultvalue)
{
META_MD_ASSERT_FAIL(md, "default value needs to be stored");
}
META_LOG_DEBUG("MANDATORY_ON_CREATE|CREATE_AND_SET values needs to be stored %s", md->attridname);
/*
* If attribute is mandatory on create and create and set then there is no
* default value on created object, and user can change it's value so in
* comparison logic we will need to maintain this state somewhere as
* default.
*
* Actually even if object is create only and is created on the switch we
* need to keep it's value for future reference count in metadata db.
*/
/*
* Currently we are limiting value types on existing objects that are
* mandatory on create to primitive values.
*/
switch (md->attrvaluetype)
{
case SAI_ATTR_VALUE_TYPE_INT32:
case SAI_ATTR_VALUE_TYPE_INT8:
case SAI_ATTR_VALUE_TYPE_IP_ADDRESS:
case SAI_ATTR_VALUE_TYPE_MAC:
case SAI_ATTR_VALUE_TYPE_UINT16:
case SAI_ATTR_VALUE_TYPE_UINT32:
case SAI_ATTR_VALUE_TYPE_UINT64:
case SAI_ATTR_VALUE_TYPE_UINT8:
/*
* Primitives we can skip for now, just left as was set by user
* with warning in syslog.
*/
break;
case SAI_ATTR_VALUE_TYPE_OBJECT_ID:
if (md->allownullobjectid)
{
/*
* If object allows NULL object id then we assume that this can
* be used as default value.
*/
return;
}
/*
* When type is object id we need to store it's previous value
* since we will not be able to bring it to default.
*/
META_LOG_DEBUG("Default value (oid) needs to be stored %s", md->attridname);
break;
case SAI_ATTR_VALUE_TYPE_QOS_MAP_LIST:
/*
* Allow qos maps list to enable editing qos map values.
* Since on switch initialization there are no qos map objects (all switch qos
* maps attributes are null) this shouldn't be a problem.
*/
break;
case SAI_ATTR_VALUE_TYPE_UINT32_LIST:
/*
* Allow for TAM histogram bin boundary
*/
break;
case SAI_ATTR_VALUE_TYPE_OBJECT_LIST:
/*
* Allow object list for selected objects (for now).
*/
if (md->objecttype == SAI_OBJECT_TYPE_MIRROR_SESSION)
{
break;
}
META_MD_ASSERT_FAIL(md, "object list is not supported on this object type");
case SAI_ATTR_VALUE_TYPE_POINTER:
/*
* Allow pointer for switch register read and write API's.
*/
break;
default:
META_MD_ASSERT_FAIL(md, "not supported attr value type on existing object");
}
}
void check_attr_sai_pointer(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
if (md->iscallback)
{
META_ASSERT_TRUE(md->attrvaluetype == SAI_ATTR_VALUE_TYPE_POINTER, "callback can be set only on pointer type");
META_ASSERT_TRUE(md->notificationtype == -1, "callback can't be notification");
}
if (md->notificationtype != -1)
{
META_ASSERT_FALSE(md->iscallback, "notification can't be callback");
}
if (md->pointertype != -1)
{
META_ASSERT_TRUE(md->attrvaluetype == SAI_ATTR_VALUE_TYPE_POINTER, "pointer can be set only on pointer type");
}
/*
* Purpose of this test is to check whether sai_pointer_t
* is only used on SAI_OBJECT_TYPE_SWITCH.
*/
if (md->objecttype == SAI_OBJECT_TYPE_SWITCH)
{
if (md->attrvaluetype == SAI_ATTR_VALUE_TYPE_POINTER)
{
/*
* Make sure that all pointers are CREATE_AND_SET.
*/
if (!SAI_HAS_FLAG_CREATE_AND_SET(md->flags))
{
META_MD_ASSERT_FAIL(md, "all pointers should be CREATE_AND_SET");
}
if (md->iscallback)
{
META_ASSERT_TRUE(md->notificationtype == -1, "notification type should be marked as callback");
}
else
{
META_ASSERT_TRUE(md->notificationtype >= 0, "notification type should be set to value on pointer");
}
if (md->pointertype < 0)
{
META_MD_ASSERT_FAIL(md, "pointer type should be set to value on pointer");
}
}
else
{
META_ASSERT_TRUE(md->pointertype == -1, "pointer type should not be set to value on non pointer");
META_ASSERT_TRUE(md->notificationtype == -1, "notification type should not be set to value on non pointer");
META_ASSERT_TRUE(md->iscallback == false, "callback type should not be set to value on non pointer");
}
return;
}
if (md->attrvaluetype == SAI_ATTR_VALUE_TYPE_POINTER)
{
META_MD_ASSERT_FAIL(md, "attribute value pointer is only allowed on SAI_OBJECT_TYPE_SWITCH");
}
}
void check_attr_brief_description(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
/*
* Purpose of this check is to see if brief description extract from
* header is present and not too long.
*/
META_ASSERT_NOT_NULL(md->brief);
if (strlen(md->brief) > 200)
{
META_MD_ASSERT_FAIL(md, "brief description is too long > 200");
}
}
void check_attr_is_primitive(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
/*
* Purpose of this check is to see if isprimitive flag is correct.
*/
switch (md->attrvaluetype)
{
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_LIST:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_LIST:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT8_LIST:
case SAI_ATTR_VALUE_TYPE_INT32_LIST:
case SAI_ATTR_VALUE_TYPE_INT8_LIST:
case SAI_ATTR_VALUE_TYPE_OBJECT_LIST:
case SAI_ATTR_VALUE_TYPE_QOS_MAP_LIST:
case SAI_ATTR_VALUE_TYPE_MAP_LIST:
case SAI_ATTR_VALUE_TYPE_UINT32_LIST:
case SAI_ATTR_VALUE_TYPE_UINT8_LIST:
case SAI_ATTR_VALUE_TYPE_UINT16_LIST:
case SAI_ATTR_VALUE_TYPE_VLAN_LIST:
case SAI_ATTR_VALUE_TYPE_ACL_CAPABILITY:
case SAI_ATTR_VALUE_TYPE_ACL_RESOURCE_LIST:
case SAI_ATTR_VALUE_TYPE_TLV_LIST:
case SAI_ATTR_VALUE_TYPE_SEGMENT_LIST:
case SAI_ATTR_VALUE_TYPE_IP_ADDRESS_LIST:
case SAI_ATTR_VALUE_TYPE_PORT_EYE_VALUES_LIST:
case SAI_ATTR_VALUE_TYPE_PORT_FREQUENCY_OFFSET_PPM_LIST:
case SAI_ATTR_VALUE_TYPE_PORT_SNR_LIST:
case SAI_ATTR_VALUE_TYPE_SYSTEM_PORT_CONFIG_LIST:
case SAI_ATTR_VALUE_TYPE_PORT_ERR_STATUS_LIST:
case SAI_ATTR_VALUE_TYPE_UINT16_RANGE_LIST:
case SAI_ATTR_VALUE_TYPE_PORT_LANE_LATCH_STATUS_LIST:
case SAI_ATTR_VALUE_TYPE_JSON:
case SAI_ATTR_VALUE_TYPE_IP_PREFIX_LIST:
case SAI_ATTR_VALUE_TYPE_ACL_CHAIN_LIST:
if (md->isprimitive)
{
META_MD_ASSERT_FAIL(md, "marked as primitive on list")
}
break;
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_BOOL:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_INT16:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_INT32:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_INT8:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_IPV4:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_IPV6:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_IP_ADDRESS:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_MAC:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_ID:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_UINT16:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_UINT32:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_UINT8:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_BOOL:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_INT16:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_INT32:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_INT8:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_IPV4:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_IPV6:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_MAC:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_ID:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT16:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT32:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT64:
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT8:
case SAI_ATTR_VALUE_TYPE_BOOL:
case SAI_ATTR_VALUE_TYPE_CHARDATA:
case SAI_ATTR_VALUE_TYPE_INT32:
case SAI_ATTR_VALUE_TYPE_INT8:
case SAI_ATTR_VALUE_TYPE_IP_ADDRESS:
case SAI_ATTR_VALUE_TYPE_IP_PREFIX:
case SAI_ATTR_VALUE_TYPE_PRBS_RX_STATE:
case SAI_ATTR_VALUE_TYPE_MAC:
case SAI_ATTR_VALUE_TYPE_OBJECT_ID:
case SAI_ATTR_VALUE_TYPE_POINTER:
case SAI_ATTR_VALUE_TYPE_UINT16:
case SAI_ATTR_VALUE_TYPE_INT16:
case SAI_ATTR_VALUE_TYPE_UINT32:
case SAI_ATTR_VALUE_TYPE_UINT32_RANGE:
case SAI_ATTR_VALUE_TYPE_UINT64:
case SAI_ATTR_VALUE_TYPE_UINT8:
case SAI_ATTR_VALUE_TYPE_TIMESPEC:
case SAI_ATTR_VALUE_TYPE_IPV4:
case SAI_ATTR_VALUE_TYPE_IPV6:
case SAI_ATTR_VALUE_TYPE_ENCRYPT_KEY:
case SAI_ATTR_VALUE_TYPE_AUTH_KEY:
case SAI_ATTR_VALUE_TYPE_MACSEC_SAK:
case SAI_ATTR_VALUE_TYPE_MACSEC_AUTH_KEY:
case SAI_ATTR_VALUE_TYPE_MACSEC_SALT:
case SAI_ATTR_VALUE_TYPE_SYSTEM_PORT_CONFIG:
case SAI_ATTR_VALUE_TYPE_FABRIC_PORT_REACHABILITY:
case SAI_ATTR_VALUE_TYPE_LATCH_STATUS:
case SAI_ATTR_VALUE_TYPE_POE_PORT_POWER_CONSUMPTION:
if (!md->isprimitive)
{
META_MD_ASSERT_FAIL(md, "not marked as primitive on primitive")
}
break;
default:
META_MD_ASSERT_FAIL(md, "attr value type not handled, FIXME");
}
}
typedef struct _stack_item_t
{
bool value;
struct _stack_item_t* next;
} stack_item_t;
typedef struct _stack_t
{
stack_item_t* top;
} stack_t;
void stack_init(
_Inout_ stack_t* stack)
{
META_LOG_ENTER();
stack->top = NULL;
}
void stack_push(
_Inout_ stack_t* stack,
_In_ bool value)
{
META_LOG_ENTER();
stack_item_t *item = (stack_item_t*)calloc(1, sizeof(stack_item_t));
item->value = value;
item->next = stack->top;
stack->top = item;
}
bool stack_pop(
_Inout_ stack_t* stack)
{
META_LOG_ENTER();
if (stack->top == NULL)
{
META_ASSERT_FAIL("stack is empty");
}
bool value = stack->top->value;
stack_item_t* top = stack->top;
stack->top = top->next;
free(top);
return value;
}
bool check_mixed_condition_list(
_In_ const sai_attr_metadata_t* md,
_In_ const sai_attr_condition_t* const* list)
{
META_LOG_ENTER();
if (list[0] == NULL)
{
META_MD_ASSERT_FAIL(md, "hit end of condition list, RPN condition logic is BROKEN");
}
stack_t stack;
stack_init(&stack);
while (list[0] != NULL)
{
const sai_attr_condition_t* c = list[0];
if (c->type == SAI_ATTR_CONDITION_TYPE_NONE)
{
stack_push(&stack, true);
list++;
continue;
}
if (c->type == SAI_ATTR_CONDITION_TYPE_AND)
{
bool value = stack_pop(&stack) & stack_pop(&stack);
stack_push(&stack, value);
list++;
continue;
}
if (c->type == SAI_ATTR_CONDITION_TYPE_OR)
{
bool value = stack_pop(&stack) | stack_pop(&stack);
stack_push(&stack, value);
list++;
continue;
}
META_MD_ASSERT_FAIL(md, "wrong condition type on list: %d", c->type);
}
bool value = stack_pop(&stack);
if (stack.top != NULL)
{
META_MD_ASSERT_FAIL(md, "stack not empty after condition list check, RPN condition logic is BROKEN");
}
return value;
}
void check_attr_mixed_condition(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
if (md->conditiontype == SAI_ATTR_CONDITION_TYPE_MIXED)
{
META_ASSERT_TRUE(md->isconditional, "must be conditional");
META_ASSERT_TRUE(md->conditions != NULL, "must be conditional");
uint32_t index = 0;
for (; index < md->conditionslength; ++index)
{
const sai_attr_condition_t* c = md->conditions[index];
if (c->type == SAI_ATTR_CONDITION_TYPE_NONE)
{
META_ASSERT_TRUE(c->attrid != SAI_INVALID_ATTRIBUTE_ID, "attribute must be defined for condition");
}
}
META_ASSERT_TRUE(md->conditions[0]->type == SAI_ATTR_CONDITION_TYPE_NONE, "first mixed condition entry must be type none");
META_ASSERT_TRUE(md->conditions[md->conditionslength-1]->type != SAI_ATTR_CONDITION_TYPE_NONE, "last mixed condition entry cannot be none");
bool value = check_mixed_condition_list(md, md->conditions);
META_ASSERT_TRUE(value, "should evaluate to true");
}
}
void check_attr_mixed_validonly(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
if (md->validonlytype == SAI_ATTR_CONDITION_TYPE_MIXED)
{
META_ASSERT_TRUE(md->isvalidonly, "must be validonly");
META_ASSERT_TRUE(md->validonly != NULL, "must be validonly");
uint32_t index = 0;
for (; index < md->validonlylength; ++index)
{
const sai_attr_condition_t* c = md->validonly[index];
if (c->type == SAI_ATTR_CONDITION_TYPE_NONE)
{
META_ASSERT_TRUE(c->attrid != SAI_INVALID_ATTRIBUTE_ID, "attribute must be defined for condition");
}
}
META_ASSERT_TRUE(md->validonly[0]->type == SAI_ATTR_CONDITION_TYPE_NONE, "first mixed condition entry must be type none");
META_ASSERT_TRUE(md->validonly[md->validonlylength-1]->type != SAI_ATTR_CONDITION_TYPE_NONE, "last mixed condition entry cannot be none");
bool value = check_mixed_condition_list(md, md->validonly);
META_ASSERT_TRUE(value, "should evaluate to true");
}
}
void check_attr_condition_met(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
sai_attribute_t attr = { 0 };
META_ASSERT_FALSE(sai_metadata_is_condition_met(NULL, 1, NULL), "condition check failed, %s", md->attridname);
META_ASSERT_FALSE(sai_metadata_is_condition_met(NULL, 1, &attr), "condition check failed, %s", md->attridname);
if (!md->isconditional)
{
META_ASSERT_FALSE(sai_metadata_is_condition_met(md, 1, &attr), "condition check failed");
return;
}
META_ASSERT_TRUE(md->conditionslength <= SAI_METADATA_MAX_CONDITIONS_LEN, "length must not be exceeded");
/* attr is conditional */
/*
* If there are multiple conditions, we need to provide fake values for all
* others to force return false to test each one separately.
*/
uint32_t count = (uint32_t)md->conditionslength;
META_ASSERT_TRUE(count < 20, "too many conditions on %s", md->attridname);
sai_attribute_t *attrs = (sai_attribute_t*)calloc(count, sizeof(sai_attribute_t));
size_t idx = 0;
for (idx = 0; idx < count; ++idx)
{
attrs[idx].id = md->conditions[idx]->attrid;
attrs[idx].value = md->conditions[idx]->condition; /* copy */
}
META_ASSERT_TRUE(sai_metadata_is_condition_met(md, count, attrs), "condition should be met on %s", md->attridname);
if (md->conditiontype == SAI_ATTR_CONDITION_TYPE_OR)
{
for (idx = 0; idx < count; ++idx)
{
attrs[idx].id ^= (uint32_t)(-1);
}
/*
* Condition can actually be met here, since we are supplying unknown attributes
* and condition by default attribute can be met
* META_ASSERT_FALSE(sai_metadata_is_condition_met(md, count, attrs), "condition should not be met");
*/
/* when condition is "or" then any of attribute should match */
for (idx = 0; idx < count; ++idx)
{
/*
* Since multiple attributes with the same ID are passed,
* sai_metadata_is_condition_met is using sai_metadata_get_attr_by_id
* and only first attribute will be selected.
*/
attrs[idx].id ^= (uint32_t)(-1);
META_ASSERT_TRUE(sai_metadata_is_condition_met(md, count, attrs), "condition should be met");
attrs[idx].id ^= (uint32_t)(-1);
}
}
else if (md->conditiontype == SAI_ATTR_CONDITION_TYPE_AND)
{
META_ASSERT_TRUE(sai_metadata_is_condition_met(md, count, attrs), "condition should not be met");
/* when condition is "and" then any of wrong attribute should fail condition */
for (idx = 0; idx < count; ++idx)
{
/*
* NOTE: it may happen that missing attribute have default value
* present, and then that default value will be used for condition
* compare, eg: SAI_PORT_ATTR_1000X_SGMII_SLAVE_AUTODETECT and
* SAI_PORT_ATTR_MEDIA_TYPE.
*/
attrs[idx].id ^= (uint32_t)(-1);
META_ASSERT_FALSE(sai_metadata_is_condition_met(md, count, attrs), "condition should be met");
attrs[idx].id ^= (uint32_t)(-1);
}
}
else if (md->conditiontype == SAI_ATTR_CONDITION_TYPE_MIXED)
{
/* OK */
}
else
{
META_MD_ASSERT_FAIL(md, "unsupported condition type");
}
free(attrs);
}
void check_attr_validonly_met(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
sai_attribute_t attr = { 0 };
META_ASSERT_FALSE(sai_metadata_is_validonly_met(NULL, 1, NULL), "validonly check failed");
META_ASSERT_FALSE(sai_metadata_is_validonly_met(NULL, 1, &attr), "validonly check failed");
if (!md->isvalidonly)
{
META_ASSERT_FALSE(sai_metadata_is_validonly_met(md, 1, &attr), "validonly check failed");
return;
}
META_ASSERT_TRUE(md->validonlylength <= SAI_METADATA_MAX_CONDITIONS_LEN, "length must not be exceeded");
/* attr is validonly */
/*
* If there are multiple validonlys, we need to provide fake values for all
* others to force return false to test each one separately.
*/
uint32_t count = (uint32_t)md->validonlylength;
META_ASSERT_TRUE(count < 20, "too many conditions on %s", md->attridname);
sai_attribute_t *attrs = (sai_attribute_t*)calloc(count, sizeof(sai_attribute_t));
size_t idx = 0;
for (idx = 0; idx < count; ++idx)
{
attrs[idx].id = md->validonly[idx]->attrid;
attrs[idx].value = md->validonly[idx]->condition; /* copy */
}
META_ASSERT_TRUE(sai_metadata_is_validonly_met(md, count, attrs), "validonly should be met on %s", md->attridname);
if (md->validonlytype == SAI_ATTR_CONDITION_TYPE_OR)
{
for (idx = 0; idx < count; ++idx)
{
attrs[idx].id ^= (uint32_t)(-1);
}
/*
* Condition can actually be met here, since we are supplying unknown attributes
* and validonly by default attribute can be met
* META_ASSERT_FALSE(sai_metadata_is_validonly_met(md, count, attrs), "validonly should not be met");
*/
/* when validonly is "or" then any of attribute should match */
for (idx = 0; idx < count; ++idx)
{
/*
* Since multiple attributes with the same ID are passed,
* sai_metadata_is_validonly_met is using sai_metadata_get_attr_by_id
* and only first attribute will be selected.
*/
attrs[idx].id ^= (uint32_t)(-1);
META_ASSERT_TRUE(sai_metadata_is_validonly_met(md, count, attrs), "validonly should be met");
attrs[idx].id ^= (uint32_t)(-1);
}
}
else if (md->validonlytype == SAI_ATTR_CONDITION_TYPE_AND)
{
META_ASSERT_TRUE(sai_metadata_is_validonly_met(md, count, attrs), "validonly should not be met");
/* when validonly is "and" then any of wrong attribute should fail validonly */
for (idx = 0; idx < count; ++idx)
{
/*
* NOTE: it may happen that missing attribute have default value
* present, and then that default value will be used for condition
* compare, eg: SAI_PORT_ATTR_1000X_SGMII_SLAVE_AUTODETECT and
* SAI_PORT_ATTR_MEDIA_TYPE.
*/
const sai_attr_metadata_t *a = sai_metadata_get_attr_metadata(md->objecttype, attrs[idx].id);
if (a && a->defaultvalue)
{
/* alter passed value */
attrs[idx].value.s32 ^= (int32_t)(-1);
META_ASSERT_FALSE(sai_metadata_is_validonly_met(md, count, attrs), "validonly should be met, %s", md->attridname);
attrs[idx].value.s32 ^= (int32_t)(-1);
}
else
{
/* simulate missing attribute */
attrs[idx].id ^= (uint32_t)(-1);
META_ASSERT_FALSE(sai_metadata_is_validonly_met(md, count, attrs), "validonly should be met, %s", md->attridname);
attrs[idx].id ^= (uint32_t)(-1);
}
}
}
else if (md->validonlytype == SAI_ATTR_CONDITION_TYPE_MIXED)
{
/* OK */
}
else
{
META_MD_ASSERT_FAIL(md, "unsupported condition type");
}
free(attrs);
}
void check_attr_default_attrvalue(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
/*
* When default value type is attrvalue, check if this attribute value is
* switch, or if there is attribute on current object, with object
* represented by default attrvalue. There can be only 1 attribute with
* this object type, since when more, then we couldn't decide which one.
*/
if (md->defaultvaluetype != SAI_DEFAULT_VALUE_TYPE_ATTR_VALUE)
{
return;
}
if (md->defaultvalueobjecttype == SAI_OBJECT_TYPE_SWITCH)
{
/* switch is ok */
return;
}
const sai_object_type_info_t* info = sai_metadata_get_object_type_info(md->objecttype);
/* search for attribute */
size_t i = 0;
int count = 0;
for (; i < info->attrmetadatalength; ++i)
{
const sai_attr_metadata_t *cmd = info->attrmetadata[i];
if (cmd->isreadonly)
{
/* skip read only attributes since we don't set them */
continue;
}
if (cmd->attrvaluetype != SAI_ATTR_VALUE_TYPE_OBJECT_ID)
{
/* skip object lists */
continue;
}
if (sai_metadata_is_allowed_object_type(cmd, md->defaultvalueobjecttype))
{
/* object type of default value is present on current object */
count++;
}
}
if (count == 1)
{
/* only 1 attribute with this object type is present */
return;
}
if (count == 0)
{
META_MD_ASSERT_FAIL(md, "oid attribute with %s is not present in %s",
sai_metadata_get_object_type_info(md->defaultvalueobjecttype)->objecttypename,
sai_metadata_get_object_type_info(md->objecttype)->objecttypename);
}
META_MD_ASSERT_FAIL(md, "too many attributes with %s for default value attrvalue",
sai_metadata_get_object_type_info(md->defaultvalueobjecttype)->objecttypename);
}
void check_attr_fdb_flush(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
if (md->objecttype != SAI_OBJECT_TYPE_FDB_FLUSH)
{
return;
}
META_ASSERT_FALSE(md->isconditional, "flush attributes should not be conditional");
META_ASSERT_FALSE(md->isvalidonly, "flush attributes should not be validonly");
/*
* Primitive check can be relaxed in the future.
*/
META_ASSERT_TRUE(md->isprimitive, "flush attributes should be primitives");
META_ASSERT_TRUE(md->flags == SAI_ATTR_FLAGS_CREATE_ONLY, "flush attributes should be create only");
}
void check_attr_hostif_packet(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
if (md->objecttype != SAI_OBJECT_TYPE_HOSTIF_PACKET)
{
return;
}
META_ASSERT_FALSE(md->isvalidonly, "hostif packet attributes should not be validonly");
/*
* Primitive check can be relaxed in the future.
*/
META_ASSERT_TRUE(md->isprimitive, "hostif packet attributes should be primitives");
bool flag = SAI_HAS_FLAG_READ_ONLY(md->flags) || SAI_HAS_FLAG_CREATE_ONLY(md->flags);
META_ASSERT_TRUE(flag, "hostif packet attributes should be read only or create only");
}
void check_attr_capability(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
if (md->capability == NULL)
{
META_ASSERT_TRUE(md->capabilitylength == 0, "capability length should be zero when capability is not defined");
return;
}
META_ASSERT_TRUE(md->capabilitylength != 0, "capability length should not be zero when capability is not defined");
size_t i = 0;
for (; i < md->capabilitylength; ++i)
{
const sai_attr_capability_metadata_t* cap = md->capability[i];
if (md->isreadonly)
{
META_ASSERT_FALSE(cap->operationcapability.create_implemented,
"create must be false on readonly attribute, %s", md->attridname);
META_ASSERT_FALSE(cap->operationcapability.set_implemented,
"set must be false on readonly attribute, %s", md->attridname);
}
if (md->iscreateonly)
{
META_ASSERT_FALSE(cap->operationcapability.set_implemented,
"set must be false on createonly attribute, %s", md->attridname);
}
if (md->ismandatoryoncreate)
{
META_ASSERT_TRUE(cap->operationcapability.create_implemented,
"create must be true on mandatoryoncreate attribute, %s", md->attridname);
}
if (!md->isenum)
{
META_ASSERT_NULL(cap->enumvalues);
META_ASSERT_TRUE(cap->enumvaluescount == 0, "enum values can't be defined when attribute %s is not enum", md->attridname);
}
}
META_ASSERT_NULL(md->capability[i]); /* guard */
}
void check_attr_extension_flag(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
const sai_object_type_info_t* oi = sai_metadata_get_object_type_info(md->objecttype);
if (md->attrid >= oi->attridend && md->attrid >= EXTENSION_RANGE_START)
{
META_ASSERT_TRUE(md->isextensionattr, "attribute %s expected to be extension", md->attridname);
}
else
{
META_ASSERT_FALSE(md->isextensionattr, "attribute %s not expected to be extension", md->attridname);
}
}
void check_attr_condition_relaxed(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
if (md->isconditionrelaxed && !md->isconditional)
{
META_MD_ASSERT_FAIL(md, "relaxed flag applied to non conditional attribute");
}
if (md->isconditionrelaxed)
{
META_LOG_WARN("condition relaxed on: %s", md->attridname);
}
}
void check_single_attribute(
_In_ const sai_attr_metadata_t* md)
{
META_LOG_ENTER();
META_LOG_DEBUG("performing metadata sanity check: object type %d, attr id: %d", md->objecttype, md->attrid);
META_ASSERT_NOT_NULL(md->attridname);
check_if_attr_was_already_defined(md);
check_attr_object_type(md);
check_attr_value_type_range(md);
check_attr_flags(md);
check_attr_object_type_provided(md);
check_attr_allowed_object_types(md);
check_attr_default_required(md);
check_attr_enums(md);
check_attr_default_value_type(md);
check_attr_conditions(md);
check_attr_enum_list_condition(md);
check_attr_validonly(md);
check_attr_enum_list_validonly(md);
check_attr_allow_flags(md);
check_attr_get_save(md);
check_attr_key(md);
check_attr_acl_fields(md);
check_attr_vlan(md);
check_attr_object_id_allownull(md);
check_attr_acl_capability(md);
check_attr_reverse_graph(md);
check_attr_acl_conditions(md);
check_attr_acl_field_or_action(md);
check_attr_acl_mask(md);
check_attr_existing_objects(md);
check_attr_sai_pointer(md);
check_attr_brief_description(md);
check_attr_is_primitive(md);
check_attr_condition_met(md);
check_attr_validonly_met(md);
check_attr_default_attrvalue(md);
check_attr_fdb_flush(md);
check_attr_hostif_packet(md);
check_attr_capability(md);
check_attr_extension_flag(md);
check_attr_mixed_condition(md);
check_attr_mixed_validonly(md);
check_attr_condition_relaxed(md);
define_attr(md);
}
void check_single_object_type_attributes(
_In_ const sai_attr_metadata_t* const* const attributes)
{
META_LOG_ENTER();
size_t index = 0;
for (; attributes[index] != NULL; ++index)
{
check_single_attribute(attributes[index]);
}
}
void check_stat_enums()
{
META_LOG_ENTER();
/*
* Purpose of this check is to find out if object types that have
* statistics (like PORT, etc) have stat enum values populated.
*/
size_t i = 1;
int count = 0;
for (; sai_metadata_all_object_type_infos[i] != NULL; ++i)
{
const sai_object_type_info_t* info = sai_metadata_all_object_type_infos[i];
if (info->statenum != NULL)
{
count++;
}
}
META_ASSERT_TRUE(count > 20, "at least some sai_object_type_into_t->statenum must be populated");
}
void check_object_infos()
{
META_LOG_ENTER();
size_t i = 1;
META_ASSERT_NULL(sai_metadata_all_object_type_infos[0]);
for (; sai_metadata_all_object_type_infos[i] != NULL; ++i)
{
const sai_object_type_info_t* info = sai_metadata_all_object_type_infos[i];
META_ASSERT_NOT_NULL(info->enummetadata);
META_ASSERT_NOT_NULL(info->objecttypename);
META_LOG_DEBUG("processing object type: %s", sai_metadata_get_object_type_name(info->objecttype));
META_ASSERT_TRUE(info->attridstart == 0, "attribute enum start should be zero");
META_ASSERT_TRUE(info->attridend > 0, "attribute enum end must be > 0");
const sai_attr_metadata_t* const* const meta = info->attrmetadata;
META_ASSERT_NOT_NULL(meta);
size_t index = 0;
int last = -1;
/* check all listed attributes under this object type */
bool has_extensions_attrs = false;
bool has_custom_range_attrs = false;
for (; meta[index] != NULL; ++index)
{
const sai_attr_metadata_t* am = meta[index];
META_ASSERT_TRUE((int)am->attrid >= 0, "attribute must be non negative");
META_ASSERT_TRUE(last < (int)am->attrid, "attributes are not increasing");
if (last + 1 != (int)am->attrid)
{
if (am->attrid == CUSTOM_ATTR_RANGE_START)
{
/*
* Object contains custom attributes in custom range, they
* still needs to be increasing by 1 but since those are
* not flags, attribute value can't be used as array index.
*/
has_custom_range_attrs = true;
}
else if (am->attrid == EXTENSION_RANGE_START)
{
has_extensions_attrs = true;
}
else
{
if (is_flag_enum(info->enummetadata))
{
/* flags, ok */
}
else
{
META_MD_ASSERT_FAIL(am, "attr id is not increasing by 1: prev %d, curr %d", last, am->attrid);
}
}
}
if (am->isextensionattr)
{
has_extensions_attrs = true;
}
last = (int)am->attrid;
if (am->attrid >= info->attridstart &&
am->attrid < info->attridend)
{
continue;
}
if (am->attrid >= CUSTOM_ATTR_RANGE_START)
{
/*
* Attribute ID is in custom range, so it will not be in
* regular start .. end range.
*/
continue;
}
if (am->attrid >= info->attridend && am->isextensionattr)
{
/* extensions attribute id can be beyond attr id end range */
continue;
}
META_MD_ASSERT_FAIL(am, "attr is is not in start .. end range");
}
META_ASSERT_NOT_NULL(info->enummetadata);
if (index != info->attridend)
{
if (is_flag_enum(info->enummetadata))
{
/* ok, flags */
}
else if (has_extensions_attrs)
{
/* ok, extension attribute */
}
else if (has_custom_range_attrs)
{
/* ok, custom range attributes */
}
else
{
META_ENUM_ASSERT_FAIL(info->enummetadata, "end of attributes don't match attr count on %s",
sai_metadata_get_object_type_name(info->objecttype));
}
}
}
/* guard */
META_ASSERT_NULL(sai_metadata_all_object_type_infos[i]);
}
void check_non_object_id_object_types()
{
META_LOG_ENTER();
size_t idx = 1;
for (; sai_metadata_all_object_type_infos[idx]; ++idx)
{
const sai_object_type_info_t* info = sai_metadata_all_object_type_infos[idx];
if (!info->isnonobjectid)
{
if (info->structmemberscount != 0 ||
info->structmembers != NULL)
{
META_ASSERT_FAIL("object type %u is non object id but struct members defined", info->objecttype);
}
continue;
}
META_ASSERT_TRUE(info->structmemberscount != 0, "non object id should have members defined");
META_ASSERT_NOT_NULL(info->structmembers);
/* check each member of the struct */
size_t j = 0;
int member_supports_switch_id = 0;
int lastoffset = -1;
for (; j < info->structmemberscount; ++j)
{
META_ASSERT_NOT_NULL(info->structmembers[j]);
const sai_struct_member_info_t *m = info->structmembers[j];
META_ASSERT_NOT_NULL(m->membername);
META_ASSERT_TRUE(m->size > 0, "struct member size must be greater than zero");
META_ASSERT_TRUE((int)m->offset > lastoffset, "struct member offset must increase from member to member");
lastoffset = (int)m->offset;
switch (m->membervaluetype)
{
case SAI_ATTR_VALUE_TYPE_MAC:
case SAI_ATTR_VALUE_TYPE_INT32:
case SAI_ATTR_VALUE_TYPE_UINT32:
case SAI_ATTR_VALUE_TYPE_UINT16:
case SAI_ATTR_VALUE_TYPE_UINT8:
case SAI_ATTR_VALUE_TYPE_UINT32_RANGE:
case SAI_ATTR_VALUE_TYPE_IP_ADDRESS:
case SAI_ATTR_VALUE_TYPE_IP_PREFIX:
case SAI_ATTR_VALUE_TYPE_OBJECT_ID:
case SAI_ATTR_VALUE_TYPE_NAT_ENTRY_DATA:
case SAI_ATTR_VALUE_TYPE_ENCRYPT_KEY:
case SAI_ATTR_VALUE_TYPE_AUTH_KEY:
case SAI_ATTR_VALUE_TYPE_MACSEC_SAK:
case SAI_ATTR_VALUE_TYPE_MACSEC_AUTH_KEY:
case SAI_ATTR_VALUE_TYPE_MACSEC_SALT:
case SAI_ATTR_VALUE_TYPE_IPV6:
break;
default:
/*
* On struct members only primitive types should be
* supported so no other structs or lists.
*/
META_ASSERT_FAIL("struct member %s have invalid value type %d", m->membername, m->membervaluetype);
}
if (m->isenum)
{
META_ASSERT_NOT_NULL(m->enummetadata);
META_ASSERT_TRUE(m->membervaluetype == SAI_ATTR_VALUE_TYPE_INT32,
"when enum is defined in struct member non objectid its type must be INT32");
}
else
{
META_ASSERT_NULL(m->enummetadata);
}
if (m->isvlan)
{
META_ASSERT_TRUE(m->membervaluetype == SAI_ATTR_VALUE_TYPE_UINT16, "member marked as vlan, but wrong type specified");
}
if (m->membervaluetype == SAI_ATTR_VALUE_TYPE_OBJECT_ID)
{
META_ASSERT_NOT_NULL(m->getoid);
META_ASSERT_NOT_NULL(m->setoid);
META_ASSERT_NOT_NULL(m->allowedobjecttypes);
META_ASSERT_TRUE(m->allowedobjecttypeslength > 0, "struct member object id, should specify some object types");
size_t k = 0;
for (; k < m->allowedobjecttypeslength; k++)
{
sai_object_type_t ot = m->allowedobjecttypes[k];
if (ot == SAI_OBJECT_TYPE_FDB_FLUSH || ot == SAI_OBJECT_TYPE_HOSTIF_PACKET)
{
META_ASSERT_FAIL("fdb flush or hostif packet can't be used as object in nonobjectid struct");
}
if (is_valid_object_type(ot))
{
if (ot == SAI_OBJECT_TYPE_SWITCH)
{
/*
* to make struct object type complete, at least
* one struct member should be type of switch
*/
member_supports_switch_id++;
if (strcmp("switch_id", m->membername) != 0)
{
META_ASSERT_FAIL("struct member %s supports object type SWITCH, should be named switch_id", m->membername);
}
META_ASSERT_TRUE(m->allowedobjecttypeslength == 1, "switch_id member should only support object type SWITCH");
}
/* non object id struct can't contain object id which is also non object id */
const sai_object_type_info_t* sinfo = sai_metadata_get_object_type_info(ot);
META_ASSERT_NOT_NULL(sinfo);
if (sinfo->isnonobjectid)
{
META_ASSERT_FAIL("struct member %s of non object id type can't be used as object id in non object id struct: %s",
m->membername,
sai_metadata_get_object_type_name(ot));
}
continue;
}
META_ASSERT_FAIL("invalid object type specified on file %s: %d", m->membername, ot);
}
}
else
{
META_ASSERT_NULL(m->getoid);
META_ASSERT_NULL(m->setoid);
META_ASSERT_NULL(m->allowedobjecttypes);
META_ASSERT_TRUE(m->allowedobjecttypeslength == 0, "member is not object id, should not specify object types");
}
}
META_ASSERT_TRUE(member_supports_switch_id == 1, "there should be only one struct member that support switch id object type");
META_ASSERT_NULL(info->structmembers[j]);
}
}
void check_non_object_id_object_attrs()
{
META_LOG_ENTER();
size_t i = 1;
for (; sai_metadata_all_object_type_infos[i] != NULL; ++i)
{
const sai_object_type_info_t* info = sai_metadata_all_object_type_infos[i];
if (!info->isnonobjectid)
{
continue;
}
const sai_attr_metadata_t* const* meta = info->attrmetadata;
META_ASSERT_NOT_NULL(meta);
size_t idx = 0;
/* iterate all attributes on non object id type */
for (; meta[idx] != NULL; ++idx)
{
const sai_attr_metadata_t* m = meta[idx];
META_ASSERT_NOT_NULL(m);
if (m->isresourcetype && (int)m->flags == SAI_ATTR_FLAGS_READ_ONLY)
{
continue;
}
switch ((int)m->flags)
{
case SAI_ATTR_FLAGS_MANDATORY_ON_CREATE | SAI_ATTR_FLAGS_CREATE_AND_SET:
case SAI_ATTR_FLAGS_CREATE_AND_SET:
case SAI_ATTR_FLAGS_CREATE_ONLY:
break;
default:
META_MD_ASSERT_FAIL(m, "non object id attribute has invalid flags: 0x%x (should be CREATE_AND_SET)", m->flags);
}
}
}
}
void check_attr_sorted_by_id_name()
{
META_LOG_ENTER();
size_t i = 0;
const char *last = "AAA";
META_ASSERT_TRUE(sai_metadata_attr_sorted_by_id_name_count > 1700,
"there should be at least 1700 attributes in total");
for (; i < sai_metadata_attr_sorted_by_id_name_count; ++i)
{
const sai_attr_metadata_t *am = sai_metadata_attr_sorted_by_id_name[i];
META_ASSERT_NOT_NULL(am);
const char *name = am->attridname;
if (strcmp(last, name) >= 0)
{
META_MD_ASSERT_FAIL(am, "attribute id name in not sorted alphabetical");
}
META_ASSERT_TRUE(strncmp(name, "SAI_", 4) == 0, "all attributes should start with SAI_");
last = name;
}
META_ASSERT_NULL(sai_metadata_attr_sorted_by_id_name[i]);
/* check search */
for (i = 0; i < sai_metadata_attr_sorted_by_id_name_count; ++i)
{
const sai_attr_metadata_t *am = sai_metadata_attr_sorted_by_id_name[i];
META_LOG_DEBUG("search for %s", am->attridname);
const sai_attr_metadata_t *found = sai_metadata_get_attr_metadata_by_attr_id_name(am->attridname);
META_ASSERT_NOT_NULL(found);
META_ASSERT_TRUE(strcmp(found->attridname, am->attridname) == 0, "search attr by id name failed to find");
const sai_attr_metadata_t *found_ext = sai_metadata_get_attr_metadata_by_attr_id_name_ext(am->attridname);
META_ASSERT_NOT_NULL(found_ext);
META_ASSERT_TRUE(strcmp(found_ext->attridname, am->attridname) == 0, "search attr by id name ext failed to find");
}
META_ASSERT_NULL(sai_metadata_get_attr_metadata_by_attr_id_name(NULL)); /* null pointer */
META_ASSERT_NULL(sai_metadata_get_attr_metadata_by_attr_id_name("AAA")); /* before all attr names */
META_ASSERT_NULL(sai_metadata_get_attr_metadata_by_attr_id_name("SAI_B")); /* in the middle of attr names */
META_ASSERT_NULL(sai_metadata_get_attr_metadata_by_attr_id_name("SAI_P")); /* in the middle of attr names */
META_ASSERT_NULL(sai_metadata_get_attr_metadata_by_attr_id_name("SAI_W")); /* in the middle of attr names */
META_ASSERT_NULL(sai_metadata_get_attr_metadata_by_attr_id_name("ZZZ")); /* after all attr names */
META_ASSERT_NULL(sai_metadata_get_attr_metadata_by_attr_id_name_ext(NULL)); /* null pointer */
META_ASSERT_NULL(sai_metadata_get_attr_metadata_by_attr_id_name_ext("AAA")); /* before all attr names */
META_ASSERT_NULL(sai_metadata_get_attr_metadata_by_attr_id_name_ext("SAI_B")); /* in the middle of attr names */
META_ASSERT_NULL(sai_metadata_get_attr_metadata_by_attr_id_name_ext("SAI_P")); /* in the middle of attr names */
META_ASSERT_NULL(sai_metadata_get_attr_metadata_by_attr_id_name_ext("SAI_W")); /* in the middle of attr names */
META_ASSERT_NULL(sai_metadata_get_attr_metadata_by_attr_id_name_ext("ZZZ")); /* after all attr names */
}
uint32_t ot2idx(
_In_ sai_object_type_t ot)
{
/*
* This function will convert extension object type to object type number
* that will be defined after SAI_OBJECT_TYPE_MAX. This will be used to
* index in array
*/
if (ot >= SAI_OBJECT_TYPE_NULL && ot < SAI_OBJECT_TYPE_MAX)
return ot;
if (ot >= (int)SAI_OBJECT_TYPE_EXTENSIONS_RANGE_START && ot < (int)SAI_OBJECT_TYPE_EXTENSIONS_RANGE_END)
return SAI_OBJECT_TYPE_MAX + (ot - SAI_OBJECT_TYPE_EXTENSIONS_RANGE_START);
META_ASSERT_FAIL("invalid object type specified %d", ot);
}
sai_object_type_t idx2ot(
_In_ uint32_t idx)
{
if (idx < SAI_OBJECT_TYPE_MAX)
return (sai_object_type_t)idx;
uint32_t i = 1;
for (; sai_metadata_all_object_type_infos[i]; i++)
{
if (i == idx)
{
return sai_metadata_all_object_type_infos[i]->objecttype;
}
}
META_ASSERT_FAIL("invalid index: %d", idx);
}
void list_loop(
_In_ const sai_object_type_info_t* info,
_In_ const sai_object_type_t *visited,
_In_ const uint32_t *attributes,
_In_ int levelidx,
_In_ int level)
{
META_LOG_ENTER();
META_LOG_WARN("LOOP DETECTED on object type: %s",
sai_metadata_get_object_type_name(info->objecttype));
for (; levelidx < level; ++levelidx)
{
sai_object_type_t ot = visited[levelidx];
const char* ot_name = sai_metadata_get_object_type_name(ot);
const sai_attr_metadata_t* m = sai_metadata_get_attr_metadata(ot, attributes[levelidx]);
META_LOG_WARN(" %s: %s", ot_name, m->attridname);
}
META_LOG_WARN(" -> %s", sai_metadata_get_object_type_name(info->objecttype));
if (level >= 0)
{
META_ASSERT_FAIL("LOOP is detected, we can't have loops in graph, please fix attributes");
}
}
void check_objects_for_loops_recursive(
_In_ const sai_object_type_info_t* info,
_Inout_ sai_object_type_t *visited,
_Inout_ uint32_t *attributes,
_In_ int level)
{
META_LOG_ENTER();
visited[level] = info->objecttype;
int levelidx = 0;
for (; levelidx < level; ++levelidx)
{
if (visited[levelidx] == info->objecttype)
{
/* object type is already defined, so we have a loop */
list_loop(info, visited, attributes, levelidx, level);
return;
}
}
const sai_attr_metadata_t* const* meta = info->attrmetadata;
META_ASSERT_NOT_NULL(meta);
size_t idx = 0;
/* iterate all attributes on non object id type */
for (; meta[idx] != NULL; ++idx)
{
const sai_attr_metadata_t* m = meta[idx];
META_ASSERT_NOT_NULL(m);
if (SAI_HAS_FLAG_READ_ONLY(m->flags))
{
/* skip read only attributes since with those we will have loops for sure */
continue;
}
/* skip known loops */
if (m->objecttype == SAI_OBJECT_TYPE_SRV6_SIDLIST)
{
if (m->attrid == SAI_SRV6_SIDLIST_ATTR_NEXT_HOP_ID)
{
continue;
}
}
if (m->objecttype == SAI_OBJECT_TYPE_PORT)
{
if (m->attrid == SAI_PORT_ATTR_EGRESS_MIRROR_SESSION ||
m->attrid == SAI_PORT_ATTR_INGRESS_MIRROR_SESSION ||
m->attrid == SAI_PORT_ATTR_EGRESS_BLOCK_PORT_LIST ||
m->attrid == SAI_PORT_ATTR_INGRESS_SAMPLE_MIRROR_SESSION ||
m->attrid == SAI_PORT_ATTR_EGRESS_SAMPLE_MIRROR_SESSION)
{
continue;
}
}
if (m->objecttype == SAI_OBJECT_TYPE_SCHEDULER_GROUP &&
m->attrid == SAI_SCHEDULER_GROUP_ATTR_PARENT_NODE)
{
continue;
}
attributes[level] = m->attrid;
size_t j = 0;
for (; j < m->allowedobjecttypeslength; ++j)
{
const sai_object_type_info_t* next = sai_metadata_get_object_type_info(m->allowedobjecttypes[j]);
check_objects_for_loops_recursive(next, visited, attributes, level + 1);
}
}
/* iterate for struct members on non object id object types */
if (info->isnonobjectid)
{
size_t j = 0;
for (; j < info->structmemberscount; ++j)
{
const sai_struct_member_info_t *m = info->structmembers[j];
if (m->membervaluetype != SAI_ATTR_VALUE_TYPE_OBJECT_ID)
{
continue;
}
size_t k = 0;
for (; k < m->allowedobjecttypeslength; k++)
{
const sai_object_type_info_t* next = sai_metadata_get_object_type_info(m->allowedobjecttypes[k]);
check_objects_for_loops_recursive(next, visited, attributes, level + 1);
}
}
}
/* clear level on exit */
visited[level] = SAI_OBJECT_TYPE_NULL;
attributes[level] = 0;
}
void check_objects_for_loops()
{
META_LOG_ENTER();
sai_object_type_t visited_objects[TOTAL_OBJECT_TYPE_COUNT];
uint32_t visited_attributes[TOTAL_OBJECT_TYPE_COUNT];
size_t i = 1;
for (; sai_metadata_all_object_type_infos[i] != NULL; ++i)
{
const sai_object_type_info_t* info = sai_metadata_all_object_type_infos[i];
memset(visited_objects, 0, TOTAL_OBJECT_TYPE_COUNT * sizeof(sai_object_type_t));
memset(visited_attributes, 0, TOTAL_OBJECT_TYPE_COUNT * sizeof(uint32_t));
check_objects_for_loops_recursive(info, visited_objects, visited_attributes, 0);
}
}
void check_null_object_id()
{
META_LOG_ENTER();
/*
* Purpose of this check is to make sure that
* SAI_NULL_OBJECT_ID is always ZERO.
*/
META_ASSERT_TRUE(SAI_NULL_OBJECT_ID == 0, "SAI_NULL_OBJECT_ID must be zero");
}
void check_read_only_attributes()
{
META_LOG_ENTER();
/*
* Purpose of this check is to find out if there is any
* object that has only READ_ONLY attributes.
*
* If given object has only read only attributes
* there should be no purpose of such object.
* With only read only attributes there is no
* way to compare 2 objects of the same type
* sine we don't track read only attributes.
*
* As additional check we will also check if given
* object type defines at least 1 attribute.
*/
size_t i = 1;
for (; sai_metadata_all_object_type_infos[i] != NULL; ++i)
{
const sai_object_type_info_t* info = sai_metadata_all_object_type_infos[i];
size_t index = 0;
/* check all listed attributes under this object type */
int non_read_only_count = 0;
const sai_attr_metadata_t* const* const meta = info->attrmetadata;
for (; meta[index] != NULL; ++index)
{
const sai_attr_metadata_t* m = meta[index];
if (!SAI_HAS_FLAG_READ_ONLY(m->flags))
{
non_read_only_count++;
}
}
if (index < 1)
{
META_ASSERT_FAIL("object %s must define at least 1 attribute",
sai_metadata_get_object_type_name(info->objecttype));
}
if (non_read_only_count == 0)
{
/*
* currently we have some objects with only read only
* attributes, we for now we just warn here until this
* issue will be resolved.
*/
META_LOG_WARN("object %s has only READ_ONLY attributes",
sai_metadata_get_object_type_name(info->objecttype));
}
}
}
void check_mixed_object_list_types()
{
META_LOG_ENTER();
/*
* Purpose of this check is to find out if any of object id lists supports
* multiple object types at the same time. For now this ability will not
* be supported.
*/
META_ASSERT_TRUE(sai_metadata_attr_sorted_by_id_name_count > 1700, "there should be at least 1700 attributes in total");
size_t idx = 0;
for (; idx < sai_metadata_attr_sorted_by_id_name_count; ++idx)
{
const sai_attr_metadata_t* meta = sai_metadata_attr_sorted_by_id_name[idx];
switch (meta->attrvaluetype)
{
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_LIST:
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_LIST:
case SAI_ATTR_VALUE_TYPE_OBJECT_LIST:
META_ASSERT_TRUE(meta->allowedobjecttypeslength > 0, "allowed object types on list can't be zero");
if (meta->allowedobjecttypeslength == 1)
{
continue;
}
if (meta->flags == SAI_ATTR_FLAGS_READ_ONLY)
{
/*
* If attribute flag is READ_ONLY, then object can support
* mixed object types returned on list, for example
* SAI_SCHEDULER_GROUP_ATTR_CHILD_LIST when it returns
* schedulers and queues.
*/
}
else
{
if (meta->objecttype == SAI_OBJECT_TYPE_ACL_ENTRY)
{
/*
* Allow mixed object types on ACL entries since they can point
* to different object types like PORT or BRIDGE_PORT etc.
*/
break;
}
if (meta->objecttype == SAI_OBJECT_TYPE_MIRROR_SESSION)
{
break;
}
/*
* For non read only attributes, there should be a good
* reason why object list should support mixed object
* types on that list. Then this restriction can be
* relaxed and description should be added why mixed
* object types should be possible.
*/
META_MD_ASSERT_FAIL(meta, "allowed object types on object id list is more then 1, not supported yet");
}
break;
default:
META_ASSERT_FALSE(meta->allowmixedobjecttypes, "allow mixed object types should be false on non object id list");
break;
}
}
}
/*
* Below are defined all generic methods needed for api name check
*/
typedef sai_status_t (*generic_create_fn)(
_Out_ sai_object_id_t *object_id,
_In_ sai_object_id_t switch_id,
_In_ uint32_t attr_count,
_In_ const sai_attribute_t *attr_list);
typedef sai_status_t (*generic_remove_fn)(
_In_ sai_object_id_t object_id);
typedef sai_status_t (*generic_set_fn)(
_In_ sai_object_id_t object_id,
_In_ const sai_attribute_t *attr);
typedef sai_status_t (*generic_get_fn)(
_In_ sai_object_id_t object_id,
_In_ uint32_t attr_count,
_Inout_ sai_attribute_t *attr_list);
typedef sai_status_t(*switch_create_fn)(
_Out_ sai_object_id_t* switch_id,
_In_ uint32_t attr_count,
_In_ const sai_attribute_t *attr_list);
void check_single_non_object_id_for_rev_graph(
_In_ const sai_struct_member_info_t *sm,
_In_ sai_object_type_t objecttype,
_In_ sai_object_type_t depobjecttype)
{
META_LOG_ENTER();
/*
* This method checks single non object id struct
* member.
*/
const sai_object_type_info_t *oi = sai_metadata_get_object_type_info(depobjecttype);
META_ASSERT_NOT_NULL(oi->revgraphmembers);
size_t revidx = 0;
bool defined = false;
for (; oi->revgraphmembers[revidx] != NULL; revidx++)
{
/*
* now let's search for graph member which defines
* this object and the same attribute value
*/
const sai_rev_graph_member_t *rm = oi->revgraphmembers[revidx];
META_ASSERT_TRUE(rm->objecttype == depobjecttype, "invalid objecttype definition");
if (rm->depobjecttype != objecttype)
{
/*
* this is not the member we are looking for
*/
continue;
}
if (rm->attrmetadata == NULL)
{
META_ASSERT_NOT_NULL(rm->structmember);
/*
* This graph entry is struct member, maybe this i the
* one we are looking for, since graph can have multiple
* entries for the same object.
*/
if (strcmp(rm->structmember->membername, sm->membername) != 0)
{
/* this is the member we are not looking for */
continue;
}
/*
* We found out member name, so our object must be on the object list
*/
size_t i = 0;
for (; i < rm->structmember->allowedobjecttypeslength; i++)
{
/*
* Object type of graph member must match and also
* attribute id must match.
*/
if (rm->structmember->allowedobjecttypes[i] == depobjecttype)
{
META_LOG_DEBUG("dep %s ot %s attr %s\n",
sai_metadata_get_object_type_name(depobjecttype),
sai_metadata_get_object_type_name(objecttype),
sm->membername);
defined = true;
break;
}
}
if (defined)
{
break;
}
}
else
{
/*
* object is attribute
*/
META_ASSERT_NOT_NULL(rm->attrmetadata);
META_ASSERT_NULL(rm->structmember);
/*
* we are not looking for attribute object
* we are looking for struct member
*/
continue;
}
}
META_ASSERT_TRUE(defined, "reverse graph object is not defined anywhere");
}
void check_reverse_graph_for_non_object_id()
{
META_LOG_ENTER();
/*
* Purpose of this check is to find out whether non object id structmembers
* which are object id are well defined inside reverse graph. Attribute
* values are checked during standard loop of attribute above.
*/
size_t i = 1;
for (; sai_metadata_all_object_type_infos[i] != NULL; ++i)
{
const sai_object_type_info_t* info = sai_metadata_all_object_type_infos[i];
if (info == NULL || !info->isnonobjectid)
{
continue;
}
/*
* This is non object id and they can't have graph members
* since non object id can't be used as any object id.
*/
META_ASSERT_NULL(info->revgraphmembers);
/*
* Now for each struct member check if it's object id member
* and process it.
*/
size_t j = 0;
for (; j < info->structmemberscount; ++j)
{
const sai_struct_member_info_t *m = info->structmembers[j];
if (m->membervaluetype != SAI_ATTR_VALUE_TYPE_OBJECT_ID)
{
continue;
}
size_t k = 0;
for (; k < m->allowedobjecttypeslength; k++)
{
/*
* For each object type check it's location in graph
*/
sai_object_type_t depobjecttype = m->allowedobjecttypes[k];
check_single_non_object_id_for_rev_graph(m, info->objecttype, depobjecttype);
}
}
}
}
void check_vlan_attributes()
{
META_LOG_ENTER();
/*
* Purpose of this check is to make sure there in vlan object there is only
* one attribute marked as a KEY and it's a VLAN_ID and it's value type is
* UINT16. This will be helpful later on on comparison logic since we can
* have so many vlan's then searching them in hash will be much faster than
* iterating each time.
*/
const sai_attr_metadata_t* const* const meta = sai_metadata_object_type_info_SAI_OBJECT_TYPE_VLAN.attrmetadata;
size_t index = 0;
int keys = 0;
for (; meta[index] != NULL; index++)
{
const sai_attr_metadata_t *md = meta[index];
if (SAI_HAS_FLAG_KEY(md->flags))
{
keys++;
}
if (md->attrid == SAI_VLAN_ATTR_VLAN_ID)
{
int expected_flags = (SAI_ATTR_FLAGS_MANDATORY_ON_CREATE | SAI_ATTR_FLAGS_CREATE_ONLY | SAI_ATTR_FLAGS_KEY);
if ((int)md->flags != expected_flags)
{
META_MD_ASSERT_FAIL(md, "vlan id should have flags MANDATORY_ON_CREATE | CREATE_ONLY | KEY, but has: %d", md->flags);
}
META_ASSERT_TRUE(md->attrvaluetype == SAI_ATTR_VALUE_TYPE_UINT16, "VLAN_ID should be UINT16");
}
}
META_ASSERT_TRUE(keys == 1, "vlan object type should have only 1 attribute marked as key which is vlan id");
}
void check_acl_table_fields_and_acl_entry_fields()
{
META_LOG_ENTER();
/*
* Purpose of this check is to find out if acl table fields and acl entry
* fields correspond to each other. We also make check if they have the
* same attribute id which is not required but it is nice to have. We also
* check if those attributes have right flags and right attribute values.
*/
META_ASSERT_TRUE(SAI_ACL_ENTRY_ATTR_FIELD_START == 0x1000, "acl entry field start value should be 0x1000");
META_ASSERT_TRUE(SAI_ACL_TABLE_ATTR_FIELD_START == 0x1000, "acl table field start value should be 0x1000");
META_ASSERT_TRUE((int)SAI_ACL_ENTRY_ATTR_FIELD_START == (int)SAI_ACL_TABLE_ATTR_FIELD_START,
"acl entry and table fields start should be the same");
/*
* We are using volatile here since if we use enum directly and values are
* different, compiler will optimize this to true and throw error on
* candidate for non return which in this case is confusing.
*/
volatile int table_end = SAI_ACL_TABLE_ATTR_FIELD_END;
volatile int entry_end = SAI_ACL_ENTRY_ATTR_FIELD_END;
if (table_end != entry_end)
{
META_ASSERT_FAIL("SAI_ACL_TABLE_ATTR_FIELD_END 0x%x is not equal to SAI_ACL_ENTRY_ATTR_FIELD_END 0x%x",
SAI_ACL_TABLE_ATTR_FIELD_END, SAI_ACL_ENTRY_ATTR_FIELD_END);
}
/*
* find both attribute fields start for entry and table
*/
const sai_attr_metadata_t* const* meta_acl_table = sai_metadata_object_type_info_SAI_OBJECT_TYPE_ACL_TABLE.attrmetadata;
const sai_attr_metadata_t* const* meta_acl_entry = sai_metadata_object_type_info_SAI_OBJECT_TYPE_ACL_ENTRY.attrmetadata;
int acl_table_field_index = 0;
for (; meta_acl_table[acl_table_field_index] != NULL; acl_table_field_index++)
{
if (meta_acl_table[acl_table_field_index]->attrid == SAI_ACL_TABLE_ATTR_FIELD_START)
{
break;
}
}
META_ASSERT_NOT_NULL(meta_acl_table[acl_table_field_index]);
int acl_entry_field_index = 0;
for (; meta_acl_entry[acl_entry_field_index] != NULL; acl_entry_field_index++)
{
if (meta_acl_entry[acl_entry_field_index]->attrid == SAI_ACL_ENTRY_ATTR_FIELD_START)
{
break;
}
}
META_ASSERT_NOT_NULL(meta_acl_entry[acl_entry_field_index]);
/*
* we found our attribute indexes, now let's compare attributes
*/
while (true)
{
const sai_attr_metadata_t *mtable = meta_acl_table[acl_table_field_index];
const sai_attr_metadata_t *mentry = meta_acl_entry[acl_entry_field_index];
if (mentry == NULL || mtable == NULL)
{
break;
}
if (mtable->attrid > SAI_ACL_TABLE_ATTR_FIELD_END ||
mentry->attrid > SAI_ACL_ENTRY_ATTR_FIELD_END)
{
break;
}
META_LOG_DEBUG("processing acl fields: %s %s", mtable->attridname, mentry->attridname);
/*
* check acl table flags and attr value type
*/
if ((mtable->attrid == SAI_ACL_TABLE_ATTR_FIELD_ACL_RANGE_TYPE) ||
((mtable->attrid >= SAI_ACL_TABLE_ATTR_USER_DEFINED_FIELD_GROUP_MIN) &&
(mtable->attrid <= SAI_ACL_TABLE_ATTR_USER_DEFINED_FIELD_GROUP_MAX)))
{
/*
* This field is exception, it's not bool, it's a list and it's
* designed in this way to save resources on device to not support
* all ranges on each acl table when it's not necessary.
*/
}
else
{
if (mtable->flags != SAI_ATTR_FLAGS_CREATE_ONLY)
{
META_MD_ASSERT_FAIL(mtable, "acl table field flags should be CREATE_ONLY");
}
if (mtable->attrvaluetype != SAI_ATTR_VALUE_TYPE_BOOL)
{
META_MD_ASSERT_FAIL(mtable, "acl table attr value type should be bool");
}
}
/*
* check acl entry flags
*/
if (mentry->flags != SAI_ATTR_FLAGS_CREATE_AND_SET)
{
META_MD_ASSERT_FAIL(mentry, "acl entry field flags should be CREATE_AND_SET");
}
if (mentry->attrid != mtable->attrid)
{
META_MD_ASSERT_FAIL(mentry, "acl entry attr id %d is different than acl table field %d", mentry->attrid, mtable->attrid);
}
/*
* check acl fields attribute if endings are the same
*/
const char * attr_table_pos = strstr(mtable->attridname, "_ATTR_");
META_ASSERT_NOT_NULL(attr_table_pos);
const char * attr_entry_pos = strstr(mentry->attridname, "_ATTR_");
META_ASSERT_NOT_NULL(attr_entry_pos);
if (strcmp(attr_table_pos, attr_entry_pos) != 0)
{
META_ASSERT_FAIL("attr entry field name %s is not ending at the same name as acl table field %s",
mentry->attridname, mtable->attridname);
}
acl_table_field_index++;
acl_entry_field_index++;
}
}
void check_acl_entry_actions()
{
META_LOG_ENTER();
/*
* Purpose of this check is to find out if acl entry actions correspond to
* sai_acl_action_type_t enum type and contain all actions in the same
* order.
*/
META_ASSERT_TRUE(SAI_ACL_ENTRY_ATTR_ACTION_START == 0x2000, "acl entry action start value should be 0x2000");
/*
* find both attribute fields start for entry and table
*/
const sai_attr_metadata_t *const * meta_acl_entry = sai_metadata_object_type_info_SAI_OBJECT_TYPE_ACL_ENTRY.attrmetadata;
size_t index = 0;
for (; meta_acl_entry[index] != NULL; index++)
{
if (meta_acl_entry[index]->attrid == SAI_ACL_ENTRY_ATTR_ACTION_START)
{
break;
}
}
META_ASSERT_NOT_NULL(meta_acl_entry[index]);
/*
* lets compare all action attributes with enum names
*/
size_t enum_index = 0;
while (true)
{
const sai_attr_metadata_t *meta = meta_acl_entry[index];
if (meta == NULL)
{
break;
}
if ((meta->isextensionattr == false) && (meta->attrid > SAI_ACL_ENTRY_ATTR_ACTION_END))
{
break;
}
if (meta->flags != SAI_ATTR_FLAGS_CREATE_AND_SET)
{
META_MD_ASSERT_FAIL(meta, "acl entry action flags should be CREATE_AND_SET");
}
const char* enum_name = sai_metadata_enum_sai_acl_action_type_t.valuesnames[enum_index];
META_ASSERT_NOT_NULL(enum_name);
META_LOG_DEBUG("processing acl action: %s %s", meta->attridname, enum_name);
/*
* check acl fields attribute if endings are the same
*/
const char * enum_name_pos = strstr(enum_name, "_ACTION_TYPE_");
META_ASSERT_NOT_NULL(enum_name_pos);
const char * attr_entry_pos = strstr(meta->attridname, "_ATTR_ACTION_");
META_ASSERT_NOT_NULL(attr_entry_pos);
if (strcmp(enum_name_pos + strlen("_ACTION_TYPE_"), attr_entry_pos + strlen("_ATTR_ACTION_")) != 0)
{
META_ASSERT_FAIL("attr entry action name %s is not ending at the same enum name %s",
meta->attridname, enum_name);
}
index++;
enum_index++;
}
META_ASSERT_TRUE(enum_index == sai_metadata_enum_sai_acl_action_type_t.valuescount,
"number of acl entry action mismatch vs number of enums in sai_acl_action_type_t");
}
void check_switch_attributes()
{
META_LOG_ENTER();
/*
* Purpose of this check is to find out whether switch object has some
* conditional or validonly attributes. Currently we are making assumptions
* that there are no such objects, so we are adding check for that, but if
* there will be need for such in the future, this check can be removed.
*/
const sai_attr_metadata_t* const* const meta = sai_metadata_object_type_info_SAI_OBJECT_TYPE_SWITCH.attrmetadata;
size_t index = 0;
for (; meta[index] != NULL; index++)
{
const sai_attr_metadata_t *md = meta[index];
/*
* Gearbox attributes can be marked as mandatory on create.
*/
if (md->isoidattribute && md->ismandatoryoncreate)
{
META_MD_ASSERT_FAIL(md, "Mandatory on create can't be object id on SWITCH");
}
if (md->isoidattribute && md->iscreateonly)
{
META_MD_ASSERT_FAIL(md, "Create only can't be object id on SWITCH");
}
}
}
void check_switch_create_only_objects()
{
META_LOG_ENTER();
/*
* Purpose of this check is to find out whether switch object has some
* attributes that are object id type and are marked as CREATE_ONLY. Such
* attribute has no sense since, you need first switch_id to create any
* other object so setting that object on create will be impossible.
*/
const sai_attr_metadata_t* const* const meta = sai_metadata_object_type_info_SAI_OBJECT_TYPE_SWITCH.attrmetadata;
size_t index = 0;
for (; meta[index] != NULL; index++)
{
const sai_attr_metadata_t *md = meta[index];
if (SAI_HAS_FLAG_CREATE_ONLY(md->flags) && md->isoidattribute)
{
META_MD_ASSERT_FAIL(md, "attribute is create_only and it's an object id, this is not allowed");
}
}
}
void check_quad_api_pointers(
_In_ const sai_object_type_info_t *oi)
{
META_LOG_ENTER();
/*
* Check if quad api pointers are not NULL, hostif packet and fdb flush are
* special, dummy functions are generated.
*/
META_ASSERT_NOT_NULL(oi->create);
META_ASSERT_NOT_NULL(oi->remove);
META_ASSERT_NOT_NULL(oi->set);
META_ASSERT_NOT_NULL(oi->get);
}
void check_stats_api_pointers(
_In_ const sai_object_type_info_t *oi)
{
META_LOG_ENTER();
/*
* Check if stats api pointers are not NULL, for objects that don't support
* stats dummy functions are generated.
*/
META_ASSERT_NOT_NULL(oi->getstats);
META_ASSERT_NOT_NULL(oi->getstatsext);
META_ASSERT_NOT_NULL(oi->clearstats);
}
void check_object_id_non_object_id(
_In_ const sai_object_type_info_t *oi)
{
META_LOG_ENTER();
/*
* Purpose of this test is to check whether isobjectid and isnonobject id
* have opposite values.
*/
META_ASSERT_TRUE(oi->isnonobjectid == !oi->isobjectid, "non object id object id not match");
}
void check_enum_to_attr_map(
_In_ const sai_object_type_info_t *oi)
{
META_LOG_ENTER();
/*
* Check whether attribute enum declared has equal number of items as the
* number of declared attributes. Item with @ignore flag should be
* removed from enum and attribute should not be created.
*/
META_LOG_DEBUG("checking %s", oi->objecttypename);
uint32_t i = 0;
META_ASSERT_TRUE(oi->enummetadata->valuescount == oi->attrmetadatalength, "attr length must be equal to enum length");
for (; i < oi->enummetadata->valuescount ;i++)
{
META_LOG_DEBUG("checking enum %s", oi->enummetadata->valuesnames[i]);
const sai_attr_metadata_t *m = oi->attrmetadata[i];
META_ASSERT_NOT_NULL(m);
META_ASSERT_TRUE(m->attrid == (uint32_t)oi->enummetadata->values[i], "attrid must be equal to enum");
}
META_ASSERT_NULL(oi->attrmetadata[i]);
}
void check_object_ro_list(
_In_ const sai_object_type_info_t *oi)
{
META_LOG_ENTER();
/*
* Purpose is to check if object is referenced in any other object as read
* only attribute to know that we can get all objects of this type.
* Example: VLAN and VLAN_MEMBER. All vlan members are listed on attribute:
* SAI_VLAN_ATTR_MEMBER_LIST.
*
* Should we only check that for leaf objects?
*/
if (oi->isnonobjectid)
{
return;
}
if (oi->objecttype == SAI_OBJECT_TYPE_FDB_FLUSH ||
oi->objecttype == SAI_OBJECT_TYPE_HOSTIF_PACKET ||
oi->objecttype == SAI_OBJECT_TYPE_SWITCH ||
oi->objecttype == SAI_OBJECT_TYPE_BFD_SESSION ||
oi->objecttype == SAI_OBJECT_TYPE_HOSTIF_TABLE_ENTRY ||
oi->objecttype == SAI_OBJECT_TYPE_DTEL ||
oi->objecttype == SAI_OBJECT_TYPE_DTEL_QUEUE_REPORT ||
oi->objecttype == SAI_OBJECT_TYPE_DTEL_EVENT ||
oi->objecttype == SAI_OBJECT_TYPE_GENERIC_PROGRAMMABLE ||
oi->objecttype == SAI_OBJECT_TYPE_TWAMP_SESSION ||
oi->objecttype == SAI_OBJECT_TYPE_ICMP_ECHO_SESSION)
{
/*
* We skip hostif table entry since there is no 1 object which can
* identify all table entries. We would need to add one attribute for
* each used object type port, lag, vlan etc.
*/
return;
}
size_t idx = 0;
for (; idx < sai_metadata_attr_sorted_by_id_name_count; ++idx)
{
const sai_attr_metadata_t *meta = sai_metadata_attr_sorted_by_id_name[idx];
if (sai_metadata_is_allowed_object_type(meta, oi->objecttype))
{
if (oi->revgraphmembers != 0)
{
/* this object is not leaf, so it must be used as attribute */
return;
}
}
if (meta->attrvaluetype != SAI_ATTR_VALUE_TYPE_OBJECT_LIST)
{
continue;
}
if (!meta->isreadonly)
{
continue;
}
if (sai_metadata_is_allowed_object_type(meta, oi->objecttype))
{
return;
}
}
if (oi->isexperimental)
{
META_LOG_DEBUG("experimental object %s not present on any object list (eg. VLAN_MEMBER is present on SAI_VLAN_ATTR_MEMBER_LIST)", oi->objecttypename);
return;
}
if (SAI_OBJECT_TYPE_DEBUG_COUNTER == oi->objecttype)
{
META_LOG_WARN("debug counter object %s not present on any object list (eg. VLAN_MEMBER is present on SAI_VLAN_ATTR_MEMBER_LIST)", oi->objecttypename);
return;
}
META_ASSERT_FAIL("%s not present on any object list (eg. VLAN_MEMBER is present on SAI_VLAN_ATTR_MEMBER_LIST)", oi->objecttypename);
}
void check_reverse_graph_count(
_In_ const sai_object_type_info_t *oi)
{
META_LOG_ENTER();
size_t i = 0;
if (oi->revgraphmemberscount == 0)
{
META_ASSERT_NULL(oi->revgraphmembers);
return;
}
META_ASSERT_NOT_NULL(oi->revgraphmembers);
for (; i < oi->revgraphmemberscount; ++i)
{
META_ASSERT_NOT_NULL(oi->revgraphmembers[i]);
}
META_ASSERT_NULL(oi->revgraphmembers[i]);
}
void check_experimental_flag(
_In_ const sai_object_type_info_t *oi)
{
META_LOG_ENTER();
if (oi->objecttype >= SAI_OBJECT_TYPE_MAX)
{
META_ASSERT_TRUE(oi->isexperimental, "object %s is expected to be marked as experimental", oi->objecttypename);
}
else
{
META_ASSERT_FALSE(oi->isexperimental, "object %s is expected to not be marked as experimental", oi->objecttypename);
}
}
void check_attr_end(
_In_ const sai_object_type_info_t *oi)
{
META_LOG_ENTER();
/*
* Check if all attributes are in start/end range.
*/
const sai_attr_metadata_t* const* const meta = oi->attrmetadata;
META_ASSERT_NOT_NULL(meta);
size_t index = 0;
for (; meta[index] != NULL; ++index)
{
if (meta[index]->attrid >= oi->attridstart)
continue;
if (meta[index]->attrid < oi->attridend)
continue;
if (meta[index]->isextensionattr)
continue;
META_MD_ASSERT_FAIL(meta[index], "attribute not in START .. END range");
}
}
void check_single_object_info(
_In_ const sai_object_type_info_t *oi)
{
META_LOG_ENTER();
check_quad_api_pointers(oi);
check_stats_api_pointers(oi);
check_object_id_non_object_id(oi);
check_enum_to_attr_map(oi);
check_object_ro_list(oi);
check_reverse_graph_count(oi);
check_experimental_flag(oi);
check_attr_end(oi);
}
void check_backward_comparibility_defines()
{
META_LOG_ENTER();
/* check assignments if type matches */
sai_switch_attr_t sw = SAI_SWITCH_ATTR_SHUTDOWN_REQUEST_NOTIFY;
sai_hostif_user_defined_trap_type_t trap = SAI_HOSTIF_USER_DEFINED_TRAP_TYPE_NEIGH;
sai_acl_bind_point_type_t bind = SAI_ACL_BIND_POINT_TYPE_ROUTER_INTF;
META_ASSERT_TRUE(sw == SAI_SWITCH_ATTR_SWITCH_SHUTDOWN_REQUEST_NOTIFY, "not equal");
META_ASSERT_TRUE(trap == SAI_HOSTIF_USER_DEFINED_TRAP_TYPE_NEIGHBOR, "not equal");
META_ASSERT_TRUE(bind == SAI_ACL_BIND_POINT_TYPE_ROUTER_INTERFACE, "not equal");
}
void helper_check_graph_connected(
_In_ sai_object_type_t ot,
_Inout_ sai_object_type_t *visited)
{
META_LOG_ENTER();
if (visited[ot2idx(ot)] == ot)
{
return;
}
visited[ot2idx(ot)] = ot;
const sai_object_type_info_t *oi = sai_metadata_get_object_type_info(ot);
size_t i = 0;
/* check all attributes */
for (; i < oi->attrmetadatalength; ++i)
{
const sai_attr_metadata_t *md = oi->attrmetadata[i];
if (!md->isoidattribute)
{
continue;
}
if (!(md->iscreateonly || md->iscreateandset))
{
continue;
}
size_t j = 0;
for (;j < md->allowedobjecttypeslength; ++j)
{
helper_check_graph_connected(md->allowedobjecttypes[j], visited);
}
}
for (i = 0; i < oi->structmemberscount; ++i)
{
const sai_struct_member_info_t *sm = oi->structmembers[i];
size_t j = 0;
for (;j < sm->allowedobjecttypeslength; ++j)
{
helper_check_graph_connected(sm->allowedobjecttypes[j], visited);
}
}
for (i = 0; i < oi->revgraphmemberscount; ++i)
{
const sai_rev_graph_member_t *rgm = oi->revgraphmembers[i];
helper_check_graph_connected(rgm->depobjecttype, visited);
}
}
void check_graph_connected()
{
META_LOG_ENTER();
/*
* Check if all objects are used and are not "disconnected" from the graph.
*/
sai_object_type_t visited[TOTAL_OBJECT_TYPE_COUNT];
memset(visited, 0, TOTAL_OBJECT_TYPE_COUNT * sizeof(sai_object_type_t));
helper_check_graph_connected(SAI_OBJECT_TYPE_PORT, visited);
uint32_t i = 1;
for (; sai_metadata_all_object_type_infos[i] != NULL; ++i)
{
if (visited[i] == sai_metadata_all_object_type_infos[i]->objecttype)
{
continue;
}
if (sai_metadata_all_object_type_infos[i]->isexperimental)
{
/* allow experimental object types to be disconnected from main graph */
META_LOG_WARN("experimental object %s is disconnected from graph",
sai_metadata_all_object_type_infos[i]->objecttypename);
continue;
}
if (SAI_OBJECT_TYPE_DEBUG_COUNTER == idx2ot(i))
{
/*
* Allow debug counters to be disconnected from main graph
* as use case is by querying base object stats and not by direct reference
*/
META_LOG_WARN("debug counter object %s is disconnected from graph",
sai_metadata_all_object_type_infos[i]->objecttypename);
continue;
}
META_ASSERT_FAIL("object %s is disconnected from graph",
sai_metadata_all_object_type_infos[i]->objecttypename);
}
}
void check_get_attr_metadata()
{
META_LOG_ENTER();
int count = 0;
size_t i = 1;
for (; sai_metadata_all_object_type_infos[i]; ++i)
{
const sai_attr_metadata_t* const* mda = sai_metadata_all_object_type_infos[i]->attrmetadata;
int idx = 0;
while (mda[idx])
{
const sai_attr_metadata_t* m = mda[idx++];
const sai_attr_metadata_t* md = sai_metadata_get_attr_metadata(sai_metadata_all_object_type_infos[i]->objecttype, m->attrid);
META_ASSERT_NOT_NULL(md);
META_ASSERT_TRUE(m == md, "different attribute found, fatal");
count++;
}
}
META_ASSERT_TRUE(count > 1700, "expected at least 1700 attributes");
}
void check_get_attr_metadata_custom_range()
{
META_LOG_ENTER();
/*
* This function will check, if attributes which are marked as no flags,
* will have values equal to attribute index in metadata array even if
* attribute is extension attribute, and custom range attributes will be
* ignored.
*
* This will make sure that we can use attr id as index in arrays except
* custom attributes.
*/
size_t count = 0;
int i = 1;
for (; sai_metadata_all_object_type_infos[i]; i++)
{
const sai_object_type_info_t* oti = sai_metadata_all_object_type_infos[i];
const sai_attr_metadata_t* const* mda = sai_metadata_all_object_type_infos[i]->attrmetadata;
if (oti->enummetadata->containsflags)
{
int idx = 0;
while (mda[idx])
{
const sai_attr_metadata_t* m = mda[idx++];
const sai_attr_metadata_t* md = sai_metadata_get_attr_metadata(sai_metadata_all_object_type_infos[i]->objecttype, m->attrid);
META_ASSERT_NOT_NULL(md);
META_ASSERT_TRUE(m == md, "different attribute found, fatal");
count++;
}
continue;
}
/* no flags attributes */
META_ASSERT_TRUE(oti->attridend <= oti->attrmetadatalength, "attridend must be less or equal to total number of attributes");
uint32_t idx = 0;
while (mda[idx])
{
const sai_attr_metadata_t* m = mda[idx];
const sai_attr_metadata_t* md = sai_metadata_get_attr_metadata(sai_metadata_all_object_type_infos[i]->objecttype, m->attrid);
META_ASSERT_NOT_NULL(md);
META_ASSERT_TRUE(m == md, "different attribute found, fatal");
if (oti->attridend == oti->attrmetadatalength || idx < oti->attridend)
{
META_ASSERT_TRUE(md->attrid == idx, "%s, attrid (%u) must be equal to index (%u)", md->attridname, md->attrid, idx);
}
else /* extensions or custom attributes */
{
if (md->attrid < CUSTOM_ATTR_RANGE_START)
{
META_ASSERT_TRUE(md->attrid == idx, "extenstion attribute %s, attrid (%u) must be equal to index (%u)", md->attridname, md->attrid, idx);
}
else
{
/* custom range attributes will not follow index increase */
}
}
idx++;
count++;
}
}
META_ASSERT_TRUE(count > 1700, "expected at least 1700 attributes, got %zu", count);
}
void check_attr_get_outside_range()
{
META_LOG_ENTER();
int ot = -10;
for (; ot < (int)(SAI_OBJECT_TYPE_MAX + 10); ++ot)
{
const sai_object_type_info_t* oti = sai_metadata_get_object_type_info(ot);
if (oti == NULL)
continue;
int idx = -10;
for (; idx < (int)(oti->attrmetadatalength + 10); idx++)
{
const sai_attr_metadata_t* md = sai_metadata_get_attr_metadata(ot, (sai_attr_id_t)idx);
if (md == NULL)
continue;
if ((int)md->attrid != idx)
{
META_MD_ASSERT_FAIL(md, "attr %u expected to be %u", md->attrid, idx);
}
}
}
ot = SAI_OBJECT_TYPE_EXTENSIONS_RANGE_START -10 ;
for (; ot < (int)(SAI_OBJECT_TYPE_EXTENSIONS_RANGE_END + 10); ++ot)
{
const sai_object_type_info_t* oti = sai_metadata_get_object_type_info(ot);
if (oti == NULL)
continue;
int idx = -10;
for (; idx < (int)(oti->attrmetadatalength + 10); idx++)
{
const sai_attr_metadata_t* md = sai_metadata_get_attr_metadata(ot, (sai_attr_id_t)idx);
if (md == NULL)
continue;
if ((int)md->attrid != idx)
{
META_MD_ASSERT_FAIL(md, "attr %u expected to be %u", md->attrid, idx);
}
}
}
}
void check_custom_range_attributes()
{
META_LOG_ENTER();
/* Checks whether attribute is correctly marked as custom */
size_t i = 1;
for (; sai_metadata_all_object_type_infos[i]; i++)
{
const sai_attr_metadata_t* const* mda = sai_metadata_all_object_type_infos[i]->attrmetadata;
int idx = 0;
while (mda[idx])
{
const sai_attr_metadata_t* m = mda[idx++];
const sai_attr_metadata_t* md = sai_metadata_get_attr_metadata(sai_metadata_all_object_type_infos[i]->objecttype, m->attrid);
META_ASSERT_NOT_NULL(md);
if (md->attrid >= CUSTOM_ATTR_RANGE_START && md->attrid < (EXTENSION_RANGE_START))
{
META_ASSERT_TRUE(md->iscustom, "expected to be marked as custom attribute, %s", md->attridname);
}
else
{
META_ASSERT_FALSE(md->iscustom, "expected to be NOT marked as custom attribute, %s", md->attridname);
}
}
}
/*sai_metadata_get_attr_metadata */
}
void check_acl_user_defined_field()
{
META_LOG_ENTER();
META_ASSERT_TRUE(SAI_ACL_USER_DEFINED_FIELD_ATTR_ID_RANGE > 0, "should be positive");
META_ASSERT_TRUE(SAI_ACL_TABLE_ATTR_USER_DEFINED_FIELD_GROUP_MIN + SAI_ACL_USER_DEFINED_FIELD_ATTR_ID_RANGE ==
SAI_ACL_TABLE_ATTR_USER_DEFINED_FIELD_GROUP_MAX, "expected true");
META_ASSERT_TRUE(SAI_ACL_ENTRY_ATTR_USER_DEFINED_FIELD_GROUP_MIN + SAI_ACL_USER_DEFINED_FIELD_ATTR_ID_RANGE ==
SAI_ACL_ENTRY_ATTR_USER_DEFINED_FIELD_GROUP_MAX, "expected true");
}
void check_label_size()
{
META_LOG_ENTER();
META_ASSERT_TRUE(sizeof(sai_label_id_t) == sizeof(uint32_t), "label is expected to be 32 bit");
}
void check_switch_notify_list()
{
META_LOG_ENTER();
size_t i;
for (i = 0; i < sai_metadata_switch_notify_attr_count; ++i)
{
META_ASSERT_NOT_NULL(sai_metadata_switch_notify_attr[i]);
}
/* check for NULL guard */
META_ASSERT_NULL(sai_metadata_switch_notify_attr[i]);
}
void check_switch_pointers_list()
{
META_LOG_ENTER();
size_t i;
for (i = 0; i < sai_metadata_switch_pointers_attr_count; ++i)
{
META_ASSERT_NOT_NULL(sai_metadata_switch_pointers_attr[i]);
}
/* check for NULL guard */
META_ASSERT_NULL(sai_metadata_switch_pointers_attr[i]);
}
void check_defines()
{
META_LOG_ENTER();
/*
* Check if defines are equal to their static values.
*/
META_ASSERT_TRUE(SAI_METADATA_SWITCH_NOTIFY_ATTR_COUNT == sai_metadata_switch_notify_attr_count, "notify define must be equal");
META_ASSERT_TRUE(SAI_METADATA_SWITCH_NOTIFY_ATTR_COUNT >= 15, "there must be at least 15 notifications defined");
}
void check_object_type_attributes()
{
META_LOG_ENTER();
size_t i = 1;
for (; sai_metadata_all_object_type_infos[i]; i++)
{
check_single_object_type_attributes(sai_metadata_all_object_type_infos[i]->attrmetadata);
}
}
void check_all_object_infos()
{
META_LOG_ENTER();
size_t i = 1;
for (; sai_metadata_all_object_type_infos[i] != NULL; ++i)
{
check_single_object_info(sai_metadata_all_object_type_infos[i]);
}
}
void check_ignored_attributes()
{
META_LOG_ENTER();
META_ASSERT_NULL(sai_metadata_get_attr_metadata_by_attr_id_name("SAI_BUFFER_PROFILE_ATTR_BUFFER_SIZE"));
const sai_attr_metadata_t* meta = sai_metadata_get_ignored_attr_metadata_by_attr_id_name("SAI_BUFFER_PROFILE_ATTR_BUFFER_SIZE");
if (meta == NULL)
{
META_ASSERT_FAIL("Failed to find ignored attribute SAI_BUFFER_PROFILE_ATTR_BUFFER_SIZE");
}
META_ASSERT_TRUE(strcmp(meta->attridname, "SAI_BUFFER_PROFILE_ATTR_RESERVED_BUFFER_SIZE") == 0,
"expected attribute was SAI_BUFFER_PROFILE_ATTR_RESERVED_BUFFER_SIZE");
}
#define RANGE_BASE 0x1000
#define SKIP_ENUM(x) if (strcmp(emd->name, #x) == 0) { return; }
void check_enum_object_type(
_In_ const sai_enum_metadata_t* emd)
{
META_LOG_ENTER();
if (emd->objecttype == SAI_OBJECT_TYPE_NULL)
{
return;
}
const sai_object_type_info_t* oi = sai_metadata_get_object_type_info(emd->objecttype);
META_ASSERT_NOT_NULL(oi);
META_ASSERT_TRUE(emd == oi->enummetadata, "should be equal");
}
void check_enum_flags_type_strict(
_In_ const sai_enum_metadata_t* emd)
{
META_LOG_ENTER();
if (emd->flagstype == SAI_ENUM_FLAGS_TYPE_STRICT)
{
META_ASSERT_TRUE(emd->containsflags, "must be marked as contains flags");
int current = 1 << 0;
size_t i = 0;
if (emd->values[0] == 0)
{
/* first value in strict flags is zero (no flags, we allow this case) */
i = 1;
}
for (; i < emd->valuescount; ++i)
{
int val = emd->values[i];
if (val != current)
{
const char*name = emd->valuesnames[i];
META_ASSERT_FAIL("enum %s value is 0x%x, but probably should be 0x%x to be a flag", name, val, current);
}
current = current << 1;
}
META_ASSERT_TRUE(emd->values[i] == -1, "missing guard at the end of enum");
}
}
void check_enum_flags_type_ranges(
_In_ const sai_enum_metadata_t* emd)
{
META_LOG_ENTER();
if (emd->flagstype == SAI_ENUM_FLAGS_TYPE_RANGES)
{
META_ASSERT_TRUE(emd->containsflags, "must be marked as contains flags");
size_t i = 0;
int32_t start = 0;
int32_t prev = -1;
for (; i < emd->valuescount; ++i)
{
int val = emd->values[i];
const char*name = emd->valuesnames[i];
/* this check can be relaxed, we allow now 16 types of ranges */
if (val < EXTENSION_RANGE_START)
{
META_ASSERT_TRUE((val < (16*RANGE_BASE)), "range value 0x%x is too high on %s", val, name);
}
else
{
META_ASSERT_TRUE((val < (16*RANGE_BASE + EXTENSION_RANGE_START)), "range value 0x%x is too high on %s", val, name);
}
if ((val != prev + 1) && (val & 0xFFF) && ((val & ~0xFFF) == (prev & ~0xFFF)))
{
if ((emd->objecttype == SAI_OBJECT_TYPE_ACL_ENTRY &&
val == SAI_ACL_ENTRY_ATTR_USER_DEFINED_FIELD_GROUP_MAX) ||
(emd->objecttype == SAI_OBJECT_TYPE_ACL_TABLE &&
val == SAI_ACL_TABLE_ATTR_USER_DEFINED_FIELD_GROUP_MAX))
{
/* this is ACL explicit range which is auto generated by metadata */
}
else
{
META_ASSERT_FAIL("value %s = 0x%x not increasing by 1, previous 0x%x", name, val, prev);
}
}
prev = val;
if (val < start)
{
continue;
}
while (val >= start)
{
start += RANGE_BASE;
}
start -= RANGE_BASE;
if ((val & ~start) != 0)
{
META_ASSERT_FAIL("enum %s value is 0x%x, but probably should be 0x%x, missing = SAI_.._RANGE_BASE?", name, val, start);
}
start += RANGE_BASE;
}
META_ASSERT_TRUE(emd->values[i] == -1, "missing guard at the end of enum");
}
}
void check_enum_flags_type_free(
_In_ const sai_enum_metadata_t* emd)
{
META_LOG_ENTER();
if (emd->flagstype == SAI_ENUM_FLAGS_TYPE_FREE)
{
META_ASSERT_TRUE(emd->containsflags, "must be marked as contains flags");
size_t i = 0;
for (; i < emd->valuescount; ++i)
{
/* allow all */
}
META_ASSERT_TRUE(emd->values[i] == -1, "missing guard at the end of enum");
}
}
void check_enum_flags_type_none(
_In_ const sai_enum_metadata_t* emd)
{
META_LOG_ENTER();
if (emd->flagstype == SAI_ENUM_FLAGS_TYPE_NONE)
{
META_ASSERT_FALSE(emd->containsflags, "contains flags must be false");
size_t j = 0;
int last = -1;
for (; j < emd->valuescount; ++j)
{
META_LOG_DEBUG("value: %s", emd->valuesnames[j]);
int value = emd->values[j];
META_ASSERT_FALSE(value < 0, "enum values are negative");
META_ASSERT_TRUE(last < value, "enum values are not increasing");
if (value != last + 1)
{
if (value == CUSTOM_ATTR_RANGE_START)
{
/*
* Object contains custom attributes in custom range, they
* still needs to be increasing by 1 but since those are
* not flags, attribute value can't be used as array index.
*/
}
else if (value == EXTENSION_RANGE_START)
{
/*
* Object contains extensions attributes, they still needs
* to be increasing by 1 but since those are not flags,
* attribute value can't be used as array index.
*/
}
else
{
META_ENUM_ASSERT_FAIL(emd, "values are not increasing by 1: last: %d current: %d, should be marked as @flags?", last, value);
}
}
last = value;
}
META_ASSERT_TRUE(emd->values[j] == -1, "missing guard at the end of enum");
}
}
void check_enum_flags_type(
_In_ const sai_enum_metadata_t* emd)
{
META_LOG_ENTER();
if (emd->containsflags)
{
META_ASSERT_TRUE(emd->flagstype != SAI_ENUM_FLAGS_TYPE_NONE,
"invalid combination of containsflags == true and flagstype == NONE on %s", emd->name);
}
if (emd->containsflags == false)
{
META_ASSERT_TRUE(emd->flagstype == SAI_ENUM_FLAGS_TYPE_NONE,
"invalid combination of containsflags == false and flagstype != NONE on %s", emd->name);
}
if (emd->flagstype == SAI_ENUM_FLAGS_TYPE_NONE)
return check_enum_flags_type_none(emd);
if (emd->flagstype == SAI_ENUM_FLAGS_TYPE_STRICT)
return check_enum_flags_type_strict(emd);
if (emd->flagstype == SAI_ENUM_FLAGS_TYPE_RANGES)
return check_enum_flags_type_ranges(emd);
if (emd->flagstype == SAI_ENUM_FLAGS_TYPE_FREE)
return check_enum_flags_type_free(emd);
META_ASSERT_FAIL("enum %s flags type %d not supported yet, FIXME", emd->name, emd->flagstype);
}
void check_single_enum(
_In_ const sai_enum_metadata_t* emd)
{
META_LOG_ENTER();
check_enum_flags_type(emd);
check_enum_flags_type_none(emd);
check_enum_flags_type_strict(emd);
check_enum_flags_type_ranges(emd);
check_enum_flags_type_free(emd);
check_enum_object_type(emd);
}
void check_all_enums()
{
META_LOG_ENTER();
size_t i = 0;
for (; i < sai_metadata_all_enums_count; ++i)
{
const sai_enum_metadata_t* emd = sai_metadata_all_enums[i];
META_LOG_DEBUG("enum: %s", emd->name);
check_single_enum(emd);
}
check_single_enum(&sai_metadata_enum_sai_global_api_type_t);
check_single_enum(&sai_metadata_enum_sai_switch_notification_type_t);
check_single_enum(&sai_metadata_enum_sai_switch_pointer_type_t);
}
void check_sai_version()
{
META_LOG_ENTER();
/* SAI_VERSION uses 100 base for each component, so each define must not exceed this value */
/* Make sure sai version components are assignable to uint32_t */
uint32_t major = SAI_MAJOR;
uint32_t minor = SAI_MINOR;
uint32_t revision = SAI_REVISION;
META_ASSERT_TRUE((major) < 100, "invalid SAI_MAJOR version: %d", (SAI_MAJOR));
META_ASSERT_TRUE((minor) < 100, "invalid SAI_MINOR version: %d", (SAI_MINOR));
META_ASSERT_TRUE((revision) < 100, "invalid SAI_REVISION version: %d", (SAI_REVISION));
}
void check_max_conditions_len()
{
META_LOG_ENTER();
META_ASSERT_TRUE(SAI_METADATA_MAX_CONDITIONS_LEN > 0, "must be positive");
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsuggest-attribute=noreturn"
void check_object_type_extension_max_value()
{
META_LOG_ENTER();
/*
* It can be handy for vendors to encode object type value on single byte
* in every object it for easy object identification. We assume that we
* will have no more than 255 objects types on SAI right now.
*
* But since we are moving object type extensions to higher range to be
* backward compatible to not shift enums, vendor still may want to encode
* that in 1 byte, for example 127 id's for regular object types and 127
* for extended, or 191 for base, and 63 for extended.
*
* At the time this comment is made, we have 110 base and 20 extended.
* Allowing extra 32 on each range we can fit in 191 and 63 on 1 byte.
*
*/
META_LOG_INFO("SAI_OBJECT_TYPE_MAX = %d, EXTENSION_OBJECT_TYPE_COUNT = %d", SAI_OBJECT_TYPE_MAX, EXTENSION_OBJECT_TYPE_COUNT);
/*
* This check may be removed, but it will need to be brought into attention on SAI meeting.
*/
META_ASSERT_TRUE(SAI_OBJECT_TYPE_MAX < 192 && EXTENSION_OBJECT_TYPE_COUNT < 64, "exceeding this range will not allow to encode object types to single byte");
META_ASSERT_TRUE(TOTAL_OBJECT_TYPE_COUNT < 256, "TOTAL_OBJECT_TYPE_COUNT bust be < 256 if it should be possible to encode object type on single byte");
}
#pragma GCC diagnostic pop
void check_global_apis()
{
META_LOG_ENTER();
sai_global_apis_t apis;
apis.api_initialize = NULL;
META_ASSERT_TRUE(sizeof(apis)/sizeof(void*) > 15, "there should be at least 15 global apis");
sai_global_api_type_t type = SAI_GLOBAL_API_TYPE_API_INITIALIZE;
META_ASSERT_TRUE(sizeof(type) >= sizeof(int32_t), "apis type should be at least int32");
}
/* will check single struct size, as well as array alignment and packing */
#define CHECK_STRUCT_SIZE(name,size) \
META_ASSERT_TRUE(sizeof(name) == (size), "wrong size of " #name ", expected %d, got %zu", (size), sizeof(name)); \
META_ASSERT_TRUE(sizeof(name[3]) == (3*(size)), "wrong size of " #name "[3], expected %d, got %zu", (3*size), sizeof(name[3]));
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsuggest-attribute=noreturn"
void check_struct_and_union_size()
{
META_LOG_ENTER();
/*
* At this point we want to be binary backward compatible, which means that
* each union and struct must have the same size (since structs and unions
* are used in arrays for example sai_attibute_t* when creating object).
*
* Also in structs we must check if order of members did not changed. This
* is done via external automated script.
*
* WARNING: !!! DO NOT CHANGE NUMERICAL VALUES !!!
*
* Since this is manual size check, then this list may need to be updated
* in the future when new unions or structures are added. Experimental
* headers are and should not be checked here since they can disappear.
* Also *_api_t structures should not be changed since they are subject to
* be expanded.
*
* Those struct sizes may change on non x86_64 architecture, for example
* armhf, which is 32 bit. Then please add ifdef statement here and provide
* corresponding values.
*
* NOTE: at some point, it may be required to modify some structures for
* some objects, and this should be permitted after consulting with SAI
* community, to allow binary compatibility break. When this happens, a
* specific comment should be added here why that struct change happened.
*
* This is already done automatically using size.sh and size.pl, but this
* only works when git repository is present, since check is getting that
* data from previous commit. We still need to preserve some of those check
* her.
*/
/* unions */
CHECK_STRUCT_SIZE(sai_acl_action_parameter_t, 24);
CHECK_STRUCT_SIZE(sai_acl_field_data_data_t, 16);
CHECK_STRUCT_SIZE(sai_acl_field_data_mask_t, 16);
CHECK_STRUCT_SIZE(sai_attribute_value_t, 40);
CHECK_STRUCT_SIZE(sai_ip_addr_t, 16);
CHECK_STRUCT_SIZE(sai_object_key_entry_t, 64);
CHECK_STRUCT_SIZE(sai_tlv_entry_t, 36);
/* structs */
CHECK_STRUCT_SIZE(sai_acl_action_data_t, 32);
CHECK_STRUCT_SIZE(sai_acl_capability_t, 32);
CHECK_STRUCT_SIZE(sai_acl_chain_list_t, 16);
CHECK_STRUCT_SIZE(sai_acl_chain_t, 8);
CHECK_STRUCT_SIZE(sai_acl_field_data_t, 40);
CHECK_STRUCT_SIZE(sai_acl_resource_list_t, 16);
CHECK_STRUCT_SIZE(sai_acl_resource_t, 12);
CHECK_STRUCT_SIZE(sai_attr_capability_t, 3);
CHECK_STRUCT_SIZE(sai_attribute_t, 48);
CHECK_STRUCT_SIZE(sai_bfd_session_state_notification_t, 16);
CHECK_STRUCT_SIZE(sai_fabric_port_reachability_t, 8);
CHECK_STRUCT_SIZE(sai_fdb_entry_t, 24);
CHECK_STRUCT_SIZE(sai_fdb_event_notification_data_t, 48);
CHECK_STRUCT_SIZE(sai_hmac_t, 36);
CHECK_STRUCT_SIZE(sai_inseg_entry_t, 16);
CHECK_STRUCT_SIZE(sai_ip_address_list_t, 16);
CHECK_STRUCT_SIZE(sai_ip_address_t, 20);
CHECK_STRUCT_SIZE(sai_ipmc_entry_t, 64);
CHECK_STRUCT_SIZE(sai_ip_prefix_list_t, 16);
CHECK_STRUCT_SIZE(sai_ip_prefix_t, 36);
CHECK_STRUCT_SIZE(sai_ipsec_sa_status_notification_t, 16);
CHECK_STRUCT_SIZE(sai_json_t, 16);
CHECK_STRUCT_SIZE(sai_l2mc_entry_t, 64);
CHECK_STRUCT_SIZE(sai_latch_status_t, 2);
CHECK_STRUCT_SIZE(sai_map_list_t, 16);
CHECK_STRUCT_SIZE(sai_map_t, 8);
CHECK_STRUCT_SIZE(sai_mcast_fdb_entry_t, 24);
CHECK_STRUCT_SIZE(sai_my_sid_entry_t, 40);
CHECK_STRUCT_SIZE(sai_nat_entry_data_t, 32);
CHECK_STRUCT_SIZE(sai_nat_entry_key_t, 16);
CHECK_STRUCT_SIZE(sai_nat_entry_mask_t, 16);
CHECK_STRUCT_SIZE(sai_nat_entry_t, 56);
CHECK_STRUCT_SIZE(sai_nat_event_notification_data_t, 64);
CHECK_STRUCT_SIZE(sai_neighbor_entry_t, 40);
CHECK_STRUCT_SIZE(sai_object_key_t, 64);
CHECK_STRUCT_SIZE(sai_object_list_t, 16);
CHECK_STRUCT_SIZE(sai_port_err_status_list_t, 16);
CHECK_STRUCT_SIZE(sai_port_eye_values_list_t, 16);
CHECK_STRUCT_SIZE(sai_port_lane_eye_values_t, 20);
CHECK_STRUCT_SIZE(sai_port_lane_latch_status_list_t, 16);
CHECK_STRUCT_SIZE(sai_port_lane_latch_status_t, 8);
CHECK_STRUCT_SIZE(sai_port_frequency_offset_ppm_list_t, 16);
CHECK_STRUCT_SIZE(sai_port_frequency_offset_ppm_values_t, 8);
CHECK_STRUCT_SIZE(sai_port_snr_list_t, 16);
CHECK_STRUCT_SIZE(sai_port_snr_values_t, 8);
CHECK_STRUCT_SIZE(sai_port_oper_status_notification_t, 16);
CHECK_STRUCT_SIZE(sai_prbs_rx_state_t, 8);
CHECK_STRUCT_SIZE(sai_qos_map_list_t, 16);
CHECK_STRUCT_SIZE(sai_qos_map_params_t, 16);
CHECK_STRUCT_SIZE(sai_qos_map_t, 32);
CHECK_STRUCT_SIZE(sai_queue_deadlock_notification_data_t, 16);
CHECK_STRUCT_SIZE(sai_route_entry_t, 56);
CHECK_STRUCT_SIZE(sai_s16_list_t, 16);
CHECK_STRUCT_SIZE(sai_s32_list_t, 16);
CHECK_STRUCT_SIZE(sai_s32_range_t, 8);
CHECK_STRUCT_SIZE(sai_s8_list_t, 16);
CHECK_STRUCT_SIZE(sai_segment_list_t, 16);
CHECK_STRUCT_SIZE(sai_service_method_table_t, 16);
CHECK_STRUCT_SIZE(sai_stat_capability_list_t, 16);
CHECK_STRUCT_SIZE(sai_stat_capability_t, 8);
CHECK_STRUCT_SIZE(sai_system_port_config_list_t, 16);
CHECK_STRUCT_SIZE(sai_system_port_config_t, 24);
CHECK_STRUCT_SIZE(sai_timespec_t, 16);
CHECK_STRUCT_SIZE(sai_tlv_list_t, 16);
CHECK_STRUCT_SIZE(sai_tlv_t, 40);
CHECK_STRUCT_SIZE(sai_u16_list_t, 16);
CHECK_STRUCT_SIZE(sai_u16_range_list_t, 16);
CHECK_STRUCT_SIZE(sai_u16_range_t, 4);
CHECK_STRUCT_SIZE(sai_u32_list_t, 16);
CHECK_STRUCT_SIZE(sai_u32_range_t, 8);
CHECK_STRUCT_SIZE(sai_u8_list_t, 16);
CHECK_STRUCT_SIZE(sai_vlan_list_t, 16);
}
#pragma GCC diagnostic pop
#define _ENTRY(X,x) META_LOG_DEBUG("%s: %d, %s: %zu", #X, SAI_OBJECT_TYPE_ ## X, # x, sizeof(sai_ ## x ## _t));
#define _BULK_ENTRY(X,x) META_LOG_DEBUG("%s: %d, %s: %zu", #X, SAI_OBJECT_TYPE_ ## X, # x, sizeof(sai_ ## x ## _t));
void check_declare_entry_macro()
{
SAI_META_LOG_ENTER();
SAI_METADATA_DECLARE_EVERY_ENTRY(_ENTRY);
SAI_METADATA_DECLARE_EVERY_BULK_ENTRY(_BULK_ENTRY);
}
void check_json_type_size()
{
SAI_META_LOG_ENTER();
META_ASSERT_TRUE(sizeof(sai_s8_list_t) == sizeof(sai_json_t), "json type is expected to have same size as s8 list");
}
void check_api_extensions()
{
SAI_META_LOG_ENTER();
/*
* Check if defined extensions api can be fit into 1 byte.
* This check can be relaxed in the future.
*/
if (SAI_API_EXTENSIONS_RANGE_END - SAI_API_EXTENSIONS_RANGE_START > 255)
{
META_ASSERT_FAIL("api extensions %d > 255", (SAI_API_EXTENSIONS_RANGE_END - SAI_API_EXTENSIONS_RANGE_START));
}
sai_api_t api = (sai_api_t)SAI_API_EXTENSIONS_RANGE_START;
META_ASSERT_TRUE(api == 0x20000000, "api should be correctly assigned");
}
void check_object_type_index()
{
META_LOG_ENTER();
META_ASSERT_TRUE(ot2idx(0) == 0, "must be zero");
META_ASSERT_TRUE(idx2ot(0) == 0, "must be zero");
uint32_t i = 1;
for (; sai_metadata_all_object_type_infos[i]; i++)
{
META_ASSERT_TRUE(ot2idx(sai_metadata_all_object_type_infos[i]->objecttype) == i, "invalid ot2idx");
META_ASSERT_TRUE(idx2ot(i) == sai_metadata_all_object_type_infos[i]->objecttype, "invalid idx2ot");
}
}
int main(int argc, char **argv)
{
debug = (argc > 1);
SAI_META_LOG_ENTER();
check_all_enums_name_pointers();
check_all_enums_values();
check_enums_ignore_values();
check_sai_status();
check_object_type_index();
check_object_type();
check_attr_by_object_type();
check_object_type_attributes();
check_object_infos();
check_stat_enums();
check_attr_sorted_by_id_name();
check_non_object_id_object_types();
check_non_object_id_object_attrs();
check_objects_for_loops();
check_null_object_id();
check_read_only_attributes();
check_mixed_object_list_types();
check_vlan_attributes();
check_switch_create_only_objects();
check_switch_attributes();
check_reverse_graph_for_non_object_id();
check_acl_table_fields_and_acl_entry_fields();
check_acl_entry_actions();
check_backward_comparibility_defines();
check_graph_connected();
check_get_attr_metadata();
check_get_attr_metadata_custom_range();
check_acl_user_defined_field();
check_label_size();
check_switch_notify_list();
check_switch_pointers_list();
check_defines();
check_all_object_infos();
check_ignored_attributes();
check_all_enums();
check_sai_version();
check_max_conditions_len();
check_object_type_extension_max_value();
check_global_apis();
check_struct_and_union_size();
check_declare_entry_macro();
check_json_type_size();
check_custom_range_attributes();
check_attr_get_outside_range();
check_api_extensions();
SAI_META_LOG_DEBUG("log test");
printf("\n [ %s ]\n\n", sai_metadata_get_status_name(SAI_STATUS_SUCCESS));
SAI_META_LOG_EXIT();
return 0;
}