-
Notifications
You must be signed in to change notification settings - Fork 667
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[GCU] Loading yang-models only once #1981
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -43,6 +43,7 @@ def __eq__(self, other): | |
class ConfigWrapper: | ||
def __init__(self, yang_dir = YANG_DIR): | ||
self.yang_dir = YANG_DIR | ||
self.sonic_yang_with_loaded_models = None | ||
|
||
def get_config_db_as_json(self): | ||
text = self._get_config_db_as_text() | ||
|
@@ -63,8 +64,7 @@ def get_sonic_yang_as_json(self): | |
return self.convert_config_db_to_sonic_yang(config_db_json) | ||
|
||
def convert_config_db_to_sonic_yang(self, config_db_as_json): | ||
sy = sonic_yang.SonicYang(self.yang_dir) | ||
sy.loadYangModel() | ||
sy = self.create_sonic_yang_with_loaded_models() | ||
|
||
# Crop config_db tables that do not have sonic yang models | ||
cropped_config_db_as_json = self.crop_tables_without_yang(config_db_as_json) | ||
|
@@ -76,8 +76,7 @@ def convert_config_db_to_sonic_yang(self, config_db_as_json): | |
return sonic_yang_as_json | ||
|
||
def convert_sonic_yang_to_config_db(self, sonic_yang_as_json): | ||
sy = sonic_yang.SonicYang(self.yang_dir) | ||
sy.loadYangModel() | ||
sy = self.create_sonic_yang_with_loaded_models() | ||
|
||
# replace container of the format 'module:table' with just 'table' | ||
new_sonic_yang_json = {} | ||
|
@@ -100,8 +99,7 @@ def convert_sonic_yang_to_config_db(self, sonic_yang_as_json): | |
def validate_sonic_yang_config(self, sonic_yang_as_json): | ||
config_db_as_json = self.convert_sonic_yang_to_config_db(sonic_yang_as_json) | ||
|
||
sy = sonic_yang.SonicYang(self.yang_dir) | ||
sy.loadYangModel() | ||
sy = self.create_sonic_yang_with_loaded_models() | ||
|
||
try: | ||
sy.loadData(config_db_as_json) | ||
|
@@ -112,8 +110,7 @@ def validate_sonic_yang_config(self, sonic_yang_as_json): | |
return False | ||
|
||
def validate_config_db_config(self, config_db_as_json): | ||
sy = sonic_yang.SonicYang(self.yang_dir) | ||
sy.loadYangModel() | ||
sy = self.create_sonic_yang_with_loaded_models() | ||
|
||
try: | ||
tmp_config_db_as_json = copy.deepcopy(config_db_as_json) | ||
|
@@ -126,8 +123,7 @@ def validate_config_db_config(self, config_db_as_json): | |
return False | ||
|
||
def crop_tables_without_yang(self, config_db_as_json): | ||
sy = sonic_yang.SonicYang(self.yang_dir) | ||
sy.loadYangModel() | ||
sy = self.create_sonic_yang_with_loaded_models() | ||
|
||
sy.jIn = copy.deepcopy(config_db_as_json) | ||
|
||
|
@@ -151,6 +147,16 @@ def remove_empty_tables(self, config): | |
config_with_non_empty_tables[table] = copy.deepcopy(config[table]) | ||
return config_with_non_empty_tables | ||
|
||
# TODO: move creating copies of sonic_yang with loaded models to sonic-yang-mgmt directly | ||
def create_sonic_yang_with_loaded_models(self): | ||
# sonic_yang_with_loaded_models will only be initialized once the first time this method is called | ||
if self.sonic_yang_with_loaded_models is None: | ||
loaded_models_sy = sonic_yang.SonicYang(self.yang_dir) | ||
loaded_models_sy.loadYangModel() # This call takes a long time (100s of ms) because it reads files from disk | ||
self.sonic_yang_with_loaded_models = loaded_models_sy | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you add comment which line spends most of the time? #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. updated |
||
|
||
return copy.copy(self.sonic_yang_with_loaded_models) | ||
|
||
class DryRunConfigWrapper(ConfigWrapper): | ||
# This class will simulate all read/write operations to ConfigDB on a virtual storage unit. | ||
def __init__(self, initial_imitated_config_db = None): | ||
|
@@ -176,7 +182,7 @@ def _init_imitated_config_db_if_none(self): | |
class PatchWrapper: | ||
def __init__(self, config_wrapper=None): | ||
self.config_wrapper = config_wrapper if config_wrapper is not None else ConfigWrapper() | ||
self.path_addressing = PathAddressing() | ||
self.path_addressing = PathAddressing(self.config_wrapper) | ||
|
||
def validate_config_db_patch_has_yang_models(self, patch): | ||
config_db = {} | ||
|
@@ -256,6 +262,10 @@ class PathAddressing: | |
""" | ||
PATH_SEPARATOR = "/" | ||
XPATH_SEPARATOR = "/" | ||
|
||
def __init__(self, config_wrapper=None): | ||
self.config_wrapper = config_wrapper | ||
|
||
def get_path_tokens(self, path): | ||
return JsonPointer(path).parts | ||
|
||
|
@@ -391,8 +401,7 @@ def find_ref_paths(self, path, config): | |
return self._find_leafref_paths(path, config) | ||
|
||
def _find_leafref_paths(self, path, config): | ||
sy = sonic_yang.SonicYang(YANG_DIR) | ||
sy.loadYangModel() | ||
sy = self.config_wrapper.create_sonic_yang_with_loaded_models() | ||
|
||
sy.loadData(config) | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -258,6 +258,49 @@ def test_remove_empty_tables__multiple_empty_tables__returns_config_without_empt | |
# Assert | ||
self.assertDictEqual({"any_table": {"key": "value"}}, actual) | ||
|
||
def test_create_sonic_yang_with_loaded_models__creates_new_sonic_yang_every_call(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. updated pr description with profiling data for a specific case instead of all cases in |
||
# check yang models fields are the same or None, non-yang model fields are different | ||
def check(sy1, sy2): | ||
# instances are different | ||
self.assertNotEqual(sy1, sy2) | ||
|
||
# yang models fields are same or None | ||
self.assertTrue(sy1.confDbYangMap is sy2.confDbYangMap) | ||
self.assertTrue(sy1.ctx is sy2.ctx) | ||
self.assertTrue(sy1.DEBUG is sy2.DEBUG) | ||
self.assertTrue(sy1.preProcessedYang is sy2.preProcessedYang) | ||
self.assertTrue(sy1.SYSLOG_IDENTIFIER is sy2.SYSLOG_IDENTIFIER) | ||
self.assertTrue(sy1.yang_dir is sy2.yang_dir) | ||
self.assertTrue(sy1.yangFiles is sy2.yangFiles) | ||
self.assertTrue(sy1.yJson is sy2.yJson) | ||
self.assertTrue(not(hasattr(sy1, 'module')) or sy1.module is None) # module is unused, might get deleted | ||
self.assertTrue(not(hasattr(sy2, 'module')) or sy2.module is None) | ||
|
||
# non yang models fields are different | ||
self.assertFalse(sy1.root is sy2.root) | ||
self.assertFalse(sy1.jIn is sy2.jIn) | ||
self.assertFalse(sy1.tablesWithOutYang is sy2.tablesWithOutYang) | ||
self.assertFalse(sy1.xlateJson is sy2.xlateJson) | ||
self.assertFalse(sy1.revXlateJson is sy2.revXlateJson) | ||
|
||
config_wrapper = gu_common.ConfigWrapper() | ||
self.assertTrue(config_wrapper.sonic_yang_with_loaded_models is None) | ||
|
||
sy1 = config_wrapper.create_sonic_yang_with_loaded_models() | ||
sy2 = config_wrapper.create_sonic_yang_with_loaded_models() | ||
|
||
# Simulating loading non-yang model fields | ||
sy1.loadData(Files.ANY_CONFIG_DB) | ||
sy1.getData() | ||
|
||
# Simulating loading non-yang model fields | ||
sy2.loadData(Files.ANY_CONFIG_DB) | ||
sy2.getData() | ||
|
||
check(sy1, sy2) | ||
check(sy1, config_wrapper.sonic_yang_with_loaded_models) | ||
check(sy2, config_wrapper.sonic_yang_with_loaded_models) | ||
|
||
class TestPatchWrapper(unittest.TestCase): | ||
def setUp(self): | ||
self.config_wrapper_mock = gu_common.ConfigWrapper() | ||
|
@@ -443,7 +486,7 @@ def __assert_same_patch(self, config_db_patch, sonic_yang_patch, config_wrapper, | |
|
||
class TestPathAddressing(unittest.TestCase): | ||
def setUp(self): | ||
self.path_addressing = gu_common.PathAddressing() | ||
self.path_addressing = gu_common.PathAddressing(gu_common.ConfigWrapper()) | ||
self.sy_only_models = sonic_yang.SonicYang(gu_common.YANG_DIR) | ||
self.sy_only_models.loadYangModel() | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is
sonic_yang_model
good enough? #ClosedThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so.
sonic_yang_model
refers to the model but here we are referring toSonicYang
object check codeThis variable will hold an instance of
SonicYang
but with the models loaded hence the name