├── .gitignore ├── LICENSE.txt ├── README.md ├── README_CHINESE.md ├── bin ├── sdwan-apps-generator ├── sdwan-policy-tool └── sdwan-template-tool ├── cisco_sdwan_policy ├── BaseObject.py ├── Helper │ ├── PolicyLoader.py │ ├── Sequence.py │ └── __init__.py ├── List │ ├── ASPath.py │ ├── Application.py │ ├── ClassMap.py │ ├── Color.py │ ├── Community.py │ ├── DataPrefix.py │ ├── ExtendedCommunity.py │ ├── Mirror.py │ ├── Policer.py │ ├── Prefix.py │ ├── Site.py │ ├── SlaClass.py │ ├── Tloc.py │ ├── Vpn.py │ └── __init__.py ├── LocalPolicy │ ├── ACL.py │ ├── ACLv6.py │ ├── PolicyRewrite.py │ ├── QosMap.py │ ├── RoutePolicy.py │ └── __init__.py ├── MainPolicy.py ├── README.md ├── Topology │ ├── CustomTopo.py │ ├── HubAndSpoke.py │ ├── Mesh.py │ ├── VpnMembership.py │ └── __init__.py ├── TrafficPolicy │ ├── AppRoute.py │ ├── DataPolicy.py │ └── __init__.py ├── ViptelaRest.py ├── __init__.py ├── demo.py └── requirements.txt ├── domain_to_prefix.py ├── domain_to_prefix.yaml ├── example1.py ├── example2.py ├── setup.cfg ├── setup.py └── tools_template_backup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # celery beat schedule file 95 | celerybeat-schedule 96 | 97 | # SageMath parsed files 98 | *.sage.py 99 | 100 | # Environments 101 | .env 102 | .venv 103 | env/ 104 | venv/ 105 | ENV/ 106 | env.bak/ 107 | venv.bak/ 108 | 109 | # Spyder project settings 110 | .spyderproject 111 | .spyproject 112 | 113 | # Rope project settings 114 | .ropeproject 115 | 116 | # mkdocs documentation 117 | /site 118 | 119 | # mypy 120 | .mypy_cache/ 121 | .dmypy.json 122 | dmypy.json 123 | 124 | # Pyre type checker 125 | .pyre/ 126 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | Copyright (c) 2020 Cisco Systems, Inc. and/or its affiliates 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | The above copyright notice and this permission notice shall be included in all 10 | copies or substantial portions of the Software. 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 17 | SOFTWARE. 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cisco SD-WAN(Viptela) Policy Module 2 | 3 | This Module is intend to make generating/modifying Cisco SD-WAN Policy easier as well as backing up policy. 4 | 5 | Currently tested on 19.1.x and 19.2.x vManage. 6 | 7 | Also included two cli tools for easiler backup/restore policy and template base on the project. 8 | 9 | [中文文档](https://github.com/ljm625/cisco_sdwan_policy_python/blob/master/README_CHINESE.md) 10 | 11 | 12 | ## Usage 13 | 14 | ``` 15 | pip install cisco-sdwan-policy 16 | ``` 17 | 18 | ```python 19 | from cisco_sdwan_policy import * 20 | 21 | 22 | # vManage Info 23 | server_info = { 24 | "hostname":"198.18.1.10", 25 | "port":8443, 26 | "username":"admin", 27 | "password":"admin" 28 | } 29 | # Load all policy in vManage 30 | pl = PolicyLoader.init(server_info) 31 | pl.load() 32 | 33 | # Show all the loaded Policy. 34 | print([i.name for i in pl.main_policies]) 35 | print([i.name for i in pl.topo_policies]) 36 | print([i.name for i in pl.traffic_policies]) 37 | print([i.name for i in pl.list_policies]) 38 | 39 | 40 | 41 | # Create a new Policy 42 | prefix_list1=[ 43 | "10.0.0.0/24" 44 | ] 45 | prefix_list2=[ 46 | "192.168.1.0/24" 47 | ] 48 | 49 | # Create Prefix list 50 | 51 | data_prefix_source = DataPrefix(name="Prefix_source2",prefix_list=prefix_list1,is_ipv6=False) 52 | data_prefix_dest = DataPrefix(name="Prefix_dest2",prefix_list=prefix_list2,is_ipv6=False) 53 | 54 | # Create Policer 55 | pc = Policer("SpeedLimit1",rate="150000",exceed="drop",burst="15000") 56 | 57 | # Create Site List 58 | 59 | site = Site("TestSite2",["100","1000-2000"]) 60 | 61 | # Create VPN List 62 | 63 | vpn = Vpn("TestVPN2",["10"]) 64 | 65 | 66 | sq = Sequence(1,"Custom","data","accept","ipv4",match=[],actions=[]) 67 | # Create Match 68 | sq.add_match("sourceDataPrefixList",data_prefix_source) 69 | sq.add_match("destinationDataPrefixList",data_prefix_dest) 70 | # Create Action 71 | sq.add_action("set","policer",pc) 72 | sq.add_action("nat","useVpn","0") 73 | sq.add_action("nat","fallback","") 74 | print(sq.to_json()) 75 | 76 | # Create Data Policy 77 | dp = DataPolicy("NAT_Data_policy2","NAT",[sq],default_action="accept") 78 | 79 | # Create Main Policy 80 | 81 | main_policy = MainPolicy(name="API_Policy",description="API",control_policy_list=[],data_policy_list=[],vpn_membership_list=[],approute_policy_list=[]) 82 | main_policy.add_data_policy(dp,"service",[site],[vpn]) 83 | # Print Policy json 84 | print(main_policy.to_json()) 85 | # Save Policy (Create) 86 | main_policy.save() 87 | 88 | 89 | ``` 90 | 91 | The Server Info Part: 92 | 93 | ```json 94 | server_info = { 95 | "hostname":"198.18.1.10", 96 | "port":8443, 97 | "username":"admin", 98 | "password":"admin", 99 | "tenant": "xxx" 100 | } 101 | ``` 102 | - hostname : The IP/Domain of vManage controller 103 | - port : The port for vManage web portal, by default its 443/8443 104 | - username : The username for vManage 105 | - password : The password for vManage 106 | - tenant : Optional, if not using multi-tenant mode, just don't present in the json. It **CAN** be Tenant name, Tenant-id or VSessionId, For example: "Tenant1" or "1554923113309" or "MTU1NDkyMzExMzMwOQ==" 107 | 108 | When re-initiating ViptelaRest class, all the existing object will auto change to new server as well, so **make sure to reload the policy after changing server info** 109 | 110 | 111 | 112 | ## CLI tools usage 113 | 114 | ``` 115 | pip install cisco-sdwan-policy 116 | ``` 117 | 118 | #### For template backup/restore, run: 119 | 120 | ```bash 121 | sdwan-template-tool -h 122 | ``` 123 | 124 | Example usage: 125 | ```bash 126 | [*] Transfer template test: 127 | sdwan-template-tool --mode=transfer --template=test --server1-ip=10.0.0.1 --server1-port=443 --server1-user=admin --server1-pw=admin --server2-ip=10.0.0.2 --server2-port=443 --server2-user=admin --server2-pw=admin 128 | [*] Backup all template: 129 | sdwan-template-tool --mode=backup --all-template --file=backup.json --server1-ip=10.0.0.1 --server1-port=443 --server1-user=admin --server1-pw=admin 130 | [*] Restore template from a file: 131 | sdwan-template-tool --mode=restore --file=backup.json --server1-ip=10.0.0.1 --server1-port=443 --server1-user=admin --server1-pw=admin``` 132 | ``` 133 | 134 | #### For policy backup/restore, run: 135 | 136 | ```bash 137 | sdwan-policy-tool -h 138 | ``` 139 | 140 | Example usage: 141 | ```bash 142 | [*] Transfer policy 'Policy1': 143 | sdwan-policy-tool --mode=transfer --policy=Policy1 --server1-ip=10.0.0.1 --server1-port=443 --server1-user=admin --server1-pw=admin --server2-ip=10.0.0.2 --server2-port=443 --server2-user=admin --server2-pw=admin 144 | [*] Backup all policy: 145 | sdwan-policy-tool --mode=backup --all-policy --file=backup.json --server1-ip=10.0.0.1 --server1-port=443 --server1-user=admin --server1-pw=admin 146 | [*] Restore policy from a file: 147 | sdwan-policy-tool --mode=restore --file=backup.json --server1-ip=10.0.0.1 --server1-port=443 --server1-user=admin --server1-pw=admin 148 | ``` 149 | 150 | #### Example 1 : Policy Backup & Restore 151 | 152 | Below is the example of backing up policy into a json file, then transfer policy to a new vManage or restore to existing vManage. You can also tranfer policies between tenants. 153 | 154 | 155 | [Link](https://github.com/ljm625/cisco_sdwan_policy_python/blob/master/example1.py) 156 | 157 | 158 | #### Example 2 : Transfer a Main policy from Tenant1 to Tenant2 159 | 160 | Below is the example of transfering a main policy from tenant 1 to tenant 2, and all the policy dependencies will automatically be transferred as well. 161 | 162 | [Link](https://github.com/ljm625/cisco_sdwan_policy_python/blob/master/example2.py) 163 | 164 | 165 | #### Example 3 : Generate a IP list base on domains 166 | 167 | Below is the example of generate a IP list base on given domains, useful for not supported applications. 168 | 169 | Before using this, please install sublist3r manually, it's not supported yet. 170 | 171 | [Link](https://github.com/ljm625/cisco_sdwan_policy_python/blob/master/domain_to_prefix.py) 172 | 173 | 174 | 175 | More examples will be added later. 176 | 177 | 178 | ## Questions and Contact Info 179 | 180 | If you have any issues or a pull request, you can submit a Issue or contact me directly。 181 | 182 | My Cisco CEC ID: jiaminli 183 | 184 | Pull request of enhancements and examples are welcomed! 185 | 186 | -------------------------------------------------------------------------------- /README_CHINESE.md: -------------------------------------------------------------------------------- 1 | # Cisco SD-WAN(Viptela) Policy 模块 2 | 3 | 该模块将Cisco SD-WAN当中的Policy部分通过Python的类进行了建模,简化了程序化创建Policy,修改Policy,备份Policy,迁移Policy的工作量。 4 | 5 | [English](https://github.com/ljm625/cisco_sdwan_policy_python/blob/master/README.md) 6 | 7 | 8 | 9 | 目前只支持Python3, 10 | 11 | 安装: 12 | ``` 13 | pip install cisco-sdwan-policy 14 | ``` 15 | 16 | 简单的样例代码: 17 | ```python 18 | from cisco_sdwan_policy import * 19 | 20 | 21 | # vManage Info 22 | server_info = { 23 | "hostname":"198.18.1.10", 24 | "port":8443, 25 | "username":"admin", 26 | "password":"admin" 27 | } 28 | # Load all policy in vManage 29 | pl = PolicyLoader.init(server_info) 30 | pl.load() 31 | 32 | # Show all the loaded Policy. 33 | print([i.name for i in pl.main_policies]) 34 | print([i.name for i in pl.topo_policies]) 35 | print([i.name for i in pl.traffic_policies]) 36 | print([i.name for i in pl.list_policies]) 37 | 38 | 39 | 40 | # Create a new Policy 41 | prefix_list1=[ 42 | "10.0.0.0/24" 43 | ] 44 | prefix_list2=[ 45 | "192.168.1.0/24" 46 | ] 47 | 48 | # Create Prefix list 49 | 50 | data_prefix_source = DataPrefix(name="Prefix_source2",prefix_list=prefix_list1,is_ipv6=False) 51 | data_prefix_dest = DataPrefix(name="Prefix_dest2",prefix_list=prefix_list2,is_ipv6=False) 52 | 53 | # Create Policer 54 | pc = Policer("SpeedLimit1",rate="150000",exceed="drop",burst="15000") 55 | 56 | # Create Site List 57 | 58 | site = Site("TestSite2",["100","1000-2000"]) 59 | 60 | # Create VPN List 61 | 62 | vpn = Vpn("TestVPN2",["10"]) 63 | 64 | 65 | sq = Sequence(1,"Custom","data","accept","ipv4",match=[],actions=[]) 66 | # Create Match 67 | sq.add_match("sourceDataPrefixList",data_prefix_source) 68 | sq.add_match("destinationDataPrefixList",data_prefix_dest) 69 | # Create Action 70 | sq.add_action("set","policer",pc) 71 | sq.add_action("nat","useVpn","0") 72 | sq.add_action("nat","fallback","") 73 | print(sq.to_json()) 74 | 75 | # Create Data Policy 76 | dp = DataPolicy("NAT_Data_policy2","NAT",[sq],default_action="accept") 77 | 78 | # Create Main Policy 79 | 80 | main_policy = MainPolicy(name="API_Policy",description="API",control_policy_list=[],data_policy_list=[],vpn_membership_list=[],approute_policy_list=[]) 81 | main_policy.add_data_policy(dp,"service",[site],[vpn]) 82 | # Print Policy json 83 | print(main_policy.to_json()) 84 | # Save Policy (Create) 85 | main_policy.save() 86 | 87 | 88 | ``` 89 | 90 | 服务器信息部分: 91 | 92 | ```json 93 | server_info = { 94 | "hostname":"198.18.1.10", 95 | "port":8443, 96 | "username":"admin", 97 | "password":"admin", 98 | "tenant": "xxx" 99 | } 100 | ``` 101 | - hostname : vManage控制器的域名/IP地址 102 | - port : vManage控制器的网页IP,默认为 443/8443 103 | - username : vManage用户名 104 | - password : vManage密码 105 | - tenant : 租户名称,可选,如果非多租户环境,请不要提供此项。可以设置为租户名称,Tenant-id 或者 VSessionId, 如: "Tenant1" 或 "1554923113309" 或 "MTU1NDkyMzExMzMwOQ==" 106 | 107 | 当重新初始化ViptelaRest类,PolicyLoader对象时,已经创建的policy对象会自动被导向到新的服务器信息,这可能会导致严重的问题,因此再重新初始化之后,请务必使用PolicyLoader的load功能重新载入Policy。 108 | 109 | #### 例子 1 : 备份 & 恢复Policy 110 | 111 | 下面的例子是Policy的全量备份,保存到Json文件中,再通过加载备份的文件将Policy恢复到新的vManage中。 112 | 113 | [Link](https://github.com/ljm625/cisco_sdwan_policy_python/blob/master/example1.py) 114 | 115 | 116 | #### 例子2 2 : 将租户1中的一个主Policy迁移到租户2中 117 | 118 | 下面的例子是将一个主Policy从租户1迁移到租户2中,由于本代码中实现了依赖的自动检测&保存,因此主Policy中的所有依赖也会全部被迁移过来,无需人工干预 119 | 120 | [Link](https://github.com/ljm625/cisco_sdwan_policy_python/blob/master/example2.py) 121 | 122 | 123 | 之后会更新更多的样例代码. 124 | 125 | ## 问题和联系方式 126 | 127 | 如果您有任何问题,可以提交Github Issue 或者直接联系我。 128 | 129 | 我的CEC ID: jiaminli 130 | -------------------------------------------------------------------------------- /bin/sdwan-apps-generator: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | import argparse 5 | from pprint import pprint 6 | try: 7 | import yaml 8 | import sublist3r 9 | import dns.resolver 10 | except Exception as e: 11 | print("Error loading libraries, please run following commands first:") 12 | print("pip install pyyaml dnspython") 13 | print("git clone https://github.com/aboul3la/Sublist3r") 14 | print("cd Sublist3r") 15 | print("python setup.py install") 16 | exit(1) 17 | 18 | from cisco_sdwan_policy.List.DataPrefix import DataPrefix 19 | 20 | from cisco_sdwan_policy.List.Prefix import Prefix 21 | 22 | from cisco_sdwan_policy import PolicyLoader 23 | 24 | 25 | def config_reader(config_file): 26 | ''' 27 | Read config from yaml file 28 | :return: config in dict format 29 | ''' 30 | with open(config_file) as file: 31 | config = yaml.load(file.read(),Loader=yaml.FullLoader) 32 | # print(result) 33 | return config 34 | 35 | def parse_domain(domain,nameserver): 36 | domain_list=[] 37 | ip_list=set() 38 | if "*" in domain and domain[0:2]!="*.": 39 | raise Exception("Invalid domain: {}".format(domain)) 40 | elif "*" in domain: 41 | sub_domains = sublist3r.main(domain[2:], 40, None, ports=None, silent=False, verbose=False, 42 | enable_bruteforce=False, engines=None) 43 | 44 | print(sub_domains) 45 | domain_list.extend(sub_domains) 46 | else: 47 | domain_list.append(domain) 48 | # Use DNSPYTHON to get info. 49 | resolver = dns.resolver.Resolver() 50 | resolver.lifetime = resolver.timeout = 20.0 51 | for domain_name in domain_list: 52 | print("Resolving: {}".format(domain_name)) 53 | try: 54 | resolver.nameservers=[nameserver] 55 | response =resolver.query(domain_name) 56 | for answer in response.response.answer: 57 | for ip in answer.items: 58 | if ip.rdtype == 1: 59 | ip_list.add(ip.address+"/32") 60 | except: 61 | pass 62 | # try: 63 | # response = dns.resolver.query(domain_name, "CNAME") 64 | # for answer in response.response.answer: 65 | # for ip in answer.items: 66 | # if ip.rdtype == 1: 67 | # ip_list.add(ip.address+"/32") 68 | # except: 69 | # pass 70 | 71 | return ip_list 72 | 73 | 74 | 75 | if __name__ == '__main__': 76 | # First read all the configurations from config file. 77 | parser = argparse.ArgumentParser(description='App List Genenrator.') 78 | parser.add_argument('config', metavar='config_file_path', type=str, 79 | help='config yaml path') 80 | args = parser.parse_args() 81 | config_file=args.config 82 | try: 83 | config = config_reader(config_file) 84 | print("Config file {} loaded".format(args.config)) 85 | app_ip_info ={} 86 | assert type(config["sdwan_server"])==dict 87 | assert type(config["apps"])==dict 88 | assert type(config["dns_server"])==str 89 | except Exception as e: 90 | print("ERROR : Invalid config file. Example config file:https://github.com/ljm625/cisco_sdwan_policy_python/blob/master/domain_to_prefix.yaml") 91 | print(e) 92 | exit(1) 93 | 94 | for appname,domain_list in config["apps"].items(): 95 | app_ips=set() 96 | for domain in domain_list: 97 | ip_list = parse_domain(domain,config["dns_server"]) 98 | app_ips = app_ips | ip_list 99 | app_ip_info[appname]=list(app_ips) 100 | pprint(app_ip_info) 101 | print("Start creating Prefix Lists") 102 | pl = PolicyLoader.init(config["sdwan_server"]) 103 | pl.load() 104 | existing_list=[i.name for i in pl.list_policies] 105 | for appname,ip_list in app_ip_info.items(): 106 | if "{}_prefix".format(appname) not in existing_list: 107 | Prefix("{}_prefix".format(appname),prefix_list=ip_list).save() 108 | print("Created Prefix List: {}_prefix".format(appname)) 109 | else: 110 | for i in pl.list_policies: 111 | if i.name=="{}_prefix".format(appname): 112 | i.set_entries(ip_list) 113 | i.save() 114 | print("Updated Prefix List: {}".format(i.name)) 115 | if "{}_dataprefix".format(appname) not in existing_list: 116 | DataPrefix("{}_dataprefix".format(appname),prefix_list=ip_list).save() 117 | print("Created Data Prefix List: {}_dataprefix".format(appname)) 118 | 119 | else: 120 | for i in pl.list_policies: 121 | if i.name=="{}_dataprefix".format(appname): 122 | i.set_entries(ip_list) 123 | i.save() 124 | print("Updated Data Prefix List: {}".format(i.name)) 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /bin/sdwan-policy-tool: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import getopt 4 | import json 5 | import sys 6 | 7 | import gc 8 | 9 | from cisco_sdwan_policy.Helper.PolicyLoader import PolicyLoader 10 | 11 | # def update_template(data): 12 | # data["templateId"] = uuid_pairs[data["templateId"]] 13 | # if data.get("subTemplates"): 14 | # for sub_tmp in data["subTemplates"]: 15 | # sub_tmp["templateId"] = uuid_pairs[sub_tmp["templateId"]] 16 | # return data 17 | # 18 | # 19 | # def load_template(rest, data): 20 | # result = rest.get_request("template/feature/object/{}".format(data["templateId"])) 21 | # info = result.json() 22 | # if data.get("subTemplates"): 23 | # for sub_tmp in data["subTemplates"]: 24 | # load_template(rest,sub_tmp) 25 | # if not loaded_templates.get(data["templateId"]): 26 | # loaded_templates[data['templateId']] = info 27 | # # print(result.json()) 28 | 29 | 30 | def backup_policy(server_info,policy_name=None): 31 | 32 | # Load all policy in vManage 33 | pl = PolicyLoader.init(server_info) 34 | pl.load() 35 | if policy_name: 36 | # Backup only selected policy 37 | # Will be kinda complicated since needs to remove unused policies. 38 | # old_main = pl.main_policies 39 | # old_topo = pl.topo_policies 40 | # old_traffic = pl.traffic_policies 41 | # old_list = pl.list_policies 42 | 43 | # Find the policy to move 44 | policy_to_move = None 45 | for i in pl.main_policies: 46 | if i.name == policy_name: 47 | policy_to_move = i 48 | if not policy_to_move: 49 | raise Exception("Policy name not found {}".format(policy_name)) 50 | output ={ 51 | "list_policies":[i.export() for i in pl.list_policies], 52 | "topo_policies":[i.export() for i in pl.topo_policies], 53 | "traffic_policies":[i.export() for i in pl.traffic_policies], 54 | "main_policies":[policy_to_move.export()], 55 | "only_ref":True 56 | } 57 | return json.dumps(output) 58 | else: 59 | return pl.save_to_json() 60 | # Backup fininshed 61 | 62 | def restore_policy(server_info,json_data): 63 | 64 | # Start recovery process 65 | pl = PolicyLoader.init(server_info) 66 | pl.load() 67 | if json.loads(json_data).get("only_ref"): 68 | pl.load_from_json(json_data, only_ref=True) 69 | else: 70 | pl.load_from_json(json_data) 71 | 72 | def transfer_policy(server1,server2,policy_name=None): 73 | resp = backup_policy(server1,policy_name) 74 | restore_policy(server2,resp) 75 | 76 | 77 | 78 | def print_help(): 79 | print("[*] A tool for backup policies / transfer policies between vManage controllers.") 80 | print("[*] Usage:") 81 | print("[*] --mode : transfer/backup/restore") 82 | print( 83 | "[*] If choosing transfer, server1 & server2's info need to be given in prompt. Otherwise just input server1's info.") 84 | print("[*] --all-policy") 85 | print("[*] If given, backup all templates in controller, can't be used with --policy.") 86 | print("[*] --policy : policy name") 87 | print("[*] Input policy name for backup.") 88 | print("[*] --file : file path") 89 | print( 90 | "[*] Input a file path to save/read file in backup & restore modes, in transfer mode this parameter is not needed.") 91 | print("[*] --server1-ip : Server 1's hostname") 92 | print("[*] Input vManage Server1's hostname.") 93 | print("[*] --server1-port : Server 1's port") 94 | print("[*] Input vManage Server1's port.") 95 | print("[*] --server1-user : Server 1's username") 96 | print("[*] Input vManage Server1's username.") 97 | print("[*] --server1-pw : Server 1's password") 98 | print("[*] Input vManage Server1's password.") 99 | print("[*] --server1-tenant : Server 1's tenant name") 100 | print("[*] Input vManage Server1's tenant name, if not in multi tenant mode, skip this.") 101 | 102 | print("[*] --server2-ip : Server 2's hostname") 103 | print("[*] Input vManage Server2's hostname.") 104 | print("[*] --server2-port : Server 2's port") 105 | print("[*] Input vManage Server2's port.") 106 | print("[*] --server2-user : Server 2's username") 107 | print("[*] Input vManage Server2's username.") 108 | print("[*] --server2-pw : Server 2's password") 109 | print("[*] Input vManage Server2's password.") 110 | print("[*] --server2-tenant : Server 2's tenant name") 111 | print("[*] Input vManage Server2's tenant name, if not in multi tenant mode, skip this.") 112 | print("[*]\n[*] Example:") 113 | print("[*] Transfer policy 'Policy1':") 114 | print( 115 | "[*] sdwan-policy-tool --mode=transfer --policy=Policy1 --server1-ip=10.0.0.1 --server1-port=443 --server1-user=admin --server1-pw=admin --server2-ip=10.0.0.2 --server2-port=443 --server2-user=admin --server2-pw=admin") 116 | print("[*] Backup all policy:") 117 | print( 118 | "[*] sdwan-policy-tool --mode=backup --all-policy --file=backup.json --server1-ip=10.0.0.1 --server1-port=443 --server1-user=admin --server1-pw=admin") 119 | print("[*] Restore policy from a file:") 120 | print( 121 | "[*] sdwan-policy-tool --mode=restore --file=backup.json --server1-ip=10.0.0.1 --server1-port=443 --server1-user=admin --server1-pw=admin") 122 | 123 | 124 | if __name__ == '__main__': 125 | 126 | 127 | opts, args = getopt.getopt(sys.argv[1:], '-h-s1:-u1:-pw1:-p1:-t1:-s2:-u2:-pw2:-p2:-t2:-a-p:-m:-f', ['help', 'server1-ip=', 'server1-user=', 'server1-pw=', 'server1-port=', 'server1-tenant=','server2-ip=', 'server2-user=', 'server2-pw=', 'server2-port=', 'server2-tenant=', 'all-policy', 'policy=','mode=','file=']) 128 | 129 | server1_user = server2_user = server1_port = server2_port = server1_pw = server2_pw =server1_ip = server2_ip =server1_tenant =server2_tenant = all_policy =policy_name = mode =file = None 130 | for opt_name, opt_value in opts: 131 | if opt_name in ('-h', '--help'): 132 | print_help() 133 | exit() 134 | 135 | if opt_name in ('-s1', '--server1-ip'): 136 | server1_ip = opt_value 137 | print("[*] Server1 IP is {}".format(server1_ip)) 138 | if opt_name in ('-u1', '--server1-user'): 139 | server1_user = opt_value 140 | print("[*] Server1 Username is {}".format(server1_user)) 141 | if opt_name in ('-pw1', '--server1-pw'): 142 | server1_pw = opt_value 143 | print("[*] Server1 Password is {}".format(server1_pw)) 144 | if opt_name in ('-p1', '--server1-port'): 145 | server1_port = opt_value 146 | print("[*] Server1 Port is {}".format(server1_port)) 147 | if opt_name in ('-t1', '--server1-tenant'): 148 | server1_tenant = opt_value 149 | print("[*] Server1 Tenant is {}".format(server1_tenant)) 150 | 151 | 152 | if opt_name in ('-s2', '--server2-ip'): 153 | server2_ip = opt_value 154 | print("[*] Server2 IP is {}".format(server2_ip)) 155 | if opt_name in ('-u2', '--server2-user'): 156 | server2_user = opt_value 157 | print("[*] Server2 Username is {}".format(server2_user)) 158 | if opt_name in ('-pw2', '--server2-pw'): 159 | server2_pw = opt_value 160 | print("[*] Server2 Password is {}".format(server2_pw)) 161 | if opt_name in ('-p2', '--server2-port'): 162 | server2_port = opt_value 163 | print("[*] Server2 Port is {}".format(server2_port)) 164 | if opt_name in ('-t2', '--server2-tenant'): 165 | server2_tenant = opt_value 166 | print("[*] Server2 Tenant is {}".format(server2_tenant)) 167 | 168 | if opt_name in ('-a', '--all-policy'): 169 | all_policy=True 170 | print("[*] Transfering all policy") 171 | if opt_name in ('-p', '--policy'): 172 | all_policy = False 173 | policy_name = opt_value 174 | print("[*] Transfering policy {}".format(policy_name)) 175 | 176 | if opt_name in ('-m', '--mode'): 177 | if opt_value not in ["transfer","backup","restore"]: 178 | print("Please enter valid modes") 179 | exit() 180 | mode = opt_value 181 | print("[*] Choosing mode {}".format(mode)) 182 | 183 | if opt_name in ('-f', '--file'): 184 | file = opt_value 185 | print("[*] Choosing file {}".format(mode)) 186 | if not (mode and (all_policy or policy_name or mode=="restore")): 187 | print("Help : Need to input mode and choose if transfering all policy or single policy.") 188 | print_help() 189 | exit() 190 | 191 | if mode =="transfer": 192 | 193 | if None in [server1_ip,server2_ip, server1_user,server2_user,server1_pw,server2_pw,server1_port,server2_port]: 194 | print( 195 | "[*] Help: Please enter the server 1 and server 2's info for transfer.") 196 | exit() 197 | elif mode =="backup": 198 | if None in [server1_ip, server1_user,server1_pw,server1_port,file]: 199 | print( 200 | "[*] Help: Please enter the server 1's info for backup.") 201 | exit() 202 | elif mode =="restore": 203 | if None in [server1_ip, server1_user,server1_pw,server1_port,file]: 204 | print( 205 | "[*] Help: Please enter the server 1's info for restore.") 206 | exit() 207 | 208 | if mode =="transfer": 209 | server1_info = { 210 | "hostname": server1_ip, 211 | "port": server1_port, 212 | "username": server1_user, 213 | "password": server1_pw, 214 | "tenant": server1_tenant 215 | } 216 | 217 | server2_info = { 218 | "hostname": server2_ip, 219 | "port": server2_port, 220 | "username": server2_user, 221 | "password": server2_pw, 222 | "tenant": server2_tenant 223 | } 224 | 225 | transfer_policy(server1_info,server2_info,policy_name) 226 | print("Transfer Complete.") 227 | 228 | elif mode =="backup": 229 | server1_info = { 230 | "hostname": server1_ip, 231 | "port": server1_port, 232 | "username": server1_user, 233 | "password": server1_pw, 234 | "tenant": server1_tenant 235 | } 236 | 237 | 238 | data = backup_policy(server1_info,policy_name) 239 | with open(file,"w") as handler: 240 | handler.write(data) 241 | 242 | print("Backup Complete, store at: {}".format(file)) 243 | 244 | elif mode =="restore": 245 | 246 | server1_info = { 247 | "hostname": server1_ip, 248 | "port": server1_port, 249 | "username": server1_user, 250 | "password": server1_pw, 251 | "tenant": server1_tenant 252 | } 253 | 254 | with open(file,"r") as handler: 255 | json_data = handler.read() 256 | restore_policy(server1_info,json_data) 257 | print("Restore Complete") 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | -------------------------------------------------------------------------------- /bin/sdwan-template-tool: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import getopt 4 | import json 5 | import sys 6 | 7 | from cisco_sdwan_policy.ViptelaRest import ViptelaRest 8 | 9 | main_templates={} 10 | loaded_templates={} 11 | policy_templates={} 12 | uuid_pairs={} 13 | 14 | def update_template(data): 15 | data["templateId"] = uuid_pairs[data["templateId"]] 16 | if data.get("subTemplates"): 17 | for sub_tmp in data["subTemplates"]: 18 | sub_tmp["templateId"] = uuid_pairs[sub_tmp["templateId"]] 19 | return data 20 | 21 | 22 | def load_template(rest, data): 23 | result = rest.get_request("template/feature/object/{}".format(data["templateId"])) 24 | info = result.json() 25 | if data.get("subTemplates"): 26 | for sub_tmp in data["subTemplates"]: 27 | load_template(rest,sub_tmp) 28 | if not loaded_templates.get(data["templateId"]): 29 | loaded_templates[data['templateId']] = info 30 | # print(result.json()) 31 | 32 | 33 | def backup_template(server_info,template_name=None): 34 | 35 | # Load all policy in vManage 36 | rest = ViptelaRest.init(server_info) 37 | resp = rest.get_request("template/device") 38 | templates = resp.json()["data"] 39 | for template in templates: 40 | result = rest.get_request("template/device/object/{}".format(template["templateId"])) 41 | obj= result.json() 42 | if template_name: 43 | if obj["templateName"]!=template_name: 44 | continue 45 | if obj["configType"]=="template": 46 | for sub_template in obj["generalTemplates"]: 47 | load_template(rest,sub_template) 48 | if "policyId" in obj and obj["policyId"]!="": 49 | policy_id = obj["policyId"] 50 | # Currently we do not support feature local policies. Stay tuned for the support from cisco-sdwan-policy module. So if the template has a feature policy, just ignore this template. 51 | result_t = rest.get_request("template/policy/vedge/definition/{}".format(policy_id)) 52 | result=result_t.json() 53 | if result["policyType"]=="feature": 54 | # Don't support yet. 55 | del obj["policyId"] 56 | else: 57 | policy_templates[policy_id] = result 58 | if "securityPolicyId" in obj and obj["securityPolicyId"]!="": 59 | security_policy = obj["securityPolicyId"] 60 | # Currently we do not support feature security policies. 61 | # result = rest.get_request("template/policy/vedge/definition/{}".format(policy_id)) 62 | # if result["policyType"]=="feature": 63 | # Don't support yet. 64 | del obj["securityPolicyId"] 65 | main_templates[template["templateId"]] = obj 66 | return {"main_templates":main_templates,"loaded_templates":loaded_templates,"policy_templates":policy_templates} 67 | # Backup fininshed 68 | 69 | def restore_template(server_info,data): 70 | main_templates=data["main_templates"] 71 | loaded_templates=data["loaded_templates"] 72 | policy_templates=data["policy_templates"] 73 | 74 | 75 | # Start recovery process 76 | rest = ViptelaRest.init(server_info) 77 | # First recover the feature templates. 78 | result = rest.get_request("template/feature") 79 | exisiting_feature = [ i["templateName"] for i in result.json()["data"]] 80 | for uuid,temp in loaded_templates.items(): 81 | if temp["templateName"] in exisiting_feature: 82 | temp["templateName"] = temp["templateName"]+"_1" 83 | if temp["templateName"] in exisiting_feature: 84 | raise Exception("ERROR : Still having name conflicts, are you running the script multiple times?") 85 | result = rest.post_request("template/feature/",temp) 86 | new_id = result.json()["templateId"] 87 | uuid_pairs[uuid]=new_id 88 | result = rest.get_request("template/policy/vedge") 89 | exisiting_policy = [ i["policyName"] for i in result.json()["data"]] 90 | for uuid,temp in policy_templates.items(): 91 | if temp["policyName"] in exisiting_policy: 92 | temp["policyName"] = temp["policyName"]+"_1" 93 | if temp["policyName"] in exisiting_policy: 94 | raise Exception("ERROR : Still having name conflicts, are you running the script multiple times?") 95 | result = rest.post_request("template/policy/vedge/",temp) 96 | # For some **weird** reasons, the vManage policy API won't return policyId upon creation, so we have to use a workaround. 97 | # Hopefully it will be fixed soon. 98 | new_id = None 99 | if result.status_code!=200: 100 | result.raise_for_status() 101 | else: 102 | pl = rest.get_request("template/policy/vedge") 103 | pcs = pl.json()["data"] 104 | for tmp in pcs: 105 | if tmp["policyName"]==temp["policyName"]: 106 | new_id = tmp["policyId"] 107 | break 108 | if new_id: 109 | uuid_pairs[uuid]=new_id 110 | else: 111 | raise Exception("Policy create failed.") 112 | 113 | # Recover the main policies. 114 | result = rest.get_request("template/device") 115 | exisiting_main = [ i["templateName"] for i in result.json()["data"]] 116 | for uuid,temp in main_templates.items(): 117 | feature = False 118 | if temp["templateName"] in exisiting_main: 119 | temp["templateName"] = temp["templateName"]+"_1" 120 | if temp["templateName"] in exisiting_main: 121 | raise Exception("ERROR : Still having name conflicts, are you running the script multiple times?") 122 | if temp["configType"] == "template": 123 | feature=True 124 | for sub_template in temp["generalTemplates"]: 125 | sub_template=update_template(sub_template) 126 | if "policyId" in temp and temp["policyId"]!="": 127 | temp["policyId"] = uuid_pairs[temp["policyId"]] 128 | new_temp = { 129 | "templateName": temp["templateName"], 130 | "templateDescription": temp["templateDescription"], 131 | "deviceType": temp["deviceType"], 132 | "configType": temp["configType"], 133 | "factoryDefault": temp["factoryDefault"] 134 | } 135 | if feature: 136 | if temp.get("policyId"): new_temp["policyId"]= temp["policyId"] 137 | else: new_temp["policyId"]="" 138 | if temp.get("featureTemplateUidRange"): new_temp["featureTemplateUidRange"]= temp["featureTemplateUidRange"] 139 | else: new_temp["featureTemplateUidRange"]=[] 140 | if temp.get("generalTemplates")!=None: new_temp["generalTemplates"]= temp["generalTemplates"] 141 | else: new_temp["generalTemplates"]=[] 142 | if temp.get("securityPolicyId")!=None: new_temp["securityPolicyId"]= temp["securityPolicyId"] 143 | else: new_temp["securityPolicyId"]="" 144 | 145 | result = rest.post_request("template/device/feature/",new_temp) 146 | else: 147 | new_temp["templateConfiguration"]= temp["templateConfiguration"] 148 | result = rest.post_request("template/device/cli/",new_temp) 149 | 150 | new_id = result.json()["templateId"] 151 | print("Created New Template {}".format(temp["templateName"])) 152 | 153 | 154 | def transfer_template(server1,server2,template_name=None): 155 | resp = backup_template(server1,template_name) 156 | restore_template(server2,resp) 157 | 158 | 159 | 160 | def print_help(): 161 | print("[*] A tool for transfer templates between vManage controller.") 162 | print("[*] Usage:") 163 | print("[*] --mode : transfer/backup/restore") 164 | print( 165 | "[*] If choosing transfer, server1 & server2's info need to be given in prompt. Otherwise just input server1's info.") 166 | print("[*] --all-template") 167 | print("[*] If given, backup all templates in controller, can't be used with --template.") 168 | print("[*] --template : template name") 169 | print("[*] Input template name for backup.") 170 | print("[*] --file : file path") 171 | print( 172 | "[*] Input a file path to save/read file in backup & restore modes, in transfer mode this parameter is not needed.") 173 | print("[*] --server1-ip : Server 1's hostname") 174 | print("[*] Input vManage Server1's hostname.") 175 | print("[*] --server1-port : Server 1's port") 176 | print("[*] Input vManage Server1's port.") 177 | print("[*] --server1-user : Server 1's username") 178 | print("[*] Input vManage Server1's username.") 179 | print("[*] --server1-pw : Server 1's password") 180 | print("[*] Input vManage Server1's password.") 181 | print("[*] --server1-tenant : Server 1's tenant name") 182 | print("[*] Input vManage Server1's tenant name, if not in multi tenant mode, skip this.") 183 | 184 | print("[*] --server2-ip : Server 2's hostname") 185 | print("[*] Input vManage Server2's hostname.") 186 | print("[*] --server2-port : Server 2's port") 187 | print("[*] Input vManage Server2's port.") 188 | print("[*] --server2-user : Server 2's username") 189 | print("[*] Input vManage Server2's username.") 190 | print("[*] --server2-pw : Server 2's password") 191 | print("[*] Input vManage Server2's password.") 192 | print("[*] --server2-tenant : Server 2's tenant name") 193 | print("[*] Input vManage Server2's tenant name, if not in multi tenant mode, skip this.") 194 | print("[*]\n[*] Example:") 195 | print("[*] Transfer template test:") 196 | print( 197 | "[*] sdwan-template-tool --mode=transfer --template=test --server1-ip=10.0.0.1 --server1-port=443 --server1-user=admin --server1-pw=admin --server2-ip=10.0.0.2 --server2-port=443 --server2-user=admin --server2-pw=admin") 198 | print("[*] Backup all template:") 199 | print( 200 | "[*] sdwan-template-tool --mode=backup --all-template --file=backup.json --server1-ip=10.0.0.1 --server1-port=443 --server1-user=admin --server1-pw=admin") 201 | print("[*] Restore template from a file:") 202 | print( 203 | "[*] sdwan-template-tool --mode=restore --file=backup.json --server1-ip=10.0.0.1 --server1-port=443 --server1-user=admin --server1-pw=admin") 204 | 205 | 206 | if __name__ == '__main__': 207 | 208 | 209 | opts, args = getopt.getopt(sys.argv[1:], '-h-s1:-u1:-pw1:-p1:-t1:-s2:-u2:-pw2:-p2:-t2:-a-t:-m:-f', ['help', 'server1-ip=', 'server1-user=', 'server1-pw=', 'server1-port=', 'server1-tenant=','server2-ip=', 'server2-user=', 'server2-pw=', 'server2-port=', 'server2-tenant=', 'all-template', 'template=','mode=','file=']) 210 | 211 | server1_user = server2_user = server1_port = server2_port = server1_pw = server2_pw =server1_ip = server2_ip =server1_tenant =server2_tenant = all_template =template_name = mode =file = None 212 | for opt_name, opt_value in opts: 213 | if opt_name in ('-h', '--help'): 214 | print_help() 215 | exit() 216 | 217 | if opt_name in ('-s1', '--server1-ip'): 218 | server1_ip = opt_value 219 | print("[*] Server1 IP is {}".format(server1_ip)) 220 | if opt_name in ('-u1', '--server1-user'): 221 | server1_user = opt_value 222 | print("[*] Server1 Username is {}".format(server1_user)) 223 | if opt_name in ('-pw1', '--server1-pw'): 224 | server1_pw = opt_value 225 | print("[*] Server1 Password is {}".format(server1_pw)) 226 | if opt_name in ('-p1', '--server1-port'): 227 | server1_port = opt_value 228 | print("[*] Server1 Port is {}".format(server1_port)) 229 | if opt_name in ('-t1', '--server1-tenant'): 230 | server1_tenant = opt_value 231 | print("[*] Server1 Tenant is {}".format(server1_tenant)) 232 | 233 | 234 | if opt_name in ('-s2', '--server2-ip'): 235 | server2_ip = opt_value 236 | print("[*] Server2 IP is {}".format(server2_ip)) 237 | if opt_name in ('-u2', '--server2-user'): 238 | server2_user = opt_value 239 | print("[*] Server2 Username is {}".format(server2_user)) 240 | if opt_name in ('-pw2', '--server2-pw'): 241 | server2_pw = opt_value 242 | print("[*] Server2 Password is {}".format(server2_pw)) 243 | if opt_name in ('-p2', '--server2-port'): 244 | server2_port = opt_value 245 | print("[*] Server2 Port is {}".format(server2_port)) 246 | if opt_name in ('-t2', '--server2-tenant'): 247 | server2_tenant = opt_value 248 | print("[*] Server2 Tenant is {}".format(server2_tenant)) 249 | 250 | if opt_name in ('-a', '--all-template'): 251 | all_template=True 252 | print("[*] Transfering all template") 253 | if opt_name in ('-t', '--template'): 254 | all_template = False 255 | template_name = opt_value 256 | print("[*] Transfering template {}".format(template_name)) 257 | 258 | if opt_name in ('-m', '--mode'): 259 | if opt_value not in ["transfer","backup","restore"]: 260 | print("Please enter valid modes") 261 | exit() 262 | mode = opt_value 263 | print("[*] Choosing mode {}".format(mode)) 264 | 265 | if opt_name in ('-f', '--file'): 266 | file = opt_value 267 | print("[*] Choosing file {}".format(mode)) 268 | if not (mode and (all_template or template_name or mode=="restore")): 269 | print("Help : Need to input mode and choose if transfering all policy or single policy.") 270 | print_help() 271 | exit() 272 | 273 | if mode =="transfer": 274 | 275 | if None in [server1_ip,server2_ip, server1_user,server2_user,server1_pw,server2_pw,server1_port,server2_port]: 276 | print( 277 | "[*] Help: Please enter the server 1 and server 2's info for transfer.") 278 | exit() 279 | elif mode =="backup": 280 | if None in [server1_ip, server1_user,server1_pw,server1_port,file]: 281 | print( 282 | "[*] Help: Please enter the server 1's info for backup.") 283 | exit() 284 | elif mode =="restore": 285 | if None in [server1_ip, server1_user,server1_pw,server1_port,file]: 286 | print( 287 | "[*] Help: Please enter the server 1's info for restore.") 288 | exit() 289 | 290 | if mode =="transfer": 291 | server1_info = { 292 | "hostname": server1_ip, 293 | "port": server1_port, 294 | "username": server1_user, 295 | "password": server1_pw, 296 | "tenant": server1_tenant 297 | } 298 | 299 | server2_info = { 300 | "hostname": server2_ip, 301 | "port": server2_port, 302 | "username": server2_user, 303 | "password": server2_pw, 304 | "tenant": server2_tenant 305 | } 306 | 307 | transfer_template(server1_info,server2_info,template_name) 308 | print("Transfer Complete.") 309 | 310 | elif mode =="backup": 311 | server1_info = { 312 | "hostname": server1_ip, 313 | "port": server1_port, 314 | "username": server1_user, 315 | "password": server1_pw, 316 | "tenant": server1_tenant 317 | } 318 | 319 | 320 | data = backup_template(server1_info,template_name) 321 | with open(file,"w") as handler: 322 | handler.write(json.dumps(data)) 323 | 324 | print("Backup Complete, store at: {}".format(file)) 325 | 326 | elif mode =="restore": 327 | 328 | server1_info = { 329 | "hostname": server1_ip, 330 | "port": server1_port, 331 | "username": server1_user, 332 | "password": server1_pw, 333 | "tenant": server1_tenant 334 | } 335 | 336 | with open(file,"r") as handler: 337 | json_data = handler.read() 338 | restore_template(server1_info,json.loads(json_data)) 339 | print("Restore Complete") 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | -------------------------------------------------------------------------------- /cisco_sdwan_policy/BaseObject.py: -------------------------------------------------------------------------------- 1 | import json 2 | from pprint import pprint 3 | 4 | from cisco_sdwan_policy.ViptelaRest import ViptelaRest 5 | 6 | 7 | class BaseObject(object): 8 | 9 | def __init__(self,**kwargs): 10 | # Initialized API Endpoints. 11 | self.modified=False 12 | self.rest = ViptelaRest.init() 13 | if kwargs.get("debug"): 14 | self.debug=kwargs["debug"] 15 | else: 16 | self.debug=False 17 | 18 | def to_json(self): 19 | "Used for override" 20 | return {} 21 | 22 | def get_id(self): 23 | self.save() 24 | return self.id 25 | 26 | def save(self): 27 | if self.__getattribute__("id"): 28 | if self.modified: 29 | self.update(self.url,self.to_json()) 30 | else: 31 | id = self.create(self.url,self.to_json()) 32 | self.id = id 33 | 34 | def create(self,path,data): 35 | if self.debug: 36 | print("Creating Object:") 37 | print("Calling: {}".format(path)) 38 | print("Body:") 39 | pprint(data) 40 | 41 | if self.__getattribute__("id"): 42 | return False 43 | result = self.rest.post_request(path,data) 44 | if result.status_code == 200: 45 | if result.content: 46 | for k,v in result.json().items(): 47 | if "id" in k.lower(): 48 | return v 49 | else: 50 | return None 51 | else: 52 | raise Exception("Error Creating: {} {}".format(result.status_code, result.content)) 53 | return None 54 | 55 | def export(self): 56 | result = self.to_json() 57 | result["class"]=type(self).__name__ 58 | result["id"]=self.id 59 | return result 60 | def __del__(self): 61 | pass 62 | 63 | def __setattr__(self, instance, value): 64 | if instance=="modified": 65 | pass 66 | else: 67 | super().__setattr__("modified",True) 68 | super().__setattr__(instance,value) 69 | 70 | def update(self,path,data): 71 | if self.modified and self.id: 72 | if self.debug: 73 | print("Updating Object:") 74 | print("Calling: {}".format(path)) 75 | print("Body:") 76 | pprint(data) 77 | 78 | result = self.rest.put_request("{}/{}".format(path,self.id),data) 79 | if result.status_code == 200: 80 | return True 81 | else: 82 | raise Exception("Error Updating: {} {}".format(result.status_code, result.content)) 83 | 84 | -------------------------------------------------------------------------------- /cisco_sdwan_policy/Helper/PolicyLoader.py: -------------------------------------------------------------------------------- 1 | import importlib 2 | import json 3 | 4 | from cisco_sdwan_policy.List.Application import Application 5 | from cisco_sdwan_policy.List.Color import Color 6 | from cisco_sdwan_policy.List.DataPrefix import DataPrefix 7 | from cisco_sdwan_policy.List.Policer import Policer 8 | from cisco_sdwan_policy.List.Prefix import Prefix 9 | from cisco_sdwan_policy.List.Site import Site 10 | from cisco_sdwan_policy.List.SlaClass import SlaClass 11 | from cisco_sdwan_policy.List.Tloc import Tloc 12 | from cisco_sdwan_policy.List.Vpn import Vpn 13 | from cisco_sdwan_policy.MainPolicy import MainPolicy 14 | from cisco_sdwan_policy.Topology.CustomTopo import CustomTopo 15 | from cisco_sdwan_policy.Topology.HubAndSpoke import HubAndSpoke 16 | from cisco_sdwan_policy.Topology.Mesh import Mesh 17 | from cisco_sdwan_policy.Topology.VpnMembership import VpnMembership 18 | from cisco_sdwan_policy.TrafficPolicy.AppRoute import AppRoute 19 | from cisco_sdwan_policy.TrafficPolicy.DataPolicy import DataPolicy 20 | from cisco_sdwan_policy.ViptelaRest import ViptelaRest 21 | 22 | 23 | class PolicyLoader(object): 24 | instance = None 25 | 26 | def __init__(self,server_info,debug=False): 27 | self.rest = ViptelaRest.init(server_info) 28 | self.list_policies = [] 29 | self.topo_policies = [] 30 | self.traffic_policies = [] 31 | self.main_policies = [] 32 | self.debug = debug 33 | 34 | def load(self): 35 | self.get_list_policies() 36 | self.get_topo_policies() 37 | self.get_traffic_policies() 38 | self.get_main_policies() 39 | 40 | def get_list_policies(self): 41 | self.list_policies=[] 42 | policers = self.rest.get_request("template/policy/list/policer").json() 43 | for policer in policers["data"]: 44 | pc = Policer.from_json(policer,debug=self.debug) 45 | if pc: 46 | self.list_policies.append(pc) 47 | 48 | vpns = self.rest.get_request("template/policy/list/vpn").json() 49 | for vpn in vpns["data"]: 50 | v = Vpn.from_json(vpn,debug=self.debug) 51 | if v: 52 | self.list_policies.append(v) 53 | 54 | sites = self.rest.get_request("template/policy/list/site").json() 55 | for site in sites["data"]: 56 | v = Site.from_json(site,debug=self.debug) 57 | if v: 58 | self.list_policies.append(v) 59 | 60 | if int(self.rest.version.split(".")[0])>=19: 61 | prefix_url = "template/policy/list/ipprefixall" 62 | else: 63 | prefix_url = "template/policy/list/prefix" 64 | 65 | prefixs = self.rest.get_request(prefix_url).json() 66 | for prefix in prefixs["data"]: 67 | v = Prefix.from_json(prefix,debug=self.debug) 68 | if v: 69 | self.list_policies.append(v) 70 | 71 | if int(self.rest.version.split(".")[0])>=19: 72 | data_prefix_url = "template/policy/list/dataprefixall" 73 | else: 74 | data_prefix_url = "template/policy/list/dataprefix" 75 | 76 | 77 | prefixs = self.rest.get_request(data_prefix_url).json() 78 | for prefix in prefixs["data"]: 79 | v = DataPrefix.from_json(prefix,debug=self.debug) 80 | if v: 81 | self.list_policies.append(v) 82 | 83 | tlocs = self.rest.get_request("template/policy/list/tloc").json() 84 | for tloc in tlocs["data"]: 85 | v = Tloc.from_json(tloc,debug=self.debug) 86 | if v: 87 | self.list_policies.append(v) 88 | 89 | slas = self.rest.get_request("template/policy/list/sla").json() 90 | for sla in slas["data"]: 91 | v = SlaClass.from_json(sla,debug=self.debug) 92 | if v: 93 | self.list_policies.append(v) 94 | 95 | apps = self.rest.get_request("template/policy/list/app").json() 96 | for app in apps["data"]: 97 | v = Application.from_json(app,debug=self.debug) 98 | if v: 99 | self.list_policies.append(v) 100 | 101 | colors = self.rest.get_request("template/policy/list/color").json() 102 | for color in colors["data"]: 103 | v = Color.from_json(color,debug=self.debug) 104 | if v: 105 | self.list_policies.append(v) 106 | 107 | def get_topo_policies(self): 108 | self.topo_policies=[] 109 | 110 | hubandspokes = self.rest.get_request("template/policy/definition/hubandspoke").json() 111 | for hs in hubandspokes["data"]: 112 | topo_info = self.rest.get_request( 113 | "template/policy/definition/hubandspoke/{}".format(hs["definitionId"])).json() 114 | res = HubAndSpoke.from_json(topo_info, self.list_policies,debug=self.debug) 115 | self.topo_policies.append(res) 116 | 117 | meshes = self.rest.get_request("template/policy/definition/mesh").json() 118 | for mesh in meshes["data"]: 119 | topo_info = self.rest.get_request( 120 | "template/policy/definition/mesh/{}".format(mesh["definitionId"])).json() 121 | res = Mesh.from_json(topo_info, self.list_policies,debug=self.debug) 122 | self.topo_policies.append(res) 123 | 124 | 125 | custom_topos = self.rest.get_request("template/policy/definition/control").json() 126 | for custom_topo in custom_topos["data"]: 127 | topo_info = self.rest.get_request( 128 | "template/policy/definition/control/{}".format(custom_topo["definitionId"])).json() 129 | res = CustomTopo.from_json(topo_info, self.list_policies,debug=self.debug) 130 | self.topo_policies.append(res) 131 | # print(res.to_json()) 132 | # print(res.name) 133 | 134 | vpn_memberships = self.rest.get_request("template/policy/definition/vpnmembershipgroup").json() 135 | for vpn_membership in vpn_memberships["data"]: 136 | vpn_info = self.rest.get_request( 137 | "template/policy/definition/vpnmembershipgroup/{}".format(vpn_membership["definitionId"])).json() 138 | res = VpnMembership.from_json(vpn_info, self.list_policies,debug=self.debug) 139 | self.topo_policies.append(res) 140 | # print(res.to_json()) 141 | # print(res.name) 142 | 143 | def get_traffic_policies(self): 144 | self.traffic_policies=[] 145 | app_routes = self.rest.get_request("template/policy/definition/approute").json() 146 | for app_route in app_routes["data"]: 147 | route_info = self.rest.get_request( 148 | "template/policy/definition/approute/{}".format(app_route["definitionId"])).json() 149 | res = AppRoute.from_json(route_info, self.list_policies,debug=self.debug) 150 | # print(res.name) 151 | # print(res.to_json()) 152 | self.traffic_policies.append(res) 153 | 154 | datas = self.rest.get_request("template/policy/definition/data").json() 155 | for data in datas["data"]: 156 | route_info = self.rest.get_request("template/policy/definition/data/{}".format(data["definitionId"])).json() 157 | res = DataPolicy.from_json(route_info, self.list_policies,debug=self.debug) 158 | # print(res.name) 159 | # print(res.to_json()) 160 | self.traffic_policies.append(res) 161 | 162 | def get_main_policies(self): 163 | self.main_policies=[] 164 | main_policies = self.rest.get_request("template/policy/vsmart").json() 165 | for policy in main_policies["data"]: 166 | if policy["policyType"] == "feature": 167 | policy_info = self.rest.get_request("template/policy/vsmart/definition/{}".format(policy["policyId"])).json() 168 | res = MainPolicy.from_json(policy["policyId"], policy_info, self.topo_policies, self.traffic_policies, self.list_policies,debug=self.debug) 169 | # print(res.name) 170 | self.main_policies.append(res) 171 | 172 | def save_to_json(self): 173 | output ={ 174 | "list_policies":[i.export() for i in self.list_policies], 175 | "topo_policies":[i.export() for i in self.topo_policies], 176 | "traffic_policies":[i.export() for i in self.traffic_policies], 177 | "main_policies":[i.export() for i in self.main_policies] 178 | } 179 | return json.dumps(output) 180 | 181 | def load_from_json(self,json_file,only_ref=False): 182 | backup = json.loads(json_file) 183 | # First load in temporary variables, then determine if there's any conflict. 184 | list_policies = [] 185 | topo_policies = [] 186 | traffic_policies = [] 187 | main_policies = [] 188 | for i in backup["list_policies"]: 189 | i["listId"] = i["id"] 190 | cls= self.get_class("List",i["class"]) 191 | obj = cls.from_json(i,debug=self.debug) 192 | list_policies.append(obj) 193 | 194 | for i in backup["topo_policies"]: 195 | i["definitionId"] = i["id"] 196 | cls= self.get_class("Topology",i["class"]) 197 | obj = cls.from_json(i,list_policies,debug=self.debug) 198 | topo_policies.append(obj) 199 | 200 | for i in backup["traffic_policies"]: 201 | i["definitionId"] = i["id"] 202 | cls= self.get_class("TrafficPolicy",i["class"]) 203 | obj = cls.from_json(i,list_policies,debug=self.debug) 204 | traffic_policies.append(obj) 205 | 206 | for i in backup["main_policies"]: 207 | obj = MainPolicy.from_json(i["id"],i,topo_policies,traffic_policies,list_policies,debug=self.debug) 208 | main_policies.append(obj) 209 | 210 | if only_ref: 211 | for i in list_policies: 212 | names = [j.name for j in self.list_policies] 213 | if i.name not in names: 214 | pass 215 | else: 216 | while i.name in names: 217 | i.name = i.name + "_1" 218 | i.id = None 219 | for i in topo_policies: 220 | names = [j.name for j in self.topo_policies] 221 | if i.name not in names: 222 | pass 223 | else: 224 | while i.name in names: 225 | i.name = i.name + "_1" 226 | i.id = None 227 | for i in traffic_policies: 228 | names = [j.name for j in self.traffic_policies] 229 | if i.name not in names: 230 | pass 231 | else: 232 | while i.name in names: 233 | i.name = i.name + "_1" 234 | i.id = None 235 | for i in main_policies: 236 | names = [j.name for j in self.traffic_policies] 237 | while i.name in names: 238 | i.name = i.name + "_1" 239 | i.id = None 240 | # Save the new policy, all the dependencies will automatically created. 241 | i.save() 242 | else: 243 | 244 | # Find duplicate from current vManage (Only checks the name.) 245 | for i in list_policies: 246 | names = [j.name for j in self.list_policies] 247 | if i.name not in names: 248 | i.id=None 249 | i.save() 250 | self.list_policies.append(i) 251 | names = [j.name for j in self.list_policies] 252 | else: 253 | conflict = True 254 | for j in self.list_policies: 255 | if i.name==j.name and i.id==j.id: 256 | conflict=False 257 | break 258 | if conflict: 259 | i.name = i.name+"_1" 260 | if i.name in names: 261 | raise Exception("Error: Seems already restored.") 262 | i.id = None 263 | i.save() 264 | self.list_policies.append(i) 265 | for i in topo_policies: 266 | 267 | if i.name not in [ j.name for j in self.topo_policies]: 268 | i.id=None 269 | i.save() 270 | self.topo_policies.append(i) 271 | else: 272 | conflict = True 273 | for j in self.topo_policies: 274 | if i.name==j.name and i.id==j.id: 275 | conflict=False 276 | break 277 | if conflict: 278 | i.name = i.name+"_1" 279 | if i.name in [ j.name for j in self.topo_policies]: 280 | raise Exception("Error: Seems already restored.") 281 | i.id = None 282 | i.save() 283 | self.topo_policies.append(i) 284 | 285 | for i in traffic_policies: 286 | if i.name not in [ j.name for j in self.traffic_policies]: 287 | i.id=None 288 | i.save() 289 | self.traffic_policies.append(i) 290 | else: 291 | conflict = True 292 | for j in self.traffic_policies: 293 | if i.name==j.name and i.id==j.id: 294 | conflict=False 295 | break 296 | if conflict: 297 | i.name = i.name+"_1" 298 | if i.name in [ j.name for j in self.traffic_policies]: 299 | raise Exception("Error: Seems already restored.") 300 | i.id = None 301 | i.save() 302 | self.traffic_policies.append(i) 303 | 304 | for i in main_policies: 305 | if i.name not in [ j.name for j in self.main_policies]: 306 | i.id=None 307 | i.save() 308 | self.main_policies.append(i) 309 | else: 310 | conflict = True 311 | for j in self.main_policies: 312 | if i.name==j.name and i.id==j.id: 313 | conflict=False 314 | break 315 | if conflict: 316 | i.name = i.name+"_1" 317 | if i.name in [ j.name for j in self.main_policies]: 318 | raise Exception("Error: Seems already restored.") 319 | i.id = None 320 | i.save() 321 | self.main_policies.append(i) 322 | 323 | @staticmethod 324 | def get_class(module_name, class_name): 325 | # load the module, will raise ImportError if module cannot be loaded 326 | m = importlib.import_module("cisco_sdwan_policy.{}.{}".format(module_name, class_name)) 327 | # get the class, will raise AttributeError if class cannot be found 328 | c = getattr(m, class_name) 329 | return c 330 | 331 | @classmethod 332 | def init(cls,server_info=None): 333 | if server_info: 334 | cls.instance = cls(server_info) 335 | return cls.instance 336 | if cls.instance: 337 | return cls.instance 338 | else: 339 | cls.instance = cls(server_info) 340 | return cls.instance -------------------------------------------------------------------------------- /cisco_sdwan_policy/Helper/Sequence.py: -------------------------------------------------------------------------------- 1 | from cisco_sdwan_policy.BaseObject import BaseObject 2 | 3 | 4 | class Sequence(BaseObject): 5 | 6 | def __init__(self,id,name,type,base_action,ip_type,match,actions,**kwargs): 7 | 8 | self.id = id 9 | self.name = name 10 | self.type = type 11 | self.baseAction = base_action 12 | self.ip_type=ip_type 13 | self.actions = actions 14 | self.match = match 15 | super().__init__(**kwargs) 16 | 17 | @staticmethod 18 | def get_list_obj(obj_id,lists): 19 | for obj in lists: 20 | if obj.id==obj_id: 21 | return obj 22 | return None 23 | 24 | @classmethod 25 | def from_json(cls,config,lists,**kwargs): 26 | for action in config["actions"]: 27 | if action.get("parameter"): 28 | if type(action["parameter"])==list: 29 | for para in action["parameter"]: 30 | if para.get("ref"): 31 | resp = cls.get_list_obj(para.get("ref"),lists) 32 | if resp: 33 | action["parameter"][action["parameter"].index(para)]["ref"]=resp 34 | else: 35 | raise Exception("List not found") 36 | elif type(action["parameter"])==dict: 37 | para = action["parameter"] 38 | if para.get("ref"): 39 | resp = cls.get_list_obj(para.get("ref"), lists) 40 | if resp: 41 | action["parameter"]["ref"] = resp 42 | else: 43 | raise Exception("List not found") 44 | 45 | else: 46 | # Might be cflowd 47 | pass 48 | 49 | new_match=[] 50 | for match in config["match"]["entries"]: 51 | matched=False 52 | if match.get("ref"): 53 | resp = cls.get_list_obj(match.get("ref"), lists) 54 | if resp: 55 | config["match"]["entries"][config["match"]["entries"].index(match)]["ref"]=resp 56 | else: 57 | raise Exception("Undefined List found.") 58 | config["match"]=config["match"]["entries"] 59 | 60 | id = config["sequenceId"] 61 | name = config["sequenceName"] 62 | types = config["sequenceType"] 63 | baseAction = config.get("baseAction") 64 | ip_type = config.get("sequenceIpType") 65 | actions = config["actions"] 66 | match = config["match"] 67 | 68 | return cls(id,name,types,baseAction,ip_type,match,actions,**kwargs) 69 | 70 | def to_json(self): 71 | resp = { 72 | "sequenceId": self.id, 73 | "sequenceName": self.name, 74 | "baseAction": self.baseAction, 75 | "sequenceType": self.type, 76 | "match": { 77 | "entries": [] 78 | }, 79 | "actions": [] 80 | } 81 | if self.ip_type: 82 | resp["sequenceIpType"]=self.ip_type 83 | 84 | for match in self.match: 85 | if match.get("ref"): 86 | resp["match"]["entries"].append({ 87 | "field":match["field"], 88 | "ref":match["ref"].get_id() 89 | }) 90 | else: 91 | resp["match"]["entries"].append(match) 92 | for action in self.actions: 93 | if action.get("parameter"): 94 | if type(action["parameter"]) == list: 95 | new_para = [] 96 | for para in action["parameter"]: 97 | if para.get("ref"): 98 | new_para.append({ 99 | "field": para["field"], 100 | "ref": para["ref"].get_id() 101 | }) 102 | else: 103 | new_para.append(para) 104 | resp["actions"].append({ 105 | "type":action["type"], 106 | "parameter":new_para 107 | }) 108 | elif type(action["parameter"]) == dict: 109 | if action["parameter"].get("ref"): 110 | action["parameter"]["ref"]=action["parameter"]["ref"].get_id() 111 | resp["actions"].append(action) 112 | else: 113 | resp["actions"].append(action) 114 | else: 115 | #cflowd 116 | resp["actions"].append(action) 117 | return resp 118 | 119 | def add_match(self,field,value): 120 | for matches in self.match: 121 | if matches["field"]==field: 122 | raise Exception("Duplicate field in Match.") 123 | if type(value) in [str,int,float]: 124 | self.match.append({ 125 | "field":field, 126 | "value":value 127 | }) 128 | else: 129 | self.match.append({ 130 | "field":field, 131 | "ref":value 132 | }) 133 | 134 | def add_action(self,action_type,field,value): 135 | def generate_param(): 136 | if type(value) in [str, int, float]: 137 | return{ 138 | "field": field, 139 | "value": value 140 | } 141 | else: 142 | return{ 143 | "field": field, 144 | "ref": value 145 | } 146 | 147 | for action in self.actions: 148 | if action["type"]==action_type: 149 | for param in action["parameter"]: 150 | if param["field"]==field: 151 | raise Exception("Duplicate field in Action.") 152 | action["parameter"].append(generate_param()) 153 | return True 154 | self.actions.append({ 155 | "type":action_type, 156 | "parameter":[generate_param()] 157 | }) 158 | 159 | -------------------------------------------------------------------------------- /cisco_sdwan_policy/Helper/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljm625/cisco_sdwan_policy_python/2981a05103f27cda3399d9cecc134fb80ae2f2e1/cisco_sdwan_policy/Helper/__init__.py -------------------------------------------------------------------------------- /cisco_sdwan_policy/List/ASPath.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from cisco_sdwan_policy.BaseObject import BaseObject 4 | 5 | 6 | class ASPath(BaseObject): 7 | 8 | def __init__(self,name,aspath_list,id=None,reference=None,**kwargs): 9 | self.type = "aspath" 10 | self.id = id 11 | self.name = name 12 | self.references = reference 13 | self._entries = aspath_list 14 | self.url= "template/policy/list/aspath" 15 | super().__init__(**kwargs) 16 | self.modified=False 17 | 18 | 19 | def get_entries(self): 20 | return self._entries 21 | 22 | def set_entries(self, entries): 23 | self.modified = True 24 | self._entries = entries 25 | 26 | @classmethod 27 | def from_json(cls,config,**kwargs): 28 | id = config["listId"] 29 | name = config["name"] 30 | references = config.get("references") 31 | entries = [i["asPath"] for i in config["entries"]] 32 | 33 | return cls(name,entries,id,references,**kwargs) 34 | 35 | 36 | def to_json(self): 37 | # {"name": "DX", "description": "Desc Not Required", "type": "site", "listId": null, 38 | # "entries": [{"siteId": "100"}, {"siteId": "200"}, {"siteId": "300"}]} 39 | return { 40 | "name":self.name, 41 | "description":"Desc Not Required", 42 | "type":self.type, 43 | "listId":None, 44 | "entries":[ 45 | {"asPath":i} for i in self._entries] 46 | } 47 | 48 | -------------------------------------------------------------------------------- /cisco_sdwan_policy/List/Application.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from cisco_sdwan_policy.BaseObject import BaseObject 4 | 5 | 6 | class Application(BaseObject): 7 | 8 | def __init__(self,name,app_list,is_app_family,id=None,reference=None,**kwargs): 9 | self.type = "appList" 10 | self.id = id 11 | self.name = name 12 | self.references = reference 13 | self.app_family=is_app_family 14 | self._entries = app_list 15 | self.url = "template/policy/list/app" 16 | super().__init__(**kwargs) 17 | self.modified=False 18 | 19 | def get_entries(self): 20 | return self._entries 21 | 22 | def set_entries(self,entries): 23 | self.modified=True 24 | self._entries=entries 25 | 26 | @classmethod 27 | def from_json(cls,jsonfile,**kwargs): 28 | 29 | id = jsonfile["listId"] 30 | name = jsonfile["name"] 31 | references = jsonfile.get("references") 32 | if len(jsonfile["entries"])>0 and jsonfile["entries"][0].get("app"): 33 | appFamily=False 34 | entries = [i["app"] for i in jsonfile["entries"]] 35 | else: 36 | if not jsonfile["entries"][0].get("appFamily"): 37 | return None 38 | else: 39 | appFamily=True 40 | entries = [i["appFamily"] for i in jsonfile["entries"]] 41 | return cls(name,entries,appFamily,id,references,**kwargs) 42 | 43 | def to_json(self): 44 | return { 45 | "name":self.name, 46 | "description":"Desc Not Required", 47 | "type":"app", 48 | "entries":[ 49 | {"appFamily" if self.app_family else "app":i} for i in self._entries] 50 | } 51 | -------------------------------------------------------------------------------- /cisco_sdwan_policy/List/ClassMap.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from cisco_sdwan_policy.BaseObject import BaseObject 4 | 5 | 6 | class ClassMap(BaseObject): 7 | 8 | def __init__(self,name,queue,id=None,reference=None,**kwargs): 9 | self.type = "class" 10 | self.id = id 11 | self.name = name 12 | self.references = reference 13 | assert queue>=0 and queue<=7 and type(queue)==int 14 | self.queue = queue 15 | self.url= "template/policy/list/class" 16 | super().__init__(**kwargs) 17 | self.modified=False 18 | 19 | 20 | def get_entries(self): 21 | return self._entries 22 | 23 | def set_entries(self, entries): 24 | self.modified = True 25 | self._entries = entries 26 | 27 | @classmethod 28 | def from_json(cls,config,**kwargs): 29 | id = config["listId"] 30 | name = config["name"] 31 | references = config.get("references") 32 | queue = int(config["entries"][0]["queue"]) 33 | 34 | return cls(name,queue,id,references,**kwargs) 35 | 36 | 37 | def to_json(self): 38 | # {"name": "DX", "description": "Desc Not Required", "type": "site", "listId": null, 39 | # "entries": [{"siteId": "100"}, {"siteId": "200"}, {"siteId": "300"}]} 40 | return { 41 | "name":self.name, 42 | "description":"Desc Not Required", 43 | "type":self.type, 44 | "listId":None, 45 | "entries":[ 46 | {"queue":self.queue}] 47 | } 48 | 49 | -------------------------------------------------------------------------------- /cisco_sdwan_policy/List/Color.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from cisco_sdwan_policy.BaseObject import BaseObject 4 | 5 | 6 | class Color(BaseObject): 7 | 8 | def __init__(self,name,color_list,id=None,reference=None,**kwargs): 9 | self.type = "colorList" 10 | self.id = id 11 | self.name = name 12 | self.references = reference 13 | self._entries = color_list 14 | 15 | self.url = "template/policy/list/color" 16 | super().__init__(**kwargs) 17 | self.modified=False 18 | 19 | 20 | def get_entries(self): 21 | return self._entries 22 | 23 | def set_entries(self, entries): 24 | self.modified = True 25 | self._entries = entries 26 | 27 | @classmethod 28 | def from_json(cls,jsonfile,**kwargs): 29 | id = jsonfile["listId"] 30 | name = jsonfile["name"] 31 | references = jsonfile.get("references") 32 | entries = [i["color"] for i in jsonfile["entries"]] 33 | 34 | return cls(name,entries,id,references,**kwargs) 35 | 36 | def to_json(self): 37 | return { 38 | "name":self.name, 39 | "description":"Desc Not Required", 40 | "type":"color", 41 | "entries":[ 42 | {"color":i} for i in self._entries] 43 | } 44 | -------------------------------------------------------------------------------- /cisco_sdwan_policy/List/Community.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from cisco_sdwan_policy.BaseObject import BaseObject 4 | 5 | 6 | class Community(BaseObject): 7 | 8 | def __init__(self,name,community_list,id=None,reference=None,**kwargs): 9 | self.type = "community" 10 | self.id = id 11 | self.name = name 12 | self.references = reference 13 | self._entries = community_list 14 | self.url= "template/policy/list/community" 15 | super().__init__(**kwargs) 16 | self.modified=False 17 | 18 | 19 | def get_entries(self): 20 | return self._entries 21 | 22 | def set_entries(self, entries): 23 | self.modified = True 24 | self._entries = entries 25 | 26 | @classmethod 27 | def from_json(cls,config,**kwargs): 28 | id = config["listId"] 29 | name = config["name"] 30 | references = config.get("references") 31 | entries = [i["community"] for i in config["entries"]] 32 | 33 | return cls(name,entries,id,references,**kwargs) 34 | 35 | 36 | def to_json(self): 37 | # {"name": "DX", "description": "Desc Not Required", "type": "site", "listId": null, 38 | # "entries": [{"siteId": "100"}, {"siteId": "200"}, {"siteId": "300"}]} 39 | return { 40 | "name":self.name, 41 | "description":"Desc Not Required", 42 | "type":self.type, 43 | "listId":None, 44 | "entries":[ 45 | {"community":i} for i in self._entries] 46 | } 47 | 48 | -------------------------------------------------------------------------------- /cisco_sdwan_policy/List/DataPrefix.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from cisco_sdwan_policy.BaseObject import BaseObject 4 | 5 | 6 | class DataPrefix(BaseObject): 7 | 8 | def __init__(self,name,prefix_list,is_ipv6=False,id=None,reference=None,**kwargs): 9 | self.type = "dataprefixList" 10 | self.ipv6=is_ipv6 11 | 12 | self._entries=prefix_list 13 | self.id = id 14 | self.name = name 15 | self.references = reference 16 | super().__init__(**kwargs) 17 | if int(self.rest.version.split(".")[0])<19: 18 | self.ipv6=False 19 | if self.ipv6: 20 | self.url = "template/policy/list/dataipv6prefix" 21 | else: 22 | self.url = "template/policy/list/dataprefix" 23 | 24 | self.modified=False 25 | 26 | 27 | def get_entries(self): 28 | return self._entries 29 | 30 | def set_entries(self, entries): 31 | self.modified = True 32 | self._entries = entries 33 | 34 | @classmethod 35 | def from_json(cls,config,**kwargs): 36 | 37 | if config["type"] == "dataIpv6Prefix": 38 | ipv6=True 39 | entries = [i["ipv6Prefix"] for i in config["entries"]] 40 | 41 | else: 42 | ipv6=False 43 | entries = [i["ipPrefix"] for i in config["entries"]] 44 | 45 | id = config["listId"] 46 | name = config["name"] 47 | references = config.get("references") 48 | 49 | return cls(name,entries,ipv6,id,references,**kwargs) 50 | 51 | def to_json(self): 52 | return { 53 | "name":self.name, 54 | "description":"Desc Not Required", 55 | "type":"dataIpv6Prefix" if self.ipv6 else "dataPrefix", 56 | "entries":[ 57 | {"ipv6Prefix" if self.ipv6 else "ipPrefix":i} for i in self._entries] 58 | } 59 | -------------------------------------------------------------------------------- /cisco_sdwan_policy/List/ExtendedCommunity.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from cisco_sdwan_policy.BaseObject import BaseObject 4 | 5 | 6 | class ExtendedCommunity(BaseObject): 7 | 8 | def __init__(self,name,community_list,id=None,reference=None,**kwargs): 9 | self.type = "extCommunity" 10 | self.id = id 11 | self.name = name 12 | self.references = reference 13 | self._entries = community_list 14 | self.url= "template/policy/list/extcommunity" 15 | super().__init__(**kwargs) 16 | self.modified=False 17 | 18 | 19 | def get_entries(self): 20 | return self._entries 21 | 22 | def set_entries(self, entries): 23 | self.modified = True 24 | self._entries = entries 25 | 26 | @classmethod 27 | def from_json(cls,config,**kwargs): 28 | id = config["listId"] 29 | name = config["name"] 30 | references = config.get("references") 31 | entries = [i["community"] for i in config["entries"]] 32 | 33 | return cls(name,entries,id,references,**kwargs) 34 | 35 | 36 | def to_json(self): 37 | # {"name": "DX", "description": "Desc Not Required", "type": "site", "listId": null, 38 | # "entries": [{"siteId": "100"}, {"siteId": "200"}, {"siteId": "300"}]} 39 | return { 40 | "name":self.name, 41 | "description":"Desc Not Required", 42 | "type":self.type, 43 | "listId":None, 44 | "entries":[ 45 | {"community":i} for i in self._entries] 46 | } 47 | 48 | -------------------------------------------------------------------------------- /cisco_sdwan_policy/List/Mirror.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from cisco_sdwan_policy.BaseObject import BaseObject 4 | 5 | 6 | class Mirror(BaseObject): 7 | 8 | def __init__(self,name,source,dest,id=None,reference=None,**kwargs): 9 | self.type = "mirror" 10 | self.id = id 11 | self.name = name 12 | self.references = reference 13 | self.source = source 14 | self.dest = dest 15 | self.url= "template/policy/list/mirror" 16 | super().__init__(**kwargs) 17 | self.modified=False 18 | 19 | 20 | def get_entries(self): 21 | return self._entries 22 | 23 | def set_entries(self, entries): 24 | self.modified = True 25 | self._entries = entries 26 | 27 | @classmethod 28 | def from_json(cls,config,**kwargs): 29 | id = config["listId"] 30 | name = config["name"] 31 | references = config.get("references") 32 | source = config["entries"][0]["source"] 33 | dest = config["entries"][0]["remoteDest"] 34 | 35 | return cls(name,source,dest,id,references,**kwargs) 36 | 37 | 38 | def to_json(self): 39 | # {"name": "DX", "description": "Desc Not Required", "type": "site", "listId": null, 40 | # "entries": [{"siteId": "100"}, {"siteId": "200"}, {"siteId": "300"}]} 41 | return { 42 | "name":self.name, 43 | "description":"Desc Not Required", 44 | "type":self.type, 45 | "listId":None, 46 | "entries":[ 47 | {"remoteDest": self.dest, "source": self.source } 48 | ] 49 | } 50 | 51 | -------------------------------------------------------------------------------- /cisco_sdwan_policy/List/Policer.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from cisco_sdwan_policy.BaseObject import BaseObject 4 | 5 | 6 | class Policer(BaseObject): 7 | 8 | def __init__(self,name,rate,exceed,burst,id=None,reference=None,**kwargs): 9 | self.type = "policerList" 10 | self.id = id 11 | self.name = name 12 | self.references = reference 13 | self.burst = burst 14 | self.exceed = exceed 15 | self.rate = rate 16 | self.url = "template/policy/list/policer" 17 | super().__init__(**kwargs) 18 | self.modified=False 19 | 20 | 21 | 22 | @classmethod 23 | def from_json(cls,config,**kwargs): 24 | id = config["listId"] 25 | name = config["name"] 26 | references = config.get("references") 27 | burst = config["entries"][0]["burst"] 28 | exceed = config["entries"][0]["exceed"] 29 | rate = config["entries"][0]["rate"] 30 | 31 | return cls(name,rate,exceed,burst,id,references,**kwargs) 32 | 33 | def to_json(self): 34 | return { 35 | "name":self.name, 36 | "description":"Desc Not Required", 37 | "type":"policer", 38 | "entries":[{"burst": self.burst, "exceed": self.exceed, "rate": self.rate}] 39 | } 40 | 41 | -------------------------------------------------------------------------------- /cisco_sdwan_policy/List/Prefix.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from cisco_sdwan_policy.BaseObject import BaseObject 4 | 5 | 6 | class Prefix(BaseObject): 7 | 8 | def __init__(self,name,prefix_list,is_ipv6=False,id=None,reference=None,**kwargs): 9 | self.type = "prefixList" 10 | self.id = id 11 | self.name = name 12 | self.references = reference 13 | self.ipv6=is_ipv6 14 | 15 | self._entries=prefix_list 16 | super().__init__(**kwargs) 17 | if int(self.rest.version.split(".")[0])<19: 18 | self.ipv6=False 19 | 20 | if self.ipv6: 21 | self.url = "template/policy/list/ipv6prefix" 22 | else: 23 | self.url = "template/policy/list/prefix" 24 | 25 | self.modified=False 26 | 27 | 28 | def get_entries(self): 29 | return self._entries 30 | 31 | def set_entries(self, entries): 32 | self.modified = True 33 | self._entries = entries 34 | 35 | @classmethod 36 | def from_json(cls,config,**kwargs): 37 | 38 | if config["type"].lower() == "ipv6prefix": 39 | ipv6=True 40 | entries = [i["ipv6Prefix"] for i in config["entries"]] 41 | 42 | else: 43 | ipv6=False 44 | entries = [i["ipPrefix"] for i in config["entries"]] 45 | 46 | id = config["listId"] 47 | name = config["name"] 48 | references = config.get("references") 49 | return cls(name,entries,ipv6,id,references,**kwargs) 50 | 51 | def to_json(self): 52 | return { 53 | "name":self.name, 54 | "description":"Desc Not Required", 55 | "type":"ipv6prefix" if self.ipv6 else "prefix", 56 | "entries":[ 57 | {"ipv6Prefix" if self.ipv6 else "ipPrefix":i} for i in self._entries] 58 | } 59 | -------------------------------------------------------------------------------- /cisco_sdwan_policy/List/Site.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from cisco_sdwan_policy.BaseObject import BaseObject 4 | 5 | 6 | class Site(BaseObject): 7 | 8 | def __init__(self,name,site_list,id=None,reference=None,**kwargs): 9 | self.type = "siteList" 10 | self.id = id 11 | self.name = name 12 | self.references = reference 13 | self._entries = site_list 14 | self.url= "template/policy/list/site" 15 | super().__init__(**kwargs) 16 | self.modified=False 17 | 18 | 19 | def get_entries(self): 20 | return self._entries 21 | 22 | def set_entries(self, entries): 23 | self.modified = True 24 | self._entries = entries 25 | 26 | @classmethod 27 | def from_json(cls,config,**kwargs): 28 | id = config["listId"] 29 | name = config["name"] 30 | references = config.get("references") 31 | entries = [i["siteId"] for i in config["entries"]] 32 | 33 | return cls(name,entries,id,references,**kwargs) 34 | 35 | 36 | def to_json(self): 37 | # {"name": "DX", "description": "Desc Not Required", "type": "site", "listId": null, 38 | # "entries": [{"siteId": "100"}, {"siteId": "200"}, {"siteId": "300"}]} 39 | return { 40 | "name":self.name, 41 | "description":"Desc Not Required", 42 | "type":"site", 43 | "listId":None, 44 | "entries":[ 45 | {"siteId":i} for i in self._entries] 46 | } 47 | 48 | -------------------------------------------------------------------------------- /cisco_sdwan_policy/List/SlaClass.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from cisco_sdwan_policy.BaseObject import BaseObject 4 | 5 | 6 | class SlaClass(BaseObject): 7 | 8 | def __init__(self,name,latency,loss,jitter,id=None,reference=None,**kwargs): 9 | self.type = "slaList" 10 | self.id = id 11 | self.name = name 12 | self.references = reference 13 | self.latency = latency 14 | self.loss = loss 15 | self.jitter = jitter 16 | self.url = "template/policy/list/sla" 17 | super().__init__(**kwargs) 18 | self.modified=False 19 | 20 | 21 | @classmethod 22 | def from_json(cls,config,**kwargs): 23 | id = config["listId"] 24 | name = config["name"] 25 | references = config.get("references") 26 | latency = config["entries"][0]["latency"] 27 | loss = config["entries"][0]["loss"] 28 | jitter = config["entries"][0]["jitter"] 29 | 30 | return cls(name,latency,loss,jitter,id,references,**kwargs) 31 | 32 | def to_json(self): 33 | # {"name": "test", "description": "Desc Not Required", "type": "sla", 34 | # "entries": [{"latency": "100", "loss": "1", "jitter": "1"}]} 35 | return { 36 | "name":self.name, 37 | "description":"Desc Not Required", 38 | "type":"sla", 39 | "entries":[{"latency": self.latency, "loss": self.loss, "jitter": self.jitter}] 40 | } 41 | 42 | 43 | -------------------------------------------------------------------------------- /cisco_sdwan_policy/List/Tloc.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from cisco_sdwan_policy.BaseObject import BaseObject 4 | 5 | 6 | class Tloc(BaseObject): 7 | 8 | def __init__(self,name,tloc_list,id=None,reference=None,**kwargs): 9 | self.type = "tlocList" 10 | self.id = id 11 | self.name = name 12 | self.references = reference 13 | self._entries = tloc_list 14 | self.url = "template/policy/list/tloc" 15 | 16 | # TODO : 17 | 18 | super().__init__(**kwargs) 19 | self.modified=False 20 | 21 | 22 | def get_entries(self): 23 | return self._entries 24 | 25 | def set_entries(self, entries): 26 | self.modified = True 27 | self._entries = entries 28 | 29 | def add_tloc(self,tloc,color,encap,preference): 30 | self.modified = True 31 | self._entries.append({ 32 | "tloc":tloc, 33 | "color":color, 34 | "encap":encap, 35 | "preference":preference 36 | }) 37 | 38 | @classmethod 39 | def from_json(cls,config,**kwargs): 40 | id = config["listId"] 41 | name = config["name"] 42 | references = config.get("references") 43 | entries = config["entries"] 44 | 45 | return cls(name,entries,id,references,**kwargs) 46 | 47 | def to_json(self): 48 | # {"name": "test1", "description": "Desc Not Required", "type": "tloc", 49 | # "entries": [{"tloc": "1.1.1.1", "color": "3g", "encap": "ipsec", "preference": "1"}, 50 | # {"tloc": "2.2.2.2", "color": "bronze", "encap": "gre", "preference": "2"}]} 51 | return { 52 | "name":self.name, 53 | "description":"Desc Not Required", 54 | "type":"tloc", 55 | "entries":self._entries 56 | } 57 | 58 | -------------------------------------------------------------------------------- /cisco_sdwan_policy/List/Vpn.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from cisco_sdwan_policy.BaseObject import BaseObject 4 | 5 | 6 | class Vpn(BaseObject): 7 | 8 | def __init__(self,name,vpn_list,id=None,reference=None,**kwargs): 9 | self.type = "vpnList" 10 | self.id = id 11 | self.name = name 12 | self.references = reference 13 | self._entries = vpn_list 14 | self.url ="template/policy/list/vpn" 15 | super().__init__(**kwargs) 16 | self.modified=False 17 | 18 | 19 | def get_entries(self): 20 | return self._entries 21 | 22 | def set_entries(self, entries): 23 | self.modified = True 24 | self._entries = entries 25 | 26 | 27 | @classmethod 28 | def from_json(cls,config,**kwargs): 29 | id = config["listId"] 30 | name = config["name"] 31 | references = config.get("references") 32 | entries = [i["vpn"] for i in config["entries"]] 33 | 34 | return cls(name,entries,id,references,**kwargs) 35 | 36 | 37 | def to_json(self): 38 | # {"name": "test123", "description": "Desc Not Required", "type": "vpn", "listId": null, 39 | # "entries": [{"vpn": "100"}, {"vpn": "200"}, {"vpn": "300"}]} 40 | return { 41 | "name":self.name, 42 | "description":"Desc Not Required", 43 | "type":"vpn", 44 | "entries":[ 45 | {"vpn":i} for i in self._entries] 46 | } 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /cisco_sdwan_policy/List/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljm625/cisco_sdwan_policy_python/2981a05103f27cda3399d9cecc134fb80ae2f2e1/cisco_sdwan_policy/List/__init__.py -------------------------------------------------------------------------------- /cisco_sdwan_policy/LocalPolicy/ACL.py: -------------------------------------------------------------------------------- 1 | from cisco_sdwan_policy.BaseObject import BaseObject 2 | from cisco_sdwan_policy.Helper.Sequence import Sequence 3 | 4 | 5 | class ACL(BaseObject): 6 | 7 | def __init__(self,name,description,default_action,senquences,id=None,references=None,**kwargs): 8 | self.id = id 9 | self.name = name 10 | self.description = description 11 | self.references = references 12 | self.defaultAction = default_action 13 | self.type="acl" 14 | # config["defaultAction"]["type"] 15 | self._sequence = senquences 16 | self.url = "template/policy/definition/acl" 17 | super().__init__(**kwargs) 18 | self.modified=False 19 | 20 | 21 | 22 | 23 | def to_json(self): 24 | """ 25 | Print json for REST API calls 26 | :return: 27 | """ 28 | return { 29 | "name": self.name, 30 | "type": self.type, 31 | "description": self.description, 32 | "defaultAction": { 33 | "type": self.defaultAction 34 | }, 35 | "sequences": [ i.to_json() for i in self._sequence ] 36 | } 37 | # { 38 | # "sequenceId": 1, 39 | # "sequenceName": "Access Control List", 40 | # "baseAction": "accept", 41 | # "sequenceType": "acl", 42 | # "sequenceIpType": "ipv4", 43 | # "match": { 44 | # "entries": [ 45 | # { 46 | # "field": "sourceDataPrefixList", 47 | # "ref": "f1403abf-e964-4bb4-9adc-4d2ca8fd391c" 48 | # } 49 | # ] 50 | # }, 51 | # "actions": [ 52 | # { 53 | # "type": "class", 54 | # "parameter": { 55 | # "ref": "f3c7d739-9bd3-4698-a214-8650afa7a224" 56 | # } 57 | # } 58 | # ] 59 | # } 60 | 61 | # return { 62 | # "name": self.name, 63 | # "type": "control", 64 | # "description": self.description, 65 | # "defaultAction": { 66 | # "type": self.defaultAction 67 | # }, 68 | # "sequences":[i.to_json() for i in self._sequence] 69 | # } 70 | 71 | @classmethod 72 | def from_json(cls,config,lists,**kwargs): 73 | """ 74 | Generate object from JSON. 75 | :return: 76 | """ 77 | new_sequence=[] 78 | for sequence in config["sequences"]: 79 | tmp = Sequence.from_json(sequence,lists) 80 | new_sequence.append(tmp) 81 | config["sequences"] = new_sequence 82 | 83 | id = config["definitionId"] 84 | name = config["name"] 85 | description = config["description"] 86 | references = config.get("references") 87 | defaultAction = config["defaultAction"]["type"] 88 | sequence = config["sequences"] 89 | return cls(name,description,defaultAction,sequence,id,references,**kwargs) 90 | 91 | pass -------------------------------------------------------------------------------- /cisco_sdwan_policy/LocalPolicy/ACLv6.py: -------------------------------------------------------------------------------- 1 | from cisco_sdwan_policy.BaseObject import BaseObject 2 | from cisco_sdwan_policy.Helper.Sequence import Sequence 3 | 4 | 5 | class ACLv6(BaseObject): 6 | 7 | def __init__(self,name,description,default_action,senquences,id=None,references=None,**kwargs): 8 | self.id = id 9 | self.name = name 10 | self.description = description 11 | self.references = references 12 | self.type = "aclv6" 13 | self.defaultAction = default_action 14 | # config["defaultAction"]["type"] 15 | self._sequence = senquences 16 | self.url = "template/policy/definition/aclv6" 17 | super().__init__(**kwargs) 18 | self.modified=False 19 | 20 | 21 | 22 | 23 | def to_json(self): 24 | """ 25 | Print json for REST API calls 26 | :return: 27 | """ 28 | return { 29 | "name": self.name, 30 | "type": self.type, 31 | "description": self.description, 32 | "defaultAction": { 33 | "type": self.defaultAction 34 | }, 35 | "sequences": [ i.to_json() for i in self._sequence ] 36 | } 37 | # { 38 | # "sequenceId": 1, 39 | # "sequenceName": "Access Control List", 40 | # "baseAction": "accept", 41 | # "sequenceType": "acl", 42 | # "sequenceIpType": "ipv4", 43 | # "match": { 44 | # "entries": [ 45 | # { 46 | # "field": "sourceDataPrefixList", 47 | # "ref": "f1403abf-e964-4bb4-9adc-4d2ca8fd391c" 48 | # } 49 | # ] 50 | # }, 51 | # "actions": [ 52 | # { 53 | # "type": "class", 54 | # "parameter": { 55 | # "ref": "f3c7d739-9bd3-4698-a214-8650afa7a224" 56 | # } 57 | # } 58 | # ] 59 | # } 60 | 61 | # return { 62 | # "name": self.name, 63 | # "type": "control", 64 | # "description": self.description, 65 | # "defaultAction": { 66 | # "type": self.defaultAction 67 | # }, 68 | # "sequences":[i.to_json() for i in self._sequence] 69 | # } 70 | 71 | @classmethod 72 | def from_json(cls,config,lists,**kwargs): 73 | """ 74 | Generate object from JSON. 75 | :return: 76 | """ 77 | new_sequence=[] 78 | for sequence in config["sequences"]: 79 | tmp = Sequence.from_json(sequence,lists) 80 | new_sequence.append(tmp) 81 | config["sequences"] = new_sequence 82 | 83 | id = config["definitionId"] 84 | name = config["name"] 85 | description = config["description"] 86 | references = config.get("references") 87 | defaultAction = config["defaultAction"]["type"] 88 | sequence = config["sequences"] 89 | return cls(name,description,defaultAction,sequence,id,references,**kwargs) 90 | 91 | pass -------------------------------------------------------------------------------- /cisco_sdwan_policy/LocalPolicy/PolicyRewrite.py: -------------------------------------------------------------------------------- 1 | import copy 2 | 3 | from cisco_sdwan_policy.BaseObject import BaseObject 4 | 5 | 6 | class QosMap(BaseObject): 7 | 8 | def __init__(self,name,description,rewrite_rules=[],id=None,references=None,**kwargs): 9 | self.id = id 10 | self.name = name 11 | self.type = "rewriteRule" 12 | self.description = description 13 | self.references = references 14 | # config["defaultAction"]["type"] 15 | self._rewrite_rules = rewrite_rules 16 | self.url = "template/policy/definition/rewriterule" 17 | super().__init__(**kwargs) 18 | self.modified=False 19 | 20 | 21 | @staticmethod 22 | def get_list_obj(obj_id,lists): 23 | for obj in lists: 24 | if obj.id==obj_id: 25 | return obj 26 | raise Exception("Can't find list {}".format(obj_id)) 27 | 28 | 29 | # TODO: Implement add & modify QosMap 30 | 31 | 32 | 33 | 34 | def to_json(self): 35 | """ 36 | Print json for REST API calls 37 | :return: 38 | """ 39 | resp = { 40 | "name": self.name, 41 | "type": self.type, 42 | "description": self.description, 43 | "definition": { 44 | "rules": [] 45 | } 46 | } 47 | for rewrite_rule in self._rewrite_rules: 48 | tmp_rule = copy.copy(rewrite_rule) 49 | if type(tmp_rule["class"])!=str: 50 | tmp_rule["class"]=tmp_rule["class"].get_id() 51 | resp["definition"]["rules"].append(tmp_rule) 52 | 53 | return resp 54 | 55 | 56 | @classmethod 57 | def from_json(cls,config,lists,**kwargs): 58 | """ 59 | Generate object from JSON. 60 | :return: 61 | """ 62 | id = config["definitionId"] 63 | name = config["name"] 64 | description = config["description"] 65 | references = config.get("references") 66 | for rule in config["definition"]["rules"]: 67 | if rule["class"]!= "": 68 | rule["class"] = cls.get_list_obj(rule["class"],lists) 69 | rewrite_rules = config["definition"]["rules"] 70 | return cls(name,description,rewrite_rules,id,references,**kwargs) 71 | 72 | pass -------------------------------------------------------------------------------- /cisco_sdwan_policy/LocalPolicy/QosMap.py: -------------------------------------------------------------------------------- 1 | import copy 2 | 3 | from cisco_sdwan_policy.BaseObject import BaseObject 4 | 5 | 6 | class QosMap(BaseObject): 7 | 8 | def __init__(self,name,description,qos_queues=[],id=None,references=None,**kwargs): 9 | self.id = id 10 | self.name = name 11 | self.type = "qosMap" 12 | self.description = description 13 | self.references = references 14 | # config["defaultAction"]["type"] 15 | self._qos_queues = qos_queues 16 | self.url = "template/policy/definition/qosmap" 17 | super().__init__(**kwargs) 18 | self.modified=False 19 | 20 | 21 | @staticmethod 22 | def get_list_obj(obj_id,lists): 23 | for obj in lists: 24 | if obj.id==obj_id: 25 | return obj 26 | raise Exception("Can't find list {}".format(obj_id)) 27 | 28 | 29 | # TODO: Implement add & modify QosMap 30 | 31 | 32 | 33 | 34 | def to_json(self): 35 | """ 36 | Print json for REST API calls 37 | :return: 38 | """ 39 | resp = { 40 | "name": self.name, 41 | "type": self.type, 42 | "description": self.description, 43 | "definition": { 44 | "qosSchedulers": [] 45 | } 46 | } 47 | for qos_queue in self._qos_queues: 48 | tmp_qos = copy.copy(qos_queue) 49 | if type(tmp_qos["classMapRef"])!=str: 50 | tmp_qos["classMapRef"]=tmp_qos["classMapRef"].get_id() 51 | resp["definition"]["qosSchedulers"].append(tmp_qos) 52 | 53 | return resp 54 | 55 | 56 | @classmethod 57 | def from_json(cls,config,lists,**kwargs): 58 | """ 59 | Generate object from JSON. 60 | :return: 61 | """ 62 | id = config["definitionId"] 63 | name = config["name"] 64 | description = config["description"] 65 | references = config.get("references") 66 | for qos in config["definition"]["qosSchedulers"]: 67 | if qos["classMapRef"]!= "": 68 | qos["classMapRef"] = cls.get_list_obj(qos["classMapRef"],lists) 69 | qos_queues = config["definition"]["qosSchedulers"] 70 | return cls(name,description,qos_queues,id,references,**kwargs) 71 | 72 | pass -------------------------------------------------------------------------------- /cisco_sdwan_policy/LocalPolicy/RoutePolicy.py: -------------------------------------------------------------------------------- 1 | from cisco_sdwan_policy.BaseObject import BaseObject 2 | from cisco_sdwan_policy.Helper.Sequence import Sequence 3 | 4 | 5 | class RoutePolicy(BaseObject): 6 | 7 | def __init__(self,name,description,default_action,senquences,id=None,references=None,**kwargs): 8 | self.id = id 9 | self.name = name 10 | self.description = description 11 | self.references = references 12 | self.defaultAction = default_action 13 | # config["defaultAction"]["type"] 14 | self.type = "vedgeRoute" 15 | self._sequence = senquences 16 | self.url = "template/policy/definition/vedgeroute" 17 | super().__init__(**kwargs) 18 | self.modified=False 19 | 20 | 21 | 22 | 23 | def to_json(self): 24 | """ 25 | Print json for REST API calls 26 | :return: 27 | """ 28 | return { 29 | "name": self.name, 30 | "type": self.type, 31 | "description": self.description, 32 | "defaultAction": { 33 | "type": self.defaultAction 34 | }, 35 | "sequences": [ i.to_json() for i in self._sequence ] 36 | } 37 | # { 38 | # "sequenceId": 1, 39 | # "sequenceName": "Access Control List", 40 | # "baseAction": "accept", 41 | # "sequenceType": "acl", 42 | # "sequenceIpType": "ipv4", 43 | # "match": { 44 | # "entries": [ 45 | # { 46 | # "field": "sourceDataPrefixList", 47 | # "ref": "f1403abf-e964-4bb4-9adc-4d2ca8fd391c" 48 | # } 49 | # ] 50 | # }, 51 | # "actions": [ 52 | # { 53 | # "type": "class", 54 | # "parameter": { 55 | # "ref": "f3c7d739-9bd3-4698-a214-8650afa7a224" 56 | # } 57 | # } 58 | # ] 59 | # } 60 | 61 | # return { 62 | # "name": self.name, 63 | # "type": "control", 64 | # "description": self.description, 65 | # "defaultAction": { 66 | # "type": self.defaultAction 67 | # }, 68 | # "sequences":[i.to_json() for i in self._sequence] 69 | # } 70 | 71 | @classmethod 72 | def from_json(cls,config,lists,**kwargs): 73 | """ 74 | Generate object from JSON. 75 | :return: 76 | """ 77 | new_sequence=[] 78 | for sequence in config["sequences"]: 79 | tmp = Sequence.from_json(sequence,lists) 80 | new_sequence.append(tmp) 81 | config["sequences"] = new_sequence 82 | 83 | id = config["definitionId"] 84 | name = config["name"] 85 | description = config["description"] 86 | references = config.get("references") 87 | defaultAction = config["defaultAction"]["type"] 88 | sequence = config["sequences"] 89 | return cls(name,description,defaultAction,sequence,id,references,**kwargs) 90 | 91 | pass -------------------------------------------------------------------------------- /cisco_sdwan_policy/LocalPolicy/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljm625/cisco_sdwan_policy_python/2981a05103f27cda3399d9cecc134fb80ae2f2e1/cisco_sdwan_policy/LocalPolicy/__init__.py -------------------------------------------------------------------------------- /cisco_sdwan_policy/MainPolicy.py: -------------------------------------------------------------------------------- 1 | from cisco_sdwan_policy.BaseObject import BaseObject 2 | from cisco_sdwan_policy.Helper.Sequence import Sequence 3 | 4 | 5 | class MainPolicy(BaseObject): 6 | 7 | def __init__(self,name,description,control_policy_list,data_policy_list,vpn_membership_list,approute_policy_list,id=None,activated=False,**kwargs): 8 | self.id = id 9 | self.name = name 10 | self.description=description 11 | self.type="feature" 12 | self.vpnMembershipGroup = vpn_membership_list 13 | self.control = control_policy_list 14 | self.data = data_policy_list 15 | self.approute = approute_policy_list 16 | self.activated = activated 17 | self.url="template/policy/vsmart" 18 | super().__init__(**kwargs) 19 | 20 | @staticmethod 21 | def get_list_obj(obj_id,lists): 22 | for obj in lists: 23 | if obj.id==obj_id: 24 | return obj 25 | raise Exception("{} Not Found".format(obj_id)) 26 | 27 | def add_control_policy(self,control_policy,in_sites,out_sites): 28 | self.control.append({ 29 | "policy":control_policy, 30 | "in":in_sites, 31 | "out":out_sites 32 | }) 33 | pass 34 | 35 | def add_data_policy(self,data_policy,direction,sites,vpns): 36 | for policy in self.data: 37 | if policy.name == data_policy: 38 | policy[direction]["site"].extend(sites) 39 | policy[direction]["vpn"].extend(vpns) 40 | return True 41 | self.data.append({ 42 | "policy": data_policy, 43 | "service": { 44 | "site":[], 45 | "vpn":[] 46 | }, 47 | "tunnel": { 48 | "site":[], 49 | "vpn":[] 50 | }, 51 | "all": { 52 | "site":[], 53 | "vpn":[] 54 | } 55 | 56 | }) 57 | self.data[-1][direction]["site"].extend(sites) 58 | self.data[-1][direction]["vpn"].extend(vpns) 59 | return True 60 | 61 | def add_approute_policy(self,approute_policy,sites,vpns): 62 | for policy in self.approute: 63 | if policy.name == approute_policy: 64 | policy["entries"].append({ 65 | "site": sites, 66 | "vpn": vpns 67 | }) 68 | return True 69 | self.approute.append({ 70 | "policy": approute_policy, 71 | "entries": [{ 72 | "site": sites, 73 | "vpn": vpns 74 | }], 75 | }) 76 | return True 77 | 78 | def add_vpnmembership_policy(self,vpnmembership_policy): 79 | if vpnmembership_policy in self.vpnMembershipGroup: 80 | return False 81 | else: 82 | self.vpnMembershipGroup.append(vpnmembership_policy) 83 | return True 84 | 85 | 86 | 87 | 88 | def to_json(self): 89 | """ 90 | Print json for REST API calls 91 | :return: 92 | """ 93 | resp = { 94 | "policyDescription": self.description, 95 | "policyType": "feature", 96 | "policyName": self.name, 97 | "isPolicyActivated": self.activated, 98 | "policyDefinition": { 99 | "assembly": [ 100 | ] 101 | } 102 | } 103 | for control in self.control: 104 | tmp = { 105 | "type": "control", 106 | "entries": [ 107 | ] 108 | } 109 | sites=[] 110 | for in_site in control["in"]: 111 | sites.append(in_site.get_id()) 112 | if sites: 113 | tmp["entries"].append({ 114 | "direction": "in", 115 | "siteLists": sites 116 | }) 117 | sites=[] 118 | for out_site in control["out"]: 119 | sites.append(out_site.get_id()) 120 | if sites: 121 | tmp["entries"].append({ 122 | "direction": "out", 123 | "siteLists": sites 124 | }) 125 | # control["policy"].get_id() 126 | tmp["definitionId"]=control["policy"].get_id() 127 | assert type(tmp["definitionId"])==str 128 | resp["policyDefinition"]["assembly"].append(tmp) 129 | # Data Policy 130 | for data in self.data: 131 | tmp = { 132 | "type": "data", 133 | "entries": [ 134 | ] 135 | } 136 | if data["service"]["site"] or data["service"]["vpn"]: 137 | tmp2={ 138 | "direction": "service", 139 | "siteLists": [ 140 | ], 141 | "vpnLists": [ 142 | ] 143 | } 144 | for site in data["service"]["site"]: 145 | tmp2["siteLists"].append(site.get_id()) 146 | for vpn in data["service"]["vpn"]: 147 | tmp2["vpnLists"].append(vpn.get_id()) 148 | tmp["entries"].append(tmp2) 149 | if data["tunnel"]["site"] or data["tunnel"]["vpn"]: 150 | tmp2={ 151 | "direction": "tunnel", 152 | "siteLists": [ 153 | ], 154 | "vpnLists": [ 155 | ] 156 | } 157 | for site in data["tunnel"]["site"]: 158 | tmp2["siteLists"].append(site.get_id()) 159 | for vpn in data["tunnel"]["vpn"]: 160 | tmp2["vpnLists"].append(vpn.get_id()) 161 | tmp["entries"].append(tmp2) 162 | if data["all"]["site"] or data["all"]["vpn"]: 163 | tmp2={ 164 | "direction": "all", 165 | "siteLists": [ 166 | ], 167 | "vpnLists": [ 168 | ] 169 | } 170 | for site in data["all"]["site"]: 171 | tmp2["siteLists"].append(site.get_id()) 172 | for vpn in data["all"]["vpn"]: 173 | tmp2["vpnLists"].append(vpn.get_id()) 174 | tmp["entries"].append(tmp2) 175 | tmp["definitionId"]=data["policy"].get_id() 176 | resp["policyDefinition"]["assembly"].append(tmp) 177 | # AppRoute 178 | for approute in self.approute: 179 | tmp = { 180 | "type": "appRoute", 181 | "entries": [] 182 | } 183 | for entry in approute["entries"]: 184 | tmp_entry = { 185 | "siteLists": [ 186 | ], 187 | "vpnLists": [ 188 | ] 189 | } 190 | for site in entry["sites"]: 191 | tmp_entry["siteLists"].append(site.get_id()) 192 | for vpn in entry["vpns"]: 193 | tmp_entry["vpnLists"].append(vpn.get_id()) 194 | tmp["entries"].append(tmp_entry) 195 | 196 | tmp["definitionId"]=approute["policy"].get_id() 197 | resp["policyDefinition"]["assembly"].append(tmp) 198 | for vpnMembershipGroup in self.vpnMembershipGroup: 199 | resp["policyDefinition"]["assembly"].append({ 200 | "definitionId": vpnMembershipGroup.get_id(), 201 | "type": "vpnMembershipGroup" 202 | }) 203 | return resp 204 | 205 | @classmethod 206 | def from_json(cls,id,json_info,topo_list,traffic_list,lists,**kwargs): 207 | """ 208 | Generate object from JSON. 209 | :return: 210 | """ 211 | vpnMembershipGroup=[] 212 | control = [] 213 | data = [] 214 | approute=[] 215 | for policy in json_info["policyDefinition"]["assembly"]: 216 | if policy["type"]=="vpnMembershipGroup": 217 | # pass 218 | result = cls.get_list_obj(policy["definitionId"],topo_list) 219 | if result: 220 | vpnMembershipGroup.append(result) 221 | else: 222 | raise Exception("VPN Membership Group Not found.") 223 | elif policy["type"]=="control": 224 | result = cls.get_list_obj(policy["definitionId"],topo_list) 225 | if result: 226 | site_out=[] 227 | site_in=[] 228 | for entry in policy["entries"]: 229 | if entry["direction"]=="out": 230 | for site in entry["siteLists"]: 231 | site_out.append(cls.get_list_obj(site, lists)) 232 | elif entry["direction"]=="in": 233 | for site in entry["siteLists"]: 234 | site_in.append(cls.get_list_obj(site, lists)) 235 | resp = { 236 | "policy":result, 237 | "out":site_out, 238 | "in":site_in 239 | } 240 | control.append(resp) 241 | else: 242 | raise Exception("Control Group Not found.") 243 | elif policy["type"]=="data": 244 | result = cls.get_list_obj(policy["definitionId"],traffic_list) 245 | if result: 246 | service={ 247 | "site":[], 248 | "vpn":[] 249 | } 250 | tunnel={ 251 | "site":[], 252 | "vpn":[] 253 | } 254 | all = { 255 | "site": [], 256 | "vpn": [] 257 | } 258 | for entry in policy["entries"]: 259 | if entry["direction"]=="service": 260 | for site in entry["siteLists"]: 261 | service["site"].append(cls.get_list_obj(site, lists)) 262 | for vpn in entry["vpnLists"]: 263 | service["vpn"].append(cls.get_list_obj(vpn, lists)) 264 | elif entry["direction"] == "tunnel": 265 | for site in entry["siteLists"]: 266 | tunnel["site"].append(cls.get_list_obj(site, lists)) 267 | for vpn in entry["vpnLists"]: 268 | tunnel["vpn"].append(cls.get_list_obj(vpn, lists)) 269 | elif entry["direction"] == "all": 270 | for site in entry["siteLists"]: 271 | all["site"].append(cls.get_list_obj(site, lists)) 272 | for vpn in entry["vpnLists"]: 273 | all["vpn"].append(cls.get_list_obj(vpn, lists)) 274 | 275 | resp = { 276 | "policy":result, 277 | "service":service, 278 | "tunnel":tunnel, 279 | "all":all 280 | } 281 | data.append(resp) 282 | else: 283 | raise Exception("Control Group Not found.") 284 | elif policy["type"]=="appRoute": 285 | result = cls.get_list_obj(policy["definitionId"],traffic_list) 286 | if result: 287 | entries = [] 288 | for entry in policy["entries"]: 289 | tmp = { 290 | "sites": [], 291 | "vpns": [] 292 | } 293 | 294 | for site in entry["siteLists"]: 295 | tmp["sites"].append(cls.get_list_obj(site, lists)) 296 | for vpn in entry["vpnLists"]: 297 | tmp["vpns"].append(cls.get_list_obj(vpn, lists)) 298 | entries.append(tmp) 299 | resp = { 300 | "policy":result, 301 | "entries":entries, 302 | } 303 | approute.append(resp) 304 | else: 305 | raise Exception("AppRoute Group Not found.") 306 | 307 | name = json_info["policyName"] 308 | description=json_info["policyDescription"] 309 | activated = json_info["isPolicyActivated"] 310 | 311 | return cls(name,description,control,data,vpnMembershipGroup,approute,id,activated,**kwargs) 312 | -------------------------------------------------------------------------------- /cisco_sdwan_policy/README.md: -------------------------------------------------------------------------------- 1 | # Python Lib for Cisco SD-WAN (Viptela) Policy control 2 | 3 | 4 | Still work in progress, more info on the way. 5 | 6 | -------------------------------------------------------------------------------- /cisco_sdwan_policy/Topology/CustomTopo.py: -------------------------------------------------------------------------------- 1 | from cisco_sdwan_policy.BaseObject import BaseObject 2 | from cisco_sdwan_policy.Helper.Sequence import Sequence 3 | 4 | 5 | class CustomTopo(BaseObject): 6 | 7 | def __init__(self,name,description,default_action,senquences,id=None,references=None,**kwargs): 8 | self.id = id 9 | self.name = name 10 | self.description = description 11 | self.references = references 12 | self.defaultAction = default_action 13 | # config["defaultAction"]["type"] 14 | self._sequence = senquences 15 | self.url = "template/policy/definition/control" 16 | super().__init__(**kwargs) 17 | self.modified=False 18 | 19 | 20 | 21 | 22 | def to_json(self): 23 | """ 24 | Print json for REST API calls 25 | :return: 26 | """ 27 | return { 28 | "name": self.name, 29 | "type": "control", 30 | "description": self.description, 31 | "defaultAction": { 32 | "type": self.defaultAction 33 | }, 34 | "sequences":[i.to_json() for i in self._sequence] 35 | 36 | } 37 | @classmethod 38 | def from_json(cls,config,lists,**kwargs): 39 | """ 40 | Generate object from JSON. 41 | :return: 42 | """ 43 | new_sequence=[] 44 | for sequence in config["sequences"]: 45 | tmp = Sequence.from_json(sequence,lists) 46 | new_sequence.append(tmp) 47 | config["sequences"] = new_sequence 48 | 49 | id = config["definitionId"] 50 | name = config["name"] 51 | description = config["description"] 52 | references = config.get("references") 53 | defaultAction = config["defaultAction"]["type"] 54 | sequence = config["sequences"] 55 | return cls(name,description,defaultAction,sequence,id,references,**kwargs) 56 | 57 | pass -------------------------------------------------------------------------------- /cisco_sdwan_policy/Topology/HubAndSpoke.py: -------------------------------------------------------------------------------- 1 | import copy 2 | 3 | from cisco_sdwan_policy.BaseObject import BaseObject 4 | from cisco_sdwan_policy.Helper.Sequence import Sequence 5 | 6 | 7 | class HubAndSpoke(BaseObject): 8 | 9 | def __init__(self,name,description,vpn,topos,id=None,references=None,**kwargs): 10 | self.id = id 11 | self.name = name 12 | self.type = "hubAndSpoke" 13 | self.description = description 14 | self.references = references 15 | self.vpn =vpn 16 | self._topos =topos 17 | # config["defaultAction"]["type"] 18 | self.url = "template/policy/definition/hubandspoke" 19 | super().__init__(**kwargs) 20 | self.modified=False 21 | 22 | @staticmethod 23 | def get_list_obj(obj_id,lists): 24 | for obj in lists: 25 | if obj.id==obj_id: 26 | return obj 27 | raise Exception("Can't find list {}".format(obj_id)) 28 | 29 | def add_topology(self,name,spokes,hubs,advertise_tloc=False,equal_preference=True,tloc= None): 30 | spoke_list= [] 31 | for spoke in spokes: 32 | spoke_list.append( { 33 | "siteList": spoke, 34 | "hubs": [ 35 | { 36 | "siteList": hubs, 37 | "prefixLists": [], 38 | "ipv6PrefixLists": [] 39 | } 40 | ] 41 | }) 42 | topo = { 43 | "name": name, 44 | "equalPreference": equal_preference, 45 | "advertiseTloc": advertise_tloc, 46 | "spokes": spoke_list, 47 | } 48 | if advertise_tloc and tloc: 49 | topo["tlocList"]=tloc 50 | 51 | self._topos.append(topo) 52 | self.modified=True 53 | 54 | def add_prefix(self,name,spoke,hub,prefix_list,ipv6=False): 55 | for topo in self._topos: 56 | if topo["name"]==name: 57 | for spokes in topo["spokes"]: 58 | if spokes["siteList"]==spoke: 59 | for hubs in spokes["hubs"]: 60 | if hubs["siteList"]==hub: 61 | if ipv6: 62 | if prefix_list not in hubs["ipv6PrefixLists"]: 63 | hubs["ipv6PrefixLists"].append(prefix_list) 64 | self.modified=True 65 | else: 66 | if prefix_list not in hubs["prefixLists"]: 67 | hubs["prefixLists"].append(prefix_list) 68 | self.modified=True 69 | 70 | 71 | def to_json(self): 72 | """ 73 | Print json for REST API calls 74 | :return: 75 | """ 76 | subdefs = [] 77 | 78 | for topo in self._topos: 79 | tmp = copy.copy(topo) 80 | if tmp.get("tlocList"): 81 | tmp["tlocList"]=tmp["tlocList"].get_id() 82 | for spoke in tmp["spokes"]: 83 | spoke["siteList"]=spoke["siteList"].get_id() 84 | for hub in spoke["hubs"]: 85 | hub["siteList"]=hub["siteList"].get_id() 86 | for i in range(0,len(hub["prefixLists"])): 87 | hub["prefixLists"][i] = hub["prefixLists"][i].get_id() 88 | for i in range(0,len(hub["ipv6PrefixLists"])): 89 | hub["ipv6PrefixLists"][i] = hub["ipv6PrefixLists"][i].get_id() 90 | subdefs.append(tmp) 91 | 92 | return { 93 | "name": self.name, 94 | "type": self.type, 95 | "description": self.description, 96 | "definition":{ 97 | "vpnList": self.vpn.get_id(), 98 | "subDefinitions": subdefs 99 | } 100 | } 101 | @classmethod 102 | def from_json(cls,config,lists,**kwargs): 103 | """ 104 | Generate object from JSON. 105 | :return: 106 | """ 107 | 108 | id = config["definitionId"] 109 | name = config["name"] 110 | description = config["description"] 111 | references = config.get("references") 112 | vpn = cls.get_list_obj(config["definition"]["vpnList"],lists) 113 | topos = [] 114 | for topo in config["definition"]["subDefinitions"]: 115 | for spoke in topo["spokes"]: 116 | spoke["siteList"]= cls.get_list_obj(spoke["siteList"],lists) 117 | for hub in spoke["hubs"]: 118 | hub["siteList"] = cls.get_list_obj(hub["siteList"], lists) 119 | for i in range(0,len(hub["prefixLists"])): 120 | hub["prefixLists"][i] = cls.get_list_obj(hub["prefixLists"][i], lists) 121 | if hub.get("ipv6PrefixLists"): 122 | for i in range(0,len(hub["ipv6PrefixLists"])): 123 | hub["ipv6PrefixLists"][i] = cls.get_list_obj(hub["ipv6PrefixLists"][i], lists) 124 | else: 125 | hub["ipv6PrefixLists"]=[] 126 | if topo.get("tlocList"): 127 | topo["tlocList"]=cls.get_list_obj(topo["tlocList"],lists) 128 | topos.append(topo) 129 | return cls(name,description,vpn,topos,id,references,**kwargs) -------------------------------------------------------------------------------- /cisco_sdwan_policy/Topology/Mesh.py: -------------------------------------------------------------------------------- 1 | import copy 2 | 3 | from cisco_sdwan_policy.BaseObject import BaseObject 4 | from cisco_sdwan_policy.Helper.Sequence import Sequence 5 | 6 | 7 | class Mesh(BaseObject): 8 | 9 | def __init__(self,name,description,vpn,topos,id=None,references=None,**kwargs): 10 | self.id = id 11 | self.name = name 12 | self.type = "mesh" 13 | self.description = description 14 | self.references = references 15 | self.vpn =vpn 16 | self._topos =topos 17 | # config["defaultAction"]["type"] 18 | self.url = "template/policy/definition/mesh" 19 | super().__init__(**kwargs) 20 | self.modified=False 21 | 22 | @staticmethod 23 | def get_list_obj(obj_id,lists): 24 | for obj in lists: 25 | if obj.id==obj_id: 26 | return obj 27 | raise Exception("Can't find list {}".format(obj_id)) 28 | 29 | 30 | def to_json(self): 31 | """ 32 | Print json for REST API calls 33 | :return: 34 | """ 35 | topos = [] 36 | 37 | for topo in self._topos: 38 | tmp = { 39 | "name":topo["name"], 40 | "siteLists":[i.get_id() for i in topo["sites"]] 41 | } 42 | topos.append(tmp) 43 | 44 | return { 45 | "name": self.name, 46 | "type": "mesh", 47 | "description": self.description, 48 | "definition": { 49 | "vpnList": self.vpn.get_id(), 50 | "regions": topos 51 | } 52 | } 53 | 54 | @classmethod 55 | def from_json(cls,config,lists,**kwargs): 56 | """ 57 | Generate object from JSON. 58 | :return: 59 | """ 60 | 61 | id = config["definitionId"] 62 | name = config["name"] 63 | description = config["description"] 64 | references = config.get("references") 65 | vpn = cls.get_list_obj(config["definition"]["vpnList"],lists) 66 | topos = [] 67 | for topo in config["definition"]["regions"]: 68 | tmp={ 69 | "name":topo["name"], 70 | "sites":[cls.get_list_obj(i,lists) for i in topo["siteLists"]] 71 | } 72 | topos.append(tmp) 73 | return cls(name,description,vpn,topos,id,references,**kwargs) 74 | 75 | def add_topology(self,name,site_list): 76 | assert type(site_list)==list 77 | for topo in self._topos: 78 | if topo.name==name: 79 | raise Exception("Duplicate Name on Mesh Topo found.") 80 | self._topos.append({ 81 | "name":name, 82 | "sites":site_list 83 | }) 84 | self.modified=True -------------------------------------------------------------------------------- /cisco_sdwan_policy/Topology/VpnMembership.py: -------------------------------------------------------------------------------- 1 | from cisco_sdwan_policy.BaseObject import BaseObject 2 | from cisco_sdwan_policy.Helper.Sequence import Sequence 3 | 4 | 5 | class VpnMembership(BaseObject): 6 | 7 | def __init__(self,name,description,entries,id=None,references=None,**kwargs): 8 | self.id = id 9 | self.description = description 10 | self.name = name 11 | self.references = references 12 | self._sites = entries 13 | self.url = "template/policy/definition/vpnmembershipgroup" 14 | super().__init__(**kwargs) 15 | self.modified=False 16 | 17 | 18 | 19 | @staticmethod 20 | def get_list_obj(obj_id,lists): 21 | for obj in lists: 22 | if obj.id==obj_id: 23 | return obj 24 | raise Exception("Can't find list {}".format(obj_id)) 25 | 26 | def to_json(self): 27 | """ 28 | Print json for REST API calls 29 | :return: 30 | """ 31 | sites_json = [] 32 | for site in self._sites: 33 | tmp_site={} 34 | if type(site["siteList"])==list: 35 | tmp = [] 36 | for s in site["siteList"]: 37 | tmp.append(s.get_id()) 38 | if len(tmp)==1: 39 | tmp_site["siteList"]=tmp[0] 40 | else: 41 | tmp_site["siteList"]=tmp 42 | else: 43 | tmp_site["siteList"]=site["siteList"].get_id() 44 | if type(site["vpnList"])==list: 45 | tmp = [] 46 | for s in site["vpnList"]: 47 | tmp.append(s.get_id()) 48 | tmp_site["vpnList"]=tmp 49 | else: 50 | tmp_site["vpnList"]=site["vpnList"].get_id() 51 | sites_json.append(tmp_site) 52 | 53 | return { 54 | "name": self.name, 55 | "type": "vpnMembershipGroup", 56 | "description": self.description, 57 | "definition": { 58 | "sites": sites_json 59 | } 60 | } 61 | 62 | @classmethod 63 | def from_json(cls,json_info,lists,**kwargs): 64 | """ 65 | Generate object from JSON. 66 | Actually SiteList is not a list... 67 | :return: 68 | """ 69 | 70 | for site in json_info["definition"]["sites"]: 71 | if type(site["siteList"])==list: 72 | tmp=[] 73 | for s in site["siteList"]: 74 | tmp.append(cls.get_list_obj(s,lists)) 75 | site["siteList"]=tmp 76 | elif type(site["siteList"])==str: 77 | site["siteList"]=[cls.get_list_obj(site["siteList"],lists)] 78 | else: 79 | raise Exception("Unexpected Type") 80 | if type(site["vpnList"])==list: 81 | tmp=[] 82 | for s in site["vpnList"]: 83 | tmp.append(cls.get_list_obj(s,lists)) 84 | site["vpnList"]=tmp 85 | elif type(site["vpnList"])==str: 86 | site["vpnList"]=cls.get_list_obj(site["vpnList"],lists) 87 | else: 88 | raise Exception("Unexpected Type") 89 | json_info["sites"]=json_info["definition"]["sites"] 90 | 91 | id = json_info["definitionId"] 92 | description = json_info["description"] 93 | name = json_info["name"] 94 | references = json_info.get("references") 95 | sites = json_info["sites"] 96 | 97 | return cls(name,description,sites,id,references,**kwargs) 98 | 99 | -------------------------------------------------------------------------------- /cisco_sdwan_policy/Topology/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljm625/cisco_sdwan_policy_python/2981a05103f27cda3399d9cecc134fb80ae2f2e1/cisco_sdwan_policy/Topology/__init__.py -------------------------------------------------------------------------------- /cisco_sdwan_policy/TrafficPolicy/AppRoute.py: -------------------------------------------------------------------------------- 1 | from cisco_sdwan_policy.BaseObject import BaseObject 2 | from cisco_sdwan_policy.Helper.Sequence import Sequence 3 | 4 | 5 | class AppRoute(BaseObject): 6 | 7 | def __init__(self,name,description,sequences,default_action=None,id=None,references=None,**kwargs): 8 | self.id = id 9 | self.name = name 10 | self.references = references 11 | self.description = description 12 | # if default_action: 13 | self.default_action=default_action 14 | # if type(default_action)==str: 15 | # self.defaultAction 16 | # self.defaultAction = config["defaultAction"]["type"] 17 | # else: 18 | # self.defaultAction = config["defaultAction"] 19 | # else: 20 | # self.defaultAction = None 21 | self._sequence = sequences 22 | 23 | self.url = "template/policy/definition/approute" 24 | super().__init__(**kwargs) 25 | self.modified=False 26 | 27 | 28 | 29 | @staticmethod 30 | def get_list_obj(obj_id,lists): 31 | for obj in lists: 32 | if obj.id==obj_id: 33 | return obj 34 | raise Exception("Can't find list {}".format(obj_id)) 35 | 36 | 37 | def to_json(self): 38 | """ 39 | Print json for REST API calls 40 | :return: 41 | """ 42 | resp = { 43 | "name": self.name, 44 | "type": "appRoute", 45 | "description": self.description, 46 | "sequences": [i.to_json() for i in self._sequence] 47 | } 48 | if type(self.default_action)==str: 49 | default_action={ 50 | "type":self.default_action 51 | } 52 | elif not self.default_action: 53 | default_action=None 54 | else: 55 | default_action = { 56 | "type":self.default_action.type, 57 | "ref":self.default_action.get_id() 58 | } 59 | if default_action: 60 | resp["defaultAction"]=default_action 61 | 62 | return resp 63 | 64 | @classmethod 65 | def from_json(cls,json_info,lists,**kwargs): 66 | """ 67 | Generate object from JSON. 68 | :return: 69 | """ 70 | new_sequence=[] 71 | for sequence in json_info["sequences"]: 72 | tmp = Sequence.from_json(sequence,lists) 73 | new_sequence.append(tmp) 74 | json_info["sequences"] = new_sequence 75 | if json_info.get("defaultAction") and json_info["defaultAction"].get("ref"): 76 | json_info["defaultAction"] = cls.get_list_obj(json_info["defaultAction"].get("ref"),lists) 77 | 78 | id = json_info["definitionId"] 79 | name = json_info["name"] 80 | references = json_info.get("references") 81 | description = json_info["description"] 82 | if json_info.get("defaultAction"): 83 | if type(json_info["defaultAction"])==dict: 84 | defaultAction = json_info["defaultAction"]["type"] 85 | else: 86 | defaultAction = json_info["defaultAction"] 87 | else: 88 | defaultAction = None 89 | sequences = json_info["sequences"] 90 | return cls(name,description,sequences,defaultAction,id,references,**kwargs) 91 | -------------------------------------------------------------------------------- /cisco_sdwan_policy/TrafficPolicy/DataPolicy.py: -------------------------------------------------------------------------------- 1 | from cisco_sdwan_policy.BaseObject import BaseObject 2 | from cisco_sdwan_policy.Helper.Sequence import Sequence 3 | 4 | 5 | class DataPolicy(BaseObject): 6 | 7 | def __init__(self,name,description,sequences,default_action=None,id=None,references=None,**kwargs): 8 | self.id = id 9 | self.description= description 10 | self.name = name 11 | self.references = references 12 | # if config.get("defaultAction"): 13 | # if type(config["defaultAction"])==dict: 14 | # self.defaultAction = config["defaultAction"]["type"] 15 | # else: 16 | # self.defaultAction = config["defaultAction"] 17 | # else: 18 | self.default_action = default_action 19 | self._sequence = sequences 20 | self.url = "template/policy/definition/data" 21 | super().__init__(**kwargs) 22 | self.modified=False 23 | 24 | 25 | 26 | 27 | def to_json(self): 28 | """ 29 | Print json for REST API calls 30 | :return: 31 | """ 32 | resp = { 33 | "name": self.name, 34 | "type": "data", 35 | "description": self.description, 36 | "sequences": [i.to_json() for i in self._sequence] 37 | } 38 | 39 | 40 | if type(self.default_action)==str: 41 | default_action={ 42 | "type":self.default_action 43 | } 44 | elif not self.default_action: 45 | default_action=None 46 | else: 47 | default_action = { 48 | "type":self.default_action.type, 49 | "ref":self.default_action.get_id() 50 | } 51 | if default_action: 52 | resp["defaultAction"]=default_action 53 | return resp 54 | 55 | @classmethod 56 | def from_json(cls,json_info,lists,**kwargs): 57 | """ 58 | Generate object from JSON. 59 | :return: 60 | """ 61 | new_sequence=[] 62 | for sequence in json_info["sequences"]: 63 | tmp = Sequence.from_json(sequence,lists) 64 | new_sequence.append(tmp) 65 | json_info["sequences"] = new_sequence 66 | 67 | id = json_info["definitionId"] 68 | description=json_info["description"] 69 | name = json_info["name"] 70 | references = json_info.get("references") 71 | if json_info.get("defaultAction"): 72 | if type(json_info["defaultAction"])==dict: 73 | defaultAction = json_info["defaultAction"]["type"] 74 | else: 75 | defaultAction = json_info["defaultAction"] 76 | else: 77 | defaultAction = None 78 | sequence = json_info["sequences"] 79 | 80 | 81 | return cls(name,description,sequence,defaultAction,id,references,**kwargs) -------------------------------------------------------------------------------- /cisco_sdwan_policy/TrafficPolicy/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljm625/cisco_sdwan_policy_python/2981a05103f27cda3399d9cecc134fb80ae2f2e1/cisco_sdwan_policy/TrafficPolicy/__init__.py -------------------------------------------------------------------------------- /cisco_sdwan_policy/ViptelaRest.py: -------------------------------------------------------------------------------- 1 | import json 2 | import base64 3 | import requests 4 | import sys 5 | 6 | 7 | class ViptelaRest(object): 8 | instance=None 9 | def __init__(self, vmanage_ip, username, password,port = 443,tenant=None): 10 | self.vmanage_ip = vmanage_ip 11 | self.port = port 12 | self.session = {} 13 | self.token=None 14 | self.tenant=None 15 | self.login(self.vmanage_ip, port, username, password) 16 | self.get_version() 17 | if tenant: 18 | try: 19 | base64.b64decode(tenant) 20 | self.tenant=tenant 21 | except: 22 | result = self.set_tenant(tenant) 23 | if not result: 24 | raise Exception("ERROR : Given Tenant {} not found!".format(tenant)) 25 | 26 | 27 | @classmethod 28 | def init(cls,server_info=None): 29 | if server_info: 30 | cls.instance = cls(server_info["hostname"],server_info["username"],server_info["password"],server_info["port"],server_info.get("tenant")) 31 | return cls.instance 32 | elif cls.instance: 33 | return cls.instance 34 | else: 35 | raise Exception("Viptela server not initialized.") 36 | 37 | def get_header(self): 38 | header = {'Content-Type': 'application/json'} 39 | if self.token: 40 | header["X-XSRF-TOKEN"]=self.token 41 | if self.tenant: 42 | header["VSessionId"]=self.tenant 43 | return header 44 | 45 | 46 | 47 | def login(self, vmanage_ip, port, username, password): 48 | """Login to vmanage""" 49 | base_url_str = 'https://%s:%s' % (vmanage_ip,port) 50 | 51 | login_action = '/j_security_check' 52 | 53 | # Format data for loginForm 54 | login_data = {'j_username': username, 'j_password': password} 55 | 56 | # Url for posting login data 57 | login_url = base_url_str + login_action 58 | 59 | url = base_url_str + login_url 60 | 61 | sess = requests.session() 62 | # If the vmanage has a certificate signed by a trusted authority change verify to True 63 | login_response = sess.post(url=login_url, data=login_data, verify=False,headers={"Content-Type":"application/x-www-form-urlencoded"}) 64 | # if self.tenant: 65 | # sess.headers.update({'vsession_id':self.tenant}) 66 | 67 | if login_response.status_code>=300: 68 | # print( 69 | # "Login Failed") 70 | raise BaseException("ERROR : The username/password is not correct.") 71 | if '' in login_response.text: 72 | raise BaseException("ERROR : Login Failed.") 73 | 74 | 75 | self.session = sess 76 | # Get xsrf_token for 19.2 and later versions 77 | response = self.session.get(base_url_str+ "/dataservice/client/token", verify=False) 78 | if response.status_code==200: 79 | self.token = response.text 80 | 81 | def set_tenant(self,tenant_name): 82 | resp = self.get_request("tenant") 83 | data = resp.json() 84 | tenant_id =None 85 | if data.get("data") and len(data.get("data"))>0: 86 | for tenant in data["data"]: 87 | if str(tenant["name"])==str(tenant_name): 88 | tenant_id = tenant["tenantId"] 89 | elif str(tenant["tenantId"])==str(tenant_name): 90 | tenant_id = tenant["tenantId"] 91 | if tenant_id: 92 | resp = self.post_request("tenant/{}/vsessionid".format(tenant_id),{}) 93 | data = resp.json() 94 | if data.get("VSessionId"): 95 | self.tenant=data["VSessionId"] 96 | return True 97 | 98 | 99 | def get_request(self, mount_point): 100 | """GET request""" 101 | url = "https://%s:%s/dataservice/%s" % (self.vmanage_ip, self.port, mount_point) 102 | header = self.get_header() 103 | response = self.session.get(url, verify=False,headers=header) 104 | if response.status_code>=300: 105 | response.raise_for_status() 106 | elif response.status_code==200: 107 | return response 108 | else: 109 | return None 110 | 111 | def post_request(self, mount_point, payload): 112 | """POST request""" 113 | url = "https://%s:%s/dataservice/%s" % (self.vmanage_ip,self.port, mount_point) 114 | headers = self.get_header() 115 | payload = json.dumps(payload) 116 | response = self.session.post(url=url, data=payload, headers=headers, verify=False) 117 | return response 118 | 119 | def put_request(self, mount_point, payload=None): 120 | """ 121 | PUT Method 122 | :param mount_point: The url for API 123 | :param payload: The payload for API 124 | :param headers: The header 125 | :return: response 126 | """ 127 | url= "https://{}:{}/dataservice/{}".format(self.vmanage_ip,self.port,mount_point) 128 | headers = self.get_header() 129 | if payload: 130 | payload=json.dumps(payload) 131 | response = self.session.put(url=url,data=payload,headers=headers,verify=False) 132 | else: 133 | response=self.session.put(url=url,headers=headers,verify=False) 134 | return response 135 | 136 | def get_version(self): 137 | resp = self.get_request("device/action/install/devices/vmanage?groupId=all") 138 | self.version = resp.json()["data"][0]["version"] 139 | return resp.json()["data"][0]["version"] -------------------------------------------------------------------------------- /cisco_sdwan_policy/__init__.py: -------------------------------------------------------------------------------- 1 | from cisco_sdwan_policy.ViptelaRest import ViptelaRest 2 | from cisco_sdwan_policy.Helper.PolicyLoader import PolicyLoader 3 | from cisco_sdwan_policy.Helper.Sequence import Sequence 4 | from cisco_sdwan_policy.List.Application import Application 5 | from cisco_sdwan_policy.List.Color import Color 6 | from cisco_sdwan_policy.List.DataPrefix import DataPrefix 7 | from cisco_sdwan_policy.List.Policer import Policer 8 | from cisco_sdwan_policy.List.Prefix import Prefix 9 | from cisco_sdwan_policy.List.Site import Site 10 | from cisco_sdwan_policy.List.SlaClass import SlaClass 11 | from cisco_sdwan_policy.List.Tloc import Tloc 12 | from cisco_sdwan_policy.List.Vpn import Vpn 13 | 14 | from cisco_sdwan_policy.Topology.HubAndSpoke import HubAndSpoke 15 | from cisco_sdwan_policy.Topology.Mesh import Mesh 16 | from cisco_sdwan_policy.Topology.CustomTopo import CustomTopo 17 | from cisco_sdwan_policy.Topology.VpnMembership import VpnMembership 18 | from cisco_sdwan_policy.TrafficPolicy.DataPolicy import DataPolicy 19 | from cisco_sdwan_policy.TrafficPolicy.AppRoute import AppRoute 20 | 21 | from cisco_sdwan_policy.MainPolicy import MainPolicy 22 | -------------------------------------------------------------------------------- /cisco_sdwan_policy/demo.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from cisco_sdwan_policy.Helper.PolicyLoader import PolicyLoader 4 | from cisco_sdwan_policy.Helper.Sequence import Sequence 5 | from cisco_sdwan_policy.List.Application import Application 6 | from cisco_sdwan_policy.List.Color import Color 7 | from cisco_sdwan_policy.List.DataPrefix import DataPrefix 8 | from cisco_sdwan_policy.List.Prefix import Prefix 9 | from cisco_sdwan_policy.List.Policer import Policer 10 | from cisco_sdwan_policy.List.Site import Site 11 | from cisco_sdwan_policy.List.Tloc import Tloc 12 | from cisco_sdwan_policy.List.Vpn import Vpn 13 | from cisco_sdwan_policy.MainPolicy import MainPolicy 14 | from cisco_sdwan_policy.Topology.CustomTopo import CustomTopo 15 | from cisco_sdwan_policy.Topology.VpnMembership import VpnMembership 16 | from cisco_sdwan_policy.TrafficPolicy.AppRoute import AppRoute 17 | from cisco_sdwan_policy.TrafficPolicy.DataPolicy import DataPolicy 18 | from cisco_sdwan_policy.ViptelaRest import ViptelaRest 19 | policy_list = [] 20 | topo_list = [] 21 | traffic_policy_list=[] 22 | main_policy_list=[] 23 | 24 | 25 | 26 | 27 | 28 | 29 | if __name__ == '__main__': 30 | server_info = { 31 | "hostname":"198.18.1.10", 32 | "port":8443, 33 | "username":"admin", 34 | "password":"admin" 35 | } 36 | # 加载系统内所有的现有Policy(如果不编辑已经存在的Policy可以忽略) 37 | pl = PolicyLoader.init(server_info) 38 | pl.load() 39 | 40 | 41 | # 开始创建Policy 42 | prefix_list1=[ 43 | "10.0.0.0/24" 44 | ] 45 | prefix_list2=[ 46 | "192.168.1.0/24" 47 | ] 48 | 49 | # 创建Prefix列表 50 | 51 | data_prefix_source = DataPrefix(name="Prefix_source2",prefix_list=prefix_list1,is_ipv6=False) 52 | data_prefix_dest = DataPrefix(name="Prefix_dest2",prefix_list=prefix_list2,is_ipv6=False) 53 | 54 | # 创建Policer限速规则 55 | pc = Policer("SpeedLimit1",rate="150000",exceed="drop",burst="15000") 56 | 57 | # 创建Site列表 58 | 59 | site = Site("TestSite2",["100","1000-2000"]) 60 | 61 | # 创建VPN列表 62 | 63 | vpn = Vpn("TestVPN2",["10"]) 64 | 65 | 66 | sq = Sequence(1,"Custom","data","accept","ipv4",match=[],actions=[]) 67 | # 创建Match规则 68 | sq.add_match("sourceDataPrefixList",data_prefix_source) 69 | sq.add_match("destinationDataPrefixList",data_prefix_dest) 70 | # 创建Action规则 71 | sq.add_action("set","policer",pc) 72 | sq.add_action("nat","useVpn","0") 73 | sq.add_action("nat","fallback","") 74 | print(sq.to_json()) 75 | 76 | # 创建Data Policy 77 | dp = DataPolicy("NAT_Data_policy2","NAT",[sq],default_action="accept") 78 | 79 | # 创建Main Policy 80 | 81 | main_policy = MainPolicy(name="API_Policy",description="API",control_policy_list=[],data_policy_list=[],vpn_membership_list=[],approute_policy_list=[]) 82 | main_policy.add_data_policy(dp,"service",[site],[vpn]) 83 | # 打印Policy 84 | print(main_policy.to_json()) 85 | # 保存Policy 86 | main_policy.save() 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /cisco_sdwan_policy/requirements.txt: -------------------------------------------------------------------------------- 1 | requests -------------------------------------------------------------------------------- /domain_to_prefix.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | from pprint import pprint 3 | 4 | try: 5 | import yaml 6 | import sublist3r 7 | import dns.resolver 8 | except Exception as e: 9 | print("Error loading libraries, please run following commands first:") 10 | print("pip install pyyaml dnspython") 11 | print("git clone https://github.com/aboul3la/Sublist3r") 12 | print("cd Sublist3r") 13 | print("python setup.py install") 14 | exit(1) 15 | from cisco_sdwan_policy.List.DataPrefix import DataPrefix 16 | 17 | from cisco_sdwan_policy.List.Prefix import Prefix 18 | 19 | from cisco_sdwan_policy import PolicyLoader 20 | 21 | 22 | def config_reader(config_file): 23 | ''' 24 | Read config from yaml file 25 | :return: config in dict format 26 | ''' 27 | with open(config_file) as file: 28 | config = yaml.load(file.read(),Loader=yaml.FullLoader) 29 | # print(result) 30 | return config 31 | 32 | def parse_domain(domain,nameserver): 33 | domain_list=[] 34 | ip_list=set() 35 | if "*" in domain and domain[0:2]!="*.": 36 | raise Exception("Invalid domain: {}".format(domain)) 37 | elif "*" in domain: 38 | sub_domains = sublist3r.main(domain[2:], 40, None, ports=None, silent=False, verbose=False, 39 | enable_bruteforce=False, engines=None) 40 | 41 | print(sub_domains) 42 | domain_list.extend(sub_domains) 43 | else: 44 | domain_list.append(domain) 45 | # Use DNSPYTHON to get info. 46 | resolver = dns.resolver.Resolver() 47 | resolver.lifetime = resolver.timeout = 20.0 48 | for domain_name in domain_list: 49 | print("Resolving: {}".format(domain_name)) 50 | try: 51 | resolver.nameservers=[nameserver] 52 | response =resolver.query(domain_name) 53 | for answer in response.response.answer: 54 | for ip in answer.items: 55 | if ip.rdtype == 1: 56 | ip_list.add(ip.address+"/32") 57 | except: 58 | pass 59 | # try: 60 | # response = dns.resolver.query(domain_name, "CNAME") 61 | # for answer in response.response.answer: 62 | # for ip in answer.items: 63 | # if ip.rdtype == 1: 64 | # ip_list.add(ip.address+"/32") 65 | # except: 66 | # pass 67 | 68 | return ip_list 69 | 70 | 71 | 72 | if __name__ == '__main__': 73 | # First read all the configurations from config file. 74 | parser = argparse.ArgumentParser(description='App List Genenrator.') 75 | parser.add_argument('config', metavar='config_file_path', type=str, 76 | help='config yaml path') 77 | args = parser.parse_args() 78 | config_file=args.config 79 | try: 80 | config = config_reader(config_file) 81 | print("Config file {} loaded".format(args.config)) 82 | app_ip_info ={} 83 | assert type(config["sdwan_server"])==dict 84 | assert type(config["apps"])==dict 85 | assert type(config["dns_server"])==str 86 | except Exception as e: 87 | print("ERROR : Invalid config file.") 88 | print(e) 89 | exit(1) 90 | 91 | for appname,domain_list in config["apps"].items(): 92 | app_ips=set() 93 | for domain in domain_list: 94 | ip_list = parse_domain(domain,config["dns_server"]) 95 | app_ips = app_ips | ip_list 96 | app_ip_info[appname]=list(app_ips) 97 | pprint(app_ip_info) 98 | if config["policy_create"]: 99 | print("Start creating Prefix Lists") 100 | pl = PolicyLoader.init(config["sdwan_server"]) 101 | pl.load() 102 | existing_list=[i.name for i in pl.list_policies] 103 | for appname,ip_list in app_ip_info.items(): 104 | if "{}_prefix".format(appname) not in existing_list: 105 | Prefix("{}_prefix".format(appname),prefix_list=ip_list).save() 106 | print("Created Prefix List: {}_prefix".format(appname)) 107 | else: 108 | for i in pl.list_policies: 109 | if i.name=="{}_prefix".format(appname): 110 | i.set_entries(ip_list) 111 | i.save() 112 | print("Updated Prefix List: {}".format(i.name)) 113 | if "{}_dataprefix".format(appname) not in existing_list: 114 | DataPrefix("{}_dataprefix".format(appname),prefix_list=ip_list).save() 115 | print("Created Data Prefix List: {}_dataprefix".format(appname)) 116 | 117 | else: 118 | for i in pl.list_policies: 119 | if i.name=="{}_dataprefix".format(appname): 120 | i.set_entries(ip_list) 121 | i.save() 122 | print("Updated Data Prefix List: {}".format(i.name)) 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /domain_to_prefix.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apps: 3 | Weixin: 4 | - "*.weixin.com" 5 | - "weixin.qq.com" 6 | dns_server: 223.5.5.5 7 | 8 | policy_create: true 9 | sdwan_server: 10 | hostname: 198.18.1.10 11 | port: 8443 12 | username: admin 13 | password: admin 14 | -------------------------------------------------------------------------------- /example1.py: -------------------------------------------------------------------------------- 1 | from cisco_sdwan_policy.Helper.PolicyLoader import PolicyLoader 2 | 3 | if __name__ == '__main__': 4 | 5 | # Example 1: Save policy to local disk & restore it. 6 | 7 | # Input server Info here 8 | server_info = { 9 | "hostname": "192.168.100.32", 10 | "port": 443, 11 | "username": "admin", 12 | "password": "admin", 13 | } 14 | # Load all policy in vManage 15 | pl = PolicyLoader.init(server_info) 16 | pl.load() 17 | 18 | # Show all the loaded Policy.(Optional) 19 | print([i.name for i in pl.main_policies]) 20 | print([i.name for i in pl.topo_policies]) 21 | print([i.name for i in pl.traffic_policies]) 22 | print([i.name for i in pl.list_policies]) 23 | 24 | # Generate backup json 25 | backup = pl.save_to_json() 26 | 27 | # Save to file 28 | with open("policy_dump.json","w+") as file: 29 | file.write(backup) 30 | 31 | 32 | 33 | # Read from file 34 | with open("policy_dump.json","r") as file: 35 | info = file.read() 36 | # Load to vMange and auto merge if needed. 37 | pl.load_from_json(info) 38 | 39 | -------------------------------------------------------------------------------- /example2.py: -------------------------------------------------------------------------------- 1 | from cisco_sdwan_policy.Helper.PolicyLoader import PolicyLoader 2 | 3 | if __name__ == '__main__': 4 | 5 | # Example 2: Transfer a specific policy from tenant1 to tenant2. 6 | 7 | # Server Tenant1 Info 8 | server_info = { 9 | "hostname": "192.168.100.32", 10 | "port": 443, 11 | "username": "admin", 12 | "password": "admin", 13 | "tenant": "T1" 14 | } 15 | # Load all policy in vManage 16 | pl = PolicyLoader.init(server_info) 17 | pl.load() 18 | 19 | # Show all the loaded Policy.(Optional) 20 | print([i.name for i in pl.main_policies]) 21 | print([i.name for i in pl.topo_policies]) 22 | print([i.name for i in pl.traffic_policies]) 23 | print([i.name for i in pl.list_policies]) 24 | 25 | # Backup all the policies 26 | old_main = pl.main_policies 27 | old_topo = pl.topo_policies 28 | old_traffic = pl.traffic_policies 29 | old_list =pl.list_policies 30 | 31 | # Find the policy to move 32 | policy_to_move=None 33 | for i in pl.list_policies: 34 | if i.name=="test": 35 | policy_to_move=i 36 | 37 | # Input the Tenant2 info 38 | new_server_info = { 39 | "hostname": "192.168.100.32", 40 | "port": 443, 41 | "username": "admin", 42 | "password": "admin", 43 | "tenant": "T2" 44 | } 45 | 46 | # Load the Tenant2 Policies 47 | pl = PolicyLoader.init(new_server_info) 48 | pl.load() 49 | 50 | 51 | 52 | if policy_to_move: 53 | # In order to restore all policy referenced list/topos, we need to check the existing policy from new tenant and then make use of auto save feature. 54 | for i in old_list: 55 | if i.name not in [j.name for j in pl.list_policies]: 56 | pass 57 | else: 58 | i.name = i.name + "_1" 59 | i.id = None 60 | for i in old_topo: 61 | if i.name not in [j.name for j in pl.topo_policies]: 62 | pass 63 | else: 64 | i.name = i.name + "_1" 65 | i.id = None 66 | for i in old_traffic: 67 | if i.name not in [j.name for j in pl.traffic_policies]: 68 | pass 69 | else: 70 | i.name = i.name + "_1" 71 | i.id = None 72 | if policy_to_move.name in [j.name for j in pl.main_policies]: 73 | policy_to_move.name = policy_to_move.name+"_1" 74 | 75 | # Save the new policy, all the dependencies will automatically created. 76 | policy_to_move.save() 77 | 78 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | # Inside of setup.cfg 2 | [metadata] 3 | description-file = README.md 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | setup( 3 | name = 'cisco-sdwan-policy', # How you named your package folder (MyLib) 4 | packages = ['cisco_sdwan_policy','cisco_sdwan_policy.Helper','cisco_sdwan_policy.List','cisco_sdwan_policy.LocalPolicy','cisco_sdwan_policy.Topology','cisco_sdwan_policy.TrafficPolicy'], # Chose the same as "name" 5 | version = '0.41', # Start with a small number and increase it with every change you make 6 | license='MIT', # Chose a license from here: https://help.github.com/articles/licensing-a-repository 7 | description = 'A module for easy add/modify policies to Cisco SD-WAN(Viptela)', # Give a short description about your library 8 | author = 'Jiaming Li', # Type in your name 9 | author_email = 'jiaminli@cisco.com', # Type in your E-Mail 10 | url = 'https://github.com/ljm625/cisco_sdwan_policy_python', # Provide either the link to your github or to your website 11 | download_url = 'https://github.com/ljm625/cisco_sdwan_policy_python/archive/v0.35.tar.gz', # I explain this later on 12 | keywords = ['SDWAN', 'CiSCO', 'REST'], # Keywords that define your package best 13 | install_requires=[ # I get to this in a second 14 | 'requests', 15 | ], 16 | scripts=['bin/sdwan-policy-tool','bin/sdwan-template-tool','bin/sdwan-apps-generator'], 17 | classifiers=[ 18 | 'Development Status :: 4 - Beta', # Chose either "3 - Alpha", "4 - Beta" or "5 - Production/Stable" as the current state of your package 19 | 'Intended Audience :: Developers', # Define that your audience are developers 20 | 'Topic :: Software Development :: Libraries', 21 | 'License :: OSI Approved :: MIT License', # Again, pick a license 22 | 'Programming Language :: Python :: 3', #Specify which pyhton versions that you want to support 23 | 'Programming Language :: Python :: 3.4', 24 | 'Programming Language :: Python :: 3.5', 25 | 'Programming Language :: Python :: 3.6', 26 | 'Programming Language :: Python :: 3.7', 27 | 'Programming Language :: Python :: 3.8', 28 | 'Programming Language :: Python :: 3.9', 29 | ], 30 | ) 31 | -------------------------------------------------------------------------------- /tools_template_backup.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljm625/cisco_sdwan_policy_python/2981a05103f27cda3399d9cecc134fb80ae2f2e1/tools_template_backup.py --------------------------------------------------------------------------------