Skip to content
This repository has been archived by the owner on May 15, 2019. It is now read-only.

Commit

Permalink
Merge 438cfb7 into 80cd38f
Browse files Browse the repository at this point in the history
  • Loading branch information
matejv committed Jun 26, 2017
2 parents 80cd38f + 438cfb7 commit 9727bca
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 65 deletions.
7 changes: 5 additions & 2 deletions napalm_yang/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ def _resolve_rule(rule, **kwargs):


def resolve_rule(rule, attribute, keys, extra_vars=None, translation_model=None,
parse_bookmarks=None):
parse_bookmarks=None, replacing=False, merging=False, negating=False):
if isinstance(rule, list):
return [resolve_rule(r, attribute, keys, extra_vars, translation_model, parse_bookmarks)
return [resolve_rule(r, attribute, keys, extra_vars, translation_model, parse_bookmarks, replacing, merging, negating)
for r in rule]
elif isinstance(rule, str):
if rule in ["unnecessary"]:
Expand All @@ -91,6 +91,9 @@ def resolve_rule(rule, attribute, keys, extra_vars=None, translation_model=None,
kwargs["bookmarks"] = parse_bookmarks
kwargs["attribute"] = attribute
kwargs["extra_vars"] = extra_vars
kwargs["replacing"] = replacing
kwargs["merging"] = merging
kwargs["negating"] = negating

for k, v in rule.items():
if k.startswith('post_process_'):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,19 @@ _process:
- mode: gate
when: "{{ protocol_key != 'static static' }}"
static:
_process: unnecessary
_process:
- mode: container
prefix: "ip route {{ static_key }}"
negate: "no ip route {{ static_key }}\n"
negate_prefix: "no ip route {{ static_key }}"
when: "{{ network_instance_key == 'global' }}"
in: "network-instances"
- mode: container
prefix: "ip route vrf {{ network_instance_key }} {{ static_key }}"
negate: "no ip route vrf {{ network_instance_key }} {{ static_key }}\n"
negate_prefix: "no ip route vrf {{ network_instance_key }} {{ static_key }}"
when: "{{ network_instance_key != 'global' }}"
in: "network-instances"
config:
_process: unnecessary
prefix:
Expand All @@ -14,30 +26,21 @@ static:
_process: unnecessary
next-hop:
_process:
- mode: gate
when: "{{ replacing and negating }}"
- mode: container
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"
negate: "no ip route vrf {{ network_instance_key }} {{ static_key }} {{ next_hop_key }}\n"
when: "{{ network_instance_key != 'global' }}"
in: "network-instances"
key_value: "{{ extra_vars.prefix }} {{ next_hop_key }}"
negate: "{{ extra_vars.negate_prefix }} {{ next_hop_key }}\n"
end: "\n"
config:
_process: unnecessary
index:
_process: not_implemented
metric:
_process:
- mode: element
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"
negate: "ip route vrf {{ network_instance_key }} {{ static_key }} {{ next_hop_key }} 1\n"
when: "{{ network_instance_key != 'global' }}"
value: " {{ model }}"
negate: " 1"
next-hop:
_process: unnecessary
recurse:
Expand Down
38 changes: 26 additions & 12 deletions napalm_yang/translator.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from napalm_yang import helpers
from napalm_yang.parsers import get_parser

from copy import copy

import logging
logger = logging.getLogger("napalm-yang")
Expand All @@ -10,7 +11,7 @@ class Translator(object):

def __init__(self, model, profile,
translation=None, keys=None, bookmarks=None,
merge=None, replace=None, other=None):
merge=None, replace=None, other=None, extra_vars=None):
self.model = model
self.profile = profile
self._defining_module = model._defining_module
Expand All @@ -19,6 +20,7 @@ def __init__(self, model, profile,
self.translation = translation
self.keys = keys or {"parent_key": None}
self.bookmarks = bookmarks or {self._yang_name: translation, "parent": translation}
self.extra_vars = extra_vars or {}

self.merge = merge
self.replace = replace
Expand Down Expand Up @@ -53,24 +55,25 @@ def _translate(self, attribute, model, mapping, translation, other):
self._translate_leaf(attribute, model, mapping, translation, other)

def _translate_leaf(self, attribute, model, mapping, translation, other):
rule = helpers.resolve_rule(mapping["_process"], attribute, self.keys, None, model,
self.bookmarks)
rule = helpers.resolve_rule(mapping["_process"], attribute, self.keys, self.extra_vars, model,
self.bookmarks, bool(self.replace), bool(self.merge))
self.translator.translate_leaf(attribute, model, other, rule, translation, self.bookmarks)

def _translate_container(self, attribute, model, mapping, translation, other):
if model._yang_type:
self.bookmarks["parent"] = translation

rule = helpers.resolve_rule(mapping["_process"], attribute, self.keys,
None, model, self.bookmarks)
self.extra_vars, model, self.bookmarks, bool(self.replace), bool(self.merge))

et = self.translator.translate_container(attribute, model, other, rule,
et, extra_vars = self.translator.translate_container(attribute, model, other, rule,
translation, self.bookmarks)

if et is None:
return

self.bookmarks[attribute] = et
self.extra_vars.update(extra_vars)
else:
et = translation

Expand All @@ -84,7 +87,7 @@ def _translate_container(self, attribute, model, mapping, translation, other):
if v._defining_module != self._defining_module and v._defining_module is not None:
logger.debug("Skipping attribute: {}:{}".format(v._defining_module, attribute))
translator = Translator(v, self.profile, et, self.keys,
self.bookmarks, self.merge, self.replace, other_attr)
self.bookmarks, self.merge, self.replace, other_attr, self.extra_vars)
translator.translate()
else:
self._translate(k, v, mapping[v._yang_name], et, other_attr)
Expand All @@ -93,6 +96,7 @@ def _translate_list(self, attribute, model, mapping, translation, other):
# Saving state to restore them later
old_parent_key = self.keys["parent_key"]
old_parent_bookmark = self.bookmarks["parent"]
old_extra_vars = copy(self.extra_vars)

# We will use this to store blocks of configuration
# for each individual element of the list
Expand All @@ -111,12 +115,14 @@ def _translate_list(self, attribute, model, mapping, translation, other):
self.keys[key_name] = key
self.keys["parent_key"] = key

translation_rule_negate = helpers.resolve_rule(mapping["_process"], attribute,
self.keys, self.extra_vars, element, self.bookmarks, bool(self.replace), bool(self.merge), True)
translation_rule = helpers.resolve_rule(mapping["_process"], attribute,
self.keys, None, element, self.bookmarks)
self.keys, self.extra_vars, element, self.bookmarks, bool(self.replace), bool(self.merge), False)

