Skip to content
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

[dhcp-relay] make DHCP relay an extension #6531

Merged
merged 66 commits into from
Jul 15, 2021
Merged
Show file tree
Hide file tree
Changes from 55 commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
a624e0c
[dockers] Tag all docker images with a version number
stepanblyschak Sep 14, 2020
875650f
[dockers] Tag all docker images with a version number
stepanblyschak Sep 14, 2020
954cad9
[dockers] label SONiC Docker with manifest
stepanblyschak Nov 3, 2020
855fa60
[dockers] add package name to manifest
stepanblyschak Nov 16, 2020
44685ba
remove swss dependency from dhcp-relay & router-advertiser
stepanblyschak Nov 16, 2020
d337e08
Merge branch 'master' of github.com:azure/sonic-buildimage into docke…
stepanblyschak Jan 5, 2021
5eb48a9
Merge branch 'master' of github.com:azure/sonic-buildimage into docke…
stepanblyschak Jan 5, 2021
99d9d12
Merge branch 'master' of github.com:azure/sonic-buildimage into docke…
stepanblyschak Jan 13, 2021
ed1bd3f
remove sonic-sdk added by mistake in this change
stepanblyschak Jan 13, 2021
50237b4
Merge branch 'master' into dockers_version_tags
stepanblyschak Jan 13, 2021
caf5c9e
Merge branch 'master' of github.com:azure/sonic-buildimage into docke…
stepanblyschak Jan 18, 2021
9bc4464
[dhcp-relay] make DHCP relay an extension
stepanblyschak Jan 18, 2021
efc69f5
[slave.mk] align lines in dependencies
stepanblyschak Jan 20, 2021
31d4bbf
Merge branch 'master' of github.com:azure/sonic-buildimage into docke…
stepanblyschak Jan 21, 2021
823eede
[docker-macsec] add version number for docker-macsec
stepanblyschak Jan 21, 2021
bdf0113
Merge branch 'dockers_version_tags' of github.com:stepanblyschak/soni…
stepanblyschak Jan 21, 2021
05df4ed
[docker-macsec] add manifest for macsec docker
stepanblyschak Jan 21, 2021
e966fef
[slave.mk] fix missing comma
stepanblyschak Jan 21, 2021
d79293e
[slave.mk] fix missing comma
stepanblyschak Jan 21, 2021
a90f9b4
[slave.mk] fix missing comma
stepanblyschak Jan 21, 2021
74cd875
[sonic_debian_extension.j2] use --enable instead of --enabled
stepanblyschak Jan 21, 2021
2e5f097
[slave.mk] fix target path
stepanblyschak Jan 21, 2021
e1b2c29
[docker-dhcp-relay] add cli to docker
stepanblyschak Jan 21, 2021
db7ef7d
[docker-dhcp-relay] add cli to docker
stepanblyschak Jan 21, 2021
e0735d2
[sonic_debian_extension.j2] fix merge conflict markers
stepanblyschak Jan 25, 2021
4897003
align install from tarball command
stepanblyschak Jan 28, 2021
2066aa9
[dockers] use single manifest template
stepanblyschak Jan 29, 2021
113bf30
[dockers] use single manifest template
stepanblyschak Jan 29, 2021
d6abc30
[docker-dhcp-relay] define manifest parameters in makefile
stepanblyschak Jan 29, 2021
422c245
tag SONiC images the old way
stepanblyschak Jan 30, 2021
5dd9278
Merge branch 'dockers_version_tags' into dockers_manifest
stepanblyschak Jan 30, 2021
888a0ea
check if docker has correct manifest after loading it
stepanblyschak Jan 30, 2021
8ad8a05
address review comment
stepanblyschak Feb 10, 2021
53631d7
fix checking manifest
stepanblyschak Feb 15, 2021
a0d3124
Merge branch 'master' into dockers_manifest
stepanblyschak Mar 18, 2021
9d76601
Merge branch 'master' of github.com:azure/sonic-buildimage into docke…
stepanblyschak Mar 24, 2021
3de0cb3
remove obsolete file
stepanblyschak Mar 25, 2021
a665d73
Merge branch 'dockers_manifest' of github.com:stepanblyschak/sonic-bu…
stepanblyschak Mar 25, 2021
b2f8fc9
remove obsolete field from manifest. will be replaced by base-os comp…
stepanblyschak Mar 29, 2021
46d75eb
Merge branch 'master' of github.com:azure/sonic-buildimage into docke…
stepanblyschak Mar 30, 2021
13f3453
Merge branch 'master' into dhcp-relay-ext
stepanblyschak Apr 2, 2021
84bf4dd
fix review comments
stepanblyschak Apr 9, 2021
b427b71
fix review comments
stepanblyschak Apr 12, 2021
92ad8b1
add comment
stepanblyschak Apr 12, 2021
57ec2f2
remove not needed ARG in dockerfiles
stepanblyschak Apr 15, 2021
c74ea21
Merge branch 'master' into dockers_manifest
stepanblyschak Apr 20, 2021
119b47c
Merge branch 'dockers_manifest' of github.com:stepanblyschak/sonic-bu…
stepanblyschak Apr 21, 2021
7c11312
tests for cli plugins
stepanblyschak Apr 21, 2021
662aa3d
add tests
stepanblyschak Apr 21, 2021
628071f
Merge branch 'master' into dhcp-relay-ext
stepanblyschak Apr 29, 2021
66e2333
default-owner to set-owner
stepanblyschak Apr 29, 2021
60076ed
fix lgtm warnings
stepanblyschak Apr 30, 2021
368e9d3
Merge branch 'master' of github.com:azure/sonic-buildimage into dhcp-…
stepanblyschak Apr 30, 2021
0d5b19e
fix review comments
stepanblyschak May 5, 2021
dffcee0
add coverage report
May 7, 2021
c1f7e32
[sonic-app-ext] support app extensions installation during build
May 12, 2021
7cc9102
Merge branch 'master' of github.com:azure/sonic-buildimage into app-e…
stepanblyschak May 31, 2021
abe88c0
bring back dhcp_relay
stepanblyschak May 31, 2021
82b52be
Merge branch 'master' into dhcp-relay-ext
stepanblyschak May 31, 2021
9807d93
introduce _INSTALL_PYTHON_WHEELS and _INSTALL_DEBS to not install son…
stepanblyschak Jun 11, 2021
fa1b606
Merge branch 'app-ext-build' of github.com:stepanblyschak/sonic-build…
stepanblyschak Jun 11, 2021
306feba
add install targets for dhcp-relay
stepanblyschak Jun 11, 2021
a2e33be
Merge branch 'master' into dhcp-relay-ext
stepanblyschak Jun 24, 2021
fe19b0e
Merge branch 'master' into dhcp-relay-ext
stepanblyschak Jul 2, 2021
42f8b36
remove obsolete changes added back by mistake during upstream merge
stepanblyschak Jul 12, 2021
c86bb45
add missing INCLUDE_DHCP_RELAY condition
stepanblyschak Jul 14, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile.work
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ SONIC_BUILD_INSTRUCTION := make \
HTTPS_PROXY=$(https_proxy) \
NO_PROXY=$(no_proxy) \
SONIC_INCLUDE_SYSTEM_TELEMETRY=$(INCLUDE_SYSTEM_TELEMETRY) \
INCLUDE_DHCP_RELAY=$(INCLUDE_DHCP_RELAY) \
renukamanavalan marked this conversation as resolved.
Show resolved Hide resolved
SONIC_INCLUDE_RESTAPI=$(INCLUDE_RESTAPI) \
TELEMETRY_WRITABLE=$(TELEMETRY_WRITABLE) \
EXTRA_DOCKER_TARGETS=$(EXTRA_DOCKER_TARGETS) \
Expand Down
1 change: 1 addition & 0 deletions dockers/docker-dhcp-relay/Dockerfile.j2
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@ COPY ["docker_init.sh", "start.sh", "/usr/bin/"]
COPY ["docker-dhcp-relay.supervisord.conf.j2", "port-name-alias-map.txt.j2", "wait_for_intf.sh.j2", "/usr/share/sonic/templates/"]
COPY ["files/supervisor-proc-exit-listener", "/usr/bin"]
COPY ["critical_processes", "/etc/supervisor"]
COPY ["cli", "/cli/"]

