-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tests for bgpcfgd templates #4841
Merged
pavel-shirshov
merged 8 commits into
sonic-net:master
from
pavel-shirshov:pavelsh/bgpcfgd_tests
Jun 25, 2020
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
4a4a2b9
Tests for bgpcfgd templates
ba6d16e
Remove generator
c2005cb
Remove sample
7b778c9
Added a test for ipv6 next-hop global
a75b620
extract g_debug
699ce90
Remove extra empty comments
867019c
Use template_dirs from constants.yml
2013ab0
Use values from constants for v6 test
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,5 +2,7 @@ | |
build/ | ||
dist/ | ||
*.egg-info/ | ||
app/*.pyc | ||
tests/*.pyc | ||
tests/__pycache__/ | ||
.idea |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import os | ||
import tempfile | ||
|
||
from .vars import g_debug | ||
from .log import log_crit, log_err | ||
from .util import run_command | ||
|
||
|
||
class ConfigMgr(object): | ||
""" The class represents frr configuration """ | ||
def __init__(self): | ||
self.current_config = None | ||
|
||
def reset(self): | ||
""" Reset stored config """ | ||
self.current_config = None | ||
|
||
def update(self): | ||
""" Read current config from FRR """ | ||
self.current_config = None | ||
ret_code, out, err = run_command(["vtysh", "-c", "show running-config"]) | ||
if ret_code != 0: | ||
log_crit("can't update running config: rc=%d out='%s' err='%s'" % (ret_code, out, err)) | ||
return | ||
self.current_config = self.to_canonical(out) | ||
|
||
def push(self, cmd): | ||
""" | ||
Push new changes to FRR | ||
:param cmd: configuration change for FRR. Type: String | ||
:return: True if change was applied successfully, False otherwise | ||
""" | ||
return self.write(cmd) | ||
|
||
def write(self, cmd): | ||
""" | ||
Write configuration change to FRR. | ||
:param cmd: new configuration to write into FRR. Type: String | ||
:return: True if change was applied successfully, False otherwise | ||
""" | ||
fd, tmp_filename = tempfile.mkstemp(dir='/tmp') | ||
os.close(fd) | ||
with open(tmp_filename, 'w') as fp: | ||
fp.write("%s\n" % cmd) | ||
command = ["vtysh", "-f", tmp_filename] | ||
ret_code, out, err = run_command(command) | ||
if not g_debug: | ||
os.remove(tmp_filename) | ||
if ret_code != 0: | ||
err_tuple = str(cmd), ret_code, out, err | ||
log_err("ConfigMgr::push(): can't push configuration '%s', rc='%d', stdout='%s', stderr='%s'" % err_tuple) | ||
if ret_code == 0: | ||
self.current_config = None # invalidate config | ||
return ret_code == 0 | ||
|
||
@staticmethod | ||
def to_canonical(raw_config): | ||
""" | ||
Convert FRR config into canonical format | ||
:param raw_config: config in frr format | ||
:return: frr config in canonical format | ||
""" | ||
parsed_config = [] | ||
lines_with_comments = raw_config.split("\n") | ||
lines = [line for line in lines_with_comments | ||
if not line.strip().startswith('!') and line.strip() != ''] | ||
if len(lines) == 0: | ||
return [] | ||
cur_path = [lines[0]] | ||
cur_offset = ConfigMgr.count_spaces(lines[0]) | ||
for line in lines: | ||
n_spaces = ConfigMgr.count_spaces(line) | ||
s_line = line.strip() | ||
# assert(n_spaces == cur_offset or (n_spaces + 1) == cur_offset or (n_spaces - 1) == cur_offset) | ||
if n_spaces == cur_offset: | ||
cur_path[-1] = s_line | ||
elif n_spaces > cur_offset: | ||
cur_path.append(s_line) | ||
elif n_spaces < cur_offset: | ||
cur_path = cur_path[:-2] | ||
cur_path.append(s_line) | ||
parsed_config.append(cur_path[:]) | ||
cur_offset = n_spaces | ||
return parsed_config | ||
|
||
@staticmethod | ||
def count_spaces(line): | ||
""" Count leading spaces in the line """ | ||
return len(line) - len(line.lstrip()) | ||
|
||
@staticmethod | ||
def from_canonical(canonical_config): | ||
""" | ||
Convert config from canonical format into FRR raw format | ||
:param canonical_config: config in a canonical format | ||
:return: config in the FRR raw format | ||
""" | ||
out = "" | ||
for lines in canonical_config: | ||
spaces = len(lines) - 1 | ||
out += " " * spaces + lines[-1] + "\n" | ||
|
||
return out |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import syslog | ||
|
||
from .vars import g_debug | ||
|
||
def log_debug(msg): | ||
""" Send a message msg to the syslog as DEBUG """ | ||
if g_debug: | ||
syslog.syslog(syslog.LOG_DEBUG, msg) | ||
|
||
|
||
def log_notice(msg): | ||
""" Send a message msg to the syslog as NOTICE """ | ||
syslog.syslog(syslog.LOG_NOTICE, msg) | ||
|
||
|
||
def log_info(msg): | ||
""" Send a message msg to the syslog as INFO """ | ||
syslog.syslog(syslog.LOG_INFO, msg) | ||
|
||
|
||
def log_warn(msg): | ||
""" Send a message msg to the syslog as WARNING """ | ||
syslog.syslog(syslog.LOG_WARNING, msg) | ||
|
||
|
||
def log_err(msg): | ||
""" Send a message msg to the syslog as ERR """ | ||
syslog.syslog(syslog.LOG_ERR, msg) | ||
|
||
|
||
def log_crit(msg): | ||
""" Send a message msg to the syslog as CRIT """ | ||
syslog.syslog(syslog.LOG_CRIT, msg) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
from collections import OrderedDict | ||
from functools import partial | ||
|
||
import jinja2 | ||
import netaddr | ||
|
||
|
||
class TemplateFabric(object): | ||
""" Fabric for rendering jinja2 templates """ | ||
def __init__(self, template_path = '/usr/share/sonic/templates'): | ||
j2_template_paths = [template_path] | ||
j2_loader = jinja2.FileSystemLoader(j2_template_paths) | ||
j2_env = jinja2.Environment(loader=j2_loader, trim_blocks=False) | ||
j2_env.filters['ipv4'] = self.is_ipv4 | ||
j2_env.filters['ipv6'] = self.is_ipv6 | ||
j2_env.filters['pfx_filter'] = self.pfx_filter | ||
for attr in ['ip', 'network', 'prefixlen', 'netmask']: | ||
j2_env.filters[attr] = partial(self.prefix_attr, attr) | ||
self.env = j2_env | ||
|
||
def from_file(self, filename): | ||
""" | ||
Read a template from a file | ||
:param filename: filename of the file. Type String | ||
:return: Jinja2 template object | ||
""" | ||
return self.env.get_template(filename) | ||
|
||
def from_string(self, tmpl): | ||
""" | ||
Read a template from a string | ||
:param tmpl: Text representation of Jinja2 template | ||
:return: Jinja2 template object | ||
""" | ||
return self.env.from_string(tmpl) | ||
|
||
@staticmethod | ||
def is_ipv4(value): | ||
""" Return True if the value is an ipv4 address """ | ||
if not value: | ||
return False | ||
if isinstance(value, netaddr.IPNetwork): | ||
addr = value | ||
else: | ||
try: | ||
addr = netaddr.IPNetwork(str(value)) | ||
except (netaddr.NotRegisteredError, netaddr.AddrFormatError, netaddr.AddrConversionError): | ||
return False | ||
return addr.version == 4 | ||
|
||
@staticmethod | ||
def is_ipv6(value): | ||
""" Return True if the value is an ipv6 address """ | ||
if not value: | ||
return False | ||
if isinstance(value, netaddr.IPNetwork): | ||
addr = value | ||
else: | ||
try: | ||
addr = netaddr.IPNetwork(str(value)) | ||
except (netaddr.NotRegisteredError, netaddr.AddrFormatError, netaddr.AddrConversionError): | ||
return False | ||
return addr.version == 6 | ||
|
||
@staticmethod | ||
def prefix_attr(attr, value): | ||
""" | ||
Extract attribute from IPNetwork object | ||
:param attr: attribute to extract | ||
:param value: the string representation of ip prefix which will be converted to IPNetwork. | ||
:return: the value of the extracted attribute | ||
""" | ||
if not value: | ||
return None | ||
else: | ||
try: | ||
prefix = netaddr.IPNetwork(str(value)) | ||
except (netaddr.NotRegisteredError, netaddr.AddrFormatError, netaddr.AddrConversionError): | ||
return None | ||
return str(getattr(prefix, attr)) | ||
|
||
@staticmethod | ||
def pfx_filter(value): | ||
"""INTERFACE Table can have keys in one of the two formats: | ||
string or tuple - This filter skips the string keys and only | ||
take into account the tuple. | ||
For eg - VLAN_INTERFACE|Vlan1000 vs VLAN_INTERFACE|Vlan1000|192.168.0.1/21 | ||
""" | ||
table = OrderedDict() | ||
|
||
if not value: | ||
return table | ||
|
||
for key, val in value.items(): | ||
if not isinstance(key, tuple): | ||
continue | ||
table[key] = val | ||
return table |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import subprocess | ||
|
||
from .log import log_debug, log_err | ||
|
||
|
||
def run_command(command, shell=False, hide_errors=False): | ||
""" | ||
Run a linux command. The command is defined as a list. See subprocess.Popen documentation on format | ||
:param command: command to execute. Type: List of strings | ||
:param shell: execute the command through shell when True. Type: Boolean | ||
:param hide_errors: don't report errors to syslog when True. Type: Boolean | ||
:return: Tuple: integer exit code from the command, stdout as a string, stderr as a string | ||
""" | ||
log_debug("execute command '%s'." % str(command)) | ||
p = subprocess.Popen(command, shell=shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | ||
stdout, stderr = p.communicate() | ||
if p.returncode != 0: | ||
if not hide_errors: | ||
print_tuple = p.returncode, str(command), stdout, stderr | ||
log_err("command execution returned %d. Command: '%s', stdout: '%s', stderr: '%s'" % print_tuple) | ||
|
||
return p.returncode, stdout, stderr |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
g_debug = False |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this be fixed to add /32 if it is ip address?