-
Notifications
You must be signed in to change notification settings - Fork 1.8k
[cfggen] Extend Template Argument to Support Batch Mode #4941
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
Changes from all commits
c60f690
19cbbd9
bc7ff36
c537e96
2db26cc
143aeac
f668924
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 |
|---|---|---|
|
|
@@ -35,6 +35,7 @@ import yaml | |
| import jinja2 | ||
| import netaddr | ||
| import json | ||
| import contextlib | ||
| from functools import partial | ||
| from minigraph import minigraph_encoder | ||
| from minigraph import parse_xml | ||
|
|
@@ -204,6 +205,45 @@ def sort_data(data): | |
| data[table] = OrderedDict(natsorted(data[table].items())) | ||
| return data | ||
|
|
||
| @contextlib.contextmanager | ||
| def smart_open(filename=None, mode=None): | ||
| """ | ||
| Provide contextual file descriptor of filename if it is not a file descriptor | ||
| """ | ||
| smart_file = open(filename, mode) if not isinstance(filename, file) else filename | ||
| try: | ||
| yield smart_file | ||
| finally: | ||
| if not isinstance(filename, file): | ||
| smart_file.close() | ||
|
|
||
| def _process_json(args, data): | ||
| """ | ||
| Process JSON file and update switch configuration data | ||
| """ | ||
| for json_file in args.json: | ||
| with open(json_file, 'r') as stream: | ||
| deep_update(data, FormatConverter.to_deserialized(json.load(stream))) | ||
|
|
||
| def _get_jinja2_env(paths): | ||
| """ | ||
| Retreive Jinj2 env used to render configuration templates | ||
| """ | ||
| loader = jinja2.FileSystemLoader(paths) | ||
| redis_bcc = RedisBytecodeCache(SonicV2Connector(host='127.0.0.1')) | ||
| env = jinja2.Environment(loader=loader, trim_blocks=True, bytecode_cache=redis_bcc) | ||
| env.filters['sort_by_port_index'] = sort_by_port_index | ||
| env.filters['ipv4'] = is_ipv4 | ||
| env.filters['ipv6'] = is_ipv6 | ||
| env.filters['unique_name'] = unique_name | ||
| env.filters['pfx_filter'] = pfx_filter | ||
| env.filters['ip_network'] = ip_network | ||
| for attr in ['ip', 'network', 'prefixlen', 'netmask', 'broadcast']: | ||
| env.filters[attr] = partial(prefix_attr, attr) | ||
| # Pass the is_multi_npu function as global | ||
| env.globals['multi_asic'] = is_multi_npu | ||
|
|
||
| return env | ||
|
|
||
| def main(): | ||
| parser=argparse.ArgumentParser(description="Render configuration file from minigraph data and jinja2 template.") | ||
|
|
@@ -221,14 +261,15 @@ def main(): | |
| parser.add_argument("-H", "--platform-info", help="read platform and hardware info", action='store_true') | ||
| parser.add_argument("-s", "--redis-unix-sock-file", help="unix sock file for redis connection") | ||
| group = parser.add_mutually_exclusive_group() | ||
| group.add_argument("-t", "--template", help="render the data with the template file") | ||
| group.add_argument("-t", "--template", help="render the data with the template file", action="append", default=[], | ||
| type=lambda opt_value: tuple(opt_value.split(',')) if ',' in opt_value else (opt_value, sys.stdout)) | ||
| parser.add_argument("-T", "--template_dir", help="search base for the template files", action='store') | ||
| group.add_argument("-v", "--var", help="print the value of a variable, support jinja2 expression") | ||
| group.add_argument("--var-json", help="print the value of a variable, in json format") | ||
| group.add_argument("-w", "--write-to-db", help="write config into configdb", action='store_true') | ||
| group.add_argument("--print-data", help="print all data", action='store_true') | ||
| group.add_argument("--preset", help="generate sample configuration from a preset template", choices=get_available_config()) | ||
| group = parser.add_mutually_exclusive_group() | ||
| group.add_argument("--print-data", help="print all data", action='store_true') | ||
| group.add_argument("-K", "--key", help="Lookup for a specific key") | ||
| args = parser.parse_args() | ||
|
|
||
|
|
@@ -267,9 +308,7 @@ def main(): | |
| if brkout_table is not None: | ||
| deep_update(data, {'BREAKOUT_CFG': brkout_table}) | ||
|
|
||
| for json_file in args.json: | ||
| with open(json_file, 'r') as stream: | ||
| deep_update(data, FormatConverter.to_deserialized(json.load(stream))) | ||
| _process_json(args, data) | ||
|
|
||
| if args.minigraph != None: | ||
| minigraph = args.minigraph | ||
|
|
@@ -297,7 +336,7 @@ def main(): | |
|
|
||
| if args.from_db: | ||
| if args.namespace is None: | ||
| configdb = ConfigDBConnector(**db_kwargs) | ||
| configdb = ConfigDBConnector(use_unix_socket_path=True, **db_kwargs) | ||
| else: | ||
| configdb = ConfigDBConnector(use_unix_socket_path=True, namespace=args.namespace, **db_kwargs) | ||
|
|
||
|
|
@@ -328,28 +367,23 @@ def main(): | |
| hardware_data['DEVICE_METADATA']['localhost'].update(asic_id=asic_id) | ||
| deep_update(data, hardware_data) | ||
|
|
||
| if args.template is not None: | ||
| template_file = os.path.abspath(args.template) | ||
| paths = ['/', '/usr/share/sonic/templates', os.path.dirname(template_file)] | ||
| if args.template_dir is not None: | ||
| template_dir = os.path.abspath(args.template_dir) | ||
| paths.append(template_dir) | ||
| loader = jinja2.FileSystemLoader(paths) | ||
|
|
||
| redis_bcc = RedisBytecodeCache(SonicV2Connector(host='127.0.0.1')) | ||
| env = jinja2.Environment(loader=loader, trim_blocks=True, bytecode_cache=redis_bcc) | ||
| env.filters['sort_by_port_index'] = sort_by_port_index | ||
| env.filters['ipv4'] = is_ipv4 | ||
| env.filters['ipv6'] = is_ipv6 | ||
| env.filters['unique_name'] = unique_name | ||
| env.filters['pfx_filter'] = pfx_filter | ||
| env.filters['ip_network'] = ip_network | ||
| for attr in ['ip', 'network', 'prefixlen', 'netmask', 'broadcast']: | ||
| env.filters[attr] = partial(prefix_attr, attr) | ||
| # Pass the is_multi_npu function as global | ||
| env.globals['multi_asic'] = is_multi_npu | ||
| template = env.get_template(template_file) | ||
| print(template.render(sort_data(data))) | ||
| paths = ['/', '/usr/share/sonic/templates'] | ||
| if args.template_dir: | ||
| paths.append(os.path.abspath(args.template_dir)) | ||
|
|
||
| if args.template: | ||
| for template_file, _ in args.template: | ||
| paths.append(os.path.dirname(os.path.abspath(template_file))) | ||
| env = _get_jinja2_env(paths) | ||
| sorted_data = sort_data(data) | ||
| for template_file, dest_file in args.template: | ||
| template = env.get_template(os.path.basename(template_file)) | ||
| template_data = template.render(sorted_data) | ||
| if dest_file == "config-db": | ||
|
Collaborator
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.
This is magic string for a special mode. What if user want to write to a file called 'config-db' ?
Contributor
Author
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. The user can write and rename the file later. Currently when we save config-db.json, we simply redirect stdout to the file.
Collaborator
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. maybe we can use
Contributor
Author
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. the user can use this syntax I can changed it to [config-db], however the premise of argument still exist. What if the file name is [config-db]. I tend to think this a user error since there is clear way if config-db is used as a file name.
Contributor
Author
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. Just tried the ./config-db and it does generate the file as intended: |
||
| deep_update(data, FormatConverter.to_deserialized(json.loads(template_data))) | ||
| else: | ||
| with smart_open(dest_file, 'w') as df: | ||
| print(template_data, file=df) | ||
|
|
||
| if args.var != None: | ||
| template = jinja2.Template('{{' + args.var + '}}') | ||
|
|
@@ -363,7 +397,7 @@ def main(): | |
|
|
||
| if args.write_to_db: | ||
| if args.namespace is None: | ||
| configdb = ConfigDBConnector(**db_kwargs) | ||
| configdb = ConfigDBConnector(use_unix_socket_path=True, **db_kwargs) | ||
| else: | ||
| configdb = ConfigDBConnector(use_unix_socket_path=True, namespace=args.namespace, **db_kwargs) | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| { | ||
| "jk1_1": "{{ key1_1 }}", | ||
| "jk1_2": "{{ key1_2 }}" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| { | ||
| "jk2_1": "{{ key2_1 }}", | ||
| "jk2_2": "{{ key2_2 }}" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| {{ key1 }} |
Uh oh!
There was an error while loading. Please reload this page.