ENTRYPOINT ["/usr/bin/docker_init.sh"]
27 changes: 27 additions & 0 deletions dockers/docker-dhcp-relay/cli-plugin-tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import pytest
import mock_tables # lgtm [py/unused-import]
from unittest import mock

@pytest.fixture()
def mock_cfgdb():
cfgdb = mock.Mock()
CONFIG = {
'VLAN': {
'Vlan1000': {
'dhcp_servers': ['192.0.0.1']
}
}
}

def get_entry(table, key):
return CONFIG[table][key]

def set_entry(table, key, data):
CONFIG[table].setdefault(key, {})
CONFIG[table][key] = data

cfgdb.get_entry = mock.Mock(side_effect=get_entry)
cfgdb.set_entry = mock.Mock(side_effect=set_entry)

yield cfgdb

154 changes: 154 additions & 0 deletions dockers/docker-dhcp-relay/cli-plugin-tests/mock_tables.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# MONKEY PATCH!!!
import json
import os
from unittest import mock

import mockredis
import redis
import swsssdk
from sonic_py_common import multi_asic
from swsssdk import SonicDBConfig, SonicV2Connector, ConfigDBConnector, ConfigDBPipeConnector
from swsscommon import swsscommon


topo = None
dedicated_dbs = {}

def clean_up_config():
# Set SonicDBConfig variables to initial state
# so that it can be loaded with single or multiple
# namespaces before the test begins.
SonicDBConfig._sonic_db_config = {}
SonicDBConfig._sonic_db_global_config_init = False
SonicDBConfig._sonic_db_config_init = False

