├── LICENSE ├── Python-for-network-engineers.pdf ├── Python-training.pdf ├── README.md ├── configuration_builder ├── configuration_builder.py ├── configuration_builder_2.py ├── template.j2 ├── template_build.j2 ├── variables.yml └── variables_build.yml ├── configuration_management ├── conf_int_with_vlan.py ├── confjunos.conf ├── enable_lldp.py ├── enable_lldp_all_devices.py ├── interfaces.yml ├── list_int_vlan.yml ├── template_int_vlan.j2 └── template_lldp.j2 ├── exceptions ├── exceptions_handling.py └── exceptions_handling2.py ├── facts ├── print_facts.py ├── test_junos_version.py └── test_junos_version_2.py ├── flask ├── app.py └── readme.md ├── jinja2_basics ├── template_int_vlan.j2 └── template_int_vlan_2.j2 ├── logging-and-arguments ├── argparse.md ├── logging.md ├── print_facts_with_custom_debug.py └── print_facts_with_debug.py ├── napalm ├── conf.txt └── readme.md ├── python_basics ├── list_of_ip.txt ├── regex_hostname.py ├── show_config.txt └── show_config_vlans.txt ├── rest_basics ├── get_mx_software_information.py ├── google_map_api.py ├── junos_space.py └── readme.md ├── rpc-netconf-lxml-ncclient ├── lxml.md ├── ncclient.md ├── netconf.md └── rpc.md ├── tables_and_views ├── bgp.yml ├── bgp_non_established.py ├── bgp_states.py ├── devices.yml ├── get_interfaces_down.py ├── get_uplinks_state.py ├── lldp_neighbor_status.py ├── search_an_lldp_neighbor.py └── uplinks_and_downlinks_last_flap.py └── yaml_basics ├── device_list.yml └── this_is_a_dictionary.yml /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | MIT License 3 | 4 | Copyright (c) 2018 Juniper Networks, Inc. All rights reserved 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /Python-for-network-engineers.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ksator/python-training-for-network-engineers/0d2265fd5441607d3b1152172244a26738fcb9c5/Python-for-network-engineers.pdf -------------------------------------------------------------------------------- /Python-training.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ksator/python-training-for-network-engineers/0d2265fd5441607d3b1152172244a26738fcb9c5/Python-training.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### What to find in thos project 2 | A "Python for network enginners" course. 3 | It is useful to network engineers with no python programming knowledge, who wants to use python to manage junos devices. 4 | This is a hands-on training, with step-by-step instructions and many basics examples. 5 | The course covers an introduction to Python programming, it includes also the PyEZ library to interact with JUNOS devices as well as an overview of some other Python modules to manipulate IP addresses, files, regular expressions, templates and rest calls. 6 | 7 | ### Presentations 8 | The presentations are available into this repo 9 | [- **Python-for-network-engineers.pdf**](https://github.com/ksator/python-for-network-engineers/blob/master/Python-for-network-engineers.pdf) 10 | [- **Python-training.pdf**](https://github.com/ksator/python-for-network-engineers/blob/master/Python-training.pdf) 11 | 12 | ### Scripts 13 | All the scripts are available into this project. 14 | The examples and code in this document are for learning and educational purposes. 15 | The samples were created with the goals of clarity and ease of understanding. 16 | If you are writing code for a real application, you would write some code differently :-) 17 | 18 | ### Agenda 19 | - introduction to network automation 20 | - introduction to python programming 21 | - IP addresses manipulation 22 | - files manipulation 23 | - building documents with jinja2 templates 24 | - YAML (humans to programs) 25 | - junos automation with PyEZ python library 26 | - JSON data format (exchange data between applications) 27 | - programmatic access with REST APIs 28 | 29 | ### Looking for more Junos automation solutions: 30 | 31 | https://github.com/ksator?tab=repositories 32 | https://gitlab.com/users/ksator/projects 33 | https://gist.github.com/ksator/ 34 | 35 | 36 | ### Contributions, bugs, questions or enhancement requests: 37 | please submit github issues or pull requests. 38 | -------------------------------------------------------------------------------- /configuration_builder/configuration_builder.py: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------------------------------------------------- 2 | # DESCRIPTION: 3 | # Merge the variables YML and Jinja2 template to create JUNOS configuration files. 4 | # 5 | # CONTACT: bvilletard@juniper.net; cbinckly@juniper.net; ksator@juniper.net; pgeenens@juniper.net; tgrimonet@juniper.net 6 | # 7 | # CREATED: 2015-11-11 8 | # 9 | # VERSION: 1 10 | # 11 | # USAGE: 12 | # ----------------------------------------------------------------------------------------------------------------------- 13 | import yaml 14 | from jinja2 import Template 15 | 16 | f=open('configuration_builder/variables_build.yml') 17 | data=f.read() 18 | my_vars=yaml.load (data) 19 | f.close() 20 | 21 | f2=open('configuration_builder/template_build.j2') 22 | s2=f2.read() 23 | template=Template(s2) 24 | f2.close() 25 | 26 | print 'building configuration files ...' 27 | for ex in my_vars: 28 | print 'generate config file for device '+ex["host_name"]+' : conf_file_build_phase_'+ex["host_name"]+'.conf' 29 | conffile=open('conf_file_build_phase_'+ex["host_name"]+'.conf','w') 30 | conffile.write(template.render(ex)) 31 | conffile.close() 32 | print 'done' 33 | 34 | -------------------------------------------------------------------------------- /configuration_builder/configuration_builder_2.py: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------------------------------------------------- 2 | # DESCRIPTION: 3 | # Generate configuration files based on variables stored in YML and a Jinja 2 template and apply the configuration 4 | # to the devices. 5 | # 6 | # CONTACT: bvilletard@juniper.net; cbinckly@juniper.net; ksator@juniper.net; pgeenens@juniper.net; tgrimonet@juniper.net 7 | # 8 | # CREATED: 2015-11-11 9 | # 10 | # VERSION: 1 11 | # 12 | # USAGE: configuration_builder.py 13 | # ----------------------------------------------------------------------------------------------------------------------- 14 | from jnpr.junos import Device 15 | from jnpr.junos.utils.config import Config 16 | import yaml 17 | from jinja2 import Template 18 | 19 | f=open('configuration_builder/variables.yml') 20 | data=f.read() 21 | my_vars=yaml.load (data) 22 | 23 | f2=open('configuration_builder/template.j2') 24 | s2=f2.read() 25 | template=Template(s2) 26 | 27 | print 'Start configuration building' 28 | for ex in my_vars: 29 | print 'generate config file for device '+ex["host_name"]+' : conf_file_build_phase_'+ex["host_name"]+'.conf' 30 | conffile=open('conf_file_build_phase_'+ex["host_name"]+'.conf','w') 31 | conffile.write(template.render(ex)) 32 | conffile.close() 33 | print 'done' 34 | 35 | 36 | print 'applying the conf to the devices ...' 37 | for ex in my_vars: 38 | dev = Device(host=ex["management_ip"], user='pytraining', password='Poclab123') 39 | dev.open() 40 | cfg=Config(dev) 41 | cfg.rollback() # Execute Rollback to prevent commit change from old config session 42 | #cfg.load(template_path='template_for_ex.j2', template_vars=my_vars, format='text') 43 | cfg.load(path='conf_file_build_phase_'+ex["host_name"]+'.conf', format='text') 44 | if cfg.commit() == True: 45 | print ('configuration commited on ' + dev.facts["hostname"]) 46 | else: 47 | print ('commit failed on ' + dev.facts["hostname"]) 48 | dev.close() 49 | print ('done') 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /configuration_builder/template.j2: -------------------------------------------------------------------------------- 1 | interfaces { 2 | {%- for item in uplinks %} 3 | {{uplinks[item]['interface']}} { 4 | description "{{uplinks[item]['description']}}"; 5 | unit 0 { 6 | family inet { 7 | address {{uplinks[item]['address']}}/31; 8 | } 9 | } 10 | } 11 | {%- endfor %} 12 | } 13 | routing-options { 14 | router-id {{router_id}}; 15 | forwarding-table { 16 | export bgp-ecmp; 17 | } 18 | } 19 | protocols { 20 | bgp { 21 | group PyEZtraining { 22 | type external; 23 | import bgp-in; 24 | family inet { 25 | unicast; 26 | } 27 | export bgp-out; 28 | local-as {{bgp.local_asn}}; 29 | multipath multiple-as; 30 | {%- for item in bgp.neighbors %} 31 | neighbor {{bgp.neighbors[item]['ip']}} { 32 | peer-as {{bgp.neighbors[item]['peer_as']}}; 33 | } 34 | {%- endfor %} 35 | } 36 | } 37 | lldp { 38 | interface all; 39 | } 40 | } 41 | policy-options { 42 | policy-statement bgp-ecmp { 43 | then { 44 | load-balance per-packet; 45 | } 46 | } 47 | policy-statement bgp-in { 48 | then accept; 49 | } 50 | policy-statement bgp-out { 51 | then accept; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /configuration_builder/template_build.j2: -------------------------------------------------------------------------------- 1 | system { 2 | host-name {{host_name}}; 3 | root-authentication { 4 | encrypted-password $1$X1Voripv$2Z70Lm20kqKLF.JBxoD2B1; 5 | } 6 | services { 7 | ssh { 8 | root-login allow; 9 | } 10 | netconf { 11 | ssh; 12 | } 13 | } 14 | } 15 | interfaces { 16 | vme { 17 | description "management interface"; 18 | unit 0 { 19 | family inet { 20 | address {{vme_ip}}/24; 21 | } 22 | } 23 | } 24 | } 25 | routing-options { 26 | static { 27 | route 0.0.0.0/0 next-hop 172.30.179.1; 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /configuration_builder/variables.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - host_name: ex4300-4 3 | management_ip: 172.30.179.65 4 | router_id: 10.20.1.1 5 | uplinks: 6 | to_neighbor1: 7 | interface: ge-0/0/0 8 | description: "UPLINK to ex4300-9" 9 | address: 192.168.0.0 10 | to_neighbor2: 11 | interface: ge-0/0/1 12 | description: "UPLINK to ex4300-10" 13 | address: 192.168.0.2 14 | bgp: 15 | local_asn: 104 16 | neighbors: 17 | neighbor_1: 18 | ip: 192.168.0.1 19 | peer_as: 109 20 | neighbor_2: 21 | ip: 192.168.0.3 22 | peer_as: 110 23 | ## ------------------------------------------ ## 24 | - host_name: ex4300-9 25 | management_ip: 172.30.179.95 26 | router_id: 10.20.1.2 27 | uplinks: 28 | to_neighbor1: 29 | interface: ge-0/0/0 30 | description: "UPLINK to ex4300-10" 31 | address: 192.168.0.5 32 | to_neighbor2: 33 | interface: ge-0/0/1 34 | description: "UPLINK to ex4300-4" 35 | address: 192.168.0.1 36 | bgp: 37 | local_asn: 109 38 | neighbors: 39 | neighbor1: 40 | ip: 192.168.0.4 41 | peer_as: 110 42 | neighbor2: 43 | ip: 192.168.0.0 44 | peer_as: 104 45 | ## ------------------------------------------ ## 46 | - host_name: ex4300-10 47 | management_ip: 172.30.179.96 48 | router_id: 10.20.1.3 49 | uplinks: 50 | to_neighbor1: 51 | interface: ge-0/0/0 52 | description: "UPLINK to ex4300-9" 53 | address: 192.168.0.4 54 | to_neighbor2: 55 | interface: ge-0/0/1 56 | description: "UPLINK to ex4300-4" 57 | address: 192.168.0.3 58 | bgp: 59 | local_asn: 110 60 | neighbors: 61 | neighbor1: 62 | ip: 192.168.0.5 63 | peer_as: 109 64 | neighbor2: 65 | ip: 192.168.0.2 66 | peer_as: 104 67 | ## ------------------------------------------ ## 68 | -------------------------------------------------------------------------------- /configuration_builder/variables_build.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # this is a yaml list. there is one item per device. 3 | # each item is a dictionnary with 2 key:value pairs. 4 | # if you change this yaml structure you'll have to adapt the way you parse it from both jinja2 and python. 5 | - host_name: ex4300-4 6 | vme_ip: 172.30.179.65 7 | ## ------------------------------------------ ## 8 | - host_name: ex4300-9 9 | vme_ip: 172.30.179.95 10 | ## ------------------------------------------ ## 11 | - host_name: ex4300-10 12 | vme_ip: 172.30.179.96 13 | -------------------------------------------------------------------------------- /configuration_management/conf_int_with_vlan.py: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------------------------------------------------- 2 | # DESCRIPTION: 3 | # Use PyEZ to automatically merge a variables YML file and a Jinja2 template and update the candidate configuration on 4 | # a device. 5 | # 6 | # CONTACT: bvilletard@juniper.net; cbinckly@juniper.net; ksator@juniper.net; pgeenens@juniper.net; tgrimonet@juniper.net 7 | # 8 | # CREATED: 2015-11-11 9 | # 10 | # VERSION: 1 11 | # 12 | # USAGE: conf_int_with_vlan.py 13 | # ----------------------------------------------------------------------------------------------------------------------- 14 | from jnpr.junos import Device 15 | from jnpr.junos.utils.config import Config 16 | import yaml 17 | ip=raw_input("ip address of the device:") 18 | a_device=Device (host=ip, user="pytraining", password="Poclab123") 19 | a_device.open() 20 | cfg = Config(a_device) 21 | cfg.rollback() 22 | s=open('configuration_management/list_int_vlan.yml').read() 23 | myvars=yaml.load(s) 24 | cfg.load(template_path='configuration_management/template_int_vlan.j2', template_vars=myvars, format='set') 25 | cfg.pdiff() 26 | #cfg.commit() 27 | cfg.rollback() 28 | -------------------------------------------------------------------------------- /configuration_management/confjunos.conf: -------------------------------------------------------------------------------- 1 | set vlans vlan-911 vlan-id 911 2 | set vlans vlan-911 description "created with python" 3 | -------------------------------------------------------------------------------- /configuration_management/enable_lldp.py: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------------------------------------------------- 2 | # DESCRIPTION: 3 | # Use PyEZ to enable LLDP on a subset of interfaces for a specific device. 4 | # 5 | # CONTACT: bvilletard@juniper.net; cbinckly@juniper.net; ksator@juniper.net; pgeenens@juniper.net; tgrimonet@juniper.net 6 | # 7 | # CREATED: 2015-11-11 8 | # 9 | # VERSION: 1 10 | # 11 | # USAGE: enable_lldp.py 12 | # ----------------------------------------------------------------------------------------------------------------------- 13 | 14 | from jnpr.junos import Device # import the class Device 15 | from jnpr.junos.utils.config import Config # import the class Config 16 | import yaml 17 | s=open('configuration_management/interfaces.yml').read() #s is a string 18 | my_variables=yaml.load(s) # my_variables is a dictionary 19 | a_device=Device (host="172.30.179.113", user="pytraining", password="Poclab123") 20 | a_device.open() 21 | cfg=Config(a_device) # cfg is the candidate configuration 22 | cfg.rollback() 23 | cfg.load(template_path='configuration_management/template_lldp.j2', template_vars=my_variables, format='set') 24 | cfg.pdiff() 25 | cfg.commit() 26 | -------------------------------------------------------------------------------- /configuration_management/enable_lldp_all_devices.py: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------------------------------------------------- 2 | # DESCRIPTION: 3 | # Use PyEZ to enable LLDP on devices listed in a YAML file. 4 | # 5 | # CONTACT: bvilletard@juniper.net; cbinckly@juniper.net; ksator@juniper.net; pgeenens@juniper.net; tgrimonet@juniper.net 6 | # 7 | # CREATED: 2015-11-11 8 | # 9 | # VERSION: 1 10 | # 11 | # USAGE: enable_lldp_all_devices.py 12 | # ----------------------------------------------------------------------------------------------------------------------- 13 | from jnpr.junos import Device # import the class Device 14 | from jnpr.junos.utils.config import Config # import the class Config 15 | import yaml 16 | 17 | interface_file_contents = open('configuration_management/interfaces.yml').read() 18 | interface_variables = yaml.load(interface_file_contents) 19 | 20 | device_file_contents = open('configuration_management/device_list.yml').read() 21 | device_list = yaml.load(device_file_contents) 22 | 23 | for device in device_list: 24 | print device 25 | dev = Device (host=device, user="pytraining", password="Poclab123") 26 | dev.open() 27 | cfg=Config(dev) # cfg is the candidate configuration 28 | cfg.rollback() 29 | cfg.load(template_path='configuration_management/template_lldp.j2', template_vars=interface_variables, format='set') 30 | cfg.pdiff() 31 | cfg.commit() 32 | -------------------------------------------------------------------------------- /configuration_management/interfaces.yml: -------------------------------------------------------------------------------- 1 | --- 2 | interfaces: 3 | - ge-0/0/1 4 | - ge-0/0/2 5 | - ge-0/0/3 6 | -------------------------------------------------------------------------------- /configuration_management/list_int_vlan.yml: -------------------------------------------------------------------------------- 1 | --- 2 | host_ports: 3 | - ge-0/0/9 4 | - ge-0/0/10 5 | - ge-0/0/16 6 | - ge-0/0/18 7 | 8 | vlan: 9 | name: v14 10 | vlan_id: 14 11 | -------------------------------------------------------------------------------- /configuration_management/template_int_vlan.j2: -------------------------------------------------------------------------------- 1 | set vlans {{ vlan.name }} vlan-id {{ vlan.vlan_id }} 2 | {%- for iface in host_ports %} 3 | set interfaces {{ iface }} unit 0 family ethernet-switching port-mode access vlan members {{ vlan.name }} 4 | {%- endfor %} 5 | 6 | -------------------------------------------------------------------------------- /configuration_management/template_lldp.j2: -------------------------------------------------------------------------------- 1 | {%- for interface in interfaces %} 2 | set protocols lldp interface {{ interface }} 3 | {%- endfor %} -------------------------------------------------------------------------------- /exceptions/exceptions_handling.py: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------------------------------------------------- 2 | # DESCRIPTION: 3 | # Attempt to connect to a device and print the device hostname and version. Handle all exceptions related to device 4 | # connection. 5 | # 6 | # CONTACT: bvilletard@juniper.net; cbinckly@juniper.net; ksator@juniper.net; pgeenens@juniper.net; tgrimonet@juniper.net 7 | # 8 | # CREATED: 2015-11-11 9 | # 10 | # VERSION: 1 11 | # 12 | # USAGE: exception_handling.py HOST USERNAME PASSWORD 13 | # ----------------------------------------------------------------------------------------------------------------------- 14 | from jnpr.junos import Device 15 | import sys 16 | from jnpr.junos.exception import * 17 | dev=Device (host=sys.argv[1], user=sys.argv[2], password=sys.argv[3]) 18 | try: 19 | dev.open() 20 | except ConnectUnknownHostError: 21 | print(sys.argv[1] + " is not a valid host!") 22 | except ConnectAuthError: 23 | print("invalid username or password for host " + sys.argv[1]) 24 | except ConnectTimeoutError: 25 | print("failed to connect to " + sys.argv[1] + ". could be: a bad ip addr, ip addr is not reachable due to a routing issue or a firewall filtering, ...") 26 | except : 27 | print("another error ...") 28 | else: 29 | print ("the device "+ dev.facts["hostname"]+ " runs " + dev.facts["version"]) 30 | 31 | 32 | -------------------------------------------------------------------------------- /exceptions/exceptions_handling2.py: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------------------------------------------------- 2 | # DESCRIPTION: 3 | # Print the hostname and version of a list of devices with connection timeout exception handling. 4 | # 5 | # CONTACT: bvilletard@juniper.net; cbinckly@juniper.net; ksator@juniper.net; pgeenens@juniper.net; tgrimonet@juniper.net 6 | # 7 | # CREATED: 2015-11-11 8 | # 9 | # VERSION: 1 10 | # 11 | # USAGE: exceptions_handling2.py 12 | # ----------------------------------------------------------------------------------------------------------------------- 13 | 14 | # mydeviceslist is a list of device running junos 15 | # for each device in mydeviceslist, we will connect to the device and print some facts 16 | # if a device is not reacheable, we will print something and continue the program 17 | 18 | from jnpr.junos import Device 19 | from jnpr.junos.exception import * 20 | mydeviceslist=["172.30.179.101", "172.30.179.102", "172.30.205.102", "172.30.179.104"] 21 | for item in mydeviceslist: 22 | dev=Device(host=item, user="pytraining", password="Poclab123") 23 | try: 24 | dev.open() 25 | except ConnectTimeoutError: 26 | print("failed to connect to " + item) 27 | continue 28 | 29 | print ("the device "+ item + " runs " + dev.facts["version"]) 30 | dev.close() 31 | -------------------------------------------------------------------------------- /facts/print_facts.py: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------------------------------------------------- 2 | # DESCRIPTION: 3 | # Connect to a series of devices and print facts related to each device to both the console and an inventory text file. 4 | # 5 | # CONTACT: bvilletard@juniper.net; cbinckly@juniper.net; ksator@juniper.net; pgeenens@juniper.net; tgrimonet@juniper.net 6 | # 7 | # CREATED: 2015-11-11 8 | # 9 | # VERSION: 1 10 | # 11 | # USAGE: print_facts.py 12 | # ----------------------------------------------------------------------------------------------------------------------- 13 | 14 | from jnpr.junos import Device 15 | from datetime import datetime 16 | 17 | mydeviceslist=["172.30.179.103", "172.30.179.104", "172.30.179.105"] 18 | f=open("my_devices_inventory.txt", "a") 19 | f.write(str(datetime.now()) + '\n') 20 | for item in mydeviceslist: 21 | dev=Device(host=item, user="pytraining", password="Poclab123") 22 | dev.open() 23 | dev.close() 24 | if dev.facts["version"]!="15.1": 25 | print ("the device "+ dev.facts["hostname"]+ " is a " + dev.facts['model'] + " running " + dev.facts["version"]) 26 | f.write ( "the device "+ dev.facts["hostname"]+ " is a " + dev.facts['model'] + " running " + dev.facts["version"] + "\n") 27 | f.close() 28 | 29 | -------------------------------------------------------------------------------- /facts/test_junos_version.py: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------------------------------------------------- 2 | # DESCRIPTION: 3 | # Retrieve and print the hostname and version of a JUNOS device. 4 | # 5 | # CONTACT: bvilletard@juniper.net; cbinckly@juniper.net; ksator@juniper.net; pgeenens@juniper.net; tgrimonet@juniper.net 6 | # 7 | # CREATED: 2015-11-11 8 | # 9 | # VERSION: 1 10 | # 11 | # USAGE: test_junos_version.py 12 | # ----------------------------------------------------------------------------------------------------------------------- 13 | 14 | from jnpr.junos import Device 15 | ip=raw_input("ip address of the device:") 16 | login =raw_input("login :") 17 | pwd=raw_input("passwd:") 18 | dev=Device(host=ip, user=login, password=pwd) 19 | dev.open() 20 | print "the junos version is " +dev.facts['version'] 21 | dev.close() 22 | 23 | -------------------------------------------------------------------------------- /facts/test_junos_version_2.py: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------------------------------------------------- 2 | # DESCRIPTION: 3 | # Retrieve and print the hostname and version of a JUNOS device. 4 | # 5 | # CONTACT: bvilletard@juniper.net; cbinckly@juniper.net; ksator@juniper.net; pgeenens@juniper.net; tgrimonet@juniper.net 6 | # 7 | # CREATED: 2015-11-11 8 | # 9 | # VERSION: 1 10 | # 11 | # USAGE: test_junos_version_2.py HOST USER PASSWORD 12 | # ----------------------------------------------------------------------------------------------------------------------- 13 | 14 | from jnpr.junos import Device 15 | import sys 16 | dev=Device (host=sys.argv[1], user=sys.argv[2], password=sys.argv[3]) 17 | dev.open() 18 | print ("the device "+ dev.facts["hostname"]+ " runs " + dev.facts["version"]) 19 | dev.close() 20 | -------------------------------------------------------------------------------- /flask/app.py: -------------------------------------------------------------------------------- 1 | # you need to "sudo pip install flask" 2 | 3 | # import flask 4 | from flask import Flask, jsonify, abort, make_response, request 5 | 6 | # instakciate the class Flask. app is an istance of the class Flask so this is an object. app is a variable. 7 | app = Flask(__name__) 8 | 9 | # tasks is a variable, of type list. each item is a dictionnary. 10 | tasks = [ 11 | { 12 | 'id': 1, 13 | 'title': u'Buy groceries', 14 | 'description': u'Milk, Cheese, Pizza, Fruit, Tylenol', 15 | 'done': False 16 | }, 17 | { 18 | 'id': 2, 19 | 'title': u'Learn Python', 20 | 'description': u'Need to find a good Python tutorial on the web', 21 | 'done': False 22 | } 23 | ] 24 | 25 | # route() is used to bind a function to a URL. 26 | # this is what to do wit an http call using get method to /todo/api/v1.0/tasks 27 | @app.route('/todo/api/v1.0/tasks', methods=['GET']) 28 | def get_tasks(): 29 | return jsonify({'tasks': tasks}) 30 | 31 | # this is what to do wit an http call using get method to /todo/api/v1.0/tasks/xxx 32 | @app.route('/todo/api/v1.0/tasks/', methods=['GET']) 33 | def get_task(task_id): 34 | task = [task for task in tasks if task['id'] == task_id] 35 | if len(task) == 0: 36 | abort(404) 37 | return jsonify({'task': task[0]}) 38 | 39 | # handle http 404 differently 40 | @app.errorhandler(404) 41 | def not_found(error): 42 | return make_response(jsonify({'error': 'Not found'}), 404) 43 | 44 | # this is what to do wit an http call using post method to /todo/api/v1.0/tasks 45 | @app.route('/todo/api/v1.0/tasks', methods=['POST']) 46 | def create_task(): 47 | if not request.json or not 'title' in request.json: 48 | abort(400) 49 | task = { 50 | 'id': tasks[-1]['id'] + 1, 51 | 'title': request.json['title'], 52 | 'description': request.json.get('description', ""), 53 | 'done': False 54 | } 55 | tasks.append(task) 56 | return jsonify({'task': task}), 201 57 | 58 | # rest call to delete 59 | @app.route('/todo/api/v1.0/tasks/', methods=['DELETE']) 60 | def delete_task(task_id): 61 | task = [task for task in tasks if task['id'] == task_id] 62 | if len(task) == 0: 63 | abort(404) 64 | tasks.remove(task[0]) 65 | return jsonify({'result': True}) 66 | 67 | # rest call to update 68 | @app.route('/todo/api/v1.0/tasks/', methods=['PUT']) 69 | def update_task(task_id): 70 | task = [task for task in tasks if task['id'] == task_id] 71 | task[0]['title'] = request.json.get('title', task[0]['title']) 72 | task[0]['description'] = request.json.get('description', task[0]['description']) 73 | task[0]['done'] = request.json.get('done', task[0]['done']) 74 | return jsonify({'task': task[0]}) 75 | 76 | 77 | # use the run method of the class Flask 78 | # Default port is 5000. 79 | # Default host is 127.0.0.1. Use ```host='0.0.0.0'``` to have the server available externally as well. 80 | app.run(debug=True) 81 | -------------------------------------------------------------------------------- /flask/readme.md: -------------------------------------------------------------------------------- 1 | ## Designing a restfull api with flask 2 | 3 | Inspired by these tuto: 4 | https://blog.miguelgrinberg.com/post/designing-a-restful-api-with-python-and-flask 5 | https://www.raspberrypi.org/learning/python-web-server-with-flask/worksheet/ 6 | 7 | 8 | ### requirements 9 | you need to ```sudo pip install flask``` 10 | 11 | ### python script content: 12 | ``` 13 | $ more app.py 14 | 15 | # import flask 16 | from flask import Flask, jsonify, abort, make_response, request 17 | 18 | # instanciate the class Flask. app is an istance of the class Flask so this is an object. app is a variable. 19 | app = Flask(__name__) 20 | 21 | # tasks is a variable, of type list. each item is a dictionnary. 22 | tasks = [ 23 | { 24 | 'id': 1, 25 | 'title': u'Buy groceries', 26 | 'description': u'Milk, Cheese, Pizza, Fruit, Tylenol', 27 | 'done': False 28 | }, 29 | { 30 | 'id': 2, 31 | 'title': u'Learn Python', 32 | 'description': u'Need to find a good Python tutorial on the web', 33 | 'done': False 34 | } 35 | ] 36 | 37 | # route() is used to bind a function to a URL. 38 | # this is what to do with an http call using get method to /todo/api/v1.0/tasks 39 | @app.route('/todo/api/v1.0/tasks', methods=['GET']) 40 | def get_tasks(): 41 | return jsonify({'tasks': tasks}) 42 | 43 | # this is what to do with an http call using get method to /todo/api/v1.0/tasks/xxx 44 | @app.route('/todo/api/v1.0/tasks/', methods=['GET']) 45 | def get_task(task_id): 46 | task = [task for task in tasks if task['id'] == task_id] 47 | if len(task) == 0: 48 | abort(404) 49 | return jsonify({'task': task[0]}) 50 | 51 | # handle http 404 differently 52 | @app.errorhandler(404) 53 | def not_found(error): 54 | return make_response(jsonify({'error': 'Not found'}), 404) 55 | 56 | # this is what to do wit an http call using post method to /todo/api/v1.0/tasks 57 | @app.route('/todo/api/v1.0/tasks', methods=['POST']) 58 | def create_task(): 59 | if not request.json or not 'title' in request.json: 60 | abort(400) 61 | task = { 62 | 'id': tasks[-1]['id'] + 1, 63 | 'title': request.json['title'], 64 | 'description': request.json.get('description', ""), 65 | 'done': False 66 | } 67 | tasks.append(task) 68 | return jsonify({'task': task}), 201 69 | 70 | # rest call to delete 71 | @app.route('/todo/api/v1.0/tasks/', methods=['DELETE']) 72 | def delete_task(task_id): 73 | task = [task for task in tasks if task['id'] == task_id] 74 | if len(task) == 0: 75 | abort(404) 76 | tasks.remove(task[0]) 77 | return jsonify({'result': True}) 78 | 79 | # rest call to update 80 | @app.route('/todo/api/v1.0/tasks/', methods=['PUT']) 81 | def update_task(task_id): 82 | task = [task for task in tasks if task['id'] == task_id] 83 | task[0]['title'] = request.json.get('title', task[0]['title']) 84 | task[0]['description'] = request.json.get('description', task[0]['description']) 85 | task[0]['done'] = request.json.get('done', task[0]['done']) 86 | return jsonify({'task': task[0]}) 87 | 88 | 89 | # use the run method of the class Flask 90 | # Default port is 5000. 91 | # Default host is 127.0.0.1. Use ```host='0.0.0.0'``` to have the server available externally as well. 92 | app.run(debug=True) 93 | 94 | ``` 95 | ### Execute the python script: 96 | 97 | ``` 98 | $ export FLASK_APP=app.py 99 | $ env | grep FLASK_APP 100 | FLASK_APP=app.py 101 | $ echo $FLASK_APP 102 | app.py 103 | $ flask run 104 | * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) 105 | * Restarting with stat 106 | * Debugger is active! 107 | * Debugger PIN: 761-615-843 108 | ``` 109 | or 110 | ``` 111 | $ python app.py 112 | * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) 113 | * Restarting with stat 114 | * Debugger is active! 115 | * Debugger PIN: 761-615-843 116 | ``` 117 | ### make rest calls: 118 | you need a rest client: 119 | The below rest calls are with curl. 120 | if you prefer to make rest calls using a python rest client, please refer to these examples about the requests module https://github.com/ksator/python-training-for-network-engineers/tree/master/rest_basics 121 | You can aslo use a graphical rest client (postman ....) 122 | 123 | ##### Retrieve list of tasks: 124 | 125 | ``` 126 | $ curl -i http://localhost:5000/todo/api/v1.0/tasks 127 | HTTP/1.0 200 OK 128 | Content-Type: application/json 129 | Content-Length: 317 130 | Server: Werkzeug/0.12.2 Python/2.7.6 131 | Date: Mon, 22 May 2017 11:20:58 GMT 132 | 133 | { 134 | "tasks": [ 135 | { 136 | "description": "Milk, Cheese, Pizza, Fruit, Tylenol", 137 | "done": false, 138 | "id": 1, 139 | "title": "Buy groceries" 140 | }, 141 | { 142 | "description": "Need to find a good Python tutorial on the web", 143 | "done": false, 144 | "id": 2, 145 | "title": "Learn Python" 146 | } 147 | ] 148 | } 149 | ``` 150 | ##### Retrieve a task: 151 | ``` 152 | $ curl -i http://localhost:5000/todo/api/v1.0/tasks/1 153 | HTTP/1.0 200 OK 154 | Content-Type: application/json 155 | Content-Length: 142 156 | Server: Werkzeug/0.12.2 Python/2.7.6 157 | Date: Mon, 22 May 2017 11:21:08 GMT 158 | 159 | { 160 | "task": { 161 | "description": "Milk, Cheese, Pizza, Fruit, Tylenol", 162 | "done": false, 163 | "id": 1, 164 | "title": "Buy groceries" 165 | } 166 | } 167 | ``` 168 | ``` 169 | $ curl -i http://localhost:5000/todo/api/v1.0/tasks/2 170 | HTTP/1.0 200 OK 171 | Content-Type: application/json 172 | Content-Length: 152 173 | Server: Werkzeug/0.12.2 Python/2.7.6 174 | Date: Mon, 22 May 2017 11:21:10 GMT 175 | 176 | { 177 | "task": { 178 | "description": "Need to find a good Python tutorial on the web", 179 | "done": false, 180 | "id": 2, 181 | "title": "Learn Python" 182 | } 183 | } 184 | ``` 185 | ##### Note the 404 error is in json instead of html: 186 | ``` 187 | $ curl -i http://localhost:5000/todo/api/v1.0/tasks/3 188 | HTTP/1.0 404 NOT FOUND 189 | Content-Type: application/json 190 | Content-Length: 27 191 | Server: Werkzeug/0.12.2 Python/2.7.6 192 | Date: Mon, 22 May 2017 11:21:12 GMT 193 | 194 | { 195 | "error": "Not found" 196 | } 197 | ``` 198 | ##### create a new task (http method is post, http response code is 201): 199 | ``` 200 | $ curl -i -H "Content-Type: application/json" -X POST -d '{"title":"Read a book"}' http://localhost:5000/todo/api/v1.0/tasks 201 | HTTP/1.0 201 CREATED 202 | Content-Type: application/json 203 | Content-Length: 105 204 | Server: Werkzeug/0.12.2 Python/2.7.6 205 | Date: Mon, 22 May 2017 11:21:24 GMT 206 | 207 | { 208 | "task": { 209 | "description": "", 210 | "done": false, 211 | "id": 3, 212 | "title": "Read a book" 213 | } 214 | } 215 | ``` 216 | ###### Retrieve list of tasks: 217 | ``` 218 | $ curl -i http://localhost:5000/todo/api/v1.0/tasks 219 | HTTP/1.0 200 OK 220 | Content-Type: application/json 221 | Content-Length: 424 222 | Server: Werkzeug/0.12.2 Python/2.7.6 223 | Date: Mon, 22 May 2017 11:21:27 GMT 224 | 225 | { 226 | "tasks": [ 227 | { 228 | "description": "Milk, Cheese, Pizza, Fruit, Tylenol", 229 | "done": false, 230 | "id": 1, 231 | "title": "Buy groceries" 232 | }, 233 | { 234 | "description": "Need to find a good Python tutorial on the web", 235 | "done": false, 236 | "id": 2, 237 | "title": "Learn Python" 238 | }, 239 | { 240 | "description": "", 241 | "done": false, 242 | "id": 3, 243 | "title": "Read a book" 244 | } 245 | ] 246 | } 247 | ``` 248 | ###### Retrieve a task: 249 | ``` 250 | $ curl -i http://localhost:5000/todo/api/v1.0/tasks/3 251 | HTTP/1.0 200 OK 252 | Content-Type: application/json 253 | Content-Length: 105 254 | Server: Werkzeug/0.12.2 Python/2.7.6 255 | Date: Mon, 22 May 2017 11:21:30 GMT 256 | 257 | { 258 | "task": { 259 | "description": "", 260 | "done": false, 261 | "id": 3, 262 | "title": "Read a book" 263 | } 264 | } 265 | ``` 266 | 267 | ##### Delete an existing task: 268 | ``` 269 | $ curl -i -X DELETE http://localhost:5000/todo/api/v1.0/tasks/1 270 | HTTP/1.0 200 OK 271 | Content-Type: application/json 272 | Content-Length: 21 273 | Server: Werkzeug/0.12.2 Python/2.7.6 274 | Date: Mon, 22 May 2017 11:51:01 GMT 275 | 276 | { 277 | "result": true 278 | } 279 | ``` 280 | ``` 281 | $ curl -i http://localhost:5000/todo/api/v1.0/tasks 282 | HTTP/1.0 200 OK 283 | Content-Type: application/json 284 | Content-Length: 173 285 | Server: Werkzeug/0.12.2 Python/2.7.6 286 | Date: Mon, 22 May 2017 11:51:07 GMT 287 | 288 | { 289 | "tasks": [ 290 | { 291 | "description": "Need to find a good Python tutorial on the web", 292 | "done": false, 293 | "id": 2, 294 | "title": "Learn Python" 295 | } 296 | ] 297 | } 298 | ``` 299 | ``` 300 | $ curl -i http://localhost:5000/todo/api/v1.0/tasks/1 301 | HTTP/1.0 404 NOT FOUND 302 | Content-Type: application/json 303 | Content-Length: 27 304 | Server: Werkzeug/0.12.2 Python/2.7.6 305 | Date: Mon, 22 May 2017 11:51:12 GMT 306 | 307 | { 308 | "error": "Not found" 309 | } 310 | ``` 311 | ``` 312 | $ curl -i -X DELETE http://localhost:5000/todo/api/v1.0/tasks/1 313 | HTTP/1.0 404 NOT FOUND 314 | Content-Type: application/json 315 | Content-Length: 27 316 | Server: Werkzeug/0.12.2 Python/2.7.6 317 | Date: Mon, 22 May 2017 11:51:25 GMT 318 | 319 | { 320 | "error": "Not found" 321 | } 322 | 323 | ``` 324 | 325 | #### Update existing tasks 326 | ``` 327 | $ curl -i http://localhost:5000/todo/api/v1.0/tasks 328 | HTTP/1.0 200 OK 329 | Content-Type: application/json 330 | Content-Length: 173 331 | Server: Werkzeug/0.12.2 Python/2.7.6 332 | Date: Mon, 22 May 2017 12:11:29 GMT 333 | 334 | { 335 | "tasks": [ 336 | { 337 | "description": "Need to find a good Python tutorial on the web", 338 | "done": false, 339 | "id": 2, 340 | "title": "Learn Python" 341 | } 342 | ] 343 | } 344 | ``` 345 | ``` 346 | $ curl -i -H "Content-Type: application/json" -X PUT -d '{"done":true}' http://localhost:5000/todo/api/v1.0/tasks/2 347 | HTTP/1.0 200 OK 348 | Content-Type: application/json 349 | Content-Length: 151 350 | Server: Werkzeug/0.12.2 Python/2.7.6 351 | Date: Mon, 22 May 2017 12:11:43 GMT 352 | 353 | { 354 | "task": { 355 | "description": "Need to find a good Python tutorial on the web", 356 | "done": true, 357 | "id": 2, 358 | "title": "Learn Python" 359 | } 360 | } 361 | ``` 362 | ``` 363 | $ curl -i http://localhost:5000/todo/api/v1.0/tasks/2 364 | HTTP/1.0 200 OK 365 | Content-Type: application/json 366 | Content-Length: 151 367 | Server: Werkzeug/0.12.2 Python/2.7.6 368 | Date: Mon, 22 May 2017 12:11:55 GMT 369 | 370 | { 371 | "task": { 372 | "description": "Need to find a good Python tutorial on the web", 373 | "done": true, 374 | "id": 2, 375 | "title": "Learn Python" 376 | } 377 | } 378 | ``` 379 | ``` 380 | $ curl -i http://localhost:5000/todo/api/v1.0/tasks 381 | HTTP/1.0 200 OK 382 | Content-Type: application/json 383 | Content-Length: 172 384 | Server: Werkzeug/0.12.2 Python/2.7.6 385 | Date: Mon, 22 May 2017 12:12:03 GMT 386 | 387 | { 388 | "tasks": [ 389 | { 390 | "description": "Need to find a good Python tutorial on the web", 391 | "done": true, 392 | "id": 2, 393 | "title": "Learn Python" 394 | } 395 | ] 396 | } 397 | 398 | ``` 399 | 400 | ### Python script debug output 401 | 402 | There is a debug output because we used ```app.run(debug=True)``` 403 | 404 | ``` 405 | $ python app.py 406 | * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) 407 | * Restarting with stat 408 | * Debugger is active! 409 | * Debugger PIN: 761-615-843 410 | 127.0.0.1 - - [22/May/2017 13:20:58] "GET /todo/api/v1.0/tasks HTTP/1.1" 200 - 411 | 127.0.0.1 - - [22/May/2017 13:21:08] "GET /todo/api/v1.0/tasks/1 HTTP/1.1" 200 - 412 | 127.0.0.1 - - [22/May/2017 13:21:10] "GET /todo/api/v1.0/tasks/2 HTTP/1.1" 200 - 413 | 127.0.0.1 - - [22/May/2017 13:21:12] "GET /todo/api/v1.0/tasks/3 HTTP/1.1" 404 - 414 | 127.0.0.1 - - [22/May/2017 13:21:24] "POST /todo/api/v1.0/tasks HTTP/1.1" 201 - 415 | 127.0.0.1 - - [22/May/2017 13:21:27] "GET /todo/api/v1.0/tasks HTTP/1.1" 200 - 416 | 127.0.0.1 - - [22/May/2017 13:21:30] "GET /todo/api/v1.0/tasks/3 HTTP/1.1" 200 - 417 | ... 418 | ``` 419 | ### Execute shell commands with Python 420 | ``` 421 | >>> import subprocess 422 | >>> 423 | >>> subprocess.check_output(["echo", "Hello World!"]) 424 | 'Hello World!\n' 425 | >>> 426 | >>> p = subprocess.Popen("echo Hello World!", shell=True, stdout = subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) 427 | >>> out,err = p.communicate() 428 | >>> out 429 | 'Hello World!\n' 430 | >>> err 431 | '' 432 | >>> 433 | >>> shell_cmd1 = "echo Hello World!" 434 | >>> shell_cmd1_p = subprocess.Popen(shell_cmd1, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 435 | >>> shell_cmd1_p.communicate()[0] 436 | 'Hello World!\n' 437 | >>> 438 | >>> 439 | ``` 440 | 441 | ### How to Execute ansible playboolks or python scripts making rest calls to server API 442 | we know from the previous section how to execute shell commands from python. so we can use this to execute python scripts or ansible playbooks. 443 | we also need to change the app.py file in order to execute shell commands in the functions. as example: 444 | ``` 445 | @app.route('/todo/api/v1.0/tasks', methods=['GET']) 446 | def get_tasks(): 447 | shell_cmd1 = "ls -l" 448 | shell_cmd1_p = subprocess.Popen(shell_cmd1, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 449 | out, err = shell_cmd1_p.communicate() 450 | return out 451 | ``` 452 | -------------------------------------------------------------------------------- /jinja2_basics/template_int_vlan.j2: -------------------------------------------------------------------------------- 1 | set interfaces {{ interface }} unit 0 family ethernet-switching port-mode access vlan members {{ vlan_name }} 2 | -------------------------------------------------------------------------------- /jinja2_basics/template_int_vlan_2.j2: -------------------------------------------------------------------------------- 1 | {%- for interface in interfaces_list %} 2 | set interfaces {{ interface }} unit 0 family ethernet-switching port-mode access vlan members {{ vlan_name }} 3 | {%- endfor %} 4 | 5 | -------------------------------------------------------------------------------- /logging-and-arguments/argparse.md: -------------------------------------------------------------------------------- 1 | # argparse 2 | It is a parsing module 3 | 4 | ## Example 5 | import argparse 6 | parser = argparse.ArgumentParser() 7 | parser.parse_args() 8 | 9 | $ python prog.py --help 10 | usage: prog.py [-h] 11 | 12 | optional arguments: 13 | -h, --help show this help message and exit 14 | 15 | The --help option, which can also be shortened to -h, is the only option we get for free (i.e. no need to specify it) 16 | ## Introducing Positional arguments 17 | import argparse 18 | parser = argparse.ArgumentParser() 19 | parser.add_argument("echo", help="echo the string you use here") 20 | args = parser.parse_args() 21 | print args.echo 22 | 23 | And we get: 24 | $ python prog.py -h 25 | usage: prog.py [-h] echo 26 | 27 | positional arguments: 28 | echo echo the string you use here 29 | 30 | optional arguments: 31 | -h, --help show this help message and exit 32 | 33 | ## Introducing Optional arguments 34 | 35 | import argparse 36 | parser = argparse.ArgumentParser() 37 | parser.add_argument("--verbose", help="increase output verbosity",action="store_true") 38 | args = parser.parse_args() 39 | if args.verbose: 40 | print "verbosity turned on" 41 | 42 | And the output: 43 | $ python prog.py --verbose 44 | verbosity turned on 45 | 46 | $ python prog.py --help 47 | usage: prog.py [-h] [--verbose] 48 | 49 | optional arguments: 50 | -h, --help show this help message and exit 51 | --verbose increase output verbosity 52 | 53 | ### store_true 54 | keyword, action, and give it the value "store_true". 55 | This means that, if the option is specified, assign the value True to args.verbose 56 | 57 | ### Short options 58 | import argparse 59 | parser = argparse.ArgumentParser() 60 | parser.add_argument("-v", "--verbose", help="increase output verbosity", action="store_true") 61 | args = parser.parse_args() 62 | if args.verbose: 63 | print "verbosity turned on" 64 | 65 | And here goes: 66 | $ python prog.py -v 67 | verbosity turned on 68 | $ python prog.py --help 69 | usage: prog.py [-h] [-v] 70 | 71 | optional arguments: 72 | -h, --help show this help message and exit 73 | -v, --verbose increase output verbosity 74 | 75 | 76 | ### choices 77 | import argparse 78 | parser = argparse.ArgumentParser() 79 | parser.add_argument("square", type=int, help="display a square of a given number") 80 | parser.add_argument("-v", "--verbosity", type=int, choices=[0, 1, 2], help="increase output verbosity") 81 | args = parser.parse_args() 82 | answer = args.square**2 83 | if args.verbosity == 2: 84 | print "the square of {} equals {}".format(args.square, answer) 85 | elif args.verbosity == 1: 86 | print "{}^2 == {}".format(args.square, answer) 87 | else: 88 | print answer 89 | -------------------------------------------------------------------------------- /logging-and-arguments/logging.md: -------------------------------------------------------------------------------- 1 | # logging level: 2 | debug, info, warning, error, critical 3 | The default level is WARNING 4 | 5 | # A simple example: 6 | 7 | import logging 8 | logging.warning('Watch out!') \# will print a message to the console 9 | logging.info('I told you so') \# will not print anything 10 | 11 | which would print: 12 | 13 | WARNING:root:Watch out! 14 | 15 | # Logging to a file: 16 | 17 | import logging 18 | logging.basicConfig(filename='example.log',level=logging.DEBUG) 19 | logging.debug('This message should go to the log file') 20 | logging.info('So should this') 21 | logging.warning('And this, too') 22 | 23 | which would write in example.log: 24 | 25 | DEBUG:root:This message should go to the log file 26 | INFO:root:So should this 27 | WARNING:root:And this, too 28 | 29 | # Changing the format of displayed messages: 30 | 31 | import logging 32 | logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG) 33 | logging.debug('This message should appear on the console') 34 | logging.info('So should this') 35 | logging.warning('And this, too') 36 | 37 | which would print: 38 | 39 | DEBUG:This message should appear on the console 40 | INFO:So should this 41 | WARNING:And this, too 42 | 43 | 44 | # Displaying the date/time in messages: 45 | 46 | import logging 47 | logging.basicConfig(format='%(asctime)s %(message)s') 48 | logging.warning('is when this event was logged.') 49 | 50 | which should print something like this: 51 | 52 | 2010-12-12 11:41:42,612 is when this event was logged. 53 | 54 | ## changing the date/time format: 55 | 56 | The default format for date/time display (shown above) is ISO8601. If you need more control over the formatting of the date/time, provide a datefmt argument to basicConfig, as in this example: 57 | 58 | import logging 59 | logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p') 60 | logging.warning('is when this event was logged.') 61 | 62 | which would display something like this: 63 | 64 | 12/12/2010 11:46:36 AM is when this event was logged. 65 | 66 | 67 | # Logger objects: 68 | 69 | Logger.setLevel() 70 | specifies the lowest-severity log message a logger will handle 71 | 72 | Logger.addHandler() and Logger.removeHandler() 73 | add and remove handler objects from the logger object. 74 | 75 | the following methods create log messages: 76 | Logger.debug(), Logger.info(), Logger.warning(), Logger.error(), and Logger.critical() 77 | 78 | getLogger() 79 | returns a reference to a logger instance with the specified name if it is provided, or root if not. 80 | 81 | ## handlers: 82 | 83 | few handler types: StreamHandler, FileHandler, ... 84 | 85 | ## methods: 86 | 87 | ### setLevel() 88 | method, just as in logger objects, specifies the lowest severity that will be dispatched to the appropriate destination. 89 | Why are there two setLevel() methods? 90 | The level set in the logger determines which severity of messages it will pass to its handlers. 91 | The level set in each handler determines which messages that handler will send on. 92 | 93 | ### setFormatter() 94 | selects a Formatter object for this handler to use. 95 | 96 | the default date format is: 97 | %Y-%m-%d %H:%M:%S 98 | with the milliseconds at the end 99 | 100 | The following message format string will log the time in a human-readable format, the severity of the message, and the contents of the message, in that order: 101 | '%(asctime)s - %(levelname)s - %(message)s' 102 | 103 | # full example 104 | 105 | import logging 106 | 107 | \# create logger 108 | logger = logging.getLogger('simple_example') 109 | logger.setLevel(logging.DEBUG) 110 | 111 | \# create console handler and set level to debug 112 | ch = logging.StreamHandler() 113 | ch.setLevel(logging.DEBUG) 114 | 115 | \# create formatter 116 | formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 117 | 118 | \# add formatter to ch 119 | ch.setFormatter(formatter) 120 | 121 | \# add ch to logger 122 | logger.addHandler(ch) 123 | 124 | \# 'application' code 125 | logger.debug('debug message') 126 | logger.info('info message') 127 | logger.warn('warn message') 128 | logger.error('error message') 129 | logger.critical('critical message') 130 | 131 | ## Running this module from the command line produces the following output: 132 | 133 | $ python simple_logging_module.py 134 | 2005-03-19 15:10:26,618 - simple_example - DEBUG - debug message 135 | 2005-03-19 15:10:26,620 - simple_example - INFO - info message 136 | 2005-03-19 15:10:26,695 - simple_example - WARNING - warn message 137 | 2005-03-19 15:10:26,697 - simple_example - ERROR - error message 138 | 2005-03-19 15:10:26,773 - simple_example - CRITICAL - critical message 139 | 140 | 141 | FORMAT = '%(asctime)-15s %(clientip)s %(user)-8s %(message)s' 142 | logging.basicConfig(format=FORMAT) 143 | d = {'clientip': '192.168.0.1', 'user': 'fbloggs'} 144 | logger = logging.getLogger('tcpserver') 145 | logger.warning('Protocol problem: %s', 'connection reset', extra=d) 146 | 147 | would print something like: 148 | 149 | 2006-02-08 22:20:02,165 192.168.0.1 fbloggs Protocol problem: connection reset 150 | 151 | 152 | -------------------------------------------------------------------------------- /logging-and-arguments/print_facts_with_custom_debug.py: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------------------------------------------------- 2 | # DESCRIPTION: 3 | # Connect to a series of devices and print facts related to each device to both the console and an inventory text file. 4 | # 5 | # CONTACT: bvilletard@juniper.net; cbinckly@juniper.net; ksator@juniper.net; pgeenens@juniper.net; tgrimonet@juniper.net 6 | # 7 | # 8 | # CREATED: 2015-11-11 9 | # 10 | # VERSION: 1 11 | # 12 | # USAGE: python facts/print_facts_3.py -h 13 | # ----------------------------------------------------------------------------------------------------------------------- 14 | 15 | from jnpr.junos import Device 16 | from datetime import datetime 17 | import logging 18 | import argparse 19 | 20 | parser = argparse.ArgumentParser(description="print hostname of devices not running the expected junos version") 21 | parser.add_argument('-u','--username' ,help='Username required to connect to devices',default="pytraining") 22 | parser.add_argument('-p','--password' ,help='User password to connect to devices',default="Poclab123") 23 | parser.add_argument("--log", default='info', choices=['info', 'warn', 'debug', 'error'], help="Specify the log level") 24 | 25 | # create logger 26 | logger = logging.getLogger( 'facts' ) 27 | 28 | # specifies the severity the logger will handle 29 | options = parser.parse_args() 30 | if options.log == 'debug': 31 | logger.setLevel(logging.DEBUG) 32 | elif options.log == 'warn': 33 | logger.setLevel(logging.WARN) 34 | elif options.log == 'error': 35 | logger.setLevel(logging.ERROR) 36 | else: 37 | logger.setLevel(logging.INFO) 38 | 39 | # create formatter 40 | formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 41 | 42 | # create a console handler 43 | steam_handler = logging.StreamHandler() 44 | steam_handler.setLevel(logging.DEBUG) 45 | steam_handler.setFormatter(formatter) 46 | 47 | # add a handler to the logger object 48 | logger.addHandler(steam_handler) 49 | 50 | # create a handler 51 | file_handler = logging.FileHandler("facts.log") 52 | file_handler.setLevel(logging.DEBUG) 53 | file_handler.setFormatter(formatter) 54 | 55 | # Add the handler to logger 56 | logger.addHandler(file_handler) 57 | 58 | # logger.debug('debug message') 59 | # logger.info('info message') 60 | # logger.warn('warn message') 61 | # logger.error('error message') 62 | # logger.critical('critical message') 63 | 64 | t1 = datetime.now() 65 | mydeviceslist=["172.30.179.101","172.30.179.103", "172.30.179.104", "172.30.179.105"] 66 | logger.debug ("size of the list is: " + str(len(mydeviceslist)) + " devices") 67 | 68 | f=open("my_devices_inventory.txt", "a") 69 | f.write(str(datetime.now()) + '\n') 70 | for item in mydeviceslist: 71 | dev=Device(host=item, user=options.username, password=options.password) 72 | dev.open() 73 | dev.close() 74 | if dev.facts["version"]!="12.3R11.2": 75 | print ("the device "+ dev.facts["hostname"]+ " is a " + dev.facts['model'] + " running " + dev.facts["version"] 76 | ) 77 | f.write ( "the device "+ dev.facts["hostname"]+ " is a " + dev.facts['model'] + " running " + dev.facts["versio 78 | n"] + "\n") 79 | else: 80 | logger.debug("the device " + dev.facts["hostname"] + " is checked") 81 | t2 = datetime.now() 82 | t3 = t2-t1 83 | print("done for all devices in " + str(t3) ) 84 | f.close() 85 | -------------------------------------------------------------------------------- /logging-and-arguments/print_facts_with_debug.py: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------------------------------------------------- 2 | # DESCRIPTION: 3 | # Connect to a series of devices and print facts related to each device to both the console and an inventory text file. 4 | # 5 | # CONTACT: bvilletard@juniper.net; cbinckly@juniper.net; ksator@juniper.net; pgeenens@juniper.net; tgrimonet@juniper.net 6 | 7 | # 8 | # CREATED: 2015-11-11 9 | # 10 | # VERSION: 1 11 | # 12 | # USAGE: python facts/print_facts_2.py -h 13 | # ----------------------------------------------------------------------------------------------------------------------- 14 | 15 | from jnpr.junos import Device 16 | from datetime import datetime 17 | import logging 18 | import argparse 19 | 20 | parser = argparse.ArgumentParser(description="print hostname of devices not running the expected junos version") 21 | 22 | parser.add_argument('-u','--username' ,help='Username required to connect to devices',default="pytraining") 23 | parser.add_argument('-p','--password' ,help='User password to connect to devices',default="Poclab123") 24 | parser.add_argument('-v','--verbose' ,help='Increase verbosity, default=False',action='store_true', default=False) 25 | parser.add_argument('-vv','--very_verbose' ,help='Increase verbosity, default=False',action='store_true', default=False) 26 | 27 | 28 | options = parser.parse_args() 29 | 30 | if options.very_verbose: 31 | loglevel=logging.DEBUG 32 | elif options.verbose: 33 | loglevel=logging.INFO 34 | else: 35 | loglevel=logging.CRITICAL 36 | 37 | logging.basicConfig(level=loglevel, filename="logging.txt") 38 | 39 | 40 | t1 = datetime.now() 41 | mydeviceslist=["172.30.179.101","172.30.179.103", "172.30.179.104", "172.30.179.105"] 42 | print ("size of the list is: " + str(len(mydeviceslist)) + " devices") 43 | 44 | f=open("my_devices_inventory.txt", "a") 45 | f.write(str(datetime.now()) + '\n') 46 | for item in mydeviceslist: 47 | dev=Device(host=item, user=options.username, password=options.password) 48 | dev.open() 49 | dev.close() 50 | if dev.facts["version"]!="12.3R11.2": 51 | print ("the device "+ dev.facts["hostname"]+ " is a " + dev.facts['model'] + " running " + dev.facts["version"] 52 | ) 53 | f.write ( "the device "+ dev.facts["hostname"]+ " is a " + dev.facts['model'] + " running " + dev.facts["versio 54 | n"] + "\n") 55 | else: 56 | print("the device " + dev.facts["hostname"] + " is checked") 57 | t2 = datetime.now() 58 | t3 = t2-t1 59 | print("done for all devices in " + str(t3) ) 60 | f.close() 61 | -------------------------------------------------------------------------------- /napalm/conf.txt: -------------------------------------------------------------------------------- 1 | system { 2 | host-name newhostname; 3 | } 4 | -------------------------------------------------------------------------------- /napalm/readme.md: -------------------------------------------------------------------------------- 1 | NAPALM (Network Automation and Programmability Abstraction Layer with Multivendor support) is a Python library to interact with different network operating systems. 2 | source code: https://github.com/napalm-automation/napalm 3 | doc: https://napalm.readthedocs.io/en/latest/index.html 4 | 5 | **For more details about Junos automation with NAPALM you can refer to https://github.com/ksator/junos-automation-with-NAPALM** 6 | 7 | installation: 8 | ``` 9 | sudo pip install napalm 10 | 11 | ``` 12 | Usages: 13 | you can use napalm into a python script or as a cli tool. 14 | into a python script: 15 | ``` 16 | ksator@ubuntu:~$ more conf.txt 17 | system { 18 | host-name newhostname; 19 | } 20 | ``` 21 | ``` 22 | ksator@ubuntu:~$ python 23 | >>> import napalm 24 | >>> driver = napalm.get_network_driver('junos') 25 | >>> device = driver(hostname='172.30.179.107', username='pytraining', password='Poclab123', optional_args={'port': 830}) 26 | >>> device.open() 27 | >>> from pprint import pprint as pp 28 | >>> pp(device.get_facts()) 29 | {u'fqdn': u'newname.poc-nl.jnpr.net', 30 | u'hostname': u'newname', 31 | u'interface_list': ['ge-0/0/0', 32 | 'ge-0/0/1', 33 | 'ge-0/0/2', 34 | 'ge-0/0/3', 35 | 'ge-0/0/4', 36 | 'ge-0/0/5', 37 | 'ge-0/0/6', 38 | 'ge-0/0/7', 39 | 'ge-0/0/8', 40 | 'ge-0/0/9', 41 | 'ge-0/0/10', 42 | 'ge-0/0/11', 43 | 'ge-0/0/12', 44 | 'ge-0/0/13', 45 | 'ge-0/0/14', 46 | 'ge-0/0/15', 47 | 'ge-0/0/16', 48 | 'ge-0/0/17', 49 | 'ge-0/0/18', 50 | 'ge-0/0/19', 51 | 'ge-0/0/20', 52 | 'ge-0/0/21', 53 | 'ge-0/0/22', 54 | 'ge-0/0/23', 55 | 'ge-0/0/24', 56 | 'ge-0/0/25', 57 | 'ge-0/0/26', 58 | 'ge-0/0/27', 59 | 'ge-0/0/28', 60 | 'ge-0/0/29', 61 | 'ge-0/0/30', 62 | 'ge-0/0/31', 63 | 'ge-0/0/32', 64 | 'ge-0/0/33', 65 | 'ge-0/0/34', 66 | 'ge-0/0/35', 67 | 'ge-0/0/36', 68 | 'ge-0/0/37', 69 | 'ge-0/0/38', 70 | 'ge-0/0/39', 71 | 'ge-0/0/40', 72 | 'ge-0/0/41', 73 | 'ge-0/0/42', 74 | 'ge-0/0/43', 75 | 'ge-0/0/44', 76 | 'ge-0/0/45', 77 | 'ge-0/0/46', 78 | 'ge-0/0/47', 79 | '.local.', 80 | 'vcp-0', 81 | 'vcp-1', 82 | 'bme0', 83 | 'dsc', 84 | 'gre', 85 | 'ipip', 86 | 'lo0', 87 | 'lsi', 88 | 'me0', 89 | 'mtun', 90 | 'pimd', 91 | 'pime', 92 | 'tap', 93 | 'vlan', 94 | 'vme'], 95 | u'model': u'EX4200-48T', 96 | u'os_version': u'12.3R11.2', 97 | u'serial_number': u'BP0208111225', 98 | u'uptime': 15827760, 99 | u'vendor': u'Juniper'} 100 | >>> 101 | >>> device.load_merge_candidate(filename='conf.txt') 102 | >>> device.compare_config() 103 | '[edit system]\n- host-name newname;\n+ host-name newhostname;' 104 | >>> device.rollback() 105 | >>> 106 | >>> device.is_alive() 107 | {u'is_alive': True} 108 | >>> 109 | >>> device.close() 110 | >>> device.is_alive() 111 | {u'is_alive': False} 112 | >>> 113 | >>> exit() 114 | ``` 115 | You can use napalm cli tool: 116 | 117 | to get the help, run this command: 118 | ``` 119 | cl_napalm_configure --help 120 | ``` 121 | Example with dry run: 122 | ``` 123 | ksator@ubuntu:~$ cl_napalm_configure --user pytraining --vendor junos --strategy merge --dry-run 172.30.177.170 napalm/conf.txt 124 | Enter password: 125 | [edit system] 126 | - host-name mx80-17; 127 | + host-name newhostname; 128 | ksator@ubuntu:~$ 129 | ``` 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /python_basics/list_of_ip.txt: -------------------------------------------------------------------------------- 1 | 172.30.179.101 2 | 172.30.179.102 3 | 172.30.179.103 4 | 172.30.179.104 5 | 172.30.179.105 6 | -------------------------------------------------------------------------------- /python_basics/regex_hostname.py: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------------------------------------------------- 2 | # DESCRIPTION: 3 | # Extract the hostname from a JUNOS configuration file using regular expressions. 4 | # 5 | # CONTACT: bvilletard@juniper.net; cbinckly@juniper.net; ksator@juniper.net; pgeenens@juniper.net; tgrimonet@juniper.net 6 | # 7 | # CREATED: 2015-11-11 8 | # 9 | # VERSION: 1 10 | # 11 | # USAGE: regex_hostname.py 12 | # ----------------------------------------------------------------------------------------------------------------------- 13 | 14 | import re 15 | # show_config.txt is a JUNOS configuration file 16 | f=open("python_basics/show_config.txt") 17 | for line in f: 18 | if re.search("host-name",line): 19 | hostname=line.split(" ")[-1].strip() 20 | print hostname 21 | 22 | -------------------------------------------------------------------------------- /python_basics/show_config.txt: -------------------------------------------------------------------------------- 1 | admin@FR-EX2200-110> show configuration | display set 2 | set version 12.3R3.4 3 | set system host-name FR-EX2200-110 4 | set system auto-snapshot 5 | set system services ftp 6 | set system services ssh root-login allow 7 | set system services ssh protocol-version v2 8 | set system services ssh max-sessions-per-connection 32 9 | set system services netconf ssh 10 | set system services web-management http 11 | set system syslog user * any emergency 12 | set system syslog file messages any notice 13 | set system syslog file messages authorization info 14 | set system syslog file interactive-commands interactive-commands any 15 | set system syslog file default-log-messages any any 16 | set system syslog file default-log-messages match "(requested 'commit' operation)|(copying configuration to juniper.save)|(commit complete)|ifAdminStatus|(FRU power)|(FRU removal)|(FRU insertion)|(link UP)|transitioned|Transferred|transfer-file|(license add)|(license delete)|(package -X update)|(package -X delete)|(FRU Online)|(FRU Offline)|(plugged in)|(unplugged)|cm_device|(Master Unchanged, Members Changed)|(Master Changed, Members Changed)|(Master Detected, Members Changed)|(vc add)|(vc delete)|(Master detected)|(Master changed)|(Backup detected)|(Backup changed)|(interface vcp-)" 17 | set system syslog file default-log-messages structured-data 18 | set chassis aggregated-devices ethernet device-count 5 19 | set interfaces ge-0/0/0 description "EX2200-VC-FRONT ge-2/0/4" 20 | set interfaces ge-0/0/0 unit 0 family ethernet-switching port-mode trunk 21 | set interfaces ge-0/0/0 unit 0 family ethernet-switching vlan members vlan-workshop-171 22 | set interfaces ge-0/0/0 unit 0 family ethernet-switching vlan members vlan-workshop-172 23 | set interfaces ge-0/0/0 unit 0 family ethernet-switching vlan members vlan-workshop-173 24 | set interfaces ge-0/0/0 unit 0 family ethernet-switching vlan members vlan-109 25 | set interfaces ge-0/0/0 unit 0 family ethernet-switching vlan members vlan-workshop-174 26 | set interfaces ge-0/0/0 unit 0 family ethernet-switching vlan members vlan-workshop-175 27 | set interfaces ge-0/0/0 unit 0 family ethernet-switching vlan members vlan-110 28 | set interfaces ge-0/0/0 unit 0 family ethernet-switching vlan members vlan-111 29 | set interfaces ge-0/0/0 unit 0 family ethernet-switching vlan members vlan-roma-1068 30 | set interfaces ge-0/0/0 unit 0 family ethernet-switching native-vlan-id default 31 | set interfaces ge-0/0/1 description "J2320-TOKYO ge-0/0/2" 32 | set interfaces ge-0/0/1 unit 0 family ethernet-switching port-mode trunk 33 | set interfaces ge-0/0/1 unit 0 family ethernet-switching vlan members vlan-1002 34 | set interfaces ge-0/0/1 unit 0 family ethernet-switching vlan members vlan-1004 35 | set interfaces ge-0/0/2 description SRX550-Site1 36 | set interfaces ge-0/0/2 unit 0 family ethernet-switching port-mode access 37 | set interfaces ge-0/0/2 unit 0 family ethernet-switching vlan members vlan-workshop-172 38 | set interfaces ge-0/0/3 description SRX650-Site2 39 | set interfaces ge-0/0/3 unit 0 family ethernet-switching port-mode access 40 | set interfaces ge-0/0/3 unit 0 family ethernet-switching vlan members vlan-workshop-173 41 | set interfaces ge-0/0/4 description "Trafic client" 42 | set interfaces ge-0/0/4 unit 0 family ethernet-switching port-mode access 43 | set interfaces ge-0/0/4 unit 0 family ethernet-switching vlan members vlan-workshop-175 44 | set interfaces ge-0/0/5 mtu 9216 45 | set interfaces ge-0/0/5 unit 0 family ethernet-switching port-mode access 46 | set interfaces ge-0/0/5 unit 0 family ethernet-switching vlan members vlan-104 47 | set interfaces ge-0/0/6 unit 0 family ethernet-switching port-mode access 48 | set interfaces ge-0/0/6 unit 0 family ethernet-switching vlan members vlan-workshop-174 49 | set interfaces ge-0/0/7 unit 0 description "POC SG FGU - VLAN 172 - SITE BRUGES" 50 | set interfaces ge-0/0/7 unit 0 family ethernet-switching port-mode access 51 | set interfaces ge-0/0/7 unit 0 family ethernet-switching vlan members vlan-workshop-172 52 | set interfaces ge-0/0/8 description "SRX210-BERLIN ge-0/0/2" 53 | set interfaces ge-0/0/8 unit 0 family ethernet-switching port-mode access 54 | set interfaces ge-0/0/8 unit 0 family ethernet-switching vlan members vlan-109 55 | 56 | set interfaces ge-0/0/9 unit 0 description "POC SG FGU - VLAN 172 - SITE BRUGES" 57 | set interfaces ge-0/0/9 unit 0 family ethernet-switching port-mode access 58 | set interfaces ge-0/0/9 unit 0 family ethernet-switching vlan members vlan-workshop-172 59 | set interfaces ge-0/0/10 unit 0 family ethernet-switching port-mode access 60 | set interfaces ge-0/0/10 unit 0 family ethernet-switching vlan members vlan-109 61 | set interfaces ge-0/0/11 unit 0 description "POC SG FGU - VLAN 172 - SITE BRUGES" 62 | set interfaces ge-0/0/11 unit 0 family ethernet-switching port-mode access 63 | set interfaces ge-0/0/11 unit 0 family ethernet-switching vlan members vlan-workshop-172 64 | set interfaces ge-0/0/12 description "SRX550-IBIZA-A port ge-0/0/4" 65 | set interfaces ge-0/0/12 ether-options 802.3ad ae2 66 | set interfaces ge-0/0/13 description "SRX550-IBIZA-B port ge-0/0/4" 67 | set interfaces ge-0/0/13 unit 0 family ethernet-switching port-mode access 68 | set interfaces ge-0/0/13 unit 0 family ethernet-switching vlan members vlan-202 69 | set interfaces ge-0/0/15 description "SRX550-IBIZA-B port ge-0/0/3" 70 | set interfaces ge-0/0/15 unit 0 family ethernet-switching port-mode access 71 | set interfaces ge-0/0/15 unit 0 family ethernet-switching vlan members vlan-201 72 | set interfaces ge-0/0/16 description "SRX210NYC - ge-0/0/3" 73 | set interfaces ge-0/0/16 unit 0 family ethernet-switching port-mode trunk 74 | set interfaces ge-0/0/16 unit 0 family ethernet-switching vlan members vlan-150 75 | set interfaces ge-0/0/16 unit 0 family ethernet-switching vlan members vlan-1111 76 | set interfaces ge-0/0/18 description "SRX3400-HAUT ge-0/0/0" 77 | set interfaces ge-0/0/18 ether-options 802.3ad ae0 78 | set interfaces ge-0/0/19 description "SRX3400-BAS ge-0/0/0" 79 | set interfaces ge-0/0/19 ether-options 802.3ad ae1 80 | set interfaces ge-0/0/20 vlan-tagging 81 | set interfaces ge-0/0/20 unit 10 vlan-id 10 82 | set interfaces ge-0/0/20 unit 20 vlan-id 20 83 | set interfaces ge-0/0/22 unit 0 family ethernet-switching port-mode access 84 | set interfaces ge-0/0/23 unit 0 family ethernet-switching port-mode access 85 | set interfaces ge-0/1/0 ether-options 802.3ad ae3 86 | set interfaces ge-0/1/1 description "SRX3400-BAS HA Control link" 87 | set interfaces ge-0/1/1 unit 0 family ethernet-switching port-mode access 88 | set interfaces ge-0/1/1 unit 0 family ethernet-switching vlan members vlan-SRXHA-ControlLink 89 | set interfaces ge-1/0/3 description "Control Link SRX550" 90 | set interfaces ge-1/0/3 mtu 9216 91 | set interfaces ge-1/0/3 unit 0 family ethernet-switching port-mode access 92 | set interfaces ge-1/0/3 unit 0 family ethernet-switching vlan members vlan-104 93 | set interfaces ge-1/0/4 description "FABRIC Link SRX550" 94 | set interfaces ge-1/0/4 mtu 9216 95 | set interfaces ge-1/0/4 unit 0 family ethernet-switching port-mode access 96 | set interfaces ge-1/0/4 unit 0 family ethernet-switching vlan members vlan-103 97 | set interfaces ge-1/0/12 ether-options 802.3ad ae2 98 | set interfaces ge-1/1/0 ether-options 802.3ad ae3 99 | set interfaces ae0 description "LAG to SRX3400-HAUT ge-0/0/0 ge-0/0/2" 100 | set interfaces ae0 unit 0 family ethernet-switching port-mode trunk 101 | set interfaces ae0 unit 0 family ethernet-switching vlan members vlan-workshop-171 102 | set interfaces ae0 unit 0 family ethernet-switching vlan members vlan-workshop-172 103 | set interfaces ae0 unit 0 family ethernet-switching vlan members vlan-workshop-173 104 | set interfaces ae0 unit 0 family ethernet-switching vlan members vlan-workshop-174 105 | set interfaces ae0 unit 0 family ethernet-switching vlan members vlan-workshop-175 106 | set interfaces ae0 unit 0 family ethernet-switching vlan members vlan-109 107 | set interfaces ae1 description "LAG to SRX3400-BAS ge-0/0/0 ge-0/0/2" 108 | set interfaces ae1 unit 0 family ethernet-switching port-mode trunk 109 | set interfaces ae1 unit 0 family ethernet-switching vlan members vlan-workshop-171 110 | set interfaces ae1 unit 0 family ethernet-switching vlan members vlan-workshop-172 111 | set interfaces ae1 unit 0 family ethernet-switching vlan members vlan-workshop-173 112 | ---(more 53%)---[]--- 113 | 114 | set interfaces ae1 unit 0 family ethernet-switching vlan members vlan-workshop-174 115 | set interfaces ae1 unit 0 family ethernet-switching vlan members vlan-workshop-175 116 | set interfaces ae1 unit 0 family ethernet-switching vlan members vlan-109 117 | set interfaces ae2 unit 0 family ethernet-switching port-mode trunk 118 | set interfaces ae2 unit 0 family ethernet-switching vlan members vlan-110 119 | set interfaces ae2 unit 0 family ethernet-switching vlan members vlan-workshop-175 120 | set interfaces ae3 mtu 9216 121 | set interfaces ae3 unit 0 family ethernet-switching port-mode trunk 122 | set interfaces ae3 unit 0 family ethernet-switching vlan members vlan-103 123 | set interfaces ae3 unit 0 family ethernet-switching vlan members vlan-104 124 | set interfaces ae3 unit 0 family ethernet-switching vlan members vlan-102 125 | set interfaces ae3 unit 0 family ethernet-switching vlan members vlan-101 126 | set interfaces me0 unit 0 family inet address 172.30.108.110/24 127 | set interfaces vlan unit 0 family inet address 172.30.108.14/24 128 | set interfaces vlan unit 109 family inet address 172.30.109.14/24 129 | set interfaces vlan unit 171 family inet address 10.0.1.14/24 130 | set interfaces vlan unit 172 family inet address 10.0.2.14/24 131 | set interfaces vlan unit 173 family inet address 10.10.173.12/24 132 | set interfaces vlan unit 201 family inet address 1.1.1.254/24 133 | set interfaces vlan unit 202 family inet address 2.2.2.254/24 134 | ---(more 61%)---[]--- 135 | 136 | set snmp community public authorization read-only 137 | set routing-options static route 172.26.0.0/16 next-hop 172.30.108.1 138 | set routing-options static route 0.0.0.0/0 next-hop 172.30.108.1 139 | set routing-options static route 10.0.2.0/24 next-hop 10.0.1.1 140 | set routing-options static route 10.10.172.0/24 next-hop 10.10.173.1 141 | deactivate protocols rstp 142 | set protocols lldp port-id-subtype interface-name 143 | set protocols lldp interface all 144 | set protocols lldp interface vme.0 145 | set protocols lldp-med interface all 146 | set ethernet-switching-options storm-control interface all level 50 147 | set vlans Vlan-3333 description "VLAN de test" 148 | set vlans Vlan-3333 vlan-id 3333 149 | set vlans default l3-interface vlan.0 150 | set vlans vlan-1001 vlan-id 1001 151 | set vlans vlan-1002 vlan-id 1002 152 | set vlans vlan-1003 vlan-id 1003 153 | set vlans vlan-1004 vlan-id 1004 154 | set vlans vlan-1005 vlan-id 1005 155 | set vlans vlan-101 vlan-id 101 156 | set vlans vlan-102 vlan-id 102 157 | set vlans vlan-103 vlan-id 103 158 | set vlans vlan-104 vlan-id 104 159 | ---(more 82%)---[]--- 160 | 161 | set vlans vlan-109 description "Profile created as part of device discovery" 162 | set vlans vlan-109 vlan-id 109 163 | set vlans vlan-109 l3-interface vlan.109 164 | set vlans vlan-110 description "Vlan 172.30.110.0/24" 165 | set vlans vlan-110 vlan-id 110 166 | set vlans vlan-111 description "Vlan 172.30.111.0/24" 167 | set vlans vlan-111 vlan-id 111 168 | set vlans vlan-1111 vlan-id 1111 169 | set vlans vlan-150 vlan-id 150 170 | set vlans vlan-172 171 | set vlans vlan-173 172 | set vlans vlan-175 173 | set vlans vlan-2001 vlan-id 2001 174 | set vlans vlan-2002 vlan-id 2002 175 | set vlans vlan-201 vlan-id 201 176 | set vlans vlan-201 l3-interface vlan.201 177 | set vlans vlan-202 vlan-id 202 178 | set vlans vlan-202 l3-interface vlan.202 179 | set vlans vlan-3001 vlan-id 3001 180 | set vlans vlan-3002 vlan-id 3002 181 | set vlans vlan-SRXHA-ControlLink vlan-id 1997 182 | set vlans vlan-SRXHA-ControlLink interface ge-0/1/1.0 183 | set vlans vlan-SRXHA-FabricLink vlan-id 1996 184 | ---(more 92%)---[]--- 185 | 186 | set vlans vlan-SRXHA-FabricLink interface ge-0/0/22.0 187 | set vlans vlan-SRXHA-FabricLink interface ge-0/0/23.0 188 | set vlans vlan-rewrite-177 vlan-id 177 189 | set vlans vlan-rewrite-178 vlan-id 178 190 | set vlans vlan-rewrite-179 vlan-id 179 191 | set vlans vlan-rewrite-180 vlan-id 180 192 | set vlans vlan-roma-1068 description "Vlan Site-Roma" 193 | set vlans vlan-roma-1068 vlan-id 1068 194 | set vlans vlan-workshop-171 vlan-id 171 195 | set vlans vlan-workshop-171 l3-interface vlan.171 196 | set vlans vlan-workshop-172 vlan-id 172 197 | set vlans vlan-workshop-172 l3-interface vlan.172 198 | set vlans vlan-workshop-173 vlan-id 173 199 | set vlans vlan-workshop-173 l3-interface vlan.173 200 | set vlans vlan-workshop-174 vlan-id 174 201 | set vlans vlan-workshop-175 vlan-id 175 202 | set vlans vlan10 vlan-id 10 203 | set vlans vlan20 vlan-id 20 204 | 205 | {master:0} 206 | admin@FR-EX2200-110> 207 | -------------------------------------------------------------------------------- /python_basics/show_config_vlans.txt: -------------------------------------------------------------------------------- 1 | admin@FR-EX2200-110> show configuration vlans | display set 2 | set vlans Vlan-3333 description "VLAN de test" 3 | set vlans Vlan-3333 vlan-id 3333 4 | set vlans default l3-interface vlan.0 5 | set vlans vlan-1001 vlan-id 1001 6 | set vlans vlan-1002 vlan-id 1002 7 | set vlans vlan-1003 vlan-id 1003 8 | set vlans vlan-1004 vlan-id 1004 9 | set vlans vlan-1005 vlan-id 1005 10 | set vlans vlan-101 vlan-id 101 11 | set vlans vlan-102 vlan-id 102 12 | set vlans vlan-103 vlan-id 103 13 | set vlans vlan-104 vlan-id 104 14 | set vlans vlan-109 description "Profile created as part of device discovery" 15 | set vlans vlan-109 vlan-id 109 16 | set vlans vlan-109 l3-interface vlan.109 17 | set vlans vlan-110 description "Vlan 172.30.110.0/24" 18 | set vlans vlan-110 vlan-id 110 19 | set vlans vlan-111 description "Vlan 172.30.111.0/24" 20 | set vlans vlan-111 vlan-id 111 21 | set vlans vlan-1111 vlan-id 1111 22 | set vlans vlan-150 vlan-id 150 23 | set vlans vlan-172 24 | set vlans vlan-173 25 | set vlans vlan-175 26 | set vlans vlan-2001 vlan-id 2001 27 | set vlans vlan-2002 vlan-id 2002 28 | set vlans vlan-201 vlan-id 201 29 | set vlans vlan-201 l3-interface vlan.201 30 | set vlans vlan-202 vlan-id 202 31 | set vlans vlan-202 l3-interface vlan.202 32 | set vlans vlan-3001 vlan-id 3001 33 | set vlans vlan-3002 vlan-id 3002 34 | set vlans vlan-SRXHA-ControlLink vlan-id 1997 35 | set vlans vlan-SRXHA-ControlLink interface ge-0/1/1.0 36 | set vlans vlan-SRXHA-FabricLink vlan-id 1996 37 | set vlans vlan-SRXHA-FabricLink interface ge-0/0/22.0 38 | set vlans vlan-SRXHA-FabricLink interface ge-0/0/23.0 39 | set vlans vlan-rewrite-177 vlan-id 177 40 | set vlans vlan-rewrite-178 vlan-id 178 41 | set vlans vlan-rewrite-179 vlan-id 179 42 | set vlans vlan-rewrite-180 vlan-id 180 43 | set vlans vlan-roma-1068 description "Vlan Site-Roma" 44 | set vlans vlan-roma-1068 vlan-id 1068 45 | set vlans vlan-workshop-171 vlan-id 171 46 | set vlans vlan-workshop-171 l3-interface vlan.171 47 | set vlans vlan-workshop-172 vlan-id 172 48 | set vlans vlan-workshop-172 l3-interface vlan.172 49 | set vlans vlan-workshop-173 vlan-id 173 50 | set vlans vlan-workshop-173 l3-interface vlan.173 51 | set vlans vlan-workshop-174 vlan-id 174 52 | set vlans vlan-workshop-175 vlan-id 175 53 | set vlans vlan10 vlan-id 10 54 | set vlans vlan20 vlan-id 20 55 | 56 | {master:0} 57 | admin@FR-EX2200-110> 58 | -------------------------------------------------------------------------------- /rest_basics/get_mx_software_information.py: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------------------------------------------------- 2 | # DESCRIPTION: 3 | # Retrieve software information from an MX router with a REST API call. 4 | # 5 | # CONTACT: bvilletard@juniper.net; cbinckly@juniper.net; ksator@juniper.net; pgeenens@juniper.net; tgrimonet@juniper.net 6 | # 7 | # CREATED: 2015-11-11 8 | # 9 | # VERSION: 1 10 | # 11 | # USAGE: get_mx_software_information.py 12 | # ----------------------------------------------------------------------------------------------------------------------- 13 | 14 | import requests 15 | from pprint import pprint 16 | from requests.auth import HTTPBasicAuth 17 | 18 | my_headers = { 'Accept': 'application/json' } 19 | 20 | r = requests.get('http://172.30.177.170:3000/rpc/get-software-information', auth=HTTPBasicAuth('pytraining', 'Poclab123'), headers=my_headers) 21 | 22 | #pprint(r.json()) 23 | print "Software version: " + r.json()['software-information'][0]['junos-version'][0]['data'] 24 | print "Host-name: " + r.json()['software-information'][0]['host-name'][0]['data'] 25 | print "Product name: " + r.json()['software-information'][0]['product-name'][0]['data'] 26 | -------------------------------------------------------------------------------- /rest_basics/google_map_api.py: -------------------------------------------------------------------------------- 1 | 2 | #!/usr/bin/env python 3 | 4 | # google map url is https://www.google.co.uk/maps/place/41+Rue+de+Villiers,+92200+Neuilly-sur-Seine,+France 5 | # google map api is http://maps.googleapis.com/maps/api/geocode/json?address=41 rue de villiers neuilly sur seine 6 | 7 | import requests 8 | addr= raw_input("which address: ") 9 | url='http://maps.googleapis.com/maps/api/geocode/json?address=' + addr 10 | r = requests.get(url) 11 | # r.status_code 12 | # r.json() 13 | print "latitude is " + str(r.json()['results'][0]['geometry']['location']['lat']) 14 | print "longitude is " + str(r.json()['results'][0]['geometry']['location']['lng']) 15 | 16 | -------------------------------------------------------------------------------- /rest_basics/junos_space.py: -------------------------------------------------------------------------------- 1 | #extract the ip address of all EX4300-48T from junos space 2 | 3 | from pprint import pprint as pp 4 | from requests import get 5 | from requests.auth import HTTPBasicAuth 6 | 7 | ''' 8 | https://www.juniper.net/techpubs/en_US/junos-space-sdk/13.3/apiref/com.juniper.junos_space.sdk.help/JSInventoryManagerSVC/Docs/rest.managed-elements.html 9 | ''' 10 | url='https://172.30.109.80' 11 | uri=url + '/api/space/managed-domain/managed-elements' 12 | my_headers= {'Accept': 'application/vnd.net.juniper.space.managed-domain.managed-elements+json;version=1'} 13 | r = get(uri, auth=HTTPBasicAuth('super', 'Juniper123'), verify=False, headers=my_headers) 14 | 15 | #r.json()['managed-elements']['managed-element'][-1] 16 | 17 | #for item in r.json()['managed-elements']['managed-element']: 18 | # print item[u'ipAddr'] 19 | 20 | ''' 21 | https://www.juniper.net/techpubs/en_US/junos-space-sdk/13.3/apiref/com.juniper.junos_space.sdk.help/JSInventoryManagerSVC/Docs/rest.managed-elements.id.html 22 | ''' 23 | my_headers2={'Accept': 'application/vnd.net.juniper.space.managed-domain.managed-element+json;version=1;q=.01'} 24 | for item in r.json()['managed-elements']['managed-element']: 25 | id = int(item[u'@key']) 26 | uri = url + "/api/space/managed-domain/managed-elements/"+str(id) 27 | r2 = get(uri, auth=HTTPBasicAuth('super', 'Juniper123'), verify=False, headers=my_headers2) 28 | if str(r2.json()['managed-element']['device']['platform'])=="EX4300-48T": 29 | print r2.json()['managed-element']['ipAddr'] 30 | 31 | ''' 32 | output is the ip address of EX4300-48T devices in junos space 33 | 172.30.108.138 34 | 172.30.108.134 35 | ''' 36 | -------------------------------------------------------------------------------- /rest_basics/readme.md: -------------------------------------------------------------------------------- 1 | This is about making rest calls from a rest client in python. 2 | if you are looking to design a restfull api, refer to the [flask directory](https://github.com/ksator/python-training-for-network-engineers/tree/master/flask) 3 | 4 | -------------------------------------------------------------------------------- /rpc-netconf-lxml-ncclient/lxml.md: -------------------------------------------------------------------------------- 1 | # lxml python package tutorial 2 | 3 | ### etree module 4 | 5 | ###### import the etree module from the the lxml package 6 | ``` 7 | >>> from lxml import etree 8 | ``` 9 | 10 | ###### Call the Element class 11 | ``` 12 | >>> root=etree.Element('root') 13 | >>> type(root) 14 | 15 | >>> 16 | ``` 17 | 18 | ###### The function tostring returns a string representation of an element 19 | ``` 20 | >>> etree.tostring(root) 21 | '' 22 | >>> 23 | ``` 24 | 25 | ###### To create child elements and add them to a parent element, you can use the append() method: 26 | ``` 27 | >>> root.append(etree.Element('child1')) 28 | >>> etree.tostring(root) 29 | '' 30 | >>> 31 | >>> print etree.tostring(root, pretty_print=True) 32 | 33 | 34 | 35 | >>> 36 | ``` 37 | 38 | ###### Subelement class creates an element instance, and appends it to an existing element. 39 | ``` 40 | >>> child2 = etree.SubElement(root, "child2") 41 | >>> child3 = etree.SubElement(root, "child3") 42 | >>> print(etree.tostring(root, pretty_print=True)) 43 | 44 | 45 | 46 | 47 | 48 | ``` 49 | 50 | ###### the function dump 51 | ``` 52 | >>> etree.dump(root) 53 | 54 | 55 | 56 | 57 | 58 | >>> 59 | ``` 60 | 61 | ###### we can handle elements like lists 62 | ``` 63 | >>>len(root) 64 | 3 65 | >>> for item in root: 66 | ... print item 67 | ... 68 | 69 | 70 | 71 | >>> for item in root: 72 | ... print item.tag 73 | ... 74 | child1 75 | child2 76 | child3 77 | >>> root[0] 78 | 79 | >>> root[-1] 80 | 81 | >>> 82 | ``` 83 | 84 | ###### getparent(), getchildren(), getprevious(), getnext() 85 | we can call getparent(), getchildren(), getprevious(), getnext() to get the parent, children, neigbbors of an element 86 | ``` 87 | >>> root.getchildren() 88 | [, , ] 89 | >>> child2.getprevious() 90 | 91 | >>> child2.getparent() 92 | 93 | >>> child2.getnext() 94 | 95 | ``` 96 | 97 | ###### Elements can contain text: 98 | ``` 99 | >>> child2.text = 'qwerty' 100 | >>> print child2.text 101 | qwerty 102 | >>> etree.tostring(root) 103 | 'qwerty' 104 | >>> print etree.tostring(root) 105 | qwerty 106 | >>> print etree.tostring(root, pretty_print=True) 107 | 108 | 109 | qwerty 110 | 111 | 112 | >>> etree.SubElement(root, "another").text = "zxcvb" 113 | >>> print etree.tostring(root, pretty_print=True) 114 | 115 | 116 | qwerty 117 | 118 | zxcvb 119 | 120 | ``` 121 | 122 | ### findtext, find, findall 123 | 124 | ###### findtext function returns the text for the matching subelement 125 | ``` 126 | >>> etree.dump(root) 127 | 128 | 129 | 130 | 131 | zxcvb 132 | 133 | >>> root.findtext('another') 134 | 'zxcvb' 135 | >>> 136 | ``` 137 | ###### find function returns a subelement 138 | ``` 139 | >>> root.find('another') 140 | 141 | >>> root.find('another').text 142 | 'zxcvb' 143 | >>> 144 | ``` 145 | 146 | ###### findall function returns a list of subelements 147 | ``` 148 | >>> etree.SubElement(root, "another").text = "advcrwvc" 149 | >>> etree.SubElement(root, "another").text = "vfet" 150 | >>> etree.dump(root) 151 | 152 | 153 | 154 | 155 | zxcvb 156 | advcrwvc 157 | vfet 158 | 159 | >>> root.findall('another') 160 | [, , ] 161 | >>> for item in root.findall('another'): 162 | ... print item.text 163 | ... 164 | zxcvb 165 | advcrwvc 166 | vfet 167 | >>> 168 | ``` 169 | 170 | ### builder module 171 | 172 | ###### Import the class E (aka ElementMaker) from the module lxml.builder 173 | ``` 174 | >>> from lxml.builder import E 175 | ``` 176 | ###### Call the class E 177 | ``` 178 | >>> rpc = E('root', E('child1')) 179 | >>> type(rpc) 180 | 181 | >>> print(etree.tostring(rpc, pretty_print=True)) 182 | 183 | 184 | 185 | >>> rpc = E('root', E('child1', 'blabla')) 186 | >>> print(etree.tostring(rpc, pretty_print=True)) 187 | 188 | blabla 189 | 190 | >>> from lxml.builder import E 191 | >>> rpc = E('get', E('filter', {'type': 'xpath', 'source': '/bgp'})) 192 | >>> type(rpc) 193 | 194 | >>> etree.tostring(rpc) 195 | '' 196 | >>> print(etree.tostring(rpc, pretty_print=True)) 197 | 198 | 199 | 200 | ``` 201 | 202 | -------------------------------------------------------------------------------- /rpc-netconf-lxml-ncclient/ncclient.md: -------------------------------------------------------------------------------- 1 | ## What is it? 2 | NetConf client implementation in Python 3 | Github https://github.com/ncclient/ncclient 4 | PyPI (Python Package Index) https://pypi.python.org/pypi/ncclient 5 | 6 | ## Connect to a device 7 | ``` 8 | >>> from ncclient import manager 9 | >>> dev=manager.connect(host="ex4200-10", port=830, username="pytraining", password="Poclab123", hostkey_verify=False) 10 | >>> dev.connected 11 | True 12 | ``` 13 | 14 | ## NetConf capabilities 15 | 16 | #### print NetConf server capabilities 17 | ``` 18 | >>> for item in dev.server_capabilities: 19 | ... print item 20 | ... 21 | http://xml.juniper.net/dmi/system/1.0 22 | urn:ietf:params:xml:ns:netconf:capability:confirmed-commit:1.0 23 | urn:ietf:params:xml:ns:netconf:capability:validate:1.0 24 | urn:ietf:params:netconf:capability:validate:1.0 25 | urn:ietf:params:netconf:capability:confirmed-commit:1.0 26 | http://xml.juniper.net/netconf/junos/1.0 27 | urn:ietf:params:netconf:base:1.0 28 | urn:ietf:params:netconf:capability:url:1.0?scheme=http,ftp,file 29 | urn:ietf:params:netconf:capability:candidate:1.0 30 | urn:ietf:params:xml:ns:netconf:capability:candidate:1.0 31 | urn:ietf:params:xml:ns:netconf:capability:url:1.0?protocol=http,ftp,file 32 | urn:ietf:params:xml:ns:netconf:base:1.0 33 | >>> 34 | ``` 35 | #### check if the server advertised some NetConf capabilities 36 | ``` 37 | >>> assert(":validate" in dev.server_capabilities), "NetConf server did not advertise the capability :validate" 38 | >>> assert(":candidate" in dev.server_capabilities), "NetConf server did not advertise the capability :candidate" 39 | >>> assert(":confirmed-commit" in dev.server_capabilities), "NetConf server did not advertise the capability :confirmed-commit" 40 | >>> 41 | ``` 42 | 43 | #### print NetConf client capabilities 44 | ``` 45 | >>> for item in dev.client_capabilities: 46 | ... print item 47 | ... 48 | urn:ietf:params:netconf:capability:writable-running:1.0 49 | urn:ietf:params:netconf:capability:rollback-on-error:1.0 50 | urn:ietf:params:netconf:capability:validate:1.0 51 | urn:ietf:params:netconf:capability:confirmed-commit:1.0 52 | urn:ietf:params:netconf:capability:url:1.0?scheme=http,ftp,file,https,sftp 53 | urn:ietf:params:netconf:base:1.0 54 | urn:liberouter:params:netconf:capability:power-control:1.0 55 | urn:ietf:params:netconf:capability:candidate:1.0 56 | urn:ietf:params:netconf:capability:xpath:1.0 57 | urn:ietf:params:netconf:capability:startup:1.0 58 | urn:ietf:params:netconf:capability:interleave:1.0 59 | >>> 60 | ``` 61 | 62 | ## get_config 63 | 64 | #### backup the active configuration on your labtop 65 | ``` 66 | f=open ("config", 'w') 67 | f.write(str(dev.get_config(source='running'))) 68 | f.close() 69 | ``` 70 | 71 | #### print the candidate configuration 72 | ``` 73 | print dev.get_config(source="candidate") 74 | ``` 75 | 76 | #### use a subtree filter to get only interfaces configuration from the active configuration 77 | These 4 examples provide the same output: 78 | ``` 79 | criteria=''' 80 | 81 | 82 | 83 | 84 | ''' 85 | print dev.get_config(source="running", filter=("subtree", criteria)) 86 | ``` 87 | ``` 88 | criteria2=''' 89 | 90 | 91 | 92 | ''' 93 | print dev.get_config(source="running", filter=("subtree", criteria2)) 94 | ``` 95 | ``` 96 | criteria3=''' 97 | 98 | 99 | 100 | ''' 101 | print dev.get_config(source="running", filter=("subtree", criteria3)) 102 | ``` 103 | ``` 104 | criteria4=''' 105 | 106 | 107 | 108 | ''' 109 | print dev.get_config(source="running", filter=("subtree", criteria4)) 110 | ``` 111 | 112 | ## Update the candidate configuration, and commit 113 | ``` 114 | print 'locking configuration' 115 | dev.lock('candidate') 116 | snippet='''newname''' 117 | dev.edit_config(target='candidate', config=snippet) 118 | dev.commit() 119 | dev.unlock('candidate') 120 | ``` 121 | 122 | ## Revert the candidate configuration to the currently running configuration. 123 | 124 | #### Update the candidate configuration 125 | ``` 126 | snippet='''anothername''' 127 | dev.edit_config(target='candidate', config=snippet) 128 | ``` 129 | 130 | #### Get the candidate configuration 131 | ``` 132 | >>> criteria=''' 133 | ... 134 | ... 135 | ... 136 | ... 137 | ... 138 | ... ''' 139 | >>> print dev.get_config(source="candidate", filter=("subtree", criteria)) 140 | 141 | 142 | 143 | 144 | anothername 145 | 146 | 147 | 148 | 149 | ``` 150 | 151 | #### Revert the candidate configuration to the currently running configuration. 152 | Any uncommitted changes are discarded. 153 | ``` 154 | >>> dev.discard_changes() 155 | 156 | 157 | 158 | ``` 159 | #### Check the candidate configuration 160 | ``` 161 | >>> print dev.get_config(source="candidate", filter=("subtree", criteria)) 162 | 163 | 164 | 165 | 166 | ex4200-10 167 | 168 | 169 | 170 | 171 | >>> 172 | ``` 173 | 174 | ## lxml 175 | 176 | ``` 177 | >>> from lxml import etree 178 | >>> config_e = etree.Element("config") 179 | >>> configuration_e = etree.SubElement(config_e, "configuration") 180 | >>> system_e = etree.SubElement(configuration_e, "system") 181 | >>> name_server_e = etree.SubElement(system_e, "name-server") 182 | >>> name_server_e.text = '8.8.8.8' 183 | >>> print etree.tostring(config_e) 184 | 8.8.8.8 185 | >>> 186 | >>> print etree.tostring(config_e, pretty_print = True) 187 | 188 | 189 | 190 | 8.8.8.8 191 | 192 | 193 | 194 | >>> 195 | >>> etree.dump(config_e) 196 | 197 | 198 | 199 | 8.8.8.8 200 | 201 | 202 | 203 | >>> 204 | >>> name_server_e.text 205 | '8.8.8.8' 206 | >>> 207 | ``` 208 | ``` 209 | >>> from ncclient import manager 210 | >>> dev=manager.connect(host="ex4200-10", port=830, username="pytraining", password="Poclab123", hostkey_verify=False) 211 | >>> dev.edit_config(config=config_e, default_operation="merge", target="candidate") 212 | 213 | 214 | 215 | >>> dev.commit() 216 | ``` 217 | 218 | ## Close the NetConf session 219 | 220 | ``` 221 | >>> dev.connected 222 | True 223 | >>> dev.close_session() 224 | 225 | 226 | 227 | >>> 228 | >>> dev.connected 229 | False 230 | ``` 231 | 232 | ## device handlers 233 | if you add device_params={'name':'junos'} as an argument of the class connect, you can even better manage Junos devices. 234 | Few examples: 235 | - you can call the method commit with the argument comment: commit(comment="from ncclient") 236 | - you can use an xpath despite Junos device does not advertise the NetConf capability :xpath 237 | 238 | #### Commit comment 239 | ``` 240 | from ncclient import manager 241 | dev=manager.connect(host="ex4200-10", port=830, username="pytraining", password="Poclab123", hostkey_verify=False, device_params={'name':'junos'}) 242 | dev.lock('candidate') 243 | snippet='''newname''' 244 | dev.edit_config(target='candidate', config=snippet) 245 | dev.commit(comment="from ncclient") 246 | dev.unlock('candidate') 247 | ``` 248 | 249 | #### xpath 250 | ``` 251 | ttt=dev.get() 252 | print ttt 253 | ``` 254 | ``` 255 | >>> ttt.xpath('//name-server/name') 256 | [, ] 257 | >>> type(ttt.xpath('//name-server/name')) 258 | 259 | >>> len(ttt.xpath('//name-server/name')) 260 | 2 261 | >>> ttt.xpath('//name-server/name')[0] 262 | 263 | >>> ttt.xpath('//name-server/name')[0].text 264 | '172.30.179.2' 265 | >>> ttt.xpath('//name-server/name')[1].text 266 | '172.30.179.3' 267 | >>> for item in ttt.xpath('//name-server/name'): 268 | ... print item 269 | ... 270 | 271 | 272 | 273 | >>> for item in ttt.xpath('//name-server/name'): 274 | ... print item.text 275 | ... 276 | 172.30.179.2 277 | 172.30.179.3 278 | >>> 279 | 280 | >>> from lxml import etree 281 | >>> for item in ttt.xpath('//name-server/name'): 282 | ... print etree.tostring(item) 283 | ... 284 | 172.30.179.2 285 | 286 | 172.30.179.3 287 | 288 | >>> 289 | ``` 290 | 291 | 292 | 293 | 294 | 295 | -------------------------------------------------------------------------------- /rpc-netconf-lxml-ncclient/netconf.md: -------------------------------------------------------------------------------- 1 | You can refer to this [link] (https://github.com/ksator/openconfig-demo/wiki/3.3-Openconfig-using-NetConf-with-SSH) for NetConf examples 2 | 3 | -------------------------------------------------------------------------------- /rpc-netconf-lxml-ncclient/rpc.md: -------------------------------------------------------------------------------- 1 | ## RPC discovery method 2 | 3 | #### Using cli 4 | Use **| display xml rpc** 5 | ``` 6 | show route protocol isis 10.0.15.0/24 active-path | display xml rpc 7 | ``` 8 | 9 | #### Using rpc 10 | You can fetch the RPC for a CLI with **display_xml_rpc()** 11 | ``` 12 | >>> r0.display_xml_rpc('show system users') 13 | 14 | ``` 15 | ## RPC responses 16 | 17 | #### lxml.etree.Element 18 | By default, all PyEZ RPC responses are returned as an lxml.etree.Element object. 19 | 20 | ##### tag 21 | You can view the actual RPC name by invoking the tag property on this response object 22 | ``` 23 | >>> from lxml import etree 24 | >>> r0.display_xml_rpc('show system users').tag 25 | 'get-system-users-information' 26 | ``` 27 | ##### replace 28 | You can take this one step further by using the built in replace() string method to substitute hyphens with underscores: 29 | ``` 30 | >>> r0.display_xml_rpc('show system users').tag.replace('-','_') 31 | 'get_system_users_information' 32 | ``` 33 | ## Discover rpc and rpc paramters 34 | This recipe can be used to discover the RPC name for any Junos CLI command, but does not provide details on an RPC’s parameters. 35 | Passing the response from the display_xml_rpc() method to the etree.dump() function displays the full XML response (including RPC parameters): 36 | ``` 37 | >>> from lxml import etree 38 | >>> etree.dump(r0.display_xml_rpc('show route protocol isis 10.0.15.0/24 active-path')) 39 | 40 | 10.0.15.0/24 41 | 42 | isis 43 | 44 | ``` 45 | 46 | ## rpc on demand 47 | 48 | With RPC on Demand, there is no tight coupling. 49 | New features are added to each platform with each Junos release and the existing version of PyEZ can instantly access those RPCs. 50 | ``` 51 | >>> isis_route = r0.rpc.get_route_information(protocol='isis', destination='10.0.15.0/24', active_path=True) 52 | ``` 53 | 54 | ## rpc timeout 55 | 56 | ##### rpc default timeout 57 | 30s 58 | 59 | #### first method to change the timeout (for all rpc) 60 | ``` 61 | >>> r0.timeout 62 | 30 63 | >>> r0.timeout = 10 64 | >>> r0.timeout 65 | 10 66 | ``` 67 | #### second method (only for one rpc) 68 | ``` 69 | >>> bgp_routes = r0.rpc.get_route_information(dev_timeout = 180, protocol='bgp') 70 | ``` 71 | ## RPC Responses 72 | 73 | PyEZ responses is an lxml.etree.Element 74 | ``` 75 | >>> response = r0.rpc.get_system_users_information(normalize=True) 76 | ``` 77 | Each lxml.etree.Element object has links to parent, child, and sibling lxml.etree. 78 | Element objects, which form a tree representing the parsed XML response. 79 | 80 | #### lxml.etree.dump() 81 | For debugging purposes, the lxml.etree.dump() function can be used to dump the XML text of the response (albeit without the pretty formatting of the Junos CLI): 82 | ``` 83 | >>> from lxml import etree 84 | >>> etree.dump(response) 85 | 86 | 87 | ...ouput trimmed... 88 | 89 | 90 | >>> 91 | ``` 92 | ## convert lxml.etree.Element object to string (with etree.tostring) 93 | 94 | #### Rpc call 95 | 96 | https://github.com/vnitinv/pyez-examples/blob/master/2_rpc_call.py 97 | 98 | ``` 99 | from jnpr.junos import Device 100 | from lxml import etree 101 | dev = Device(host='xxxx', user='demo', password='demo123', gather_facts=False) 102 | dev.open() 103 | op = dev.rpc.get_interface_information() 104 | #op = dev.rpc.get_interface_information(interface_name='lo0', terse=True) 105 | print (etree.tostring(op)) 106 | dev.close() 107 | ``` 108 | 109 | #### get_config in xml 110 | 111 | https://github.com/vnitinv/pyez-examples/blob/master/10_get_config.py 112 | 113 | ``` 114 | from jnpr.junos import Device 115 | from lxml import etree 116 | dev = Device(host='xxxx', user='demo', password='demo123', gather_facts=False) 117 | dev.open() 118 | cnf = dev.rpc.get_config() 119 | print etree.tostring(cnf) 120 | ``` 121 | 122 | #### get_config in xml with a filter 123 | retrieve configuration from the Junos device 124 | and use Filter_xml to defines what to retrieve (when omitted the entire configuration is returned) 125 | http://junos-pyez.readthedocs.io/en/2.0.1/jnpr.junos.html#jnpr.junos.rpcmeta._RpcMetaExec.get_config 126 | the following returns the device host-name configured with “set system host-name”: 127 | ``` 128 | config = dev.rpc.get_config(filter_xml=etree.XML(‘’) 129 | ``` 130 | the following returns the "show configuration security ipsec | display xml": 131 | ``` 132 | filter=”” 133 | result=router.rpc.get_config(filter_xml=etree.XML(filter)) 134 | ``` 135 | 136 | 137 | 138 | 139 | 140 | ##### example: 141 | 142 | ``` 143 | >>> from jnpr.junos import Device 144 | >>> dev=Device(host="172.30.179.101", user="pytraining", password="Poclab123") 145 | >>> dev.open() 146 | Device(172.30.179.101) 147 | >>> rsp=dev.rpc.get_configuration() 148 | >>> type(rsp) 149 | 150 | >>> from lxml import etree 151 | >>> etree.dump(rsp) 152 | ... 153 | >>> print etree.tostring(rsp) 154 | ... 155 | ``` 156 | 157 | #### Print diff with rollback id 158 | 159 | ``` 160 | from jnpr.junos import Device 161 | from lxml import etree 162 | dev=Device(host="172.30.177.170", user=xxx, password=xxx) 163 | dev.open() 164 | rsp = dev.rpc.get_configuration(dict(compare='rollback', rollback='0', format='xml')) 165 | print etree.tostring(rsp) 166 | ``` 167 | 168 | ``` 169 | >>> print etree.tostring(rsp) 170 | 171 | 172 | [edit services analytics resource] 173 | +...interfaces { 174 | +.....ge-1/0/0 { 175 | +.......resource-profile default_resource_profile; 176 | +.....} 177 | +.....ge-1/0/1 { 178 | +.......resource-profile default_resource_profile; 179 | +.....} 180 | +...} 181 | ``` 182 | 183 | #### In set format 184 | 185 | Requirement: junos 15.1 or above 186 | ``` 187 | from jnpr.junos import Device 188 | from lxml import etree 189 | dev=Device(host="172.30.177.170", user=pytraining, password=Poclab123) 190 | dev.open() 191 | cnf = dev.rpc.get_configuration(dict(format="set")) 192 | type(cnf) 193 | cnf.text 194 | print cnf.text 195 | ``` 196 | 197 | ## Using XPath with lxml 198 | 199 | ``` 200 | user@r0> show system users | display xml rpc 201 | ``` 202 | ``` 203 | user@r0> show system users | display xml 204 | 205 | . 206 | .. 207 | ...4:11PM\ 208 | ...1 day, 8:29\ 209 | ...4\ 210 | ...0.56\ 211 | ... 212 | ``` 213 | ``` 214 | >>> response = r0.rpc.get_system_users_information(normalize=True) 215 | ``` 216 | #### findtext() 217 | 218 | findtext() method returns a string 219 | ``` 220 | >>> response.findtext("uptime-information/up-time") 221 | '1 day, 8:29' 222 | ``` 223 | The element also contains a seconds attribute that provides the system’s uptime in seconds since the system booted 224 | 225 | #### find() 226 | 227 | While the findtext() method returns a string, the find() method returns an lxml.etree.Element object. 228 | The XML attributes of that lxml.etree.Element object can then be accessed using the attrib dictionary 229 | ``` 230 | >>> response.find("uptime-information/up-time").attrib['seconds'] 231 | '116940' 232 | ``` 233 | #### findall() 234 | The findall() method returns a list of lxml.etree.Element objects matching an XPath 235 | ``` 236 | >>> users = response.findall("uptime-information/user-table/user-entry/user") 237 | >>> for user in users: 238 | ... print user.text 239 | ... 240 | root 241 | foo 242 | bar 243 | user 244 | >>> 245 | ``` 246 | The result of this example is a list of usernames for all users currently logged into the Junos device. 247 | 248 | ### Response Normalization 249 | 250 | alter the XML content returned from an RPC method 251 | When response normalization is enabled, all whitespace characters at the beginning and end of each XML element’s value are removed. 252 | Response normalization is disabled by default 253 | 254 | ``` 255 | >>> response = r0.rpc.get_system_users_information() 256 | >>> response.findtext("uptime-information/up-time") 257 | '\n4 days, 17 mins\n' 258 | ``` 259 | ``` 260 | >>> response = r0.rpc.get_system_users_information(normalize=True) 261 | >>> response.findtext("uptime-information/up-time") 262 | '4 days, 17 mins' 263 | ``` 264 | 265 | Notice the newline characters at the beginning and end of the value have been removed 266 | -------------------------------------------------------------------------------- /tables_and_views/bgp.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # ----------------------------------------------------------------------------- 3 | # BGP Neighbor table 4 | # ----------------------------------------------------------------------------- 5 | BGPNeighborTable: 6 | rpc: get-bgp-neighbor-information 7 | item: bgp-peer 8 | view: BGPneighborView 9 | key: peer-address 10 | 11 | BGPneighborView: 12 | fields: 13 | neighbor: peer-address 14 | state: peer-state 15 | type: peer-type 16 | flap_count: flap-count 17 | -------------------------------------------------------------------------------- /tables_and_views/bgp_non_established.py: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------------------------------------------------- 2 | # DESCRIPTION: 3 | # Print a list of non-established BGP connections using PyEZ for each device in a YAML file. 4 | # 5 | # CONTACT: bvilletard@juniper.net; cbinckly@juniper.net; ksator@juniper.net; pgeenens@juniper.net; tgrimonet@juniper.net 6 | # 7 | # CREATED: 2015-11-11 8 | # 9 | # VERSION: 1 10 | # 11 | # USAGE: bgp_non_established.py 12 | # ----------------------------------------------------------------------------------------------------------------------- 13 | 14 | 15 | from jnpr.junos import Device 16 | from jnpr.junos.op.bgp import * 17 | import yaml 18 | from datetime import datetime 19 | 20 | f=open("bgp_neighbors_non_established.txt", "a") 21 | 22 | f.write ("\n" + str (datetime.now()) + "\n") 23 | 24 | my_list_of_devices=open('tables_and_views/devices.yml').read() 25 | my_list_of_switches=yaml.load (my_list_of_devices) 26 | 27 | for element in my_list_of_switches: 28 | switch = Device(host=element, user='pytraining', password='Poclab123') 29 | switch.open() 30 | bgp=BGPNeighborTable (switch) 31 | bgp.get() 32 | print "\n" + element + " (hostname " + switch.facts["hostname"]+ ") has these BGP neighbors with a non established connection:" 33 | f.write ("\n" + element + " (hostname " + switch.facts["hostname"]+ ") has these BGP neighbors with a non established connection:\n") 34 | for item in bgp: 35 | if item.state != "Established": 36 | print item.type + " bgp session with " + item.neighbor + " is not established" 37 | f.write (item.type + " bgp session with " + item.neighbor + " is not established\n") 38 | print "\ntest done accross all the devices and all their configured neighbors." 39 | 40 | f.close() 41 | 42 | -------------------------------------------------------------------------------- /tables_and_views/bgp_states.py: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------------------------------------------------- 2 | # DESCRIPTION: 3 | # Print a list of BGP neighbor states for each device in a YAML file using PyEZ. 4 | # 5 | # CONTACT: bvilletard@juniper.net; cbinckly@juniper.net; ksator@juniper.net; pgeenens@juniper.net; tgrimonet@juniper.net 6 | # 7 | # CREATED: 2015-11-11 8 | # 9 | # VERSION: 1 10 | # 11 | # USAGE: bgp_state.py 12 | # ----------------------------------------------------------------------------------------------------------------------- 13 | 14 | 15 | from jnpr.junos import Device 16 | from jnpr.junos.op.bgp import * 17 | import yaml 18 | from datetime import datetime 19 | 20 | f=open("bgp_states.txt", "a") 21 | 22 | f.write ("\n" + str (datetime.now()) + "\n") 23 | 24 | my_list_of_devices=open('tables_and_views/devices.yml').read() 25 | my_list_of_switches=yaml.load (my_list_of_devices) 26 | 27 | for element in my_list_of_switches: 28 | switch = Device(host=element, user='pytraining', password='Poclab123') 29 | switch.open() 30 | bgp=BGPNeighborTable (switch) 31 | bgp.get() 32 | print "\nstatus of BGP neighbors of device " + element + " (hostname is " + switch.facts["hostname"]+ "):" 33 | f.write ("\nstatus of BGP neighbors of device " + element + " (hostname is " + switch.facts["hostname"]+ "):\n") 34 | for item in bgp: 35 | print item.type + " BGP neighbor " + item.neighbor + " is " + item.state + " (flap count is: " + item.flap_count +")" 36 | f.write (item.type + " BGP neighbor " + item.neighbor + " is " + item.state + " (flap count is: " + item.flap_count + ")\n") 37 | 38 | f.close() 39 | -------------------------------------------------------------------------------- /tables_and_views/devices.yml: -------------------------------------------------------------------------------- 1 | --- 2 | #IPs of the devices in the lab environment 3 | - 172.30.179.65 4 | - 172.30.179.95 5 | - 172.30.179.96 6 | # - 172.30.179.97 7 | -------------------------------------------------------------------------------- /tables_and_views/get_interfaces_down.py: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------------------------------------------------- 2 | # DESCRIPTION: 3 | # List all ports that are operationally down for a device. 4 | # 5 | # CONTACT: bvilletard@juniper.net; cbinckly@juniper.net; ksator@juniper.net; pgeenens@juniper.net; tgrimonet@juniper.net 6 | # 7 | # CREATED: 2015-11-11 8 | # 9 | # VERSION: 1 10 | # 11 | # USAGE: get_interfaces_down.py 12 | # ----------------------------------------------------------------------------------------------------------------------- 13 | 14 | 15 | from jnpr.junos.op.phyport import * 16 | from jnpr.junos import Device 17 | ip=raw_input("ip address of the device:") 18 | dev=Device (host=ip, user="pytraining", password="Poclab123") 19 | dev.open() 20 | ports = PhyPortTable(dev).get() 21 | for item in ports: 22 | if item.oper == "down": 23 | print ("the port " + item.key + " is down") 24 | 25 | 26 | -------------------------------------------------------------------------------- /tables_and_views/get_uplinks_state.py: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------------------------------------------------- 2 | # DESCRIPTION: 3 | # Get the state of all interfaces with UPLINK in their description for a device. 4 | # 5 | # CONTACT: bvilletard@juniper.net; cbinckly@juniper.net; ksator@juniper.net; pgeenens@juniper.net; tgrimonet@juniper.net 6 | # 7 | # CREATED: 2015-11-11 8 | # 9 | # VERSION: 1 10 | # 11 | # USAGE: get_uplinks_state.py 12 | # ----------------------------------------------------------------------------------------------------------------------- 13 | 14 | 15 | from jnpr.junos.op.phyport import * 16 | from jnpr.junos import Device 17 | ip=raw_input("ip address of the device:") 18 | dev=Device (host=ip, user="pytraining", password="Poclab123") 19 | dev.open() 20 | ports = PhyPortTable(dev).get() 21 | for item in ports: 22 | if item.description and ( "UPLINK" in item.description): 23 | print "Uplink " + item.key + " (" + item.description + ") is " + item.oper 24 | -------------------------------------------------------------------------------- /tables_and_views/lldp_neighbor_status.py: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------------------------------------- 2 | # DESCRIPTION: 3 | # This script prints the lldp neighbors of the whole network 4 | # it connects to each device (it has the list of devices from a yaml file) and get the lldp neighbors of each device and print some details regarding this. 5 | # it also write the same ouput on a file (ip_fabric_status_of_lldp_neighbors.txt) 6 | # 7 | # AUTHOR: Khelil SATOR (ksator@juniper.net) 8 | # FILE: lldp_neighbor_status.py 9 | # CREATED: 2015-10-30 10 | # VERSION: 1 11 | # 12 | # USAGE: 13 | # c:\Python27\IP_FABRIC\audit>lldp_neighbor_status.py 14 | # --------------------------------------------------------------------------------------------------------------- 15 | 16 | from jnpr.junos import Device 17 | from jnpr.junos.op.lldp import LLDPNeighborTable 18 | import yaml 19 | from datetime import datetime 20 | 21 | my_list_of_devices=open('tables_and_views/devices.yml').read() 22 | my_list_of_switches=yaml.load (my_list_of_devices) 23 | 24 | f=open("lldp_neighbors.txt", "a") 25 | f.write ("\n" + str (datetime.now()) + "\n") 26 | 27 | for host in my_list_of_switches: 28 | switch = Device(host=host, user='device_username', password='device_password') 29 | switch.open() 30 | print "\nLLDP neighbors of device " + host + " (hostname is " + switch.facts["hostname"]+ "):" 31 | f.write ("\nLLDP neighbors of device " + host + " (hostname is " + switch.facts["hostname"]+ "):\n") 32 | lldp_neighbors=LLDPNeighborTable(switch) 33 | lldp_neighbors.get() 34 | for neighbor in lldp_neighbors: 35 | print "interface " + neighbor.local_int + " has this neighbor: " + neighbor.remote_sysname 36 | f.write ("interface " + neighbor.local_int + " has this neighbor: " + neighbor.remote_sysname + "\n") 37 | f.close() 38 | 39 | -------------------------------------------------------------------------------- /tables_and_views/search_an_lldp_neighbor.py: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------------------------------------------------- 2 | # DESCRIPTION: 3 | # Find neighbors of a specific device using LLDP information retrieved using PyEZ. 4 | # 5 | # CONTACT: bvilletard@juniper.net; cbinckly@juniper.net; ksator@juniper.net; pgeenens@juniper.net; tgrimonet@juniper.net 6 | # 7 | # CREATED: 2015-11-11 8 | # 9 | # VERSION: 1 10 | # 11 | # USAGE: search_an_lldp_neighbor.py 12 | # ----------------------------------------------------------------------------------------------------------------------- 13 | 14 | from jnpr.junos import Device 15 | from jnpr.junos.op.lldp import LLDPNeighborTable 16 | import yaml 17 | 18 | my_list_of_devices=open('tables_and_views/devices.yml').read() 19 | my_list_of_switches=yaml.load (my_list_of_devices) 20 | 21 | neighbor_you_are_looking_for=raw_input("name of the neighbor you are looking for:") 22 | 23 | print ("Looking accross the whole network ...") 24 | 25 | for element in my_list_of_switches: 26 | switch = Device(host=element, user='pytraining', password='Poclab123') 27 | switch.open() 28 | lldp_neighbors=LLDPNeighborTable(switch) 29 | lldp_neighbors.get() 30 | for item in lldp_neighbors: 31 | if neighbor_you_are_looking_for == item.remote_sysname: 32 | print ("this neighbor is connected to the interface " +item.local_int + " of the device " + switch.facts["hostname"]) 33 | print ("Done") 34 | 35 | -------------------------------------------------------------------------------- /tables_and_views/uplinks_and_downlinks_last_flap.py: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------------------------------------------------- 2 | # DESCRIPTION: 3 | # List the operational and last flap for all devices in a YAML list. 4 | # 5 | # CONTACT: bvilletard@juniper.net; cbinckly@juniper.net; ksator@juniper.net; pgeenens@juniper.net; tgrimonet@juniper.net 6 | # 7 | # CREATED: 2015-11-11 8 | # 9 | # VERSION: 1 10 | # 11 | # USAGE: uplinks_and_downlinks_last_flap.py 12 | # ----------------------------------------------------------------------------------------------------------------------- 13 | 14 | 15 | from jnpr.junos.op.phyport import * 16 | from jnpr.junos import Device 17 | from datetime import datetime 18 | import yaml 19 | 20 | my_list_of_devices=open('tables_and_views/devices.yml').read() 21 | my_list_of_switches=yaml.load (my_list_of_devices) 22 | 23 | f=open("uplinks_and_downlinks_last_flap.txt", "a") 24 | f.write ("\n" + str (datetime.now()) + "\n") 25 | 26 | for element in my_list_of_switches: 27 | switch = Device(host=element, user='pytraining', password='Poclab123') 28 | switch.open() 29 | print ("\n" + switch.facts["hostname"] + ":\n") 30 | f.write ("\n" + switch.facts["hostname"] + ":\n") 31 | #print ("\nUplinks and downlinks last flap for " + switch.facts["hostname"] + ":\n") 32 | #f.write ("\nUplinks and downlinks last flap for " + switch.facts["hostname"] + ":\n") 33 | ports = PhyPortTable(switch).get() 34 | for item in ports: 35 | if item.description: 36 | if "LINK" in item.description: 37 | print (item.key + " (" + item.description + ") " + "current status is " + item.oper + "\n"+ "last flap: " + item.flapped +"\n") 38 | f.write ("\n" + item.key + " (" + item.description + ") " + "current status is " + item.oper + "\n" + "last flap: " + item.flapped +"\n") 39 | #print (item.key + ": " + item.flapped + ". Description is: " + item.description) 40 | #f.write (item.key + ": " + item.flapped + ". Description is: " + item.description + "\n") 41 | -------------------------------------------------------------------------------- /yaml_basics/device_list.yml: -------------------------------------------------------------------------------- 1 | --- 2 | #IPs of devices in the lab environment 3 | - 172.30.179.101 4 | - 172.30.179.102 5 | - 172.30.179.103 6 | - 172.30.179.104 7 | - 172.30.179.105 8 | -------------------------------------------------------------------------------- /yaml_basics/this_is_a_dictionary.yml: -------------------------------------------------------------------------------- 1 | --- 2 | interfaces: 3 | - ge-0/0/9 4 | - ge-0/0/10 5 | - ge-0/0/16 6 | - ge-0/0/18 7 | 8 | vlan_name: v14 9 | --------------------------------------------------------------------------------