From 41da104bb9c7916c382a447c07544e72b60c77c7 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Sun, 18 Jun 2017 20:51:19 +0200 Subject: [PATCH 1/9] Parser doesn't require mode if using default mode --- .../parsers/config/openconfig-if-ip/ipv4.yaml | 3 +- .../openconfig-interfaces/interfaces.yaml | 15 ++---- .../includes/bgp.yaml | 18 +++---- .../includes/static_routes.yaml | 9 ++-- .../network-instances.yaml | 18 +++---- .../parsers/config/openconfig-vlan/vlan.yaml | 3 +- .../openconfig-interfaces/interfaces.yaml | 3 +- .../transceiver.yaml | 3 +- .../state/openconfig-platform/components.yaml | 9 ++-- .../parsers/config/openconfig-if-ip/ipv4.yaml | 3 +- .../openconfig-interfaces/interfaces.yaml | 15 ++---- .../parsers/config/openconfig-vlan/vlan.yaml | 3 +- .../parsers/config/openconfig-if-ip/ipv4.yaml | 9 ++-- .../openconfig-interfaces/interfaces.yaml | 15 ++---- .../includes/bgp.yaml | 19 +++---- .../includes/static_routes.yaml | 3 +- .../network-instances.yaml | 3 +- .../parsers/config/openconfig-vlan/vlan.yaml | 3 +- .../openconfig-interfaces/interfaces.yaml | 51 +++++++------------ napalm_yang/parsers/base.py | 6 +-- napalm_yang/parsers/json.py | 4 +- napalm_yang/parsers/text.py | 10 ++-- napalm_yang/parsers/xml.py | 10 ++-- 23 files changed, 84 insertions(+), 151 deletions(-) diff --git a/napalm_yang/mappings/eos/parsers/config/openconfig-if-ip/ipv4.yaml b/napalm_yang/mappings/eos/parsers/config/openconfig-if-ip/ipv4.yaml index 200ca4cb..6297c111 100644 --- a/napalm_yang/mappings/eos/parsers/config/openconfig-if-ip/ipv4.yaml +++ b/napalm_yang/mappings/eos/parsers/config/openconfig-if-ip/ipv4.yaml @@ -17,8 +17,7 @@ ipv4: _process: unnecessary address: _process: - - mode: block - regexp: "(?Pip address (?P(?P.*))\\/(?P\\d+))(?P secondary)*" + - regexp: "(?Pip address (?P(?P.*))\\/(?P\\d+))(?P secondary)*" from: "{{ bookmarks['parent'] }}" ip: _process: unnecessary diff --git a/napalm_yang/mappings/eos/parsers/config/openconfig-interfaces/interfaces.yaml b/napalm_yang/mappings/eos/parsers/config/openconfig-interfaces/interfaces.yaml index 44267380..6bdb13dc 100644 --- a/napalm_yang/mappings/eos/parsers/config/openconfig-interfaces/interfaces.yaml +++ b/napalm_yang/mappings/eos/parsers/config/openconfig-interfaces/interfaces.yaml @@ -9,8 +9,7 @@ interfaces: _process: unnecessary interface: _process: - - mode: block - regexp: "(?Pinterface (?P(\\w|-)*\\d+)\n(?:.|\n)*?^!$)" + - regexp: "(?Pinterface (?P(\\w|-)*\\d+)\n(?:.|\n)*?^!$)" from: "{{ bookmarks.interfaces.0 }}" name: _process: unnecessary @@ -44,20 +43,17 @@ interfaces: from: "{{ bookmarks.interface[interface_key] }}" description: _process: - - mode: search - regexp: "description (?P.*)" + - regexp: "description (?P.*)" from: "{{ bookmarks.interface[interface_key] }}" mtu: _process: - - mode: search - regexp: "mtu (?P[0-9]+)" + - regexp: "mtu (?P[0-9]+)" from: "{{ bookmarks.interface[interface_key] }}" subinterfaces: _process: unnecessary subinterface: _process: - - mode: block - regexp: "(?Pinterface {{interface_key}}\\.(?P\\d+)\\n(?:.|\\n)*?^!$)" + - regexp: "(?Pinterface {{interface_key}}\\.(?P\\d+)\\n(?:.|\\n)*?^!$)" from: "{{ bookmarks.interfaces.0 }}" index: _process: unnecessary @@ -76,6 +72,5 @@ interfaces: from: "{{ bookmarks.subinterface[subinterface_key] }}" description: _process: - - mode: search - regexp: "description (?P.*)" + - regexp: "description (?P.*)" from: "{{ bookmarks.subinterface[subinterface_key] }}" diff --git a/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/includes/bgp.yaml b/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/includes/bgp.yaml index dd0a74b9..448ae2a1 100644 --- a/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/includes/bgp.yaml +++ b/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/includes/bgp.yaml @@ -258,14 +258,12 @@ global: _process: unnecessary as: _process: - - mode: search - regexp: "local-as (?P.*)" + - regexp: "local-as (?P.*)" from: "{{ bookmarks['protocol'][protocol_key] }}" default: "{{ extra_vars.process_id|default(1) }}" router-id: _process: - - mode: search - regexp: "router-id (?P.*)" + - regexp: "router-id (?P.*)" from: "{{ bookmarks['protocol'][protocol_key] }}" default-route-distance: _process: not_implemented @@ -355,8 +353,7 @@ neighbors: _process: unnecessary neighbor: _process: - - mode: block - regexp: "(?Pneighbor (?P\\d+.\\d+.\\d+.\\d+).*)" + - regexp: "(?Pneighbor (?P\\d+.\\d+.\\d+.\\d+).*)" from: "{{ bookmarks['protocol'][protocol_key] }}" flat: true add-paths: @@ -625,8 +622,7 @@ neighbors: _process: not_implemented description: _process: - - mode: search - regexp: "neighbor {{ neighbor_key }} description (?P.*)" + - regexp: "neighbor {{ neighbor_key }} description (?P.*)" from: "{{ bookmarks['neighbor'][neighbor_key] }}" enabled: _process: @@ -635,8 +631,7 @@ neighbors: from: "{{ bookmarks['neighbor'][neighbor_key] }}" local-as: _process: - - mode: search - regexp: "neighbor {{ neighbor_key }} local-as (?P\\d+)" + - regexp: "neighbor {{ neighbor_key }} local-as (?P\\d+)" from: "{{ bookmarks['neighbor'][neighbor_key] }}" neighbor-address: _process: @@ -645,8 +640,7 @@ neighbors: from: "{{ bookmarks['neighbor'][neighbor_key] }}" peer-as: _process: - - mode: search - regexp: "neighbor {{ neighbor_key }} remote-as (?P.*)" + - regexp: "neighbor {{ neighbor_key }} remote-as (?P.*)" from: "{{ bookmarks['neighbor'][neighbor_key] }}" peer-group: _process: not_implemented diff --git a/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/includes/static_routes.yaml b/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/includes/static_routes.yaml index 68dd311d..8cabb5a3 100644 --- a/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/includes/static_routes.yaml +++ b/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/includes/static_routes.yaml @@ -4,13 +4,11 @@ _process: when: "{{ protocol_key != 'static static' }}" static: _process: - - mode: block - regexp: "(?Pip route (?P\\d+.*\\/\\d+).*)" + - regexp: "(?Pip route (?P\\d+.*\\/\\d+).*)" from: "{{ bookmarks['network-instances'][0] }}" when: "{{ network_instance_key == 'global' }}" flat: true - - mode: block - regexp: "(?Pip route vrf {{ network_instance_key }} (?P\\d+.*\\/\\d+).*)" + - regexp: "(?Pip route vrf {{ network_instance_key }} (?P\\d+.*\\/\\d+).*)" from: "{{ bookmarks['network-instances'][0] }}" when: "{{ network_instance_key != 'global' }}" flat: true @@ -26,8 +24,7 @@ static: _process: unnecessary next-hop: _process: - - mode: block - regexp: "(?Pip route (vrf {{ network_instance_key }} )*{{ static_key }} (?P\\d+.\\d+.\\d+.\\d+) (?P\\d+) tag (?P\\d+))" + - regexp: "(?Pip route (vrf {{ network_instance_key }} )*{{ static_key }} (?P\\d+.\\d+.\\d+.\\d+) (?P\\d+) tag (?P\\d+))" from: "{{ bookmarks['static'][static_key] }}" config: _process: unnecessary diff --git a/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/network-instances.yaml b/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/network-instances.yaml index e5e7afce..3c74e1b5 100644 --- a/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/network-instances.yaml +++ b/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/network-instances.yaml @@ -10,8 +10,7 @@ network-instances: _process: unnecessary network-instance: _process: - - mode: block - regexp: "(?Pvrf definition (?P(.*))\n(?:.|\n)*?^!$)" + - regexp: "(?Pvrf definition (?P(.*))\n(?:.|\n)*?^!$)" from: "{{ bookmarks['network-instances'][0] }}" mandatory: - key: "global" @@ -22,8 +21,7 @@ network-instances: _process: unnecessary description: _process: - - mode: search - regexp: "description (?P.*)" + - regexp: "description (?P.*)" from: "{{ bookmarks.parent }}" enabled: _process: @@ -37,8 +35,7 @@ network-instances: _process: unnecessary route-distinguisher: _process: - - mode: search - regexp: "rd (?P.*)" + - regexp: "rd (?P.*)" from: "{{ bookmarks.parent }}" router-id: _process: not_implemented @@ -321,18 +318,15 @@ network-instances: _process: unnecessary protocol: _process: - - mode: block - regexp: "(?Prouter (?P(bgp))\\s*(?P\\d+)*\n(?:.|\n)*?)^(!| vrf \\w+)$" + - regexp: "(?Prouter (?P(bgp))\\s*(?P\\d+)*\n(?:.|\n)*?)^(!| vrf \\w+)$" from: "{{ bookmarks['network-instances'][0] }}" composite_key: [protocol_name, protocol_name] when: "{{ network_instance_key == 'global' }}" - - mode: block - regexp: "router (?P(bgp))\\s*(?P\\d+)*\n(?:.|\n)*?^ (?Pvrf {{ network_instance_key }}\n(?:.|\n)*?)^(!| vrf.*)$" + - regexp: "router (?P(bgp))\\s*(?P\\d+)*\n(?:.|\n)*?^ (?Pvrf {{ network_instance_key }}\n(?:.|\n)*?)^(!| vrf.*)$" from: "{{ bookmarks['network-instances'][0] }}" composite_key: [protocol_name, protocol_name] when: "{{ network_instance_key != 'global' }}" - - mode: block - regexp: "(?Pip route .*\n(?:.|\n)*?^!$)" + - regexp: "(?Pip route .*\n(?:.|\n)*?^!$)" from: "{{ bookmarks['network-instances'][0] }}" key: "static static" bgp: !include includes/bgp.yaml diff --git a/napalm_yang/mappings/eos/parsers/config/openconfig-vlan/vlan.yaml b/napalm_yang/mappings/eos/parsers/config/openconfig-vlan/vlan.yaml index eef423e1..9ba682df 100644 --- a/napalm_yang/mappings/eos/parsers/config/openconfig-vlan/vlan.yaml +++ b/napalm_yang/mappings/eos/parsers/config/openconfig-vlan/vlan.yaml @@ -9,6 +9,5 @@ vlan: _process: unnecessary vlan-id: _process: - - mode: search - regexp: "^\\W*encapsulation dot1q vlan (?P[0-9]+)" + - regexp: "^\\W*encapsulation dot1q vlan (?P[0-9]+)" from: "{{ bookmarks['parent'] }}" diff --git a/napalm_yang/mappings/eos/parsers/state/openconfig-interfaces/interfaces.yaml b/napalm_yang/mappings/eos/parsers/state/openconfig-interfaces/interfaces.yaml index 4350bc79..25bc1952 100644 --- a/napalm_yang/mappings/eos/parsers/state/openconfig-interfaces/interfaces.yaml +++ b/napalm_yang/mappings/eos/parsers/state/openconfig-interfaces/interfaces.yaml @@ -10,8 +10,7 @@ interfaces: _process: unnecessary interface: _process: - - mode: dict - from: "{{ bookmarks.interfaces.0.interfaces|json }}" + - from: "{{ bookmarks.interfaces.0.interfaces|json }}" config: _process: unnecessary hold-time: diff --git a/napalm_yang/mappings/eos/parsers/state/openconfig-platform-transceiver/transceiver.yaml b/napalm_yang/mappings/eos/parsers/state/openconfig-platform-transceiver/transceiver.yaml index 602d247b..6fdbae8d 100644 --- a/napalm_yang/mappings/eos/parsers/state/openconfig-platform-transceiver/transceiver.yaml +++ b/napalm_yang/mappings/eos/parsers/state/openconfig-platform-transceiver/transceiver.yaml @@ -16,8 +16,7 @@ transceiver: _process: unnecessary channel: _process: - - mode: dict - from: "{{ bookmarks.parent.get('transceiver', {}).get('physical-channels', {}).get('channel', {})|json }}" + - from: "{{ bookmarks.parent.get('transceiver', {}).get('physical-channels', {}).get('channel', {})|json }}" config: _process: not_implemented description: diff --git a/napalm_yang/mappings/eos/parsers/state/openconfig-platform/components.yaml b/napalm_yang/mappings/eos/parsers/state/openconfig-platform/components.yaml index 7066f9b6..488496a0 100644 --- a/napalm_yang/mappings/eos/parsers/state/openconfig-platform/components.yaml +++ b/napalm_yang/mappings/eos/parsers/state/openconfig-platform/components.yaml @@ -8,8 +8,7 @@ components: _process: unnecessary component: _process: - - mode: dict - from: "{{ bookmarks.components.0.components.component|json }}" + - from: "{{ bookmarks.components.0.components.component|json }}" config: _process: not_implemented name: @@ -20,8 +19,7 @@ components: _process: unnecessary property: _process: - - mode: dict - from: "{{ bookmarks.parent.get('properties', {}).get('property', {})|json }}" + - from: "{{ bookmarks.parent.get('properties', {}).get('property', {})|json }}" config: _process: not_implemented name: @@ -100,8 +98,7 @@ components: _process: unnecessary subcomponent: _process: - - mode: dict - from: "{{ bookmarks.parent.get('subcomponents', {}).get('subcomponent', {})|json }}" + - from: "{{ bookmarks.parent.get('subcomponents', {}).get('subcomponent', {})|json }}" config: _process: not_implemented name: diff --git a/napalm_yang/mappings/ios/parsers/config/openconfig-if-ip/ipv4.yaml b/napalm_yang/mappings/ios/parsers/config/openconfig-if-ip/ipv4.yaml index f40016f8..10bda473 100644 --- a/napalm_yang/mappings/ios/parsers/config/openconfig-if-ip/ipv4.yaml +++ b/napalm_yang/mappings/ios/parsers/config/openconfig-if-ip/ipv4.yaml @@ -17,8 +17,7 @@ ipv4: _process: unnecessary address: _process: - - mode: block - regexp: "(?Pip address (?P(?P.*)) (?P([0-255]|\\.)*)(?P secondary)*)$" + - regexp: "(?Pip address (?P(?P.*)) (?P([0-255]|\\.)*)(?P secondary)*)$" from: "{{ bookmarks['parent'] }}" ip: _process: unnecessary diff --git a/napalm_yang/mappings/ios/parsers/config/openconfig-interfaces/interfaces.yaml b/napalm_yang/mappings/ios/parsers/config/openconfig-interfaces/interfaces.yaml index f01a2ef5..a603a8ed 100644 --- a/napalm_yang/mappings/ios/parsers/config/openconfig-interfaces/interfaces.yaml +++ b/napalm_yang/mappings/ios/parsers/config/openconfig-interfaces/interfaces.yaml @@ -9,8 +9,7 @@ interfaces: _process: unnecessary interface: _process: - - mode: block - regexp: "(?Pinterface (?P(\\w|-)*\\d+)\n(?:.|\n)*?^!$)" + - regexp: "(?Pinterface (?P(\\w|-)*\\d+)\n(?:.|\n)*?^!$)" from: "{{ bookmarks.interfaces.0 }}" name: _process: unnecessary @@ -46,20 +45,17 @@ interfaces: from: "{{ bookmarks.interface[interface_key] }}" description: _process: - - mode: search - regexp: "description (?P.*)" + - regexp: "description (?P.*)" from: "{{ bookmarks.interface[interface_key] }}" mtu: _process: - - mode: search - regexp: "mtu (?P[0-9]+)" + - regexp: "mtu (?P[0-9]+)" from: "{{ bookmarks.interface[interface_key] }}" subinterfaces: _process: unnecessary subinterface: _process: - - mode: block - regexp: "(?Pinterface {{interface_key}}\\.(?P\\d+)\\n(?:.|\\n)*?^!$)" + - regexp: "(?Pinterface {{interface_key}}\\.(?P\\d+)\\n(?:.|\\n)*?^!$)" from: "{{ bookmarks.interfaces.0 }}" index: _process: unnecessary @@ -78,6 +74,5 @@ interfaces: from: "{{ bookmarks.subinterface[subinterface_key] }}" description: _process: - - mode: search - regexp: "description (?P.*)" + - regexp: "description (?P.*)" from: "{{ bookmarks.subinterface[subinterface_key] }}" diff --git a/napalm_yang/mappings/ios/parsers/config/openconfig-vlan/vlan.yaml b/napalm_yang/mappings/ios/parsers/config/openconfig-vlan/vlan.yaml index 270e5a56..1f49ea7c 100644 --- a/napalm_yang/mappings/ios/parsers/config/openconfig-vlan/vlan.yaml +++ b/napalm_yang/mappings/ios/parsers/config/openconfig-vlan/vlan.yaml @@ -9,6 +9,5 @@ vlan: _process: unnecessary vlan-id: _process: - - mode: search - regexp: "^\\W*encapsulation dot1q (?P[0-9]+)" + - regexp: "^\\W*encapsulation dot1q (?P[0-9]+)" from: "{{ bookmarks['parent'] }}" diff --git a/napalm_yang/mappings/junos/parsers/config/openconfig-if-ip/ipv4.yaml b/napalm_yang/mappings/junos/parsers/config/openconfig-if-ip/ipv4.yaml index a48ab1b5..7c462e63 100644 --- a/napalm_yang/mappings/junos/parsers/config/openconfig-if-ip/ipv4.yaml +++ b/napalm_yang/mappings/junos/parsers/config/openconfig-if-ip/ipv4.yaml @@ -17,8 +17,7 @@ ipv4: _process: unnecessary address: _process: - - mode: xpath - xpath: "family/inet/address" + - xpath: "family/inet/address" key: name from: "{{ bookmarks['parent'] }}" ip: @@ -27,14 +26,12 @@ ipv4: _process: unnecessary ip: _process: - - mode: xpath - xpath: "name" + - xpath: "name" regexp: "(?P.*)/\\d+" from: "{{ bookmarks['parent'] }}" prefix-length: _process: - - mode: xpath - xpath: "name" + - xpath: "name" regexp: ".*/(?P\\d+)" from: "{{ bookmarks['parent'] }}" vrrp: diff --git a/napalm_yang/mappings/junos/parsers/config/openconfig-interfaces/interfaces.yaml b/napalm_yang/mappings/junos/parsers/config/openconfig-interfaces/interfaces.yaml index 81b988c0..5e5548b1 100644 --- a/napalm_yang/mappings/junos/parsers/config/openconfig-interfaces/interfaces.yaml +++ b/napalm_yang/mappings/junos/parsers/config/openconfig-interfaces/interfaces.yaml @@ -10,8 +10,7 @@ interfaces: _process: unnecessary interface: _process: - - mode: xpath - xpath: "interfaces/interface" + - xpath: "interfaces/interface" key: name from: "{{ bookmarks.interfaces.0 }}" name: @@ -55,20 +54,17 @@ interfaces: from: "{{ bookmarks['parent'] }}" description: _process: - - mode: xpath - xpath: description + - xpath: description from: "{{ bookmarks['parent'] }}" mtu: _process: - - mode: xpath - xpath: mtu + - xpath: mtu from: "{{ bookmarks['parent'] }}" subinterfaces: _process: unnecessary subinterface: _process: - - mode: xpath - xpath: "unit" + - xpath: "unit" key: name from: "{{ bookmarks['parent'] }}" index: @@ -88,6 +84,5 @@ interfaces: from: "{{ bookmarks['parent'] }}" description: _process: - - mode: xpath - xpath: description + - xpath: description from: "{{ bookmarks['parent'] }}" diff --git a/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/includes/bgp.yaml b/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/includes/bgp.yaml index 37d65f08..776ee6a2 100644 --- a/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/includes/bgp.yaml +++ b/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/includes/bgp.yaml @@ -258,13 +258,11 @@ global: _process: unnecessary as: _process: - - mode: xpath - xpath: "routing-options/autonomous-system/as-number" + - xpath: "routing-options/autonomous-system/as-number" from: "{{ bookmarks['network_instance'][network_instance_key] }}" router-id: _process: - - mode: xpath - xpath: "routing-options/router-id" + - xpath: "routing-options/router-id" from: "{{ bookmarks['network_instance'][network_instance_key] }}" default-route-distance: _process: not_implemented @@ -624,24 +622,21 @@ neighbors: _process: not_implemented description: _process: - - mode: xpath - xpath: "description" + - xpath: "description" from: "{{ bookmarks.parent }}" enabled: _process: not_implemented local-as: _process: - - mode: xpath - xpath: "local-as.as-number" + - xpath: "local-as.as-number" from: "{{ bookmarks.parent }}" neighbor-address: _process: - - mode: value - value: "{{ neighbor_key }}" + - mode: value + value: "{{ neighbor_key }}" peer-as: _process: - - mode: xpath - xpath: "peer-as" + - xpath: "peer-as" from: "{{ bookmarks.parent }}" peer-group: _process: diff --git a/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/includes/static_routes.yaml b/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/includes/static_routes.yaml index dbaaa7f3..18f9dc87 100644 --- a/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/includes/static_routes.yaml +++ b/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/includes/static_routes.yaml @@ -30,8 +30,7 @@ static: _process: not_implemented metric: _process: - - mode: xpath - xpath: "metric" + - xpath: "metric" from: "{{ bookmarks.parent }}" next-hop: _process: diff --git a/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/network-instances.yaml b/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/network-instances.yaml index d9a11746..a012343f 100644 --- a/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/network-instances.yaml +++ b/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/network-instances.yaml @@ -23,8 +23,7 @@ network-instances: _process: unnecessary description: _process: - - mode: xpath - xpath: description + - xpath: description from: "{{ bookmarks.parent }}" enabled: _process: diff --git a/napalm_yang/mappings/junos/parsers/config/openconfig-vlan/vlan.yaml b/napalm_yang/mappings/junos/parsers/config/openconfig-vlan/vlan.yaml index ae5eff7a..ed1a06e8 100644 --- a/napalm_yang/mappings/junos/parsers/config/openconfig-vlan/vlan.yaml +++ b/napalm_yang/mappings/junos/parsers/config/openconfig-vlan/vlan.yaml @@ -8,6 +8,5 @@ vlan: _process: unnecessary vlan-id: _process: - - mode: xpath - xpath: "vlan-id" + - xpath: "vlan-id" from: "{{ bookmarks['parent'] }}" diff --git a/napalm_yang/mappings/junos/parsers/state/openconfig-interfaces/interfaces.yaml b/napalm_yang/mappings/junos/parsers/state/openconfig-interfaces/interfaces.yaml index aae32e17..01cb2ab8 100644 --- a/napalm_yang/mappings/junos/parsers/state/openconfig-interfaces/interfaces.yaml +++ b/napalm_yang/mappings/junos/parsers/state/openconfig-interfaces/interfaces.yaml @@ -10,8 +10,7 @@ interfaces: _process: unnecessary interface: _process: - - mode: xpath - xpath: "physical-interface" + - xpath: "physical-interface" key: name from: "{{ bookmarks.interfaces.0 }}" config: @@ -38,8 +37,7 @@ interfaces: down: DOWN description: _process: - - mode: xpath - xpath: "description" + - xpath: "description" from: "{{ bookmarks['parent'] }}" enabled: _process: @@ -51,19 +49,16 @@ interfaces: down: false ifindex: _process: - - mode: xpath - xpath: "snmp-index" + - xpath: "snmp-index" from: "{{ bookmarks['parent'] }}" last-change: _process: - - mode: xpath - xpath: "interface-flapped" + - xpath: "interface-flapped" attribute: "seconds" from: "{{ bookmarks['parent'] }}" mtu: _process: - - mode: xpath - xpath: "mtu" + - xpath: "mtu" regexp: "(?P\\d+)" # Someone thought mixing strings and ints was a good idea from: "{{ bookmarks['parent'] }}" name: @@ -101,30 +96,25 @@ interfaces: _process: unnecessary in-broadcast-pkts: _process: - - mode: xpath - xpath: "ethernet-mac-statistics/input-broadcasts" + - xpath: "ethernet-mac-statistics/input-broadcasts" from: "{{ bookmarks['parent'] }}" in-discards: _process: - - mode: xpath - xpath: "input-error-list/input-discards" + - xpath: "input-error-list/input-discards" from: "{{ bookmarks['parent'] }}" in-errors: _process: - - mode: xpath - xpath: "input-error-list/input-errors" + - xpath: "input-error-list/input-errors" from: "{{ bookmarks['parent'] }}" in-multicast-pkts: _process: - - mode: xpath - xpath: "ethernet-mac-statistics/input-multicasts" + - xpath: "ethernet-mac-statistics/input-multicasts" from: "{{ bookmarks['parent'] }}" in-octets: _process: not_implemented in-unicast-pkts: _process: - - mode: xpath - xpath: "ethernet-mac-statistics/input-unicasts" + - xpath: "ethernet-mac-statistics/input-unicasts" from: "{{ bookmarks['parent'] }}" in-unknown-protos: _process: not_implemented @@ -132,34 +122,29 @@ interfaces: _process: not_implemented out-broadcast-pkts: _process: - - mode: xpath - xpath: "ethernet-mac-statistics/output-broadcasts" + - xpath: "ethernet-mac-statistics/output-broadcasts" from: "{{ bookmarks['parent'] }}" out-discards: _process: not_implemented out-errors: _process: - - mode: xpath - xpath: "output-error-list/output-errors" + - xpath: "output-error-list/output-errors" from: "{{ bookmarks['parent'] }}" out-multicast-pkts: _process: - - mode: xpath - xpath: "ethernet-mac-statistics/output-multicasts" + - xpath: "ethernet-mac-statistics/output-multicasts" from: "{{ bookmarks['parent'] }}" out-octets: _process: not_implemented out-unicast-pkts: _process: - - mode: xpath - xpath: "ethernet-mac-statistics/output-unicasts" + - xpath: "ethernet-mac-statistics/output-unicasts" from: "{{ bookmarks['parent'] }}" subinterfaces: _process: unnecessary subinterface: _process: - - mode: xpath - xpath: "logical-interface" + - xpath: "logical-interface" key: name from: "{{ bookmarks['parent'] }}" index: @@ -172,8 +157,7 @@ interfaces: _process: not_implemented description: _process: - - mode: xpath - xpath: "description" + - xpath: "description" from: "{{ bookmarks['parent'] }}" enabled: _process: not_implemented @@ -181,8 +165,7 @@ interfaces: _process: not_implemented ifindex: _process: - - mode: xpath - xpath: "snmp-index" + - xpath: "snmp-index" from: "{{ bookmarks['parent'] }}" last-change: _process: not_implemented diff --git a/napalm_yang/parsers/base.py b/napalm_yang/parsers/base.py index 33e5a647..dc9d8440 100644 --- a/napalm_yang/parsers/base.py +++ b/napalm_yang/parsers/base.py @@ -10,14 +10,14 @@ def init_native(cls, native): @classmethod def parse_list(cls, mapping): for m in mapping: - method_name = "_parse_list_{}".format(m["mode"]) + method_name = "_parse_list_{}".format(m.get("mode", "default")) for key, block, extra_vars in getattr(cls, method_name)(m): yield key, block, extra_vars @classmethod def parse_leaf(cls, mapping): for m in mapping: - method_name = "_parse_leaf_{}".format(m["mode"]) + method_name = "_parse_leaf_{}".format(m.get("mode", "default")) result = getattr(cls, method_name)(m) if result is not None: return result @@ -25,7 +25,7 @@ def parse_leaf(cls, mapping): @classmethod def parse_container(cls, mapping): for m in mapping: - method_name = "_parse_container_{}".format(m["mode"]) + method_name = "_parse_container_{}".format(m.get("mode", "default")) result, extra_vars = getattr(cls, method_name)(m) if result or extra_vars: break diff --git a/napalm_yang/parsers/json.py b/napalm_yang/parsers/json.py index ede3aa82..f37eee38 100644 --- a/napalm_yang/parsers/json.py +++ b/napalm_yang/parsers/json.py @@ -21,14 +21,14 @@ def init_native(cls, native): return super().init_native(resp) @classmethod - def _parse_list_dict(cls, mapping, key=None): + def _parse_list_default(cls, mapping, key=None): d = json.loads(mapping['from']) if isinstance(d, dict): for k, v in d.items(): yield k, v, {} @classmethod - def _parse_leaf_dict(cls, mapping, check_default=True, check_presence=False): + def _parse_leaf_default(cls, mapping, check_default=True, check_presence=False): d = mapping['from'] if d and not check_presence: regexp = mapping.get('regexp', None) diff --git a/napalm_yang/parsers/text.py b/napalm_yang/parsers/text.py index 6808d64b..7b522270 100644 --- a/napalm_yang/parsers/text.py +++ b/napalm_yang/parsers/text.py @@ -8,7 +8,7 @@ class TextParser(BaseParser): @classmethod - def _parse_list_block(cls, mapping): + def _parse_list_default(cls, mapping): block_matches = re.finditer(mapping["regexp"], mapping["from"], re.MULTILINE + re.I) mandatory_elements = mapping.pop("mandatory", []) @@ -39,7 +39,7 @@ def _parse_list_block(cls, mapping): yield key, block, extra_vars @classmethod - def _parse_leaf_search(cls, mapping, check_default=True): + def _parse_leaf_default(cls, mapping, check_default=True): match = re.search(mapping["regexp"], mapping["from"], re.MULTILINE + re.I) if match: @@ -51,15 +51,15 @@ def _parse_leaf_search(cls, mapping, check_default=True): @classmethod def _parse_leaf_is_present(cls, mapping): - value = cls._parse_leaf_search(mapping, check_default=False) + value = cls._parse_leaf_default(mapping, check_default=False) return bool(value) @classmethod def _parse_leaf_is_absent(cls, mapping): - value = cls._parse_leaf_search(mapping, check_default=False) + value = cls._parse_leaf_default(mapping, check_default=False) return not bool(value) @classmethod def _parse_leaf_map(cls, mapping): - value = cls._parse_leaf_search(mapping) + value = cls._parse_leaf_default(mapping) return mapping["map"][value] diff --git a/napalm_yang/parsers/xml.py b/napalm_yang/parsers/xml.py index adf80d6c..8356d704 100644 --- a/napalm_yang/parsers/xml.py +++ b/napalm_yang/parsers/xml.py @@ -10,7 +10,7 @@ class XMLParser(BaseParser): @classmethod - def _parse_list_xpath(cls, mapping): + def _parse_list_default(cls, mapping): xml = etree.fromstring(mapping["from"]) mandatory_elements = mapping.pop("mandatory", []) @@ -132,7 +132,7 @@ def _parse_list_container(cls, mapping): return cls._parse_list_container_helper(mapping, [root], root) @classmethod - def _parse_leaf_xpath(cls, mapping, check_default=True, check_presence=False): + def _parse_leaf_default(cls, mapping, check_default=True, check_presence=False): xml = etree.fromstring(mapping["from"]) element = xml.xpath(mapping["xpath"]) @@ -158,7 +158,7 @@ def _parse_leaf_xpath(cls, mapping, check_default=True, check_presence=False): @classmethod def _parse_leaf_map(cls, mapping): - value = cls._parse_leaf_xpath(mapping) + value = cls._parse_leaf_default(mapping) if "regex" in mapping.keys(): value = re.search(mapping["regexp"], value).group("value") @@ -167,8 +167,8 @@ def _parse_leaf_map(cls, mapping): @classmethod def _parse_leaf_is_present(cls, mapping): - return cls._parse_leaf_xpath(mapping, check_default=False, check_presence=True) + return cls._parse_leaf_default(mapping, check_default=False, check_presence=True) @classmethod def _parse_leaf_is_absent(cls, mapping): - return not cls._parse_leaf_xpath(mapping, check_default=False, check_presence=True) + return not cls._parse_leaf_default(mapping, check_default=False, check_presence=True) From cc50c2549fc2a242e16cf8a879f836930d91ee8b Mon Sep 17 00:00:00 2001 From: David Barroso Date: Mon, 19 Jun 2017 08:34:48 +0200 Subject: [PATCH 2/9] XMLParser now avoid serializing/deserializing the object all the time --- .../translators/openconfig-if-ip/ipv4.yaml | 6 +- .../openconfig-interfaces/interfaces.yaml | 21 ++---- .../includes/bgp.yaml | 15 ++-- .../includes/static_routes.yaml | 12 ++-- .../network-instances.yaml | 15 ++-- .../eos/translators/openconfig-vlan/vlan.yaml | 3 +- .../translators/openconfig-if-ip/ipv4.yaml | 6 +- .../openconfig-interfaces/interfaces.yaml | 21 ++---- .../ios/translators/openconfig-vlan/vlan.yaml | 3 +- .../parsers/config/openconfig-if-ip/ipv4.yaml | 4 -- .../openconfig-interfaces/interfaces.yaml | 12 +--- .../includes/bgp.yaml | 8 +-- .../includes/static_routes.yaml | 3 - .../network-instances.yaml | 8 +-- .../parsers/config/openconfig-vlan/vlan.yaml | 1 - .../openconfig-interfaces/interfaces.yaml | 22 +----- .../translators/openconfig-if-ip/ipv4.yaml | 9 +-- .../openconfig-interfaces/interfaces.yaml | 24 +++---- .../includes/bgp.yaml | 21 ++---- .../includes/static_routes.yaml | 9 +-- .../network-instances.yaml | 15 ++-- .../translators/openconfig-vlan/vlan.yaml | 6 +- napalm_yang/parser.py | 7 +- napalm_yang/parsers/base.py | 72 +++++++++++-------- napalm_yang/parsers/xml.py | 61 ++++++++-------- napalm_yang/translators/base.py | 28 ++++---- napalm_yang/translators/text.py | 15 ++-- napalm_yang/translators/xml.py | 10 +-- test/integration/test_profiles.py | 6 +- 29 files changed, 176 insertions(+), 267 deletions(-) diff --git a/napalm_yang/mappings/eos/translators/openconfig-if-ip/ipv4.yaml b/napalm_yang/mappings/eos/translators/openconfig-if-ip/ipv4.yaml index e75e5d6e..88a4ba8c 100644 --- a/napalm_yang/mappings/eos/translators/openconfig-if-ip/ipv4.yaml +++ b/napalm_yang/mappings/eos/translators/openconfig-if-ip/ipv4.yaml @@ -8,8 +8,7 @@ ipv4: _process: unnecessary enabled: _process: - - mode: element - value: " no switchport\n" + - value: " no switchport\n" negate: " switchport\n" in: "interface.{{ interface_key }}" when: "{{ model and interface_key[0:4] not in ['Mana', 'Loop'] }}" @@ -19,8 +18,7 @@ ipv4: _process: unnecessary address: _process: - - mode: container - key_value: " ip address {{ model.config.ip }}/{{ model.config.prefix_length }} {{ 'secondary' if model.config.secondary else '' }}\n" + - key_value: " ip address {{ model.config.ip }}/{{ model.config.prefix_length }} {{ 'secondary' if model.config.secondary else '' }}\n" negate: " default ip address {{ model.config.ip }}/{{ model.config.prefix_length }} {{ 'secondary' if model.config.secondary else '' }}\n" replace: false ip: diff --git a/napalm_yang/mappings/eos/translators/openconfig-interfaces/interfaces.yaml b/napalm_yang/mappings/eos/translators/openconfig-interfaces/interfaces.yaml index 836bf3d7..de0fe626 100644 --- a/napalm_yang/mappings/eos/translators/openconfig-interfaces/interfaces.yaml +++ b/napalm_yang/mappings/eos/translators/openconfig-interfaces/interfaces.yaml @@ -7,8 +7,7 @@ interfaces: _process: unnecessary interface: _process: - - mode: container - key_value: "interface {{ interface_key }}\n" + - key_value: "interface {{ interface_key }}\n" negate: "{{ 'no' if interface_key[0:4] in ['Port', 'Loop'] else 'default' }} interface {{ interface_key }}\n" end: " exit\n" name: @@ -29,25 +28,21 @@ interfaces: _process: unnecessary enabled: _process: - - mode: element - value: " shutdown\n" + - value: " shutdown\n" when: "{{ not model }}" description: _process: - - mode: element - value: " description {{ model }}\n" + - value: " description {{ model }}\n" negate: " default description" mtu: _process: - - mode: element - value: " mtu {{ model }}\n" + - value: " mtu {{ model }}\n" negate: " default mtu\n" subinterfaces: _process: unnecessary subinterface: _process: - - mode: container - key_value: "interface {{ interface_key}}.{{ subinterface_key }}\n" + - key_value: "interface {{ interface_key}}.{{ subinterface_key }}\n" negate: "no interface {{ interface_key}}.{{ subinterface_key }}\n" in: "interfaces" end: " exit\n" @@ -61,11 +56,9 @@ interfaces: _process: unnecessary enabled: _process: - - mode: element - value: " shutdown\n" + - value: " shutdown\n" when: "{{ not model }}" description: _process: - - mode: element - value: " description {{ model }}\n" + - value: " description {{ model }}\n" negate: " default description" diff --git a/napalm_yang/mappings/eos/translators/openconfig-network-instance/includes/bgp.yaml b/napalm_yang/mappings/eos/translators/openconfig-network-instance/includes/bgp.yaml index 8ddd1bf0..f53ade93 100644 --- a/napalm_yang/mappings/eos/translators/openconfig-network-instance/includes/bgp.yaml +++ b/napalm_yang/mappings/eos/translators/openconfig-network-instance/includes/bgp.yaml @@ -260,8 +260,7 @@ global: _process: unnecessary router-id: _process: - - mode: element - value: " router-id {{ model }}\n" + - value: " router-id {{ model }}\n" negate: " default router-id\n" default-route-distance: _process: not_implemented @@ -351,8 +350,7 @@ neighbors: _process: unnecessary neighbor: _process: - - mode: container - key_value: " neighbor {{ neighbor_key }} remote-as {{ model.config.peer_as }}\n" + - key_value: " neighbor {{ neighbor_key }} remote-as {{ model.config.peer_as }}\n" negate: " no neighbor {{ neighbor_key }}\n" add-paths: _process: not_implemented @@ -620,19 +618,16 @@ neighbors: _process: not_implemented description: _process: - - mode: element - value: " neighbor {{ neighbor_key }} description {{ model }}\n" + - value: " neighbor {{ neighbor_key }} description {{ model }}\n" negate: " default neighbor {{ neighbor_key }} description\n" enabled: _process: - - mode: element - value: " neighbor {{ neighbor_key }} shutdown\n" + - value: " neighbor {{ neighbor_key }} shutdown\n" negate: " default neighbor {{ neighbor_key }}\n" when: "{{ not model }}" local-as: _process: - - mode: element - value: " neighbor {{ neighbor_key }} local-as {{ model }} no-prepend replace-as\n" + - value: " neighbor {{ neighbor_key }} local-as {{ model }} no-prepend replace-as\n" negate: " default neighbor {{ neighbor_key }} local-as\n" neighbor-address: _process: unnecessary diff --git a/napalm_yang/mappings/eos/translators/openconfig-network-instance/includes/static_routes.yaml b/napalm_yang/mappings/eos/translators/openconfig-network-instance/includes/static_routes.yaml index c92de95a..e0ff034b 100644 --- a/napalm_yang/mappings/eos/translators/openconfig-network-instance/includes/static_routes.yaml +++ b/napalm_yang/mappings/eos/translators/openconfig-network-instance/includes/static_routes.yaml @@ -14,13 +14,11 @@ static: _process: unnecessary next-hop: _process: - - mode: container - key_value: "ip route {{ static_key }} {{ next_hop_key }}\n" + - key_value: "ip route {{ static_key }} {{ next_hop_key }}\n" negate: "no ip route {{ static_key }} {{ next_hop_key }}\n" when: "{{ network_instance_key == 'global' }}" in: "network-instances" - - mode: container - key_value: "ip route vrf {{ network_instance_key }} {{ static_key }} {{ next_hop_key }}\n" + - key_value: "ip route vrf {{ network_instance_key }} {{ static_key }} {{ next_hop_key }}\n" negate: "no ip route vrf {{ network_instance_key }} {{ static_key }} {{ next_hop_key }}\n" when: "{{ network_instance_key != 'global' }}" in: "network-instances" @@ -30,12 +28,10 @@ static: _process: not_implemented metric: _process: - - mode: element - value: "ip route {{ static_key }} {{ next_hop_key }} {{ model }}\n" + - value: "ip route {{ static_key }} {{ next_hop_key }} {{ model }}\n" negate: "ip route {{ static_key }} {{ next_hop_key }} 1\n" when: "{{ network_instance_key == 'global' }}" - - mode: element - value: "ip route vrf {{ network_instance_key }} {{ static_key }} {{ next_hop_key }} {{ model }}\n" + - value: "ip route vrf {{ network_instance_key }} {{ static_key }} {{ next_hop_key }} {{ model }}\n" negate: "ip route vrf {{ network_instance_key }} {{ static_key }} {{ next_hop_key }} 1\n" when: "{{ network_instance_key != 'global' }}" next-hop: diff --git a/napalm_yang/mappings/eos/translators/openconfig-network-instance/network-instances.yaml b/napalm_yang/mappings/eos/translators/openconfig-network-instance/network-instances.yaml index 307bbc0c..980c1e63 100644 --- a/napalm_yang/mappings/eos/translators/openconfig-network-instance/network-instances.yaml +++ b/napalm_yang/mappings/eos/translators/openconfig-network-instance/network-instances.yaml @@ -7,8 +7,7 @@ network-instances: _process: unnecessary network-instance: _process: - - mode: container - key_value: "vrf definition {{ network_instance_key }}\n" + - key_value: "vrf definition {{ network_instance_key }}\n" negate: "no vrf definition {{ network_instance_key }}\n" continue_negating: true end: " exit\n" @@ -18,8 +17,7 @@ network-instances: _process: unnecessary description: _process: - - mode: element - value: " description {{ model }}\n" + - value: " description {{ model }}\n" negate: " default description\n" enabled: _process: unnecessary @@ -31,8 +29,7 @@ network-instances: _process: unnecessary route-distinguisher: _process: - - mode: element - value: " rd {{ model }}\n" + - value: " rd {{ model }}\n" negate: " default rd\n" router-id: _process: not_implemented @@ -262,14 +259,12 @@ network-instances: _process: unnecessary protocol: _process: - - mode: container - key_value: "router bgp {{ model.bgp.global_.config.as_ }}\n" + - key_value: "router bgp {{ model.bgp.global_.config.as_ }}\n" negate: "no router bgp {{ model.bgp.global_.config.as_ }}\n" end: " exit\n" when: "{{ protocol_key == 'bgp bgp' and network_instance_key == 'global' }}" in: "network-instances" - - mode: container - key_value: "router bgp {{ model.bgp.global_.config.as_ }}\n vrf {{ network_instance_key}}\n" + - key_value: "router bgp {{ model.bgp.global_.config.as_ }}\n vrf {{ network_instance_key}}\n" negate: "router bgp {{ model.bgp.global_.config.as_ }}\n no vrf {{ network_instance_key}}\n" end: " exit\n" when: "{{ protocol_key == 'bgp bgp' and network_instance_key != 'global' }}" diff --git a/napalm_yang/mappings/eos/translators/openconfig-vlan/vlan.yaml b/napalm_yang/mappings/eos/translators/openconfig-vlan/vlan.yaml index 0c9ece30..8e6032dd 100644 --- a/napalm_yang/mappings/eos/translators/openconfig-vlan/vlan.yaml +++ b/napalm_yang/mappings/eos/translators/openconfig-vlan/vlan.yaml @@ -8,6 +8,5 @@ vlan: _process: unnecessary vlan-id: _process: - - mode: element - value: " encapsulation dot1q vlan {{ model }}\n" + - value: " encapsulation dot1q vlan {{ model }}\n" negate: " default encapsulation dot1q vlan {{ model }}\n" diff --git a/napalm_yang/mappings/ios/translators/openconfig-if-ip/ipv4.yaml b/napalm_yang/mappings/ios/translators/openconfig-if-ip/ipv4.yaml index fa61f3af..516be4bf 100644 --- a/napalm_yang/mappings/ios/translators/openconfig-if-ip/ipv4.yaml +++ b/napalm_yang/mappings/ios/translators/openconfig-if-ip/ipv4.yaml @@ -8,8 +8,7 @@ ipv4: _process: unnecessary enabled: _process: # TODO look at this one, I think this is different depending on IOS version - - mode: element - value: " no switchport\n" + - value: " no switchport\n" negate: " switchport\n" in: "interface.{{ interface_key }}" when: "{{ model and interface_key[0:4] not in ['Mana', 'Loop'] }}" @@ -19,8 +18,7 @@ ipv4: _process: unnecessary address: _process: - - mode: container - key_value: " ip address {{ model.config.ip }} {{ model.config.prefix_length|cidr_to_netmask }}{{ ' secondary' if model.config.secondary else '' }}\n" + - key_value: " ip address {{ model.config.ip }} {{ model.config.prefix_length|cidr_to_netmask }}{{ ' secondary' if model.config.secondary else '' }}\n" negate: " default ip address {{ model.config.ip }} {{ model.config.prefix_length|cidr_to_netmask }}{{ ' secondary' if model.config.secondary else '' }}\n" replace: false ip: diff --git a/napalm_yang/mappings/ios/translators/openconfig-interfaces/interfaces.yaml b/napalm_yang/mappings/ios/translators/openconfig-interfaces/interfaces.yaml index 68db90eb..5355473a 100644 --- a/napalm_yang/mappings/ios/translators/openconfig-interfaces/interfaces.yaml +++ b/napalm_yang/mappings/ios/translators/openconfig-interfaces/interfaces.yaml @@ -7,8 +7,7 @@ interfaces: _process: unnecessary interface: _process: - - mode: container - key_value: "interface {{ interface_key }}\n" + - key_value: "interface {{ interface_key }}\n" negate: "{{ 'no' if interface_key[0:4] in ['Port', 'Loop'] else 'default' }} interface {{ interface_key }}\n" end: " exit\n" name: @@ -29,18 +28,15 @@ interfaces: _process: unnecessary enabled: _process: - - mode: element - value: " shutdown\n" + - value: " shutdown\n" when: "{{ not model }}" description: _process: - - mode: element - value: " description {{ model }}\n" + - value: " description {{ model }}\n" negate: " default description\n" mtu: _process: - - mode: element - value: " mtu {{ model }}\n" + - value: " mtu {{ model }}\n" negate: " default mtu\n" # IOS will complain with % Interface Loopback1 does not support user settable mtu. when: "{{ model and interface_key[0:4] not in ['Mana', 'Loop'] }}" @@ -48,8 +44,7 @@ interfaces: _process: unnecessary subinterface: _process: - - mode: container - key_value: "interface {{ interface_key}}.{{ subinterface_key }}\n" + - key_value: "interface {{ interface_key}}.{{ subinterface_key }}\n" negate: "no interface {{ interface_key}}.{{ subinterface_key }}\n" in: "interfaces" end: " exit\n" @@ -63,11 +58,9 @@ interfaces: _process: unnecessary enabled: _process: - - mode: element - value: " shutdown\n" + - value: " shutdown\n" when: "{{ not model }}" description: _process: - - mode: element - value: " description {{ model }}\n" + - value: " description {{ model }}\n" negate: " default description" diff --git a/napalm_yang/mappings/ios/translators/openconfig-vlan/vlan.yaml b/napalm_yang/mappings/ios/translators/openconfig-vlan/vlan.yaml index d4f22c68..c0b35e2e 100644 --- a/napalm_yang/mappings/ios/translators/openconfig-vlan/vlan.yaml +++ b/napalm_yang/mappings/ios/translators/openconfig-vlan/vlan.yaml @@ -8,6 +8,5 @@ vlan: _process: unnecessary vlan-id: _process: - - mode: element - value: " encapsulation dot1q {{ model }}\n" + - value: " encapsulation dot1q {{ model }}\n" negate: " no encapsulation dot1q\n" diff --git a/napalm_yang/mappings/junos/parsers/config/openconfig-if-ip/ipv4.yaml b/napalm_yang/mappings/junos/parsers/config/openconfig-if-ip/ipv4.yaml index 7c462e63..81e39979 100644 --- a/napalm_yang/mappings/junos/parsers/config/openconfig-if-ip/ipv4.yaml +++ b/napalm_yang/mappings/junos/parsers/config/openconfig-if-ip/ipv4.yaml @@ -10,7 +10,6 @@ ipv4: _process: - mode: is_present xpath: "family/inet" - from: "{{ bookmarks['parent'] }}" mtu: _process: not_implemented addresses: @@ -19,7 +18,6 @@ ipv4: _process: - xpath: "family/inet/address" key: name - from: "{{ bookmarks['parent'] }}" ip: _process: unnecessary config: @@ -28,12 +26,10 @@ ipv4: _process: - xpath: "name" regexp: "(?P.*)/\\d+" - from: "{{ bookmarks['parent'] }}" prefix-length: _process: - xpath: "name" regexp: ".*/(?P\\d+)" - from: "{{ bookmarks['parent'] }}" vrrp: _process: not_implemented vrrp-group: diff --git a/napalm_yang/mappings/junos/parsers/config/openconfig-interfaces/interfaces.yaml b/napalm_yang/mappings/junos/parsers/config/openconfig-interfaces/interfaces.yaml index 5e5548b1..adb0c49a 100644 --- a/napalm_yang/mappings/junos/parsers/config/openconfig-interfaces/interfaces.yaml +++ b/napalm_yang/mappings/junos/parsers/config/openconfig-interfaces/interfaces.yaml @@ -12,7 +12,7 @@ interfaces: _process: - xpath: "interfaces/interface" key: name - from: "{{ bookmarks.interfaces.0 }}" + from: interfaces.0 name: _process: unnecessary hold-time: @@ -34,10 +34,6 @@ interfaces: - mode: map xpath: name regexp: "(?P[a-z]+).*" - # Next field could easily be - # from: "{{ bookmarks['parent'] }}" - # but this is more efficient - from: "{{ parent_key }}" map: ge: ethernetCsmacd xe: ethernetCsmacd @@ -51,22 +47,18 @@ interfaces: _process: - mode: is_absent xpath: "disable" - from: "{{ bookmarks['parent'] }}" description: _process: - xpath: description - from: "{{ bookmarks['parent'] }}" mtu: _process: - xpath: mtu - from: "{{ bookmarks['parent'] }}" subinterfaces: _process: unnecessary subinterface: _process: - xpath: "unit" key: name - from: "{{ bookmarks['parent'] }}" index: _process: unnecessary config: @@ -81,8 +73,6 @@ interfaces: _process: - mode: is_absent xpath: "disable" - from: "{{ bookmarks['parent'] }}" description: _process: - xpath: description - from: "{{ bookmarks['parent'] }}" diff --git a/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/includes/bgp.yaml b/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/includes/bgp.yaml index 776ee6a2..42e6d1c1 100644 --- a/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/includes/bgp.yaml +++ b/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/includes/bgp.yaml @@ -259,11 +259,11 @@ global: as: _process: - xpath: "routing-options/autonomous-system/as-number" - from: "{{ bookmarks['network_instance'][network_instance_key] }}" + from: "network_instance.{{ network_instance_key }}" router-id: _process: - xpath: "routing-options/router-id" - from: "{{ bookmarks['network_instance'][network_instance_key] }}" + from: "network_instance.{{ network_instance_key }}" default-route-distance: _process: not_implemented config: @@ -355,7 +355,6 @@ neighbors: - mode: nested xpath: "group/?peer_group.name/neighbor" key_attribute: name - from: "{{ bookmarks.parent }}" add-paths: _process: not_implemented config: @@ -623,13 +622,11 @@ neighbors: description: _process: - xpath: "description" - from: "{{ bookmarks.parent }}" enabled: _process: not_implemented local-as: _process: - xpath: "local-as.as-number" - from: "{{ bookmarks.parent }}" neighbor-address: _process: - mode: value @@ -637,7 +634,6 @@ neighbors: peer-as: _process: - xpath: "peer-as" - from: "{{ bookmarks.parent }}" peer-group: _process: - mode: value diff --git a/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/includes/static_routes.yaml b/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/includes/static_routes.yaml index 18f9dc87..02b5172e 100644 --- a/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/includes/static_routes.yaml +++ b/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/includes/static_routes.yaml @@ -7,7 +7,6 @@ static: - mode: container xpath: "route" key_attribute: name - from: "{{ bookmarks.parent }}" config: _process: unnecessary prefix: @@ -23,7 +22,6 @@ static: - mode: container xpath: "qualified-next-hop" key_attribute: name - from: "{{ bookmarks.parent }}" config: _process: unnecessary index: @@ -31,7 +29,6 @@ static: metric: _process: - xpath: "metric" - from: "{{ bookmarks.parent }}" next-hop: _process: - mode: value diff --git a/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/network-instances.yaml b/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/network-instances.yaml index a012343f..e0ee8ac6 100644 --- a/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/network-instances.yaml +++ b/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/network-instances.yaml @@ -13,10 +13,10 @@ network-instances: - mode: container xpath: "routing-instances/instance" key_attribute: name - from: "{{ bookmarks['network-instances'].0 }}" + from: network-instances.0 mandatory: - key: "global" - block: "{{ bookmarks['network-instances'].0 }}" + block: network-instances.0 extra_vars: {} afts: !include includes/afts.yaml config: @@ -24,7 +24,6 @@ network-instances: description: _process: - xpath: description - from: "{{ bookmarks.parent }}" enabled: _process: - mode: value @@ -46,7 +45,6 @@ network-instances: when: "{{ network_instance_key == 'global' }}" - mode: map xpath: instance-type - from: "{{ bookmarks.parent }}" map: vrf: L3VRF virtual-router: L2L3 @@ -324,12 +322,10 @@ network-instances: _process: - mode: container xpath: "protocols" - from: "{{ bookmarks.parent }}" nested: true composite_key: [protocols_name, protocols_name] - mode: container xpath: "routing-options/static" - from: "{{ bookmarks.parent }}" key: "static static" bgp: !include includes/bgp.yaml config: diff --git a/napalm_yang/mappings/junos/parsers/config/openconfig-vlan/vlan.yaml b/napalm_yang/mappings/junos/parsers/config/openconfig-vlan/vlan.yaml index ed1a06e8..a4e51d99 100644 --- a/napalm_yang/mappings/junos/parsers/config/openconfig-vlan/vlan.yaml +++ b/napalm_yang/mappings/junos/parsers/config/openconfig-vlan/vlan.yaml @@ -9,4 +9,3 @@ vlan: vlan-id: _process: - xpath: "vlan-id" - from: "{{ bookmarks['parent'] }}" diff --git a/napalm_yang/mappings/junos/parsers/state/openconfig-interfaces/interfaces.yaml b/napalm_yang/mappings/junos/parsers/state/openconfig-interfaces/interfaces.yaml index 01cb2ab8..b7fff72b 100644 --- a/napalm_yang/mappings/junos/parsers/state/openconfig-interfaces/interfaces.yaml +++ b/napalm_yang/mappings/junos/parsers/state/openconfig-interfaces/interfaces.yaml @@ -12,7 +12,7 @@ interfaces: _process: - xpath: "physical-interface" key: name - from: "{{ bookmarks.interfaces.0 }}" + from: interfaces.0 config: _process: unnecessary hold-time: @@ -31,43 +31,36 @@ interfaces: _process: - mode: map xpath: "admin-status" - from: "{{ bookmarks['parent'] }}" map: up: UP down: DOWN description: _process: - xpath: "description" - from: "{{ bookmarks['parent'] }}" enabled: _process: - mode: map xpath: "admin-status" - from: "{{ bookmarks['parent'] }}" map: up: true down: false ifindex: _process: - xpath: "snmp-index" - from: "{{ bookmarks['parent'] }}" last-change: _process: - xpath: "interface-flapped" attribute: "seconds" - from: "{{ bookmarks['parent'] }}" mtu: _process: - xpath: "mtu" regexp: "(?P\\d+)" # Someone thought mixing strings and ints was a good idea - from: "{{ bookmarks['parent'] }}" name: _process: unnecessary oper-status: _process: - mode: map xpath: "oper-status" - from: "{{ bookmarks['parent'] }}" map: up: UP down: DOWN @@ -75,7 +68,6 @@ interfaces: _process: - mode: map xpath: "if-type" - from: "{{ bookmarks['parent'] }}" map: GRE: tunnel IPIP: tunnel @@ -97,25 +89,20 @@ interfaces: in-broadcast-pkts: _process: - xpath: "ethernet-mac-statistics/input-broadcasts" - from: "{{ bookmarks['parent'] }}" in-discards: _process: - xpath: "input-error-list/input-discards" - from: "{{ bookmarks['parent'] }}" in-errors: _process: - xpath: "input-error-list/input-errors" - from: "{{ bookmarks['parent'] }}" in-multicast-pkts: _process: - xpath: "ethernet-mac-statistics/input-multicasts" - from: "{{ bookmarks['parent'] }}" in-octets: _process: not_implemented in-unicast-pkts: _process: - xpath: "ethernet-mac-statistics/input-unicasts" - from: "{{ bookmarks['parent'] }}" in-unknown-protos: _process: not_implemented last-clear: @@ -123,30 +110,25 @@ interfaces: out-broadcast-pkts: _process: - xpath: "ethernet-mac-statistics/output-broadcasts" - from: "{{ bookmarks['parent'] }}" out-discards: _process: not_implemented out-errors: _process: - xpath: "output-error-list/output-errors" - from: "{{ bookmarks['parent'] }}" out-multicast-pkts: _process: - xpath: "ethernet-mac-statistics/output-multicasts" - from: "{{ bookmarks['parent'] }}" out-octets: _process: not_implemented out-unicast-pkts: _process: - xpath: "ethernet-mac-statistics/output-unicasts" - from: "{{ bookmarks['parent'] }}" subinterfaces: _process: unnecessary subinterface: _process: - xpath: "logical-interface" key: name - from: "{{ bookmarks['parent'] }}" index: _process: unnecessary config: @@ -158,7 +140,6 @@ interfaces: description: _process: - xpath: "description" - from: "{{ bookmarks['parent'] }}" enabled: _process: not_implemented admin-status: @@ -166,7 +147,6 @@ interfaces: ifindex: _process: - xpath: "snmp-index" - from: "{{ bookmarks['parent'] }}" last-change: _process: not_implemented name: diff --git a/napalm_yang/mappings/junos/translators/openconfig-if-ip/ipv4.yaml b/napalm_yang/mappings/junos/translators/openconfig-if-ip/ipv4.yaml index 2c2ee9af..b827d669 100644 --- a/napalm_yang/mappings/junos/translators/openconfig-if-ip/ipv4.yaml +++ b/napalm_yang/mappings/junos/translators/openconfig-if-ip/ipv4.yaml @@ -4,8 +4,7 @@ metadata: ipv4: _process: - - mode: container - container: "family/inet" + - container: "family/inet" when: "{{ model.config.enabled or model.addresses.address|length }}" config: _process: unnecessary @@ -17,8 +16,7 @@ ipv4: _process: unnecessary address: _process: - - mode: container - container: address + - container: address ip: _process: unnecessary config: @@ -27,8 +25,7 @@ ipv4: _process: unnecessary prefix-length: _process: - - mode: element - element: name + - element: name value: "{{ model._parent.ip }}/{{ model }}" when: "{{ model }}" vrrp: diff --git a/napalm_yang/mappings/junos/translators/openconfig-interfaces/interfaces.yaml b/napalm_yang/mappings/junos/translators/openconfig-interfaces/interfaces.yaml index 6987a56a..862bf36b 100644 --- a/napalm_yang/mappings/junos/translators/openconfig-interfaces/interfaces.yaml +++ b/napalm_yang/mappings/junos/translators/openconfig-interfaces/interfaces.yaml @@ -5,13 +5,11 @@ metadata: interfaces: _process: - - mode: container - container: interfaces + - container: interfaces replace: true interface: _process: - - mode: container - container: interface + - container: interface key_element: name key_value: "{{ interface_key }}" name: @@ -32,24 +30,20 @@ interfaces: _process: unnecessary enabled: _process: - - mode: element - element: "disable" + - element: "disable" when: "{{ not model }}" value: null description: _process: - - mode: element - element: description + - element: description mtu: _process: - - mode: element - element: mtu + - element: mtu subinterfaces: _process: unnecessary subinterface: _process: - - mode: container - container: unit + - container: unit key_element: name key_value: "{{ subinterface_key }}" index: @@ -62,11 +56,9 @@ interfaces: _process: unnecessary enabled: _process: - - mode: element - element: "disable" + - element: "disable" when: "{{ not model }}" value: null description: _process: - - mode: element - element: description + - element: description diff --git a/napalm_yang/mappings/junos/translators/openconfig-network-instance/includes/bgp.yaml b/napalm_yang/mappings/junos/translators/openconfig-network-instance/includes/bgp.yaml index 3e71d714..fb25321f 100644 --- a/napalm_yang/mappings/junos/translators/openconfig-network-instance/includes/bgp.yaml +++ b/napalm_yang/mappings/junos/translators/openconfig-network-instance/includes/bgp.yaml @@ -258,13 +258,11 @@ global: _process: unnecessary as: _process: - - mode: element - element: "routing-options/autonomous-system/as-number" + - element: "routing-options/autonomous-system/as-number" in: "network_instance.{{ network_instance_key }}" router-id: _process: - - mode: element - element: "routing-options/router-id" + - element: "routing-options/router-id" in: "network_instance.{{ network_instance_key }}" default-route-distance: _process: not_implemented @@ -354,13 +352,11 @@ neighbors: _process: unnecessary neighbor: _process: - - mode: container - container: "group" + - container: "group" delete_on_merge: false key_element: name key_value: "{{ model.config.peer_group }}" - - mode: container - container: "neighbor" + - container: "neighbor" key_element: name key_value: "{{ neighbor_key }}" add-paths: @@ -629,20 +625,17 @@ neighbors: _process: not_implemented description: _process: - - mode: element - element: description + - element: description enabled: _process: not_implemented local-as: _process: - - mode: element - element: "local-as/as-number" + - element: "local-as/as-number" neighbor-address: _process: unnecessary peer-as: _process: - - mode: element - element: "peer-as" + - element: "peer-as" peer-group: _process: unnecessary peer-type: diff --git a/napalm_yang/mappings/junos/translators/openconfig-network-instance/includes/static_routes.yaml b/napalm_yang/mappings/junos/translators/openconfig-network-instance/includes/static_routes.yaml index 549be135..9e52db43 100644 --- a/napalm_yang/mappings/junos/translators/openconfig-network-instance/includes/static_routes.yaml +++ b/napalm_yang/mappings/junos/translators/openconfig-network-instance/includes/static_routes.yaml @@ -4,8 +4,7 @@ _process: when: "{{ protocol_key != 'static static' }}" static: _process: - - mode: container - container: "route" + - container: "route" key_element: name key_value: "{{ static_key }}" config: @@ -18,8 +17,7 @@ static: _process: unnecessary next-hop: _process: - - mode: container - container: "qualified-next-hop" + - container: "qualified-next-hop" key_element: name key_value: "{{ next_hop_key }}" config: @@ -28,8 +26,7 @@ static: _process: not_implemented metric: _process: - - mode: element - element: metric + - element: metric next-hop: _process: unnecessary recurse: diff --git a/napalm_yang/mappings/junos/translators/openconfig-network-instance/network-instances.yaml b/napalm_yang/mappings/junos/translators/openconfig-network-instance/network-instances.yaml index 80562e11..a5d1701d 100644 --- a/napalm_yang/mappings/junos/translators/openconfig-network-instance/network-instances.yaml +++ b/napalm_yang/mappings/junos/translators/openconfig-network-instance/network-instances.yaml @@ -7,14 +7,12 @@ network-instances: _process: unnecessary network-instance: _process: - - mode: container - container: "routing-instances" + - container: "routing-instances" replace: true delete_on_merge: false reuse: true when: "{{ network_instance_key != 'global' }}" - - mode: container - container: instance + - container: instance key_element: name key_value: "{{ network_instance_key }}" when: "{{ network_instance_key != 'global' }}" @@ -23,8 +21,7 @@ network-instances: _process: unnecessary description: _process: - - mode: element - element: description + - element: description enabled: _process: unnecessary enabled-address-families: @@ -269,12 +266,10 @@ network-instances: _process: unnecessary protocol: _process: - - mode: container - container: "protocols/{{ model.identifier }}" + - container: "protocols/{{ model.identifier }}" replace: true when: "{{ protocol_key != 'static static' }}" - - mode: container - container: "routing-options/static" + - container: "routing-options/static" when: "{{ protocol_key == 'static static' }}" bgp: !include includes/bgp.yaml config: diff --git a/napalm_yang/mappings/junos/translators/openconfig-vlan/vlan.yaml b/napalm_yang/mappings/junos/translators/openconfig-vlan/vlan.yaml index ba20d4b7..0d728150 100644 --- a/napalm_yang/mappings/junos/translators/openconfig-vlan/vlan.yaml +++ b/napalm_yang/mappings/junos/translators/openconfig-vlan/vlan.yaml @@ -8,11 +8,9 @@ vlan: _process: unnecessary vlan-id: _process: - - mode: element - element: "vlan-tagging" + - element: "vlan-tagging" in: "interface.{{ interface_key }}" when: "{{ model > 0 }}" value: null - - mode: element - element: "vlan-id" + - element: "vlan-id" when: "{{ model > 0 }}" diff --git a/napalm_yang/parser.py b/napalm_yang/parser.py index f1c41d40..ec89ef51 100644 --- a/napalm_yang/parser.py +++ b/napalm_yang/parser.py @@ -90,7 +90,7 @@ def _parse_container(self, attribute, model, mapping): self.extra_vars, None, self.bookmarks) if model._yang_type is not None: # None means it's an element of a list - block, extra_vars = self.parser.parse_container(mapping["_process"]) + block, extra_vars = self.parser.parse_container(mapping["_process"], self.bookmarks) if block is None: return @@ -128,7 +128,8 @@ def _parse_list(self, attribute, model, mapping): # for each individual element of the list self.bookmarks[attribute] = {} - for key, block, extra_vars in self.parser.parse_list(mapping_copy["_process"]): + for key, block, extra_vars in self.parser.parse_list(mapping_copy["_process"], + self.bookmarks): logger.debug("Parsing element {}[{}]".format(attribute, key)) try: @@ -167,7 +168,7 @@ def _parse_leaf(self, attribute, model, mapping): if model._is_keyval: return - value = self.parser.parse_leaf(mapping["_process"]) + value = self.parser.parse_leaf(mapping["_process"], self.bookmarks) if value is not None and (value != model.default() or isinstance(value, bool)): setter = getattr(model._parent, "_set_{}".format(attribute)) diff --git a/napalm_yang/parsers/base.py b/napalm_yang/parsers/base.py index dc9d8440..b6be32a6 100644 --- a/napalm_yang/parsers/base.py +++ b/napalm_yang/parsers/base.py @@ -3,56 +3,87 @@ class BaseParser(object): + @staticmethod + def resolve_bookmark(bookmarks, path): + b = bookmarks + for p in path.split("."): + try: + b = b[p] + except TypeError: + b = b[int(p)] + bookmark = b + return bookmark + @classmethod def init_native(cls, native): return native @classmethod - def parse_list(cls, mapping): + def parse_list(cls, mapping, bookmarks): for m in mapping: + # parent will change as the tree is processed so we save it + # so we can restore it + parent = bookmarks["parent"] + + mandatory_elements = m.get("mandatory", []) + for me in mandatory_elements: + me["block"] = cls.resolve_bookmark(bookmarks, me["block"]) + + data = cls.resolve_bookmark(bookmarks, m.get("from", "parent")) method_name = "_parse_list_{}".format(m.get("mode", "default")) - for key, block, extra_vars in getattr(cls, method_name)(m): + for key, block, extra_vars in getattr(cls, method_name)(m, data): yield key, block, extra_vars + # we restore the parent + bookmarks["parent"] = parent + @classmethod - def parse_leaf(cls, mapping): + def parse_leaf(cls, mapping, bookmarks): for m in mapping: + data = cls.resolve_bookmark(bookmarks, m.get("from", "parent")) method_name = "_parse_leaf_{}".format(m.get("mode", "default")) - result = getattr(cls, method_name)(m) + result = getattr(cls, method_name)(m, data) if result is not None: return result @classmethod - def parse_container(cls, mapping): + def parse_container(cls, mapping, bookmarks): for m in mapping: + # parent will change as the tree is processed so we save it + # so we can restore it + parent = bookmarks["parent"] + data = cls.resolve_bookmark(bookmarks, m.get("from", "parent")) method_name = "_parse_container_{}".format(m.get("mode", "default")) - result, extra_vars = getattr(cls, method_name)(m) + result, extra_vars = getattr(cls, method_name)(m, data) if result or extra_vars: break + + # we restore the parent + bookmarks["parent"] = parent return result, extra_vars @classmethod - def _parse_leaf_skip(cls, mapping): + def _parse_leaf_skip(cls, mapping, bookmarks): return _parse_leaf_gate = _parse_leaf_skip @classmethod - def _parse_container_skip(cls, mapping): + def _parse_container_skip(cls, mapping, bookmarks): return "", {} @classmethod - def _parse_list_skip(cls, mapping): + def _parse_list_skip(cls, mapping, bookmarks): return {} _parse_list_gate = _parse_list_skip @classmethod - def _parse_leaf_value(cls, mapping): + def _parse_leaf_value(cls, mapping, bookmarks): return mapping["value"] @classmethod - def _parse_container_gate(cls, mapping): + def _parse_container_gate(cls, mapping, bookmarks): """This basically stops parsing the tree by returning None""" return None, {} @@ -63,22 +94,3 @@ def _parse_post_process_filter(cls, post_process_filter, key, extra_vars={}): kwargs["extra_vars"] = extra_vars key = template(post_process_filter, **kwargs) return key - - """ - @classmethod - def _parse_leaf_key(cls, mapping, texts, keys, extra_vars): - leaf_extraction = mapping["_leaf_extraction"] - value = keys[leaf_extraction["value"]] - - if "regexp" in leaf_extraction.keys(): - value = re.search(mapping["_leaf_extraction"]["regexp"], value).group("value") - return value - - @classmethod - def _parse_list_not_implemented(cls, mapping, texts, keys, extra_vars): - return {} - - @classmethod - def _parse_leaf_not_implemented(cls, mapping, texts, keys, extra_vars): - return - """ diff --git a/napalm_yang/parsers/xml.py b/napalm_yang/parsers/xml.py index 8356d704..56cef5e7 100644 --- a/napalm_yang/parsers/xml.py +++ b/napalm_yang/parsers/xml.py @@ -10,32 +10,39 @@ class XMLParser(BaseParser): @classmethod - def _parse_list_default(cls, mapping): - xml = etree.fromstring(mapping["from"]) + def init_native(cls, native): + r = [] + for n in native: + if hasattr(n, "xpath"): + r.append(n) + else: + r.append(etree.fromstring(n)) + return r + @classmethod + def _parse_list_default(cls, mapping, data): mandatory_elements = mapping.pop("mandatory", []) post_process_filter = mapping.pop("post_process_filter", None) - for element in itertools.chain(xml.xpath(mapping["xpath"]), mandatory_elements): + for element in itertools.chain(data.xpath(mapping["xpath"]), mandatory_elements): if isinstance(element, dict): yield element["key"], element["block"], element["extra_vars"] else: key = element.xpath(mapping["key"])[0].text.strip() if post_process_filter: key = cls._parse_post_process_filter(post_process_filter, key) - yield key, etree.tostring(element), {} + yield key, element, {} @classmethod - def _parse_list_nested(cls, mapping): - xml = etree.fromstring(mapping["from"]) + def _parse_list_nested(cls, mapping, data): path = mapping["xpath"].split("/") iterators = [] list_vars = [] - cls._parse_list_nested_recursive(xml, path, iterators, list_vars) - return cls._parse_list_container_helper(mapping, iterators, xml, list_vars) + cls._parse_list_nested_recursive(data, path, iterators, list_vars) + return cls._parse_list_container_helper(mapping, iterators, data, list_vars) @classmethod - def _parse_list_nested_recursive(cls, xml, path, iterators, list_vars, cur_vars=None): + def _parse_list_nested_recursive(cls, data, path, iterators, list_vars, cur_vars=None): """ This helps parsing shit like: @@ -69,18 +76,18 @@ def _parse_list_nested_recursive(cls, xml, path, iterators, list_vars, cur_vars= p = path[0] path = path[1:] else: - for _ in xml: + for _ in data: list_vars.append(cur_vars) - iterators.append(xml) - return xml + iterators.append(data) + return data if p.startswith("?"): - for x in xml: + for x in data: key, var_path = p.split(".") cur_vars.update({key.lstrip("?"): x.xpath(var_path)[0].text}) cls._parse_list_nested_recursive(x, path, iterators, list_vars, cur_vars) else: - x = xml.xpath(p) + x = data.xpath(p) cls._parse_list_nested_recursive(x, path, iterators, list_vars, cur_vars) @classmethod @@ -119,22 +126,20 @@ def _parse_list_container_helper(cls, mapping, iterators, root, list_vars=None): if post_process_filter: key = cls._parse_post_process_filter(post_process_filter, key, extra_vars) - yield key, etree.tostring(element), extra_vars + yield key, element, extra_vars @classmethod - def _parse_list_container(cls, mapping): - xml = etree.fromstring(mapping["from"]) - xml = xml.xpath(mapping["xpath"]) + def _parse_list_container(cls, mapping, data): + data = data.xpath(mapping["xpath"]) nested = mapping.get("nested", False) - root = xml[0] if nested and len(xml) else xml + root = data[0] if nested and len(data) else data return cls._parse_list_container_helper(mapping, [root], root) @classmethod - def _parse_leaf_default(cls, mapping, check_default=True, check_presence=False): - xml = etree.fromstring(mapping["from"]) - element = xml.xpath(mapping["xpath"]) + def _parse_leaf_default(cls, mapping, data, check_default=True, check_presence=False): + element = data.xpath(mapping["xpath"]) if element and not check_presence: if "attribute" in mapping.keys(): @@ -157,8 +162,8 @@ def _parse_leaf_default(cls, mapping, check_default=True, check_presence=False): return None @classmethod - def _parse_leaf_map(cls, mapping): - value = cls._parse_leaf_default(mapping) + def _parse_leaf_map(cls, mapping, data): + value = cls._parse_leaf_default(mapping, data) if "regex" in mapping.keys(): value = re.search(mapping["regexp"], value).group("value") @@ -166,9 +171,9 @@ def _parse_leaf_map(cls, mapping): return mapping["map"][value] if value else None @classmethod - def _parse_leaf_is_present(cls, mapping): - return cls._parse_leaf_default(mapping, check_default=False, check_presence=True) + def _parse_leaf_is_present(cls, mapping, data): + return cls._parse_leaf_default(mapping, data, check_default=False, check_presence=True) @classmethod - def _parse_leaf_is_absent(cls, mapping): - return not cls._parse_leaf_default(mapping, check_default=False, check_presence=True) + def _parse_leaf_is_absent(cls, mapping, data): + return not cls._parse_leaf_default(mapping, data, check_default=False, check_presence=True) diff --git a/napalm_yang/translators/base.py b/napalm_yang/translators/base.py index 9cec9d3a..ee67cf98 100644 --- a/napalm_yang/translators/base.py +++ b/napalm_yang/translators/base.py @@ -19,13 +19,14 @@ def __init__(self, merge, replace): def init_element(self, attribute, model, other, mapping, translation, bookmarks): et = translation for m in mapping: - if m["mode"] == "skip": + mode = m.get("mode", "default") + if mode == "skip": continue - elif m["mode"] == "gate": + elif mode == "gate": return t = _find_translation_point(m, bookmarks, et) - method_name = "_init_element_{}".format(m["mode"]) + method_name = "_init_element_{}".format(mode) et = getattr(self, method_name)(attribute, model, other, m, t) if et is False: # if it's False we want to return None to signal we want to abort @@ -35,36 +36,39 @@ def init_element(self, attribute, model, other, mapping, translation, bookmarks) def default_element(self, mapping, translation, bookmarks, replacing=False): t = translation for m in mapping: - if m["mode"] == "skip": + mode = m.get("mode", "default") + if mode == "skip": continue - elif m["mode"] == "gate": + elif mode == "gate": return t = _find_translation_point(m, bookmarks, t) - method_name = "_default_element_{}".format(m["mode"]) + method_name = "_default_element_{}".format(mode) t = getattr(self, method_name)(m, t, replacing) def translate_leaf(self, attribute, model, other, mapping, translation, bookmarks): for m in mapping: - if m["mode"] == "skip": + mode = m.get("mode", "default") + if mode == "skip": continue - elif m["mode"] == "gate": + elif mode == "gate": return t = _find_translation_point(m, bookmarks, translation) - method_name = "_translate_leaf_{}".format(m["mode"]) + method_name = "_translate_leaf_{}".format(mode) getattr(self, method_name)(attribute, model, other, m, t) def translate_container(self, attribute, model, other, mapping, translation, bookmarks): et = translation for m in mapping: - if m["mode"] == "skip": + mode = m.get("mode", "default") + if mode == "skip": continue - elif m["mode"] == "gate": + elif mode == "gate": return t = _find_translation_point(m, bookmarks, et) - method_name = "_translate_container_{}".format(m["mode"]) + method_name = "_translate_container_{}".format(mode) et = getattr(self, method_name)(attribute, model, other, m, t) if et is False: # if it's False we want to return None to signal we want to abort diff --git a/napalm_yang/translators/text.py b/napalm_yang/translators/text.py index aab742cb..57fbc211 100644 --- a/napalm_yang/translators/text.py +++ b/napalm_yang/translators/text.py @@ -17,7 +17,7 @@ def init_translation(self, metadata, translation): def post_processing(self, translator): return self._xml_to_text(translator.translation) - def _translate_leaf_element(self, attribute, model, other, mapping, translation): + def _translate_leaf_default(self, attribute, model, other, mapping, translation): force = False if model == other and not self.replace: @@ -30,9 +30,9 @@ def _translate_leaf_element(self, attribute, model, other, mapping, translation) return mapping["element"] = "command" - super()._translate_leaf_element(attribute, model, other, mapping, translation, force) + super()._translate_leaf_default(attribute, model, other, mapping, translation, force) - def _init_element_container(self, attribute, model, other, mapping, translation): + def _init_element_default(self, attribute, model, other, mapping, translation): if other is not None: if not napalm_yang.utils.diff(model, other) and not self.replace: # If objects are equal we return None as that aborts translating @@ -47,7 +47,7 @@ def _init_element_container(self, attribute, model, other, mapping, translation) mapping["key_element"] = "command" mapping["container"] = model._yang_name - t = super()._init_element_container(attribute, model, other, mapping, translation) + t = super()._init_element_default(attribute, model, other, mapping, translation) end = mapping.get("end", "") if end and t is not None: @@ -56,12 +56,7 @@ def _init_element_container(self, attribute, model, other, mapping, translation) return t - # def _translate_container_container(self, attribute, model, other, mapping, translation): - # mapping["key_element"] = "container" - # mapping["container"] = model._yang_name - # return super()._init_element_container(attribute, model, other, mapping, translation) - - def _default_element_container(self, mapping, translation, replacing): + def _default_element_default(self, mapping, translation, replacing): if (replacing or self.replace) and not mapping.get("replace", True): return diff --git a/napalm_yang/translators/xml.py b/napalm_yang/translators/xml.py index b98c0c88..b9cddcb0 100644 --- a/napalm_yang/translators/xml.py +++ b/napalm_yang/translators/xml.py @@ -14,7 +14,7 @@ def init_translation(self, metadata, translation): else: return translation - def _init_element_container(self, attribute, model, other, mapping, translation): + def _init_element_default(self, attribute, model, other, mapping, translation): t = translation for element in mapping["container"].split("/"): @@ -40,9 +40,9 @@ def _init_element_container(self, attribute, model, other, mapping, translation) return t - _translate_container_container = _init_element_container + _translate_container_default = _init_element_default - def _default_element_container(self, mapping, translation, replacing): + def _default_element_default(self, mapping, translation, replacing): if not self.merge: return @@ -64,7 +64,7 @@ def _default_element_container(self, mapping, translation, replacing): return t - def _translate_leaf_element(self, attribute, model, other, mapping, translation, force=False): + def _translate_leaf_default(self, attribute, model, other, mapping, translation, force=False): delete = False if not model._changed() and other and self.merge: delete = True @@ -89,4 +89,4 @@ def _translate_leaf_element(self, attribute, model, other, mapping, translation, def _translate_leaf_map(self, attribute, model, other, mapping, translation): mapping["value"] = mapping["map"][model] - self._translate_leaf_element(attribute, model, other, mapping, translation) + self._translate_leaf_default(attribute, model, other, mapping, translation) diff --git a/test/integration/test_profiles.py b/test/integration/test_profiles.py index ca76840c..506d402f 100644 --- a/test/integration/test_profiles.py +++ b/test/integration/test_profiles.py @@ -45,14 +45,14 @@ def pretty_json(dictionary): test_config_profile_models = [ - ["ios", napalm_yang.models.openconfig_interfaces, "default"], - ["eos", napalm_yang.models.openconfig_network_instance, "default"], + # ["ios", napalm_yang.models.openconfig_interfaces, "default"], + # ["eos", napalm_yang.models.openconfig_network_instance, "default"], ["junos", napalm_yang.models.openconfig_network_instance, "default"], ] test_state_profile_models = [ ["junos", napalm_yang.models.openconfig_interfaces, "default"], - ["eos", napalm_yang.models.openconfig_interfaces, "default"], + # ["eos", napalm_yang.models.openconfig_interfaces, "default"], ] From 4571e648ff58b4704df3a7fc0248551ef6bdb80c Mon Sep 17 00:00:00 2001 From: David Barroso Date: Mon, 19 Jun 2017 09:25:06 +0200 Subject: [PATCH 3/9] TextParser now avoids serializing/deserializing the object all the time --- .../parsers/config/openconfig-if-ip/ipv4.yaml | 2 -- .../openconfig-interfaces/interfaces.yaml | 12 +++-------- .../includes/bgp.yaml | 8 -------- .../includes/static_routes.yaml | 3 --- .../network-instances.yaml | 12 +++++------ .../parsers/config/openconfig-vlan/vlan.yaml | 1 - .../parsers/config/openconfig-if-ip/ipv4.yaml | 2 -- .../openconfig-interfaces/interfaces.yaml | 12 +++-------- .../parsers/config/openconfig-vlan/vlan.yaml | 1 - napalm_yang/parsers/base.py | 2 ++ napalm_yang/parsers/text.py | 20 +++++++++---------- test/integration/test_profiles.py | 4 ++-- 12 files changed, 25 insertions(+), 54 deletions(-) diff --git a/napalm_yang/mappings/eos/parsers/config/openconfig-if-ip/ipv4.yaml b/napalm_yang/mappings/eos/parsers/config/openconfig-if-ip/ipv4.yaml index 6297c111..65f45cbc 100644 --- a/napalm_yang/mappings/eos/parsers/config/openconfig-if-ip/ipv4.yaml +++ b/napalm_yang/mappings/eos/parsers/config/openconfig-if-ip/ipv4.yaml @@ -10,7 +10,6 @@ ipv4: _process: - mode: is_absent regexp: "(?P^\\W*switchport$)" - from: "{{ bookmarks['parent'] }}" mtu: _process: not_implemented addresses: @@ -18,7 +17,6 @@ ipv4: address: _process: - regexp: "(?Pip address (?P(?P.*))\\/(?P\\d+))(?P secondary)*" - from: "{{ bookmarks['parent'] }}" ip: _process: unnecessary config: diff --git a/napalm_yang/mappings/eos/parsers/config/openconfig-interfaces/interfaces.yaml b/napalm_yang/mappings/eos/parsers/config/openconfig-interfaces/interfaces.yaml index 6bdb13dc..cf7b521c 100644 --- a/napalm_yang/mappings/eos/parsers/config/openconfig-interfaces/interfaces.yaml +++ b/napalm_yang/mappings/eos/parsers/config/openconfig-interfaces/interfaces.yaml @@ -10,7 +10,7 @@ interfaces: interface: _process: - regexp: "(?Pinterface (?P(\\w|-)*\\d+)\n(?:.|\n)*?^!$)" - from: "{{ bookmarks.interfaces.0 }}" + from: interfaces.0 name: _process: unnecessary hold-time: @@ -28,8 +28,7 @@ interfaces: type: _process: - mode: map - regexp: "(?P(\\w|-)*)\\d+" - from: "{{ interface_key }}" + regexp: "interface (?P(\\w|-)*)\\d+" map: Ethernet: ethernetCsmacd Management: ethernetCsmacd @@ -40,21 +39,18 @@ interfaces: _process: - mode: is_present regexp: "(?Pno shutdown)" - from: "{{ bookmarks.interface[interface_key] }}" description: _process: - regexp: "description (?P.*)" - from: "{{ bookmarks.interface[interface_key] }}" mtu: _process: - regexp: "mtu (?P[0-9]+)" - from: "{{ bookmarks.interface[interface_key] }}" subinterfaces: _process: unnecessary subinterface: _process: - regexp: "(?Pinterface {{interface_key}}\\.(?P\\d+)\\n(?:.|\\n)*?^!$)" - from: "{{ bookmarks.interfaces.0 }}" + from: interfaces.0 index: _process: unnecessary config: @@ -69,8 +65,6 @@ interfaces: _process: - mode: is_present regexp: "(?Pno shutdown)" - from: "{{ bookmarks.subinterface[subinterface_key] }}" description: _process: - regexp: "description (?P.*)" - from: "{{ bookmarks.subinterface[subinterface_key] }}" diff --git a/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/includes/bgp.yaml b/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/includes/bgp.yaml index 448ae2a1..57965bfd 100644 --- a/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/includes/bgp.yaml +++ b/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/includes/bgp.yaml @@ -259,12 +259,10 @@ global: as: _process: - regexp: "local-as (?P.*)" - from: "{{ bookmarks['protocol'][protocol_key] }}" default: "{{ extra_vars.process_id|default(1) }}" router-id: _process: - regexp: "router-id (?P.*)" - from: "{{ bookmarks['protocol'][protocol_key] }}" default-route-distance: _process: not_implemented config: @@ -354,7 +352,6 @@ neighbors: neighbor: _process: - regexp: "(?Pneighbor (?P\\d+.\\d+.\\d+.\\d+).*)" - from: "{{ bookmarks['protocol'][protocol_key] }}" flat: true add-paths: _process: not_implemented @@ -623,25 +620,20 @@ neighbors: description: _process: - regexp: "neighbor {{ neighbor_key }} description (?P.*)" - from: "{{ bookmarks['neighbor'][neighbor_key] }}" enabled: _process: - mode: is_absent regexp: "neighbor {{ neighbor_key }} (?Pshutdown)" - from: "{{ bookmarks['neighbor'][neighbor_key] }}" local-as: _process: - regexp: "neighbor {{ neighbor_key }} local-as (?P\\d+)" - from: "{{ bookmarks['neighbor'][neighbor_key] }}" neighbor-address: _process: - mode: value value: "{{ neighbor_key }}" - from: "{{ bookmarks['neighbor'][neighbor_key] }}" peer-as: _process: - regexp: "neighbor {{ neighbor_key }} remote-as (?P.*)" - from: "{{ bookmarks['neighbor'][neighbor_key] }}" peer-group: _process: not_implemented peer-type: diff --git a/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/includes/static_routes.yaml b/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/includes/static_routes.yaml index 8cabb5a3..391c8416 100644 --- a/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/includes/static_routes.yaml +++ b/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/includes/static_routes.yaml @@ -5,11 +5,9 @@ _process: static: _process: - regexp: "(?Pip route (?P\\d+.*\\/\\d+).*)" - from: "{{ bookmarks['network-instances'][0] }}" when: "{{ network_instance_key == 'global' }}" flat: true - regexp: "(?Pip route vrf {{ network_instance_key }} (?P\\d+.*\\/\\d+).*)" - from: "{{ bookmarks['network-instances'][0] }}" when: "{{ network_instance_key != 'global' }}" flat: true config: @@ -25,7 +23,6 @@ static: next-hop: _process: - regexp: "(?Pip route (vrf {{ network_instance_key }} )*{{ static_key }} (?P\\d+.\\d+.\\d+.\\d+) (?P\\d+) tag (?P\\d+))" - from: "{{ bookmarks['static'][static_key] }}" config: _process: unnecessary index: diff --git a/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/network-instances.yaml b/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/network-instances.yaml index 3c74e1b5..3fb5fc51 100644 --- a/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/network-instances.yaml +++ b/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/network-instances.yaml @@ -11,10 +11,10 @@ network-instances: network-instance: _process: - regexp: "(?Pvrf definition (?P(.*))\n(?:.|\n)*?^!$)" - from: "{{ bookmarks['network-instances'][0] }}" + from: network-instances.0 mandatory: - key: "global" - block: "" + block: null extra_vars: {} afts: !include includes/afts.yaml config: @@ -22,7 +22,6 @@ network-instances: description: _process: - regexp: "description (?P.*)" - from: "{{ bookmarks.parent }}" enabled: _process: - mode: value @@ -36,7 +35,6 @@ network-instances: route-distinguisher: _process: - regexp: "rd (?P.*)" - from: "{{ bookmarks.parent }}" router-id: _process: not_implemented type: @@ -319,15 +317,15 @@ network-instances: protocol: _process: - regexp: "(?Prouter (?P(bgp))\\s*(?P\\d+)*\n(?:.|\n)*?)^(!| vrf \\w+)$" - from: "{{ bookmarks['network-instances'][0] }}" + from: network-instances.0 composite_key: [protocol_name, protocol_name] when: "{{ network_instance_key == 'global' }}" - regexp: "router (?P(bgp))\\s*(?P\\d+)*\n(?:.|\n)*?^ (?Pvrf {{ network_instance_key }}\n(?:.|\n)*?)^(!| vrf.*)$" - from: "{{ bookmarks['network-instances'][0] }}" + from: network-instances.0 composite_key: [protocol_name, protocol_name] when: "{{ network_instance_key != 'global' }}" - regexp: "(?Pip route .*\n(?:.|\n)*?^!$)" - from: "{{ bookmarks['network-instances'][0] }}" + from: network-instances.0 key: "static static" bgp: !include includes/bgp.yaml config: diff --git a/napalm_yang/mappings/eos/parsers/config/openconfig-vlan/vlan.yaml b/napalm_yang/mappings/eos/parsers/config/openconfig-vlan/vlan.yaml index 9ba682df..637e54dc 100644 --- a/napalm_yang/mappings/eos/parsers/config/openconfig-vlan/vlan.yaml +++ b/napalm_yang/mappings/eos/parsers/config/openconfig-vlan/vlan.yaml @@ -10,4 +10,3 @@ vlan: vlan-id: _process: - regexp: "^\\W*encapsulation dot1q vlan (?P[0-9]+)" - from: "{{ bookmarks['parent'] }}" diff --git a/napalm_yang/mappings/ios/parsers/config/openconfig-if-ip/ipv4.yaml b/napalm_yang/mappings/ios/parsers/config/openconfig-if-ip/ipv4.yaml index 10bda473..863e7ee4 100644 --- a/napalm_yang/mappings/ios/parsers/config/openconfig-if-ip/ipv4.yaml +++ b/napalm_yang/mappings/ios/parsers/config/openconfig-if-ip/ipv4.yaml @@ -10,7 +10,6 @@ ipv4: _process: # TODO look at this one, I think this is different depending on IOS version - mode: is_absent regexp: "(?P^\\s*switchport mode.*$)" - from: "{{ bookmarks['parent'] }}" mtu: _process: not_implemented addresses: @@ -18,7 +17,6 @@ ipv4: address: _process: - regexp: "(?Pip address (?P(?P.*)) (?P([0-255]|\\.)*)(?P secondary)*)$" - from: "{{ bookmarks['parent'] }}" ip: _process: unnecessary config: diff --git a/napalm_yang/mappings/ios/parsers/config/openconfig-interfaces/interfaces.yaml b/napalm_yang/mappings/ios/parsers/config/openconfig-interfaces/interfaces.yaml index a603a8ed..565b9fac 100644 --- a/napalm_yang/mappings/ios/parsers/config/openconfig-interfaces/interfaces.yaml +++ b/napalm_yang/mappings/ios/parsers/config/openconfig-interfaces/interfaces.yaml @@ -10,7 +10,7 @@ interfaces: interface: _process: - regexp: "(?Pinterface (?P(\\w|-)*\\d+)\n(?:.|\n)*?^!$)" - from: "{{ bookmarks.interfaces.0 }}" + from: interfaces.0 name: _process: unnecessary hold-time: @@ -28,8 +28,7 @@ interfaces: type: _process: - mode: map - regexp: "(?P(\\w|-)*)\\d+" - from: "{{ interface_key }}" + regexp: "interface (?P(\\w|-)*)\\d+" map: FastEthernet: ethernetCsmacd GigabitEthernet: ethernetCsmacd @@ -42,21 +41,18 @@ interfaces: _process: - mode: is_present regexp: "(?Pno shutdown)" - from: "{{ bookmarks.interface[interface_key] }}" description: _process: - regexp: "description (?P.*)" - from: "{{ bookmarks.interface[interface_key] }}" mtu: _process: - regexp: "mtu (?P[0-9]+)" - from: "{{ bookmarks.interface[interface_key] }}" subinterfaces: _process: unnecessary subinterface: _process: - regexp: "(?Pinterface {{interface_key}}\\.(?P\\d+)\\n(?:.|\\n)*?^!$)" - from: "{{ bookmarks.interfaces.0 }}" + from: interfaces.0 index: _process: unnecessary config: @@ -71,8 +67,6 @@ interfaces: _process: - mode: is_present regexp: "(?Pno shutdown)" - from: "{{ bookmarks.subinterface[subinterface_key] }}" description: _process: - regexp: "description (?P.*)" - from: "{{ bookmarks.subinterface[subinterface_key] }}" diff --git a/napalm_yang/mappings/ios/parsers/config/openconfig-vlan/vlan.yaml b/napalm_yang/mappings/ios/parsers/config/openconfig-vlan/vlan.yaml index 1f49ea7c..364e66a9 100644 --- a/napalm_yang/mappings/ios/parsers/config/openconfig-vlan/vlan.yaml +++ b/napalm_yang/mappings/ios/parsers/config/openconfig-vlan/vlan.yaml @@ -10,4 +10,3 @@ vlan: vlan-id: _process: - regexp: "^\\W*encapsulation dot1q (?P[0-9]+)" - from: "{{ bookmarks['parent'] }}" diff --git a/napalm_yang/parsers/base.py b/napalm_yang/parsers/base.py index b6be32a6..e65314c4 100644 --- a/napalm_yang/parsers/base.py +++ b/napalm_yang/parsers/base.py @@ -5,6 +5,8 @@ class BaseParser(object): @staticmethod def resolve_bookmark(bookmarks, path): + if path is None: + return b = bookmarks for p in path.split("."): try: diff --git a/napalm_yang/parsers/text.py b/napalm_yang/parsers/text.py index 7b522270..1b8299ef 100644 --- a/napalm_yang/parsers/text.py +++ b/napalm_yang/parsers/text.py @@ -8,8 +8,8 @@ class TextParser(BaseParser): @classmethod - def _parse_list_default(cls, mapping): - block_matches = re.finditer(mapping["regexp"], mapping["from"], re.MULTILINE + re.I) + def _parse_list_default(cls, mapping, data): + block_matches = re.finditer(mapping["regexp"], data, re.MULTILINE + re.I) mandatory_elements = mapping.pop("mandatory", []) @@ -39,8 +39,8 @@ def _parse_list_default(cls, mapping): yield key, block, extra_vars @classmethod - def _parse_leaf_default(cls, mapping, check_default=True): - match = re.search(mapping["regexp"], mapping["from"], re.MULTILINE + re.I) + def _parse_leaf_default(cls, mapping, data, check_default=True): + match = re.search(mapping["regexp"], data, re.MULTILINE + re.I) if match: return match.group("value") @@ -50,16 +50,16 @@ def _parse_leaf_default(cls, mapping, check_default=True): return None @classmethod - def _parse_leaf_is_present(cls, mapping): - value = cls._parse_leaf_default(mapping, check_default=False) + def _parse_leaf_is_present(cls, mapping, data): + value = cls._parse_leaf_default(mapping, data, check_default=False) return bool(value) @classmethod - def _parse_leaf_is_absent(cls, mapping): - value = cls._parse_leaf_default(mapping, check_default=False) + def _parse_leaf_is_absent(cls, mapping, data): + value = cls._parse_leaf_default(mapping, data, check_default=False) return not bool(value) @classmethod - def _parse_leaf_map(cls, mapping): - value = cls._parse_leaf_default(mapping) + def _parse_leaf_map(cls, mapping, data): + value = cls._parse_leaf_default(mapping, data) return mapping["map"][value] diff --git a/test/integration/test_profiles.py b/test/integration/test_profiles.py index 506d402f..eb91c397 100644 --- a/test/integration/test_profiles.py +++ b/test/integration/test_profiles.py @@ -45,8 +45,8 @@ def pretty_json(dictionary): test_config_profile_models = [ - # ["ios", napalm_yang.models.openconfig_interfaces, "default"], - # ["eos", napalm_yang.models.openconfig_network_instance, "default"], + ["ios", napalm_yang.models.openconfig_interfaces, "default"], + ["eos", napalm_yang.models.openconfig_network_instance, "default"], ["junos", napalm_yang.models.openconfig_network_instance, "default"], ] From 0e6264173e8646c7fef821b1a882eb149ed20261 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Mon, 19 Jun 2017 10:46:49 +0200 Subject: [PATCH 4/9] Removed because of lack of tests and now they are broken --- .../transceiver.yaml | 124 ------------------ .../state/openconfig-platform/components.yaml | 114 ---------------- 2 files changed, 238 deletions(-) delete mode 100644 napalm_yang/mappings/eos/parsers/state/openconfig-platform-transceiver/transceiver.yaml delete mode 100644 napalm_yang/mappings/eos/parsers/state/openconfig-platform/components.yaml diff --git a/napalm_yang/mappings/eos/parsers/state/openconfig-platform-transceiver/transceiver.yaml b/napalm_yang/mappings/eos/parsers/state/openconfig-platform-transceiver/transceiver.yaml deleted file mode 100644 index 6fdbae8d..00000000 --- a/napalm_yang/mappings/eos/parsers/state/openconfig-platform-transceiver/transceiver.yaml +++ /dev/null @@ -1,124 +0,0 @@ ---- -metadata: - processor: JSONParser - -transceiver: - _process: unnecessary - config: - _process: not_implemented - enabled: - _process: not_implemented - ethernet-pmd-preconf: - _process: not_implemented - form-factor-preconf: - _process: not_implemented - physical-channels: - _process: unnecessary - channel: - _process: - - from: "{{ bookmarks.parent.get('transceiver', {}).get('physical-channels', {}).get('channel', {})|json }}" - config: - _process: not_implemented - description: - _process: not_implemented - index: - _process: not_implemented - target-output-power: - _process: not_implemented - tx-laser: - _process: not_implemented - index: - _process: not_implemented - state: - _process: unnecessary - description: - _process: not_implemented - index: - _process: - - mode: value - value: "{{ parent_key }}" - input-power: - _process: unnecessary - avg: - _process: not_implemented - instant: - _process: - - mode: value - value: "{{ bookmarks.parent.state.get('input-power', {}).get('instant', None) }}" - when: "{{ bookmarks.parent.state.get('input-power', {}).get('instant', 'None') is not equalto 'None' }}" - max: - _process: not_implemented - min: - _process: not_implemented - laser-bias-current: - _process: unnecessary - avg: - _process: not_implemented - instant: - _process: - - mode: value - value: "{{ bookmarks.parent.state.get('laser-bias-current', {}).get('instant', None) }}" - when: "{{ bookmarks.parent.state.get('laser-bias-current', {}).get('instant', 'None') is not equalto 'None' }}" - max: - _process: not_implemented - min: - _process: not_implemented - output-frequency: - _process: not_implemented - output-power: - _process: unnecessary - avg: - _process: not_implemented - instant: - _process: - - mode: value - value: "{{ bookmarks.parent.state.get('output-power', {}).get('instant', None) }}" - when: "{{ bookmarks.parent.state.get('output-power', {}).get('instant', 'None') is not equalto 'None' }}" - max: - _process: not_implemented - min: - _process: not_implemented - target-output-power: - _process: not_implemented - tx-laser: - _process: not_implemented - state: - _process: unnecessary - connector-type: - _process: not_implemented - date-code: - _process: not_implemented - enabled: - _process: - - mode: value - value: "{{ bookmarks.parent.get('transceiver', {}).get('state', {}).get('enabled', None) }}" - when: "{{ bookmarks.parent.get('transceiver', {}).get('state', {}).get('enabled', 'None') is not equalto 'None' }}" - ethernet-pmd: - _process: - - mode: value - value: "{{ bookmarks.parent.get('transceiver', {}).get('state', {}).get('ethernet-pmd', None) }}" - when: "{{ bookmarks.parent.get('transceiver', {}).get('state', {}).get('ethernet-pmd', 'None') is not equalto 'None' }}" - ethernet-pmd-preconf: - _process: not_implemented - fault-condition: - _process: not_implemented - form-factor: - _process: not_implemented - form-factor-preconf: - _process: not_implemented - internal-temp: - _process: not_implemented - otn-compliance-code: - _process: not_implemented - present: - _process: not_implemented - serial-no: - _process: not_implemented - sonet-sdh-compliance-code: - _process: not_implemented - vendor: - _process: not_implemented - vendor-part: - _process: not_implemented - vendor-rev: - _process: not_implemented diff --git a/napalm_yang/mappings/eos/parsers/state/openconfig-platform/components.yaml b/napalm_yang/mappings/eos/parsers/state/openconfig-platform/components.yaml deleted file mode 100644 index 488496a0..00000000 --- a/napalm_yang/mappings/eos/parsers/state/openconfig-platform/components.yaml +++ /dev/null @@ -1,114 +0,0 @@ ---- -metadata: - processor: JSONParser - execute: - - method: _oc_platform - -components: - _process: unnecessary - component: - _process: - - from: "{{ bookmarks.components.0.components.component|json }}" - config: - _process: not_implemented - name: - _process: not_implemented - name: - _process: not_implemented - properties: - _process: unnecessary - property: - _process: - - from: "{{ bookmarks.parent.get('properties', {}).get('property', {})|json }}" - config: - _process: not_implemented - name: - _process: not_implemented - value: - _process: not_implemented - name: - _process: not_implemented - state: - _process: unnecessary - configurable: - _process: not_implemented - name: - _process: - - mode: value - value: "{{ parent_key }}" - value: - _process: - - mode: value - value: "{{ bookmarks.parent.state.value }}" - state: - _process: unnecessary - description: - _process: - - mode: value - value: "{{ bookmarks.parent.state.description|default() }}" - when: "{{ bookmarks.parent.state.description|default('None') is not equalto 'None' }}" - id: - _process: not_implemented - mfg-name: - _process: - - mode: value - value: "{{ bookmarks.parent.state.get('mfg-name', None) }}" - when: "{{ bookmarks.parent.state.get('mfg-name', 'None') is not equalto 'None' }}" - name: - _process: - - mode: value - value: "{{ parent_key }}" - part-no: - _process: - - mode: value - value: "{{ bookmarks.parent.state.get('part-no', None) }}" - when: "{{ bookmarks.parent.state.get('part-no', 'None') is not equalto 'None' }}" - serial-no: - _process: - - mode: value - value: "{{ bookmarks.parent.state.get('serial-no', None) }}" - when: "{{ bookmarks.parent.state.get('serial-no', 'None') is not equalto 'None' }}" - temperature: - _process: unnecessary - avg: - _process: not_implemented - instant: - _process: - - mode: value - value: "{{ bookmarks.parent.state.get('temperature', {}).get('instant', None) }}" - when: "{{ bookmarks.parent.state.get('temperature', {}).get('instant', 'None') is not equalto 'None' }}" - max: - _process: - - mode: value - value: "{{ bookmarks.parent.state.get('temperature', {}).get('max', None) }}" - when: "{{ bookmarks.parent.state.get('temperature', {}).get('max', 'None') is not equalto 'None' }}" - min: - _process: not_implemented - type: - _process: - - mode: value - value: "{{ bookmarks.parent.state.get('type', None) }}" - when: "{{ bookmarks.parent.state.get('type', 'None') is not equalto 'None' }}" - version: - _process: - - mode: value - value: "{{ bookmarks.parent.state.get('version', None) }}" - when: "{{ bookmarks.parent.state.get('version', 'None') is not equalto 'None' }}" - subcomponents: - _process: unnecessary - subcomponent: - _process: - - from: "{{ bookmarks.parent.get('subcomponents', {}).get('subcomponent', {})|json }}" - config: - _process: not_implemented - name: - _process: not_implemented - name: - _process: not_implemented - state: - _process: unnecessary - name: - _process: - - mode: value - value: "{{ parent_key }}" - From 2b8a54f17e6dc26948bf111042be1c8b3c6c42d1 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Mon, 19 Jun 2017 10:47:06 +0200 Subject: [PATCH 5/9] JSONParser behaves similar to XMLParser --- .../openconfig-interfaces/interfaces.yaml | 60 ++++++++----------- napalm_yang/parser.py | 1 + napalm_yang/parsers/base.py | 16 ++--- napalm_yang/parsers/json.py | 35 ++++++----- test/integration/test_profiles.py | 2 +- 5 files changed, 56 insertions(+), 58 deletions(-) diff --git a/napalm_yang/mappings/eos/parsers/state/openconfig-interfaces/interfaces.yaml b/napalm_yang/mappings/eos/parsers/state/openconfig-interfaces/interfaces.yaml index 25bc1952..14512f84 100644 --- a/napalm_yang/mappings/eos/parsers/state/openconfig-interfaces/interfaces.yaml +++ b/napalm_yang/mappings/eos/parsers/state/openconfig-interfaces/interfaces.yaml @@ -10,7 +10,8 @@ interfaces: _process: unnecessary interface: _process: - - from: "{{ bookmarks.interfaces.0.interfaces|json }}" + - path: interfaces + from: interfaces.0 config: _process: unnecessary hold-time: @@ -26,40 +27,36 @@ interfaces: admin-status: _process: - mode: map - value: "{{ bookmarks.parent.get('interfaceStatus', 'disabled') }}" + path: interfaceStatus + defaul: disabled map: "disabled": DOWN "errdisabled": DOWN "connected": UP "notconnect": UP counters: - _process: unnecessary + _process: + - path: interfaceCounters in-broadcast-pkts: _process: - - mode: value - value: "{{ bookmarks.parent.interfaceCounters.inBroadcastPkts }}" + - path: inBroadcastPkts in-discards: _process: - - mode: value - value: "{{ bookmarks.parent.interfaceCounters.inDiscards }}" + - path: inDiscards in-errors: _process: - - mode: value - value: "{{ bookmarks.parent.interfaceCounters.totalInErrors }}" + - path: totalInErrors in-multicast-pkts: _process: - - mode: value - value: "{{ bookmarks.parent.interfaceCounters.inMulticastPkts }}" + - path: inMulticastPkts _process: not_implemented in-octets: _process: - - mode: value - value: "{{ bookmarks.parent.interfaceCounters.inOctets }}" + - path: inOctets _process: not_implemented in-unicast-pkts: _process: - - mode: value - value: "{{ bookmarks.parent.interfaceCounters.inUcastPkts }}" + - path: inUcastPkts _process: not_implemented in-unknown-protos: _process: not_implemented @@ -67,41 +64,35 @@ interfaces: _process: not_implemented out-broadcast-pkts: _process: - - mode: value - value: "{{ bookmarks.parent.interfaceCounters.outBroadcastPkts }}" + - path: outBroadcastPkts _process: not_implemented out-discards: _process: - - mode: value - value: "{{ bookmarks.parent.interfaceCounters.outDiscards }}" + - path: outDiscards _process: not_implemented out-errors: _process: - - mode: value - value: "{{ bookmarks.parent.interfaceCounters.totalOutErrors }}" + - path: totalOutErrors _process: not_implemented out-multicast-pkts: _process: - - mode: value - value: "{{ bookmarks.parent.interfaceCounters.outMulticastPkts }}" + - path: outMulticastPkts _process: not_implemented out-octets: _process: - - mode: value - value: "{{ bookmarks.parent.interfaceCounters.outOctets }}" + - path: outOctets _process: not_implemented out-unicast-pkts: _process: - - mode: value - value: "{{ bookmarks.parent.interfaceCounters.outUcastPkts }}" + - path: outUcastPkts description: _process: - - mode: value - value: "{{ bookmarks.parent.description }}" + - path: description enabled: _process: - mode: map - value: "{{ bookmarks.parent.get('interfaceStatus', 'disabled') }}" + path: interfaceStatus + default: disabled map: "disabled": false "errdisabled": true @@ -113,14 +104,14 @@ interfaces: _process: not_implemented mtu: _process: - - mode: value - value: "{{ bookmarks.parent.mtu }}" + - path: mtu name: _process: not_implemented oper-status: _process: - mode: map - value: "{{ bookmarks.parent.get('interfaceStatus', 'disabled') }}" + path: interfaceStatus + default: disabled map: "disabled": DOWN "errdisabled": DOWN @@ -129,7 +120,8 @@ interfaces: type: _process: - mode: map - value: "{{ bookmarks.parent.get('hardware', 'Unspecified') }}" + path: hardware + default: Unspecified map: "ethernet": ethernetCsmacd "Unspecified": null diff --git a/napalm_yang/parser.py b/napalm_yang/parser.py index ec89ef51..e0d350c6 100644 --- a/napalm_yang/parser.py +++ b/napalm_yang/parser.py @@ -95,6 +95,7 @@ def _parse_container(self, attribute, model, mapping): if block is None: return elif block != "" or extra_vars: + self.bookmarks["parent"] = block self.bookmarks[attribute] = block self.extra_vars[attribute] = extra_vars diff --git a/napalm_yang/parsers/base.py b/napalm_yang/parsers/base.py index e65314c4..856248ef 100644 --- a/napalm_yang/parsers/base.py +++ b/napalm_yang/parsers/base.py @@ -4,17 +4,17 @@ class BaseParser(object): @staticmethod - def resolve_bookmark(bookmarks, path): + def resolve_path(my_dict, path): if path is None: return - b = bookmarks + + b = my_dict for p in path.split("."): try: b = b[p] except TypeError: b = b[int(p)] - bookmark = b - return bookmark + return b @classmethod def init_native(cls, native): @@ -29,9 +29,9 @@ def parse_list(cls, mapping, bookmarks): mandatory_elements = m.get("mandatory", []) for me in mandatory_elements: - me["block"] = cls.resolve_bookmark(bookmarks, me["block"]) + me["block"] = cls.resolve_path(bookmarks, me["block"]) - data = cls.resolve_bookmark(bookmarks, m.get("from", "parent")) + data = cls.resolve_path(bookmarks, m.get("from", "parent")) method_name = "_parse_list_{}".format(m.get("mode", "default")) for key, block, extra_vars in getattr(cls, method_name)(m, data): yield key, block, extra_vars @@ -42,7 +42,7 @@ def parse_list(cls, mapping, bookmarks): @classmethod def parse_leaf(cls, mapping, bookmarks): for m in mapping: - data = cls.resolve_bookmark(bookmarks, m.get("from", "parent")) + data = cls.resolve_path(bookmarks, m.get("from", "parent")) method_name = "_parse_leaf_{}".format(m.get("mode", "default")) result = getattr(cls, method_name)(m, data) if result is not None: @@ -54,7 +54,7 @@ def parse_container(cls, mapping, bookmarks): # parent will change as the tree is processed so we save it # so we can restore it parent = bookmarks["parent"] - data = cls.resolve_bookmark(bookmarks, m.get("from", "parent")) + data = cls.resolve_path(bookmarks, m.get("from", "parent")) method_name = "_parse_container_{}".format(m.get("mode", "default")) result, extra_vars = getattr(cls, method_name)(m, data) if result or extra_vars: diff --git a/napalm_yang/parsers/json.py b/napalm_yang/parsers/json.py index f37eee38..a375548f 100644 --- a/napalm_yang/parsers/json.py +++ b/napalm_yang/parsers/json.py @@ -1,5 +1,4 @@ from __future__ import absolute_import -from builtins import super import re import json @@ -13,23 +12,23 @@ class JSONParser(BaseParser): def init_native(cls, native): resp = [] for k in native: - if isinstance(k, str): - resp.append(json.loads(k)) - else: + if isinstance(k, dict): resp.append(k) + else: + resp.append(json.loads(k)) - return super().init_native(resp) + return resp @classmethod - def _parse_list_default(cls, mapping, key=None): - d = json.loads(mapping['from']) + def _parse_list_default(cls, mapping, data, key=None): + d = cls.resolve_path(data, mapping.get("path", "")) if isinstance(d, dict): for k, v in d.items(): yield k, v, {} @classmethod - def _parse_leaf_default(cls, mapping, check_default=True, check_presence=False): - d = mapping['from'] + def _parse_leaf_default(cls, mapping, data, check_default=True, check_presence=False): + d = cls.resolve_path(data, mapping["path"]) if d and not check_presence: regexp = mapping.get('regexp', None) if regexp: @@ -47,13 +46,19 @@ def _parse_leaf_default(cls, mapping, check_default=True, check_presence=False): return @classmethod - def _parse_leaf_map(cls, mapping): - return mapping['map'][mapping['value']] + def _parse_container_default(cls, mapping, data): + d = cls.resolve_path(data, mapping["path"]) + return d, {} + + @classmethod + def _parse_leaf_map(cls, mapping, data): + v = cls._parse_leaf_default(mapping, data) + return mapping['map'][v] @classmethod - def _parse_leaf_is_present(cls, mapping): - return cls._parse_leaf_path(mapping, check_default=False, check_presence=True) + def _parse_leaf_is_present(cls, mapping, data): + return cls._parse_leaf_default(mapping, data, check_default=False, check_presence=True) @classmethod - def _parse_leaf_is_absent(cls, mapping): - return not cls._parse_leaf_path(mapping, check_default=False, check_presence=True) + def _parse_leaf_is_absent(cls, mapping, data): + return not cls._parse_leaf_default(mapping, data, check_default=False, check_presence=True) diff --git a/test/integration/test_profiles.py b/test/integration/test_profiles.py index eb91c397..ca76840c 100644 --- a/test/integration/test_profiles.py +++ b/test/integration/test_profiles.py @@ -52,7 +52,7 @@ def pretty_json(dictionary): test_state_profile_models = [ ["junos", napalm_yang.models.openconfig_interfaces, "default"], - # ["eos", napalm_yang.models.openconfig_interfaces, "default"], + ["eos", napalm_yang.models.openconfig_interfaces, "default"], ] From c0756c5d014067d9d02039a741375b36ae2cb97a Mon Sep 17 00:00:00 2001 From: David Barroso Date: Mon, 19 Jun 2017 11:42:29 +0200 Subject: [PATCH 6/9] JSONParser can now parse subinterfaces correctly --- napalm_yang/helpers.py | 2 +- .../openconfig-interfaces/interfaces.yaml | 100 +++--- napalm_yang/parsers/base.py | 16 +- napalm_yang/parsers/json.py | 18 +- .../default/openconfig-interfaces.expected | 98 +++++- .../default/openconfig-interfaces.native | 324 +++++++++++++++++- test/integration/test_profiles.py | 4 +- 7 files changed, 490 insertions(+), 72 deletions(-) diff --git a/napalm_yang/helpers.py b/napalm_yang/helpers.py index e2042828..5b15f745 100644 --- a/napalm_yang/helpers.py +++ b/napalm_yang/helpers.py @@ -80,7 +80,7 @@ def resolve_rule(rule, attribute, keys, extra_vars=None, translation_model=None, elif isinstance(rule, str): if rule in ["unnecessary"]: return [{"mode": "skip", "reason": rule}] - elif rule in ["not_implemented"]: + elif rule in ["not_implemented", "not_supported"]: return [{"mode": "gate", "reason": rule}] else: raise Exception("Not sure what to do with rule {} on attribute {}".format(rule, diff --git a/napalm_yang/mappings/eos/parsers/state/openconfig-interfaces/interfaces.yaml b/napalm_yang/mappings/eos/parsers/state/openconfig-interfaces/interfaces.yaml index 14512f84..b8215b22 100644 --- a/napalm_yang/mappings/eos/parsers/state/openconfig-interfaces/interfaces.yaml +++ b/napalm_yang/mappings/eos/parsers/state/openconfig-interfaces/interfaces.yaml @@ -12,6 +12,7 @@ interfaces: _process: - path: interfaces from: interfaces.0 + regexp: "(?P\\w+(\\w|-|\\/)*\\d+)$" config: _process: unnecessary hold-time: @@ -37,6 +38,10 @@ interfaces: counters: _process: - path: interfaceCounters + default: {} + when: "{{ not interface_key.startswith('Loopback') }}" + - mode: gate + when: "{{ interface_key.startswith('Loopback') }}" in-broadcast-pkts: _process: - path: inBroadcastPkts @@ -49,15 +54,12 @@ interfaces: in-multicast-pkts: _process: - path: inMulticastPkts - _process: not_implemented in-octets: _process: - path: inOctets - _process: not_implemented in-unicast-pkts: _process: - path: inUcastPkts - _process: not_implemented in-unknown-protos: _process: not_implemented last-clear: @@ -65,23 +67,18 @@ interfaces: out-broadcast-pkts: _process: - path: outBroadcastPkts - _process: not_implemented out-discards: _process: - path: outDiscards - _process: not_implemented out-errors: _process: - path: totalOutErrors - _process: not_implemented out-multicast-pkts: _process: - path: outMulticastPkts - _process: not_implemented out-octets: _process: - path: outOctets - _process: not_implemented out-unicast-pkts: _process: - path: outUcastPkts @@ -106,7 +103,8 @@ interfaces: _process: - path: mtu name: - _process: not_implemented + _process: + - path: name oper-status: _process: - mode: map @@ -121,52 +119,47 @@ interfaces: _process: - mode: map path: hardware - default: Unspecified map: - "ethernet": ethernetCsmacd - "Unspecified": null + ethernet: ethernetCsmacd + management: ethernetCsmacd + loopback: softwareLoopback + portchannel: ieee8023adLag + vlan: l3ipvlan subinterfaces: - _process: not_implemented + _process: unnecessary subinterface: - _process: not_implemented + _process: + - path: interfaces + from: interfaces.0 + regexp: "{{interface_key}}\\.(?P\\d+)" + config: + _process: unnecessary state: - _process: not_implemented + _process: unnecessary admin-status: - _process: not_implemented + _process: + - mode: map + path: interfaceStatus + map: + "disabled": DOWN + "errdisabled": DOWN + "connected": UP + "notconnect": DOWN counters: - _process: not_implemented - in-broadcast-pkts: - _process: not_implemented - in-discards: - _process: not_implemented - in-errors: - _process: not_implemented - in-multicast-pkts: - _process: not_implemented - in-octets: - _process: not_implemented - in-unicast-pkts: - _process: not_implemented - in-unknown-protos: - _process: not_implemented - last-clear: - _process: not_implemented - out-broadcast-pkts: - _process: not_implemented - out-discards: - _process: not_implemented - out-errors: - _process: not_implemented - out-multicast-pkts: - _process: not_implemented - out-octets: - _process: not_implemented - out-unicast-pkts: - _process: not_implemented + _process: not_supported description: - _process: not_implemented + _process: + - path: description enabled: - _process: not_implemented + _process: + - mode: map + path: interfaceStatus + default: disabled + map: + "disabled": false + "errdisabled": true + "connected": true + "notconnect": true ifindex: _process: not_implemented index: @@ -174,6 +167,15 @@ interfaces: last-change: _process: not_implemented name: - _process: not_implemented + _process: + - path: name oper-status: - _process: not_implemented + _process: + - mode: map + path: lineProtocolStatus + map: + "disabled": DOWN + "errdisabled": DOWN + "connected": UP + "notconnect": DOWN + "lowerlayerdown": LOWER_LAYER_DOWN diff --git a/napalm_yang/parsers/base.py b/napalm_yang/parsers/base.py index 856248ef..9d2e7d4a 100644 --- a/napalm_yang/parsers/base.py +++ b/napalm_yang/parsers/base.py @@ -4,16 +4,22 @@ class BaseParser(object): @staticmethod - def resolve_path(my_dict, path): + def resolve_path(my_dict, path, default=None): if path is None: return b = my_dict - for p in path.split("."): + path_split = path.split(".") + for i, p in enumerate(path_split): try: - b = b[p] - except TypeError: - b = b[int(p)] + try: + b = b[p] + except TypeError: + b = b[int(p)] + except KeyError: + if i == len(path_split) - 1 and default is not None: + return default + raise return b @classmethod diff --git a/napalm_yang/parsers/json.py b/napalm_yang/parsers/json.py index a375548f..39abe61f 100644 --- a/napalm_yang/parsers/json.py +++ b/napalm_yang/parsers/json.py @@ -21,14 +21,24 @@ def init_native(cls, native): @classmethod def _parse_list_default(cls, mapping, data, key=None): - d = cls.resolve_path(data, mapping.get("path", "")) + d = cls.resolve_path(data, mapping["path"], mapping.get("default")) + + regexp = mapping.get('regexp', None) + if regexp: + regexp = re.compile(regexp) if isinstance(d, dict): for k, v in d.items(): + if regexp: + match = regexp.match(k) + if match: + k = match.group('value') + else: + continue yield k, v, {} @classmethod def _parse_leaf_default(cls, mapping, data, check_default=True, check_presence=False): - d = cls.resolve_path(data, mapping["path"]) + d = cls.resolve_path(data, mapping["path"], mapping.get("default")) if d and not check_presence: regexp = mapping.get('regexp', None) if regexp: @@ -47,13 +57,13 @@ def _parse_leaf_default(cls, mapping, data, check_default=True, check_presence=F @classmethod def _parse_container_default(cls, mapping, data): - d = cls.resolve_path(data, mapping["path"]) + d = cls.resolve_path(data, mapping["path"], mapping.get("default")) return d, {} @classmethod def _parse_leaf_map(cls, mapping, data): v = cls._parse_leaf_default(mapping, data) - return mapping['map'][v] + return mapping['map'][v.lower()] @classmethod def _parse_leaf_is_present(cls, mapping, data): diff --git a/test/integration/profiles_data/eos/openconfig-interfaces/default/openconfig-interfaces.expected b/test/integration/profiles_data/eos/openconfig-interfaces/default/openconfig-interfaces.expected index 286601d3..4ac5c9b3 100644 --- a/test/integration/profiles_data/eos/openconfig-interfaces/default/openconfig-interfaces.expected +++ b/test/integration/profiles_data/eos/openconfig-interfaces/default/openconfig-interfaces.expected @@ -6,34 +6,110 @@ "state": { "admin-status": "UP", "counters": { - "in-broadcast-pkts": 2630, - "in-discards": 0, - "in-errors": 0, - "out-unicast-pkts": 104 + "out-multicast-pkts": 875697, + "out-octets": 114560338 }, - "description": "", + "description": "This is a description", "enabled": true, - "mtu": 1500, + "mtu": 9214, + "name": "Ethernet1", "oper-status": "UP", "type": "ethernetCsmacd" } }, + "Ethernet2": { + "name": "Ethernet2", + "state": { + "admin-status": "DOWN", + "counters": { + "out-multicast-pkts": 56, + "out-octets": 7268 + }, + "description": "so much oc", + "enabled": false, + "mtu": 1500, + "name": "Ethernet2", + "oper-status": "DOWN", + "type": "ethernetCsmacd" + }, + "subinterfaces": { + "subinterface": { + "1": { + "index": "1", + "state": { + "admin-status": "DOWN", + "description": "another subiface", + "enabled": true, + "name": "Ethernet2.1", + "oper-status": "LOWER_LAYER_DOWN" + } + }, + "2": { + "index": "2", + "state": { + "admin-status": "DOWN", + "description": "asdasdasd", + "enabled": true, + "name": "Ethernet2.2", + "oper-status": "LOWER_LAYER_DOWN" + } + } + } + } + }, + "Loopback1": { + "name": "Loopback1", + "state": { + "admin-status": "UP", + "description": "a loopback", + "enabled": true, + "mtu": 65535, + "name": "Loopback1", + "oper-status": "UP", + "type": "softwareLoopback" + } + }, "Management1": { "name": "Management1", "state": { "admin-status": "UP", "counters": { - "in-broadcast-pkts": 19, - "in-discards": 0, - "in-errors": 0, - "out-unicast-pkts": 758 + "in-broadcast-pkts": 4, + "in-octets": 4116864, + "out-octets": 13968476, + "out-unicast-pkts": 58110 }, - "description": "", "enabled": true, "mtu": 1500, + "name": "Management1", "oper-status": "UP", "type": "ethernetCsmacd" } + }, + "Port-Channel1": { + "name": "Port-Channel1", + "state": { + "admin-status": "UP", + "description": "blah", + "enabled": true, + "mtu": 9000, + "name": "Port-Channel1", + "oper-status": "DOWN", + "type": "ieee8023adLag" + }, + "subinterfaces": { + "subinterface": { + "1": { + "index": "1", + "state": { + "admin-status": "DOWN", + "enabled": true, + "name": "Port-Channel1.1", + "oper-status": "LOWER_LAYER_DOWN" + } + } + } + } } } } diff --git a/test/integration/profiles_data/eos/openconfig-interfaces/default/openconfig-interfaces.native b/test/integration/profiles_data/eos/openconfig-interfaces/default/openconfig-interfaces.native index c9bbbc39..32e4cb5a 100644 --- a/test/integration/profiles_data/eos/openconfig-interfaces/default/openconfig-interfaces.native +++ b/test/integration/profiles_data/eos/openconfig-interfaces/default/openconfig-interfaces.native @@ -1 +1,323 @@ -{"interfaces": {"Management1": {"lastStatusChangeTimestamp": 1497732472.6491787, "name": "Management1", "interfaceStatus": "connected", "autoNegotiate": "success", "description": "", "burnedInAddress": "08:00:27:6b:fa:fe", "bandwidth": 1000000000, "mtu": 1500, "hardware": "ethernet", "duplex": "duplexFull", "loopbackMode": "loopbackNone", "physicalAddress": "08:00:27:6b:fa:fe", "interfaceCounters": {"outBroadcastPkts": 0, "linkStatusChanges": 5, "totalOutErrors": 0, "inMulticastPkts": 0, "counterRefreshTime": 1497734297.210793, "inBroadcastPkts": 19, "inputErrorsDetail": {"runtFrames": 0, "fcsErrors": 0, "alignmentErrors": 0, "rxPause": 0, "symbolErrors": 0, "giantFrames": 0}, "inOctets": 97807, "outDiscards": 0, "outOctets": 112236, "inUcastPkts": 0, "outputErrorsDetail": {"lateCollisions": 0, "deferredTransmissions": 0, "txPause": 0, "collisions": 0}, "outUcastPkts": 758, "outMulticastPkts": 0, "totalInErrors": 0, "inDiscards": 0}, "interfaceStatistics": {"inBitsRate": 327.13344777831077, "updateInterval": 300.0, "outBitsRate": 513.747345263329, "outPktsRate": 0.3882478230578469, "inPktsRate": 0.00013831271000537475}, "interfaceAddress": [{"secondaryIpsOrderedList": [], "primaryIp": {"maskLen": 24, "address": "10.0.2.15"}, "broadcastAddress": "255.255.255.255", "secondaryIps": {}, "virtualIp": {"maskLen": 0, "address": "0.0.0.0"}}], "lineProtocolStatus": "up", "forwardingModel": "routed"}, "Ethernet1": {"lastStatusChangeTimestamp": 1497732461.1365635, "name": "Ethernet1", "interfaceStatus": "connected", "autoNegotiate": "unknown", "description": "", "bandwidth": 0, "mtu": 1500, "hardware": "ethernet", "duplex": "duplexFull", "loopbackMode": "loopbackNone", "physicalAddress": "08:00:27:66:ef:40", "interfaceCounters": {"outBroadcastPkts": 0, "linkStatusChanges": 1, "lastClear": 1497732401.4158947, "inMulticastPkts": 97, "counterRefreshTime": 1497734297.214895, "inBroadcastPkts": 2630, "inputErrorsDetail": {"runtFrames": 0, "fcsErrors": 0, "alignmentErrors": 0, "rxPause": 0, "symbolErrors": 0, "giantFrames": 0}, "inOctets": 212355, "outDiscards": 0, "outOctets": 43559, "inUcastPkts": 118, "outputErrorsDetail": {"lateCollisions": 0, "deferredTransmissions": 0, "txPause": 0, "collisions": 0}, "outUcastPkts": 104, "outMulticastPkts": 73, "totalInErrors": 0, "inDiscards": 0, "totalOutErrors": 0}, "interfaceStatistics": {"inBitsRate": 0.0, "updateInterval": 300.0, "outBitsRate": 0.0, "outPktsRate": 0.0, "inPktsRate": 0.0}, "interfaceAddress": [{"secondaryIpsOrderedList": [], "primaryIp": {"maskLen": 24, "address": "192.168.50.11"}, "broadcastAddress": "255.255.255.255", "secondaryIps": {}, "virtualIp": {"maskLen": 0, "address": "0.0.0.0"}}], "lineProtocolStatus": "up", "forwardingModel": "routed"}}} +{ + "interfaces": { + "Management1": { + "lastStatusChangeTimestamp": 1494227233.5120637, + "name": "Management1", + "interfaceStatus": "connected", + "autoNegotiate": "success", + "burnedInAddress": "08:00:27:7d:44:c1", + "loopbackMode": "loopbackNone", + "interfaceStatistics": { + "inBitsRate": 1785.3472207043212, + "inPktsRate": 0.0031338123173471526, + "outBitsRate": 1523.0216192154212, + "updateInterval": 300.0, + "outPktsRate": 1.4350601670071694 + }, + "mtu": 1500, + "hardware": "ethernet", + "duplex": "duplexFull", + "bandwidth": 1000000000, + "forwardingModel": "routed", + "lineProtocolStatus": "up", + "interfaceCounters": { + "outBroadcastPkts": 0, + "linkStatusChanges": 11, + "totalOutErrors": 0, + "inMulticastPkts": 0, + "counterRefreshTime": 1494227251.353215, + "inBroadcastPkts": 4, + "outputErrorsDetail": { + "deferredTransmissions": 0, + "txPause": 0, + "collisions": 0, + "lateCollisions": 0 + }, + "inOctets": 4116864, + "outDiscards": 0, + "outOctets": 13968476, + "inUcastPkts": 0, + "inputErrorsDetail": { + "runtFrames": 0, + "rxPause": 0, + "fcsErrors": 0, + "alignmentErrors": 0, + "giantFrames": 0, + "symbolErrors": 0 + }, + "outUcastPkts": 58110, + "outMulticastPkts": 0, + "totalInErrors": 0, + "inDiscards": 0 + }, + "interfaceAddress": [ + { + "secondaryIpsOrderedList": [], + "broadcastAddress": "255.255.255.255", + "secondaryIps": {}, + "primaryIp": { + "maskLen": 24, + "address": "10.0.2.15" + }, + "virtualIp": { + "maskLen": 0, + "address": "0.0.0.0" + } + } + ], + "physicalAddress": "08:00:27:7d:44:c1", + "description": "" + }, + "Loopback1": { + "hardware": "loopback", + "bandwidth": 0, + "mtu": 65535, + "name": "Loopback1", + "interfaceStatus": "connected", + "lastStatusChangeTimestamp": 1493375330.477214, + "description": "a loopback", + "interfaceAddress": [], + "lineProtocolStatus": "up", + "forwardingModel": "routed" + }, + "Ethernet2.2": { + "lastStatusChangeTimestamp": 1493375330.2531557, + "name": "Ethernet2.2", + "interfaceStatus": "notconnect", + "mtu": 1500, + "hardware": "subinterface", + "bandwidth": 0, + "forwardingModel": "routed", + "lineProtocolStatus": "lowerLayerDown", + "interfaceAddress": [ + { + "secondaryIpsOrderedList": [], + "broadcastAddress": "255.255.255.255", + "secondaryIps": {}, + "primaryIp": { + "maskLen": 24, + "address": "192.168.2.1" + }, + "virtualIp": { + "maskLen": 0, + "address": "0.0.0.0" + } + } + ], + "physicalAddress": "08:00:27:78:47:29", + "description": "asdasdasd" + }, + "Port-Channel1.1": { + "lastStatusChangeTimestamp": 1493375329.5660713, + "name": "Port-Channel1.1", + "interfaceStatus": "notconnect", + "mtu": 9000, + "hardware": "subinterface", + "bandwidth": 0, + "forwardingModel": "routed", + "lineProtocolStatus": "lowerLayerDown", + "interfaceAddress": [], + "physicalAddress": "00:00:00:00:00:00", + "description": "" + }, + "Port-Channel1": { + "lastStatusChangeTimestamp": 1493375329.2763908, + "name": "Port-Channel1", + "interfaceStatus": "notconnect", + "memberInterfaces": {}, + "interfaceStatistics": { + "inBitsRate": 0.0, + "inPktsRate": 0.0, + "outBitsRate": 0.0, + "updateInterval": 300.0, + "outPktsRate": 0.0 + }, + "mtu": 9000, + "hardware": "portChannel", + "bandwidth": 0, + "forwardingModel": "routed", + "fallbackEnabled": false, + "lineProtocolStatus": "lowerLayerDown", + "interfaceCounters": { + "outBroadcastPkts": 0, + "linkStatusChanges": 1, + "totalOutErrors": 0, + "inMulticastPkts": 0, + "counterRefreshTime": 1494227251.374082, + "inBroadcastPkts": 0, + "inOctets": 0, + "outDiscards": 0, + "outOctets": 0, + "inUcastPkts": 0, + "outUcastPkts": 0, + "outMulticastPkts": 0, + "totalInErrors": 0, + "inDiscards": 0 + }, + "fallbackEnabledType": "fallbackNone", + "interfaceAddress": [], + "physicalAddress": "08:00:27:78:47:29", + "description": "blah" + }, + "Ethernet1": { + "lastStatusChangeTimestamp": 1493375231.510267, + "name": "Ethernet1", + "interfaceStatus": "connected", + "autoNegotiate": "unknown", + "burnedInAddress": "08:00:27:76:17:76", + "loopbackMode": "loopbackNone", + "interfaceStatistics": { + "inBitsRate": 0.0, + "inPktsRate": 0.0, + "outBitsRate": 0.0, + "updateInterval": 300.0, + "outPktsRate": 0.0 + }, + "mtu": 9214, + "hardware": "ethernet", + "duplex": "duplexFull", + "bandwidth": 0, + "forwardingModel": "dataLink", + "lineProtocolStatus": "up", + "interfaceCounters": { + "outBroadcastPkts": 0, + "linkStatusChanges": 1, + "totalOutErrors": 0, + "inMulticastPkts": 0, + "counterRefreshTime": 1494227251.360102, + "inBroadcastPkts": 0, + "outputErrorsDetail": { + "deferredTransmissions": 0, + "txPause": 0, + "collisions": 0, + "lateCollisions": 0 + }, + "inOctets": 0, + "outDiscards": 0, + "outOctets": 114560338, + "inUcastPkts": 0, + "inputErrorsDetail": { + "runtFrames": 0, + "rxPause": 0, + "fcsErrors": 0, + "alignmentErrors": 0, + "giantFrames": 0, + "symbolErrors": 0 + }, + "outUcastPkts": 0, + "outMulticastPkts": 875697, + "totalInErrors": 0, + "inDiscards": 0 + }, + "interfaceMembership": "Member of Port-Channel1", + "interfaceAddress": [], + "physicalAddress": "08:00:27:76:17:76", + "description": "This is a description" + }, + "Ethernet2.1": { + "lastStatusChangeTimestamp": 1493375330.0411432, + "name": "Ethernet2.1", + "interfaceStatus": "notconnect", + "mtu": 1500, + "hardware": "subinterface", + "bandwidth": 0, + "forwardingModel": "routed", + "lineProtocolStatus": "lowerLayerDown", + "interfaceAddress": [ + { + "secondaryIpsOrderedList": [ + { + "maskLen": 24, + "address": "172.20.0.1" + } + ], + "broadcastAddress": "255.255.255.255", + "secondaryIps": { + "172.20.0.1": { + "maskLen": 24, + "address": "172.20.0.1" + } + }, + "primaryIp": { + "maskLen": 24, + "address": "192.168.1.1" + }, + "virtualIp": { + "maskLen": 0, + "address": "0.0.0.0" + } + } + ], + "physicalAddress": "08:00:27:78:47:29", + "description": "another subiface" + }, + "Ethernet2": { + "lastStatusChangeTimestamp": 1493375329.8914871, + "name": "Ethernet2", + "interfaceStatus": "disabled", + "autoNegotiate": "off", + "loopbackMode": "loopbackNone", + "interfaceStatistics": { + "inBitsRate": 0.0, + "inPktsRate": 0.0, + "outBitsRate": 0.0, + "updateInterval": 300.0, + "outPktsRate": 0.0 + }, + "mtu": 1500, + "hardware": "ethernet", + "duplex": "duplexFull", + "bandwidth": 0, + "forwardingModel": "routed", + "lineProtocolStatus": "down", + "interfaceCounters": { + "outBroadcastPkts": 0, + "linkStatusChanges": 2, + "lastClear": 1493375179.1768973, + "inMulticastPkts": 0, + "counterRefreshTime": 1494227251.356712, + "inBroadcastPkts": 0, + "outputErrorsDetail": { + "deferredTransmissions": 0, + "txPause": 0, + "collisions": 0, + "lateCollisions": 0 + }, + "inOctets": 0, + "outDiscards": 0, + "outOctets": 7268, + "inUcastPkts": 0, + "inputErrorsDetail": { + "runtFrames": 0, + "rxPause": 0, + "fcsErrors": 0, + "alignmentErrors": 0, + "giantFrames": 0, + "symbolErrors": 0 + }, + "outUcastPkts": 0, + "outMulticastPkts": 56, + "totalInErrors": 0, + "inDiscards": 0, + "totalOutErrors": 0 + }, + "interfaceAddress": [ + { + "secondaryIpsOrderedList": [], + "broadcastAddress": "255.255.255.255", + "secondaryIps": {}, + "primaryIp": { + "maskLen": 24, + "address": "192.168.0.1" + }, + "virtualIp": { + "maskLen": 0, + "address": "0.0.0.0" + } + } + ], + "physicalAddress": "08:00:27:78:47:29", + "description": "so much oc" + } + } +} diff --git a/test/integration/test_profiles.py b/test/integration/test_profiles.py index ca76840c..2dd2d23d 100644 --- a/test/integration/test_profiles.py +++ b/test/integration/test_profiles.py @@ -1,6 +1,6 @@ from __future__ import unicode_literals -from napalm_base import get_network_driver +# from napalm_base import get_network_driver import napalm_yang @@ -145,6 +145,8 @@ def test_parse_state(self, profile, model, case): state.add_model(model) state.parse_state(native=[native], profile=[profile]) + # print(pretty_json(state.get(filter=True))) + expected = napalm_yang.base.Root() expected.add_model(model) expected.load_dict(expected_json) From 192eb5a2b6130b9e1d1f82ec716e87b35e117592 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Mon, 19 Jun 2017 12:04:27 +0200 Subject: [PATCH 7/9] Move mandatory elements to a manual action --- .../network-instances.yaml | 8 ++-- .../network-instances.yaml | 8 ++-- napalm_yang/parsers/base.py | 13 +++++-- napalm_yang/parsers/text.py | 39 ++++++++----------- napalm_yang/parsers/xml.py | 14 +++---- 5 files changed, 39 insertions(+), 43 deletions(-) diff --git a/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/network-instances.yaml b/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/network-instances.yaml index 3fb5fc51..5291cb9f 100644 --- a/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/network-instances.yaml +++ b/napalm_yang/mappings/eos/parsers/config/openconfig-network-instance/network-instances.yaml @@ -12,10 +12,10 @@ network-instances: _process: - regexp: "(?Pvrf definition (?P(.*))\n(?:.|\n)*?^!$)" from: network-instances.0 - mandatory: - - key: "global" - block: null - extra_vars: {} + - mode: manual + key: "global" + block: "" + extra_vars: {} afts: !include includes/afts.yaml config: _process: unnecessary diff --git a/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/network-instances.yaml b/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/network-instances.yaml index e0ee8ac6..c9cffc5b 100644 --- a/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/network-instances.yaml +++ b/napalm_yang/mappings/junos/parsers/config/openconfig-network-instance/network-instances.yaml @@ -14,10 +14,10 @@ network-instances: xpath: "routing-instances/instance" key_attribute: name from: network-instances.0 - mandatory: - - key: "global" - block: network-instances.0 - extra_vars: {} + - mode: manual + key: "global" + block: network-instances.0 + extra_vars: {} afts: !include includes/afts.yaml config: _process: unnecessary diff --git a/napalm_yang/parsers/base.py b/napalm_yang/parsers/base.py index 9d2e7d4a..b8509664 100644 --- a/napalm_yang/parsers/base.py +++ b/napalm_yang/parsers/base.py @@ -33,18 +33,23 @@ def parse_list(cls, mapping, bookmarks): # so we can restore it parent = bookmarks["parent"] - mandatory_elements = m.get("mandatory", []) - for me in mandatory_elements: - me["block"] = cls.resolve_path(bookmarks, me["block"]) - data = cls.resolve_path(bookmarks, m.get("from", "parent")) method_name = "_parse_list_{}".format(m.get("mode", "default")) + + if method_name == "_parse_list_manual": + m["block"] = cls.resolve_path(bookmarks, m["block"], + default=m["block"]) + for key, block, extra_vars in getattr(cls, method_name)(m, data): yield key, block, extra_vars # we restore the parent bookmarks["parent"] = parent + @classmethod + def _parse_list_manual(cls, mapping, data): + yield mapping["key"], mapping["block"], mapping["extra_vars"] + @classmethod def parse_leaf(cls, mapping, bookmarks): for m in mapping: diff --git a/napalm_yang/parsers/text.py b/napalm_yang/parsers/text.py index 1b8299ef..26ca9005 100644 --- a/napalm_yang/parsers/text.py +++ b/napalm_yang/parsers/text.py @@ -11,32 +11,27 @@ class TextParser(BaseParser): def _parse_list_default(cls, mapping, data): block_matches = re.finditer(mapping["regexp"], data, re.MULTILINE + re.I) - mandatory_elements = mapping.pop("mandatory", []) - - for match in itertools.chain(block_matches, mandatory_elements): - if isinstance(match, dict): - yield match["key"], match["block"] or "Aasd123asv", match["extra_vars"] + for match in block_matches: + composite_key = mapping.get("composite_key", None) + forced_key = mapping.get("key", None) + post_process_filter = mapping.get("post_process_filter", None) + + extra_vars = match.groupdict() + block = extra_vars.pop("block") + + if composite_key: + key = " ".join([match.group(k) for k in composite_key]) + elif forced_key: + key = forced_key else: - composite_key = mapping.get("composite_key", None) - forced_key = mapping.get("key", None) - post_process_filter = mapping.get("post_process_filter", None) - - extra_vars = match.groupdict() - block = extra_vars.pop("block") - - if composite_key: - key = " ".join([match.group(k) for k in composite_key]) - elif forced_key: - key = forced_key - else: - key = extra_vars.pop("key") + key = extra_vars.pop("key") - if post_process_filter: - key = cls._parse_post_process_filter(post_process_filter, key, extra_vars) + if post_process_filter: + key = cls._parse_post_process_filter(post_process_filter, key, extra_vars) - extra_vars["_get_duplicates"] = mapping.get("flat", False) + extra_vars["_get_duplicates"] = mapping.get("flat", False) - yield key, block, extra_vars + yield key, block, extra_vars @classmethod def _parse_leaf_default(cls, mapping, data, check_default=True): diff --git a/napalm_yang/parsers/xml.py b/napalm_yang/parsers/xml.py index 56cef5e7..b9508b7e 100644 --- a/napalm_yang/parsers/xml.py +++ b/napalm_yang/parsers/xml.py @@ -21,17 +21,13 @@ def init_native(cls, native): @classmethod def _parse_list_default(cls, mapping, data): - mandatory_elements = mapping.pop("mandatory", []) post_process_filter = mapping.pop("post_process_filter", None) - for element in itertools.chain(data.xpath(mapping["xpath"]), mandatory_elements): - if isinstance(element, dict): - yield element["key"], element["block"], element["extra_vars"] - else: - key = element.xpath(mapping["key"])[0].text.strip() - if post_process_filter: - key = cls._parse_post_process_filter(post_process_filter, key) - yield key, element, {} + for element in data.xpath(mapping["xpath"]): + key = element.xpath(mapping["key"])[0].text.strip() + if post_process_filter: + key = cls._parse_post_process_filter(post_process_filter, key) + yield key, element, {} @classmethod def _parse_list_nested(cls, mapping, data): From 2462c7490c2f219bc9215d80c5b4650ad8047cef Mon Sep 17 00:00:00 2001 From: David Barroso Date: Mon, 19 Jun 2017 12:06:41 +0200 Subject: [PATCH 8/9] updated changelog --- CHANGELOG.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f1696fbb..0d1c99d9 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -8,6 +8,12 @@ - ``TextParser``, ``list - block`` now supports composite keys via the ``composite_key`` argument - ``TextParser``, ``list - block`` now supports creating elements manually via the ``mandatory`` argument + - Move mandatory elements previously on the default action to a dedicated action + - from is optional, by default it will always follow the parent + - from is now a pointer, no need to keep serializing/deserializing + - mode is optional. All parsers have a main "default" action now. + - JSONParser added + 0.0.1 +++++ From ce2c55e7dca6853f1559c915023e235885ba8bff Mon Sep 17 00:00:00 2001 From: David Barroso Date: Tue, 20 Jun 2017 21:33:48 +0200 Subject: [PATCH 9/9] Saving/Restoring state when processing containers --- .../state/openconfig-interfaces/interfaces.yaml | 4 ---- napalm_yang/parser.py | 12 ++++++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/napalm_yang/mappings/eos/parsers/state/openconfig-interfaces/interfaces.yaml b/napalm_yang/mappings/eos/parsers/state/openconfig-interfaces/interfaces.yaml index b8215b22..2734c1df 100644 --- a/napalm_yang/mappings/eos/parsers/state/openconfig-interfaces/interfaces.yaml +++ b/napalm_yang/mappings/eos/parsers/state/openconfig-interfaces/interfaces.yaml @@ -29,7 +29,6 @@ interfaces: _process: - mode: map path: interfaceStatus - defaul: disabled map: "disabled": DOWN "errdisabled": DOWN @@ -89,7 +88,6 @@ interfaces: _process: - mode: map path: interfaceStatus - default: disabled map: "disabled": false "errdisabled": true @@ -109,7 +107,6 @@ interfaces: _process: - mode: map path: interfaceStatus - default: disabled map: "disabled": DOWN "errdisabled": DOWN @@ -154,7 +151,6 @@ interfaces: _process: - mode: map path: interfaceStatus - default: disabled map: "disabled": false "errdisabled": true diff --git a/napalm_yang/parser.py b/napalm_yang/parser.py index e0d350c6..a4bbce70 100644 --- a/napalm_yang/parser.py +++ b/napalm_yang/parser.py @@ -1,4 +1,5 @@ import os + import copy from napalm_yang import helpers @@ -88,6 +89,12 @@ def _parse(self, attribute, model, mapping): def _parse_container(self, attribute, model, mapping): mapping["_process"] = helpers.resolve_rule(mapping["_process"], attribute, self.keys, self.extra_vars, None, self.bookmarks) + + # Saving state + old_parent_key = self.keys["parent_key"] + old_parent_bookmark = self.bookmarks["parent"] + old_parent_extra_vars = self.extra_vars + if model._yang_type is not None: # None means it's an element of a list block, extra_vars = self.parser.parse_container(mapping["_process"], self.bookmarks) @@ -115,6 +122,11 @@ def _parse_container(self, attribute, model, mapping): else: self._parse(k, v, mapping[v._yang_name]) + # Restoring state + self.keys["parent_key"] = old_parent_key + self.bookmarks["parent"] = old_parent_bookmark + self.extra_vars = old_parent_extra_vars + def _parse_list(self, attribute, model, mapping): mapping_copy = copy.deepcopy(mapping) mapping_copy["_process"] = helpers.resolve_rule(mapping_copy["_process"], attribute,