def load_namespace_config():
# To support multi asic testing
# SonicDBConfig load_sonic_global_db_config
# is invoked to load multiple namespaces
clean_up_config()
SonicDBConfig.load_sonic_global_db_config(
global_db_file_path=os.path.join(
os.path.dirname(os.path.abspath(__file__)), 'database_global.json'))

def load_database_config():
# Load local database_config.json for single namespace test scenario
clean_up_config()
SonicDBConfig.load_sonic_db_config(
sonic_db_file_path=os.path.join(
os.path.dirname(os.path.abspath(__file__)), 'database_config.json'))


_old_connect_SonicV2Connector = SonicV2Connector.connect

def connect_SonicV2Connector(self, db_name, retry_on=True):
# add topo to kwargs for testing different topology
self.dbintf.redis_kwargs['topo'] = topo
# add the namespace to kwargs for testing multi asic
self.dbintf.redis_kwargs['namespace'] = self.namespace
# Mock DB filename for unit-test
global dedicated_dbs
if dedicated_dbs and dedicated_dbs.get(db_name):
self.dbintf.redis_kwargs['db_name'] = dedicated_dbs[db_name]
else:
self.dbintf.redis_kwargs['db_name'] = db_name
self.dbintf.redis_kwargs['decode_responses'] = True
_old_connect_SonicV2Connector(self, db_name, retry_on)

def _subscribe_keyspace_notification(self, db_name, client):
pass


def config_set(self, *args):
pass


class MockPubSub:
def get_message(self):
return None

def psubscribe(self, *args, **kwargs):
pass

def __call__(self, *args, **kwargs):
return self

def listen(self):
return []

def punsubscribe(self, *args, **kwargs):
pass

def clear(self):
pass

INPUT_DIR = os.path.dirname(os.path.abspath(__file__))


class SwssSyncClient(mockredis.MockRedis):
def __init__(self, *args, **kwargs):
super(SwssSyncClient, self).__init__(strict=True, *args, **kwargs)
# Namespace is added in kwargs specifically for unit-test
# to identify the file path to load the db json files.
topo = kwargs.pop('topo')
namespace = kwargs.pop('namespace')
db_name = kwargs.pop('db_name')
self.decode_responses = kwargs.pop('decode_responses', False) == True
fname = db_name.lower() + ".json"
self.pubsub = MockPubSub()

if namespace is not None and namespace is not multi_asic.DEFAULT_NAMESPACE:
fname = os.path.join(INPUT_DIR, namespace, fname)
elif topo is not None:
fname = os.path.join(INPUT_DIR, topo, fname)
else:
fname = os.path.join(INPUT_DIR, fname)

