sonic-buildimage/src/sonic-config-engine/sonic-cfggen

182 lines
6.5 KiB
Plaintext
Raw Normal View History

#!/usr/bin/env python
"""sonic-cfggen
A tool to read SONiC config data from one or more of the following sources:
minigraph file, config DB, json file(s), yaml files(s), command line input,
and write the data into DB, print as json, or render a jinja2 config template.
Examples:
Render template with minigraph:
sonic-cfggen -m -t /usr/share/template/bgpd.conf.j2
Dump config DB content into json file:
sonic-cfggen -d --print-data > db_dump.json
Load content of json file into config DB:
sonic-cfggen -j db_dump.json --write-to-db
See usage string for detail description for arguments.
"""
import sys
import os.path
import argparse
import yaml
import jinja2
import netaddr
import json
from minigraph import minigraph_encoder
from minigraph import parse_xml
from minigraph import parse_device_desc_xml
from sonic_platform import get_machine_info
from sonic_platform import get_platform_info
from swsssdk import ConfigDBConnector
def is_ipv4(value):
if not value:
return False
if isinstance(value, netaddr.IPAddress):
addr = value
else:
try:
addr = netaddr.IPAddress(str(value))
except:
return False
return addr.version == 4
def is_ipv6(value):
if not value:
return False
if isinstance(value, netaddr.IPAddress):
addr = value
else:
try:
addr = netaddr.IPAddress(str(value))
except:
return False
return addr.version == 6
def unique_name(l):
name_list = []
new_list = []
for item in l:
if item['name'] not in name_list:
name_list.append(item['name'])
new_list.append(item)
return new_list
class FormatConverter:
"""Convert config DB based schema to legacy minigraph based schema for backward capability.
We will move to DB schema and remove this class when the config templates are modified.
TODO(taoyl): Current version of config db only supports BGP admin states.
All other configuration are still loaded from minigraph. Plan to remove
minigraph and move everything into config db in a later commit.
"""
@staticmethod
def db_to_output(db_data):
return db_data
@staticmethod
def output_to_db(output_data):
db_data = {}
for table_name in output_data:
if table_name == 'BGP_NEIGHBOR' or table_name == 'BGP_PEER_RANGE' or table_name == 'DEVICE_METADATA':
db_data[table_name] = output_data[table_name]
return db_data
def deep_update(dst, src):
for key, value in src.iteritems():
if isinstance(value, dict):
node = dst.setdefault(key, {})
deep_update(node, value)
else:
dst[key] = value
return dst
def main():
parser=argparse.ArgumentParser(description="Render configuration file from minigraph data and jinja2 template.")
group = parser.add_mutually_exclusive_group()
group.add_argument("-m", "--minigraph", help="minigraph xml file", nargs='?', const='/etc/sonic/minigraph.xml')
group.add_argument("-M", "--device-description", help="device description xml file")
parser.add_argument("-p", "--port-config", help="port config file, used with -m")
parser.add_argument("-y", "--yaml", help="yaml file that contains additional variables", action='append', default=[])
parser.add_argument("-j", "--json", help="json file that contains additional variables", action='append', default=[])
parser.add_argument("-a", "--additional-data", help="addition data, in json string")
parser.add_argument("-d", "--from-db", help="read config from configdb", action='store_true')
group = parser.add_mutually_exclusive_group()
group.add_argument("-t", "--template", help="render the data with the template file")
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("--write-to-db", help="write config into configdb", action='store_true')
group.add_argument("--print-data", help="print all data", action='store_true')
args = parser.parse_args()
data = {}
machine_info = get_machine_info()
if machine_info != None:
deep_update(data, machine_info)
platform_info = get_platform_info(machine_info)
if platform_info != None:
data['platform'] = platform_info
if args.minigraph != None:
minigraph = args.minigraph
if data.has_key('platform'):
if args.port_config != None:
deep_update(data, parse_xml(minigraph, data['platform'], args.port_config))
else:
deep_update(data, parse_xml(minigraph, data['platform']))
else:
if args.port_config != None:
deep_update(data, parse_xml(minigraph, port_config_file=args.port_config))
else:
deep_update(data, parse_xml(minigraph))
if args.device_description != None:
deep_update(data, parse_device_desc_xml(args.device_description))
for yaml_file in args.yaml:
with open(yaml_file, 'r') as stream:
additional_data = yaml.load(stream)
deep_update(data, additional_data)
for json_file in args.json:
with open(json_file, 'r') as stream:
deep_update(data, json.load(stream))
if args.additional_data != None:
deep_update(data, json.loads(args.additional_data))
if args.from_db:
configdb = ConfigDBConnector()
configdb.connect()
deep_update(data, FormatConverter.db_to_output(configdb.get_config()))
if args.template != None:
template_file = os.path.abspath(args.template)
env = jinja2.Environment(loader=jinja2.FileSystemLoader('/'), trim_blocks=True)
env.filters['ipv4'] = is_ipv4
env.filters['ipv6'] = is_ipv6
env.filters['unique_name'] = unique_name
template = env.get_template(template_file)
print template.render(data)
if args.var != None:
template = jinja2.Template('{{' + args.var + '}}')
print template.render(data)
if args.var_json != None:
print json.dumps(data[args.var_json], indent=4, cls=minigraph_encoder)
if args.write_to_db:
configdb = ConfigDBConnector()
configdb.connect(False)
configdb.set_config(FormatConverter.output_to_db(data))
if args.print_data:
print json.dumps(data, indent=4, cls=minigraph_encoder)
if __name__ == "__main__":
main()