self.translator.default_element(translation_rule, translation, self.bookmarks,
self.translator.default_element(translation_rule_negate, translation, self.bookmarks,
replacing=True)
et = self.translator.init_element(attribute, element, other_element, translation_rule,
et, extra_vars = self.translator.init_element(attribute, element, other_element, translation_rule,
translation, self.bookmarks)

if et is None:
Expand All @@ -125,18 +131,22 @@ def _translate_list(self, attribute, model, mapping, translation, other):

self.bookmarks[attribute][key] = et
self.bookmarks["parent"] = et
self.extra_vars.update(extra_vars)

self._translate(attribute, element, mapping, et, other_element)

# Restore state
self.keys["parent_key"] = old_parent_key
self.bookmarks["parent"] = old_parent_bookmark
self.extra_vars = old_extra_vars

if other:
# Let's default elements not present in the model
self._default_element_list(attribute, other, mapping, translation, model)

def _default_element_list(self, attribute, running, mapping, translation, candidate):
# we'll restore old values when we leave this branch
old_extra_vars = copy(self.extra_vars)
for key in running:
logger.info("Defaulting {}: {}".format(attribute, key))
element = running[key]
Expand All @@ -149,14 +159,18 @@ def _default_element_list(self, attribute, running, mapping, translation, candid
self.keys["parent_key"] = key

translation_rule = helpers.resolve_rule(mapping["_process"], attribute,
self.keys, None, element,
self.bookmarks)
self.keys, self.extra_vars, element,
self.bookmarks, bool(self.replace), bool(self.merge), True)

self.translator.default_element(translation_rule, translation, self.bookmarks)
_, extra_vars = self.translator.default_element(translation_rule, translation, self.bookmarks)
self.extra_vars.update(extra_vars)

if any([t.get("continue_negating", False) for t in translation_rule]):
self._default_child(attribute, element, mapping, translation)

# Restore state
self.extra_vars = old_extra_vars

def _default_child(self, attribute, running, mapping, translation):
logger.debug("Defaulting child attribute: {}".format(running._yang_path()))

Expand Down
25 changes: 19 additions & 6 deletions napalm_yang/translators/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,27 @@ def __init__(self, merge, replace):

def init_element(self, attribute, model, other, mapping, translation, bookmarks):
et = translation
extra_vars = {}
for m in mapping:
if m["mode"] == "skip":
continue
elif m["mode"] == "gate":
return
return None, {}

t = _find_translation_point(m, bookmarks, et)
method_name = "_init_element_{}".format(m["mode"])
et = getattr(self, method_name)(attribute, model, other, m, t)
if isinstance(et, tuple):
extra_vars = et[1]
et = et[0]
if et is False:
# if it's False we want to return None to signal we want to abort
return None
return et
return None, {}
return et, extra_vars

def default_element(self, mapping, translation, bookmarks, replacing=False):
t = translation
extra_vars = {}
for m in mapping:
if m["mode"] == "skip":
continue
Expand All @@ -43,6 +48,10 @@ def default_element(self, mapping, translation, bookmarks, replacing=False):
t = _find_translation_point(m, bookmarks, t)
method_name = "_default_element_{}".format(m["mode"])
t = getattr(self, method_name)(m, t, replacing)
if isinstance(t, tuple):
extra_vars = t[1]
t = t[0]
return t, extra_vars

def translate_leaf(self, attribute, model, other, mapping, translation, bookmarks):
for m in mapping:
Expand All @@ -57,16 +66,20 @@ def translate_leaf(self, attribute, model, other, mapping, translation, bookmark

def translate_container(self, attribute, model, other, mapping, translation, bookmarks):
et = translation
extra_vars = {}
for m in mapping:
if m["mode"] == "skip":
continue
elif m["mode"] == "gate":
return
return None, {}

t = _find_translation_point(m, bookmarks, et)
method_name = "_translate_container_{}".format(m["mode"])
et = getattr(self, method_name)(attribute, model, other, m, t)
if isinstance(et, tuple):
extra_vars = et[1]
et = et[0]
if et is False:
# if it's False we want to return None to signal we want to abort
return None
return et
return None, {}
return et, extra_vars
22 changes: 18 additions & 4 deletions napalm_yang/translators/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,35 +33,43 @@ def _translate_leaf_element(self, attribute, model, other, mapping, translation)
super()._translate_leaf_element(attribute, model, other, mapping, translation, force)

def _init_element_container(self, attribute, model, other, mapping, translation):
extra_vars = {}

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
# the rest of the object
return False
return False, {}

if not model._changed() and other is not None and not self.replace:
mapping["key_value"] = mapping["negate"]
if not model._changed() and other is not None and self.replace:
return translation
return translation, {}

mapping["key_element"] = "command"
mapping["container"] = model._yang_name

for i in ('prefix', 'negate_prefix'):
if i in mapping:
extra_vars[i] = mapping.get(i)

t = super()._init_element_container(attribute, model, other, mapping, translation)

end = mapping.get("end", "")
if end and t is not None:
e = etree.SubElement(translation, "command")
e.text = end

return t
return t, extra_vars

# 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):
extra_vars = {}

if (replacing or self.replace) and not mapping.get("replace", True):
return

Expand All @@ -74,9 +82,15 @@ def _default_element_container(self, mapping, translation, replacing):
e = etree.SubElement(translation, "command")
e.text = mapping["negate"]

for i in ('prefix', 'negate_prefix'):
if i in mapping:
extra_vars[i] = mapping.get(i)

return None, extra_vars

def _xml_to_text(self, xml, text=""):
for element in xml:
if element.tag == "command":
if element.tag == "command" and element.text is not None:
text += element.text
text += self._xml_to_text(element)
return text
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
ip route 10.0.0.0/24 192.168.0.2
ip route 10.0.0.0/24 192.168.0.2 1
vrf definition frontend
rd 1:1
Expand All @@ -10,9 +9,7 @@ router bgp 65001
neighbor 172.20.0.200 local-as 100 no-prepend replace-as
router-id 2.2.2.2
exit
ip route vrf frontend 10.0.0.0/24 172.20.0.2
ip route vrf frontend 10.0.0.0/24 172.20.0.2 1
ip route vrf frontend 10.0.1.0/24 172.20.0.2
ip route vrf frontend 10.0.1.0/24 172.20.0.2 1
vrf definition devel
exit
Expand Down
Loading

0 comments on commit 9727bca

Please sign in to comment.