if os.path.exists(fname):
with open(fname) as f:
js = json.load(f)
for k, v in js.items():
if 'expireat' in v and 'ttl' in v and 'type' in v and 'value' in v:
# database is in redis-dump format
if v['type'] == 'hash':
# ignore other types for now since sonic has hset keys only in the db
for attr, value in v['value'].items():
self.hset(k, attr, value)
else:
for attr, value in v.items():
self.hset(k, attr, value)

# Patch mockredis/mockredis/client.py
# The offical implementation assume decode_responses=False
# Here we detect the option and decode after doing encode
def _encode(self, value):
"Return a bytestring representation of the value. Taken from redis-py connection.py"

value = super(SwssSyncClient, self)._encode(value)

if self.decode_responses:
return value.decode('utf-8')

# Patch mockredis/mockredis/client.py
# The official implementation will filter out keys with a slash '/'
# ref: https://github.com/locationlabs/mockredis/blob/master/mockredis/client.py
def keys(self, pattern='*'):
"""Emulate keys."""
import fnmatch
import re

# Make regex out of glob styled pattern.
regex = fnmatch.translate(pattern)
regex = re.compile(regex)

# Find every key that matches the pattern
return [key for key in self.redis if regex.match(key)]


swsssdk.interface.DBInterface._subscribe_keyspace_notification = _subscribe_keyspace_notification
mockredis.MockRedis.config_set = config_set
redis.StrictRedis = SwssSyncClient
SonicV2Connector.connect = connect_SonicV2Connector
swsscommon.SonicV2Connector = SonicV2Connector
swsscommon.ConfigDBConnector = ConfigDBConnector
swsscommon.ConfigDBPipeConnector = ConfigDBPipeConnector
3 changes: 3 additions & 0 deletions dockers/docker-dhcp-relay/cli-plugin-tests/pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[pytest]
addopts = --cov-config=.coveragerc --cov --cov-report html --cov-report term --cov-report xml --junitxml=test-results.xml -vv

141 changes: 141 additions & 0 deletions dockers/docker-dhcp-relay/cli-plugin-tests/test_config_dhcp_relay.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import os
import sys
import traceback
from unittest import mock

from click.testing import CliRunner

from utilities_common.db import Db

import pytest

sys.path.append('../cli/config/plugins/')
import dhcp_relay

config_vlan_add_dhcp_relay_output="""\
Added DHCP relay destination address 192.0.0.100 to Vlan1000
Restarting DHCP relay service...
"""

config_vlan_del_dhcp_relay_output="""\
tahmed-dev marked this conversation as resolved.
Show resolved Hide resolved
Removed DHCP relay destination address 192.0.0.100 from Vlan1000
Restarting DHCP relay service...
"""

class TestConfigVlanDhcpRelay(object):
def test_plugin_registration(self):
cli = mock.MagicMock()
dhcp_relay.register(cli)
cli.commands['vlan'].add_command.assert_called_once_with(dhcp_relay.vlan_dhcp_relay)

def test_config_vlan_add_dhcp_relay_with_nonexist_vlanid(self):
runner = CliRunner()

with mock.patch('utilities_common.cli.run_command') as mock_run_command:
result = runner.invoke(dhcp_relay.vlan_dhcp_relay.commands["add"],
["1001", "192.0.0.100"])
print(result.exit_code)
print(result.output)
# traceback.print_tb(result.exc_info[2])
assert result.exit_code != 0
assert "Error: Vlan1001 doesn't exist" in result.output
assert mock_run_command.call_count == 0

def test_config_vlan_add_dhcp_relay_with_invalid_vlanid(self):
runner = CliRunner()

with mock.patch('utilities_common.cli.run_command') as mock_run_command:
result = runner.invoke(dhcp_relay.vlan_dhcp_relay.commands["add"],
["4096", "192.0.0.100"])
print(result.exit_code)
print(result.output)
# traceback.print_tb(result.exc_info[2])
assert result.exit_code != 0
assert "Error: Vlan4096 doesn't exist" in result.output
assert mock_run_command.call_count == 0

def test_config_vlan_add_dhcp_relay_with_invalid_ip(self):
runner = CliRunner()

with mock.patch('utilities_common.cli.run_command') as mock_run_command:
result = runner.invoke(dhcp_relay.vlan_dhcp_relay.commands["add"],
["1000", "192.0.0.1000"])
print(result.exit_code)
print(result.output)
# traceback.print_tb(result.exc_info[2])
assert result.exit_code != 0
assert "Error: 192.0.0.1000 is invalid IP address" in result.output
assert mock_run_command.call_count == 0

def test_config_vlan_add_dhcp_relay_with_exist_ip(self, mock_cfgdb):
runner = CliRunner()
db = Db()
db.cfgdb = mock_cfgdb

with mock.patch('utilities_common.cli.run_command') as mock_run_command:
result = runner.invoke(dhcp_relay.vlan_dhcp_relay.commands["add"],
["1000", "192.0.0.1"], obj=db)
print(result.exit_code)
print(result.output)
assert result.exit_code == 0
assert "192.0.0.1 is already a DHCP relay destination for Vlan1000" in result.output
assert mock_run_command.call_count == 0

def test_config_vlan_add_del_dhcp_relay_dest(self, mock_cfgdb):
runner = CliRunner()
db = Db()
db.cfgdb = mock_cfgdb

# add new relay dest
with mock.patch("utilities_common.cli.run_command") as mock_run_command:
result = runner.invoke(dhcp_relay.vlan_dhcp_relay.commands["add"],
["1000", "192.0.0.100"], obj=db)
print(result.exit_code)
print(result.output)
assert result.exit_code == 0
assert result.output == config_vlan_add_dhcp_relay_output
assert mock_run_command.call_count == 3
db.cfgdb.set_entry.assert_called_once_with('VLAN', 'Vlan1000', {'dhcp_servers': ['192.0.0.1', '192.0.0.100']})

db.cfgdb.set_entry.reset_mock()

# del relay dest
with mock.patch("utilities_common.cli.run_command") as mock_run_command:
tahmed-dev marked this conversation as resolved.
Show resolved Hide resolved
result = runner.invoke(dhcp_relay.vlan_dhcp_relay.commands["del"],
["1000", "192.0.0.100"], obj=db)
print(result.exit_code)
print(result.output)
assert result.exit_code == 0
assert result.output == config_vlan_del_dhcp_relay_output
assert mock_run_command.call_count == 3
db.cfgdb.set_entry.assert_called_once_with('VLAN', 'Vlan1000', {'dhcp_servers': ['192.0.0.1']})

def test_config_vlan_remove_nonexist_dhcp_relay_dest(self, mock_cfgdb):
runner = CliRunner()
db = Db()
db.cfgdb = mock_cfgdb

with mock.patch('utilities_common.cli.run_command') as mock_run_command:
result = runner.invoke(dhcp_relay.vlan_dhcp_relay.commands["del"],
["1000", "192.0.0.100"], obj=db)
print(result.exit_code)
print(result.output)
# traceback.print_tb(result.exc_info[2])
assert result.exit_code != 0
assert "Error: 192.0.0.100 is not a DHCP relay destination for Vlan1000" in result.output
assert mock_run_command.call_count == 0

def test_config_vlan_remove_dhcp_relay_dest_with_nonexist_vlanid(self, mock_cfgdb):
runner = CliRunner()
db = Db()
db.cfgdb = mock_cfgdb

with mock.patch('utilities_common.cli.run_command') as mock_run_command:
result = runner.invoke(dhcp_relay.vlan_dhcp_relay.commands["del"],
["1001", "192.0.0.1"], obj=Db)
print(result.exit_code)
print(result.output)
# traceback.print_tb(result.exc_info[2])
assert result.exit_code != 0
assert "Error: Vlan1001 doesn't exist" in result.output
assert mock_run_command.call_count == 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import os
import sys
import traceback
from unittest import mock

from click.testing import CliRunner

import show.vlan as vlan
from utilities_common.db import Db

sys.path.insert(0, '../cli/show/plugins/')
import show_dhcp_relay


class TestVlanDhcpRelay(object):
def test_plugin_registration(self):
cli = mock.MagicMock()
show_dhcp_relay.register(cli)
assert 'DHCP Helper Address' in dict(vlan.VlanBrief.COLUMNS)

def test_dhcp_relay_column_output(self):
ctx = (
({'Vlan100': {'dhcp_servers': ['192.0.0.1', '192.168.0.2']}}, {}, {}),
(),
)
assert show_dhcp_relay.get_dhcp_helper_address(ctx, 'Vlan100') == '192.0.0.1\n192.168.0.2'


Loading