diff --git a/config/plugins/nvgre_tunnel.py b/config/plugins/nvgre_tunnel.py new file mode 100644 index 000000000000..cbb4ce2d735a --- /dev/null +++ b/config/plugins/nvgre_tunnel.py @@ -0,0 +1,349 @@ +""" +Autogenerated config CLI plugin for NVGRE Tunnel feature. +""" + +import copy +import click +import utilities_common.cli as clicommon +import utilities_common.general as general +from config import config_mgmt + + +# Load sonic-cfggen from source since /usr/local/bin/sonic-cfggen does not have .py extension. +sonic_cfggen = general.load_module_from_source('sonic_cfggen', '/usr/local/bin/sonic-cfggen') + + +def exit_with_error(*args, **kwargs): + """ Print a message with click.secho and abort CLI. + + Args: + args: Positional arguments to pass to click.secho + kwargs: Keyword arguments to pass to click.secho + """ + + click.secho(*args, **kwargs) + raise click.Abort() + + +def validate_config_or_raise(cfg): + """ Validate config db data using ConfigMgmt. + + Args: + cfg (Dict): Config DB data to validate. + Raises: + Exception: when cfg does not satisfy YANG schema. + """ + + try: + cfg = sonic_cfggen.FormatConverter.to_serialized(copy.deepcopy(cfg)) + config_mgmt.ConfigMgmt().loadData(cfg) + except Exception as err: + raise Exception('Failed to validate configuration: {}'.format(err)) + + +def add_entry_validated(db, table, key, data): + """ Add new entry in table and validate configuration. + + Args: + db (swsscommon.ConfigDBConnector): Config DB connector obect. + table (str): Table name to add new entry to. + key (Union[str, Tuple]): Key name in the table. + data (Dict): Entry data. + Raises: + Exception: when cfg does not satisfy YANG schema. + """ + + cfg = db.get_config() + cfg.setdefault(table, {}) + if key in cfg[table]: + raise Exception(f"{key} already exists") + + cfg[table][key] = data + + validate_config_or_raise(cfg) + db.set_entry(table, key, data) + + +def update_entry_validated(db, table, key, data, create_if_not_exists=False): + """ Update entry in table and validate configuration. + If attribute value in data is None, the attribute is deleted. + + Args: + db (swsscommon.ConfigDBConnector): Config DB connector obect. + table (str): Table name to add new entry to. + key (Union[str, Tuple]): Key name in the table. + data (Dict): Entry data. + create_if_not_exists (bool): + In case entry does not exists already a new entry + is not created if this flag is set to False and + creates a new entry if flag is set to True. + Raises: + Exception: when cfg does not satisfy YANG schema. + """ + + cfg = db.get_config() + cfg.setdefault(table, {}) + + if not data: + raise Exception(f"No field/values to update {key}") + + if create_if_not_exists: + cfg[table].setdefault(key, {}) + + if key not in cfg[table]: + raise Exception(f"{key} does not exist") + + entry_changed = False + for attr, value in data.items(): + if value == cfg[table][key][attr]: + continue + entry_changed = True + if value is None: + cfg[table][key].pop(attr, None) + else: + cfg[table][key][attr] = value + + if not entry_changed: + return + + validate_config_or_raise(cfg) + db.set_entry(table, key, cfg[table][key]) + + +def del_entry_validated(db, table, key): + """ Delete entry in table and validate configuration. + + Args: + db (swsscommon.ConfigDBConnector): Config DB connector obect. + table (str): Table name to add new entry to. + key (Union[str, Tuple]): Key name in the table. + Raises: + Exception: when cfg does not satisfy YANG schema. + """ + + cfg = db.get_config() + cfg.setdefault(table, {}) + if key not in cfg[table]: + raise Exception(f"{key} does not exist") + + cfg[table].pop(key) + + validate_config_or_raise(cfg) + db.set_entry(table, key, None) + + +def add_list_entry_validated(db, table, key, attr, data): + """ Add new entry into list in table and validate configuration. + + Args: + db (swsscommon.ConfigDBConnector): Config DB connector obect. + table (str): Table name to add data to. + key (Union[str, Tuple]): Key name in the table. + attr (str): Attribute name which represents a list the data needs to be added to. + data (List): Data list to add to config DB. + Raises: + Exception: when cfg does not satisfy YANG schema. + """ + + cfg = db.get_config() + cfg.setdefault(table, {}) + if key not in cfg[table]: + raise Exception(f"{key} does not exist") + cfg[table][key].setdefault(attr, []) + for entry in data: + if entry in cfg[table][key][attr]: + raise Exception(f"{entry} already exists") + cfg[table][key][attr].append(entry) + + validate_config_or_raise(cfg) + db.set_entry(table, key, cfg[table][key]) + + +def del_list_entry_validated(db, table, key, attr, data): + """ Delete entry from list in table and validate configuration. + + Args: + db (swsscommon.ConfigDBConnector): Config DB connector obect. + table (str): Table name to remove data from. + key (Union[str, Tuple]): Key name in the table. + attr (str): Attribute name which represents a list the data needs to be removed from. + data (Dict): Data list to remove from config DB. + Raises: + Exception: when cfg does not satisfy YANG schema. + """ + + cfg = db.get_config() + cfg.setdefault(table, {}) + if key not in cfg[table]: + raise Exception(f"{key} does not exist") + cfg[table][key].setdefault(attr, []) + for entry in data: + if entry not in cfg[table][key][attr]: + raise Exception(f"{entry} does not exist") + cfg[table][key][attr].remove(entry) + if not cfg[table][key][attr]: + cfg[table][key].pop(attr) + + validate_config_or_raise(cfg) + db.set_entry(table, key, cfg[table][key]) + + +def clear_list_entry_validated(db, table, key, attr): + """ Clear list in object and validate configuration. + + Args: + db (swsscommon.ConfigDBConnector): Config DB connector obect. + table (str): Table name to remove the list attribute from. + key (Union[str, Tuple]): Key name in the table. + attr (str): Attribute name which represents a list that needs to be removed. + Raises: + Exception: when cfg does not satisfy YANG schema. + """ + + update_entry_validated(db, table, key, {attr: None}) + + +@click.group( + name="nvgre-tunnel", + cls=clicommon.AliasedGroup) +def NVGRE_TUNNEL(): + """ NVGRE_TUNNEL part of config_db.json """ + + pass + + +@NVGRE_TUNNEL.command(name="add") +@click.argument( + "tunnel-name", + nargs=1, + required=True, +) +@click.option( + "--src-ip", + required=True, + help="Source IP address[mandatory]", +) +@clicommon.pass_db +def NVGRE_TUNNEL_add(db, tunnel_name, src_ip): + """ Add object in NVGRE_TUNNEL. """ + + table = "NVGRE_TUNNEL" + key = tunnel_name + data = {} + if src_ip is not None: + data["src_ip"] = src_ip + + try: + add_entry_validated(db.cfgdb, table, key, data) + except Exception as err: + exit_with_error(f"Error: {err}", fg="red") + + +@NVGRE_TUNNEL.command(name="delete") +@click.argument( + "tunnel-name", + nargs=1, + required=True, +) +@clicommon.pass_db +def NVGRE_TUNNEL_delete(db, tunnel_name): + """ Delete object in NVGRE_TUNNEL. """ + + table = "NVGRE_TUNNEL" + key = tunnel_name + try: + del_entry_validated(db.cfgdb, table, key) + except Exception as err: + exit_with_error(f"Error: {err}", fg="red") + + +@click.group( + name="nvgre-tunnel-map", + cls=clicommon.AliasedGroup) +def NVGRE_TUNNEL_MAP(): + """ NVGRE_TUNNEL_MAP part of config_db.json """ + + pass + + +@NVGRE_TUNNEL_MAP.command(name="add") +@click.argument( + "tunnel-name", + nargs=1, + required=True, +) +@click.argument( + "tunnel-map-name", + nargs=1, + required=True, +) + +@click.option( + "--vlan-id", + required=True, + help="VLAN identifier[mandatory]", +) +@click.option( + "--vsid", + required=True, + help="Virtual Subnet Identifier[mandatory]", +) +@clicommon.pass_db +def NVGRE_TUNNEL_MAP_add(db, tunnel_name, tunnel_map_name, vlan_id, vsid): + """ Add object in NVGRE_TUNNEL_MAP. """ + + table = "NVGRE_TUNNEL_MAP" + key = tunnel_name, tunnel_map_name + data = {} + if vlan_id is not None: + data["vlan_id"] = vlan_id + if vsid is not None: + data["vsid"] = vsid + + try: + add_entry_validated(db.cfgdb, table, key, data) + except Exception as err: + exit_with_error(f"Error: {err}", fg="red") + + +@NVGRE_TUNNEL_MAP.command(name="delete") +@click.argument( + "tunnel-name", + nargs=1, + required=True, +) +@click.argument( + "tunnel-map-name", + nargs=1, + required=True, +) +@clicommon.pass_db +def NVGRE_TUNNEL_MAP_delete(db, tunnel_name, tunnel_map_name): + """ Delete object in NVGRE_TUNNEL_MAP. """ + + table = "NVGRE_TUNNEL_MAP" + key = tunnel_name, tunnel_map_name + try: + del_entry_validated(db.cfgdb, table, key) + except Exception as err: + exit_with_error(f"Error: {err}", fg="red") + + +def register(cli): + """ Register new CLI nodes in root CLI. + + Args: + cli: Root CLI node. + Raises: + Exception: when root CLI already has a command + we are trying to register. + """ + cli_node = NVGRE_TUNNEL + if cli_node.name in cli.commands: + raise Exception(f"{cli_node.name} already exists in CLI") + cli.add_command(NVGRE_TUNNEL) + cli_node = NVGRE_TUNNEL_MAP + if cli_node.name in cli.commands: + raise Exception(f"{cli_node.name} already exists in CLI") + cli.add_command(NVGRE_TUNNEL_MAP) + diff --git a/doc/Command-Reference.md b/doc/Command-Reference.md index b1846d155132..20f0e0b86bee 100644 --- a/doc/Command-Reference.md +++ b/doc/Command-Reference.md @@ -110,6 +110,9 @@ * [NTP](#ntp) * [NTP show commands](#ntp-show-commands) * [NTP config commands](#ntp-config-commands) +* [NVGRE](#nvgre) + * [NVGRE show commands](#nvgre-show-commands) + * [NVGRE config commands](#nvgre-config-commands) * [PBH](#pbh) * [PBH show commands](#pbh-show-commands) * [PBH config commands](#pbh-config-commands) @@ -6767,6 +6770,97 @@ This command adds or deletes a member port to/from the already created portchann Go Back To [Beginning of the document](#) or [Beginning of this section](#portchannels) +## NVGRE + +This section explains the various show commands and configuration commands available for users. + +### NVGRE show commands + +This subsection explains how to display the NVGRE configuration. + +**show nvgre-tunnel** + +This command displays the NVGRE tunnel configuration. + +- Usage: +```bash +show nvgre-tunnel +``` + +- Example: +```bash +admin@sonic:~$ show nvgre-tunnel +TUNNEL NAME SRC IP +------------- -------- +tunnel_1 10.0.0.1 +``` + +**show nvgre-tunnel-map** + +This command displays the NVGRE tunnel map configuration. + +- Usage: +```bash +show nvgre-tunnel-map +``` + +- Example: +```bash +admin@sonic:~$ show nvgre-tunnel-map +TUNNEL NAME TUNNEL MAP NAME VLAN ID VSID +------------- ----------------- --------- ------ +tunnel_1 Vlan1000 1000 5000 +tunnel_1 Vlan2000 2000 6000 +``` + +### NVGRE config commands + +This subsection explains how to configure the NVGRE. + +**config nvgre-tunnel** + +This command is used to manage the NVGRE tunnel objects. +It supports add/delete operations. + +- Usage: +```bash +config nvgre-tunnel add --src-ip +config nvgre-tunnel delete +``` + +- Parameters: + - _tunnel-name_: the name of the NVGRE tunnel + - _src-ip_: source ip address + +- Examples: +```bash +config nvgre-tunnel add 'tunnel_1' --src-ip '10.0.0.1' +config nvgre-tunnel delete 'tunnel_1' +``` + +**config nvgre-tunnel-map** + +This command is used to manage the NVGRE tunnel map objects. +It supports add/delete operations. + +- Usage: +```bash +config nvgre-tunnel-map add --vlan-id --vsid +config nvgre-tunnel-map delete +``` + +- Parameters: + - _tunnel-name_: the name of the NVGRE tunnel + - _tunnel-map-name_: the name of the NVGRE tunnel map + - _vlan-id_: VLAN identifier + - _vsid_: Virtual Subnet Identifier + +- Examples: +```bash +config nvgre-tunnel-map add 'tunnel_1' 'Vlan2000' --vlan-id '2000' --vsid '6000' +config nvgre-tunnel-map delete 'tunnel_1' 'Vlan2000' +``` + ## PBH This section explains the various show commands and configuration commands available for users. diff --git a/show/plugins/nvgre_tunnel.py b/show/plugins/nvgre_tunnel.py new file mode 100644 index 000000000000..bd6d86c04c83 --- /dev/null +++ b/show/plugins/nvgre_tunnel.py @@ -0,0 +1,149 @@ +""" +Auto-generated show CLI plugin for NVGRE Tunnel feature. +""" + +import click +import tabulate +import natsort +import utilities_common.cli as clicommon + + +def format_attr_value(entry, attr): + """ Helper that formats attribute to be presented in the table output. + + Args: + entry (Dict[str, str]): CONFIG DB entry configuration. + attr (Dict): Attribute metadata. + + Returns: + str: fomatted attribute value. + """ + + if attr["is-leaf-list"]: + return "\n".join(entry.get(attr["name"], [])) + return entry.get(attr["name"], "N/A") + + +def format_group_value(entry, attrs): + """ Helper that formats grouped attribute to be presented in the table output. + + Args: + entry (Dict[str, str]): CONFIG DB entry configuration. + attrs (List[Dict]): Attributes metadata that belongs to the same group. + + Returns: + str: fomatted group attributes. + """ + + data = [] + for attr in attrs: + if entry.get(attr["name"]): + data.append((attr["name"] + ":", format_attr_value(entry, attr))) + return tabulate.tabulate(data, tablefmt="plain", numalign="left") + + +@click.group( + name="nvgre-tunnel", + cls=clicommon.AliasedGroup, + invoke_without_command=True) +@clicommon.pass_db +def NVGRE_TUNNEL(db): + """ [Callable command group] """ + + header = [ "TUNNEL NAME", "SRC IP" ] + + body = [] + + table = db.cfgdb.get_table("NVGRE_TUNNEL") + for key in natsort.natsorted(table): + entry = table[key] + if not isinstance(key, tuple): + key = (key,) + + row = [*key] + [ + format_attr_value( + entry, + { + 'name': 'src_ip', + 'description': 'Source IP address', + 'is-leaf-list': False, + 'is-mandatory': True, + 'group': '' + } + ) + ] + + body.append(row) + + click.echo(tabulate.tabulate(body, header, numalign="left")) + + +@click.group( + name="nvgre-tunnel-map", + cls=clicommon.AliasedGroup, + invoke_without_command=True) +@clicommon.pass_db +def NVGRE_TUNNEL_MAP(db): + """ [Callable command group] """ + + header = [ + "TUNNEL NAME", + "TUNNEL MAP NAME", + "VLAN ID", + "VSID" + ] + + body = [] + + table = db.cfgdb.get_table("NVGRE_TUNNEL_MAP") + for key in natsort.natsorted(table): + entry = table[key] + if not isinstance(key, tuple): + key = (key,) + + row = [*key] + [ + format_attr_value( + entry, + { + 'name': 'vlan_id', + 'description': 'VLAN identifier', + 'is-leaf-list': False, + 'is-mandatory': True, + 'group': '' + } + ), + format_attr_value( + entry, + { + 'name': 'vsid', + 'description': 'Virtual Subnet Identifier', + 'is-leaf-list': False, + 'is-mandatory': True, + 'group': '' + } + ) + ] + + body.append(row) + + click.echo(tabulate.tabulate(body, header, numalign="left")) + + +def register(cli): + """ Register new CLI nodes in root CLI. + + Args: + cli (click.core.Command): Root CLI node. + Raises: + Exception: when root CLI already has a command + we are trying to register. + """ + cli_node = NVGRE_TUNNEL + if cli_node.name in cli.commands: + raise Exception(f"{cli_node.name} already exists in CLI") + cli.add_command(NVGRE_TUNNEL) + cli_node = NVGRE_TUNNEL_MAP + if cli_node.name in cli.commands: + raise Exception(f"{cli_node.name} already exists in CLI") + cli.add_command(NVGRE_TUNNEL_MAP) + diff --git a/tests/nvgre_tunnel_input/assert_show_output.py b/tests/nvgre_tunnel_input/assert_show_output.py new file mode 100644 index 000000000000..4517eac11921 --- /dev/null +++ b/tests/nvgre_tunnel_input/assert_show_output.py @@ -0,0 +1,39 @@ +""" +Module holding the correct values for show CLI command outputs for the nvgre_tunnel_test.py +""" + + +show_nvgre_tunnel="""\ +TUNNEL NAME SRC IP +------------- -------- +tunnel_1 10.0.0.1 +""" + + +show_nvgre_tunnel_empty="""\ +TUNNEL NAME SRC IP +------------- -------- +""" + + +show_nvgre_tunnels="""\ +TUNNEL NAME SRC IP +------------- -------- +tunnel_1 10.0.0.1 +tunnel_2 10.0.0.2 +""" + + +show_nvgre_tunnel_maps="""\ +TUNNEL NAME TUNNEL MAP NAME VLAN ID VSID +------------- ----------------- --------- ------ +tunnel_1 Vlan1000 1000 5000 +tunnel_1 Vlan2000 2000 6000 +""" + + +show_nvgre_tunnel_map_empty="""\ +TUNNEL NAME TUNNEL MAP NAME VLAN ID VSID +------------- ----------------- --------- ------ +""" + diff --git a/tests/nvgre_tunnel_input/empty_config_db.json b/tests/nvgre_tunnel_input/empty_config_db.json new file mode 100644 index 000000000000..0e0dcd235c49 --- /dev/null +++ b/tests/nvgre_tunnel_input/empty_config_db.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/tests/nvgre_tunnel_input/nvgre_tunnel.json b/tests/nvgre_tunnel_input/nvgre_tunnel.json new file mode 100644 index 000000000000..8a1696fca953 --- /dev/null +++ b/tests/nvgre_tunnel_input/nvgre_tunnel.json @@ -0,0 +1,11 @@ +{ + "NVGRE_TUNNEL|tunnel_1": { + "src_ip": "10.0.0.1" + }, + "VLAN|Vlan1000": { + "vlanid": "1000" + }, + "VLAN|Vlan2000": { + "vlanid": "2000" + } +} \ No newline at end of file diff --git a/tests/nvgre_tunnel_input/nvgre_tunnel_map.json b/tests/nvgre_tunnel_input/nvgre_tunnel_map.json new file mode 100644 index 000000000000..a95fb08592cf --- /dev/null +++ b/tests/nvgre_tunnel_input/nvgre_tunnel_map.json @@ -0,0 +1,12 @@ +{ + "NVGRE_TUNNEL|tunnel_1": { + "src_ip": "10.0.0.1" + }, + "VLAN|Vlan1000": { + "vlanid": "1000" + }, + "NVGRE_TUNNEL_MAP|tunnel_1|Vlan1000": { + "vlan_id": "1000", + "vsid": "5000" + } +} \ No newline at end of file diff --git a/tests/nvgre_tunnel_test.py b/tests/nvgre_tunnel_test.py new file mode 100644 index 000000000000..c63baeee4110 --- /dev/null +++ b/tests/nvgre_tunnel_test.py @@ -0,0 +1,238 @@ +#!/usr/bin/env python + +import os +import logging +import show.main as show +import config.main as config + +from .nvgre_tunnel_input import assert_show_output +from utilities_common.db import Db +from click.testing import CliRunner +from .mock_tables import dbconnector + +logger = logging.getLogger(__name__) +test_path = os.path.dirname(os.path.abspath(__file__)) +mock_db_path = os.path.join(test_path, "nvgre_tunnel_input") + +SUCCESS = 0 +ERROR = 1 + +INVALID_VALUE = 'INVALID' + + +class TestNvgreTunnel: + @classmethod + def setup_class(cls): + logger.info("SETUP") + os.environ['UTILITIES_UNIT_TESTING'] = "2" + + + @classmethod + def teardown_class(cls): + logger.info("TEARDOWN") + os.environ['UTILITIES_UNIT_TESTING'] = "0" + os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] = "" + dbconnector.dedicated_dbs['CONFIG_DB'] = None + + + def verify_output(self, db, runner, cmd, output): + result = runner.invoke(show.cli.commands[cmd], [], obj=db) + + logger.debug("\n" + result.output) + logger.debug(result.exit_code) + assert result.exit_code == SUCCESS + assert result.output == output + + + ######### NVGRE-TUNNEL ######### + + + def test_nvgre_tunnel_add_del(self): + dbconnector.dedicated_dbs['CONFIG_DB'] = os.path.join(mock_db_path, 'empty_config_db') + db = Db() + runner = CliRunner() + + # add + result = runner.invoke( + config.config.commands["nvgre-tunnel"].commands["add"], + ["tunnel_1", "--src-ip", "10.0.0.1"], obj=db + ) + + logger.debug("\n" + result.output) + logger.debug(result.exit_code) + assert result.exit_code == SUCCESS + + # verify + self.verify_output(db, runner, "nvgre-tunnel", assert_show_output.show_nvgre_tunnel) + + # delete + result = runner.invoke( + config.config.commands["nvgre-tunnel"].commands["delete"], + ["tunnel_1"], obj=db + ) + + logger.debug("\n" + result.output) + logger.debug(result.exit_code) + assert result.exit_code == SUCCESS + + # verify + self.verify_output(db, runner, "nvgre-tunnel", assert_show_output.show_nvgre_tunnel_empty) + + + def test_nvgre_tunnels_add_del(self): + dbconnector.dedicated_dbs['CONFIG_DB'] = os.path.join(mock_db_path, 'empty_config_db') + db = Db() + runner = CliRunner() + + # add + result = runner.invoke( + config.config.commands["nvgre-tunnel"].commands["add"], + ["tunnel_1", "--src-ip", "10.0.0.1"], obj=db + ) + + logger.debug("\n" + result.output) + logger.debug(result.exit_code) + assert result.exit_code == SUCCESS + + result = runner.invoke( + config.config.commands["nvgre-tunnel"].commands["add"], + ["tunnel_2", "--src-ip", "10.0.0.2"], obj=db + ) + + logger.debug("\n" + result.output) + logger.debug(result.exit_code) + assert result.exit_code == SUCCESS + + # verify + self.verify_output(db, runner, "nvgre-tunnel", assert_show_output.show_nvgre_tunnels) + + # delete + result = runner.invoke( + config.config.commands["nvgre-tunnel"].commands["delete"], + ["tunnel_1"], obj=db + ) + + logger.debug("\n" + result.output) + logger.debug(result.exit_code) + assert result.exit_code == SUCCESS + + result = runner.invoke( + config.config.commands["nvgre-tunnel"].commands["delete"], + ["tunnel_2"], obj=db + ) + + logger.debug("\n" + result.output) + logger.debug(result.exit_code) + assert result.exit_code == SUCCESS + + # verify + self.verify_output(db, runner, "nvgre-tunnel", assert_show_output.show_nvgre_tunnel_empty) + + + def test_nvgre_tunnel_add_invalid(self): + db = Db() + runner = CliRunner() + + # add + result = runner.invoke( + config.config.commands["nvgre-tunnel"].commands["add"], + ["tunnel_1", "--src-ip", INVALID_VALUE], obj=db + ) + + logger.debug("\n" + result.output) + logger.debug(result.exit_code) + assert result.exit_code == ERROR + + # verify + self.verify_output(db, runner, "nvgre-tunnel", assert_show_output.show_nvgre_tunnel_empty) + + + ######### NVGRE-TUNNEL-MAP ######### + + + def test_nvgre_tunnel_maps_add_del(self): + dbconnector.dedicated_dbs['CONFIG_DB'] = os.path.join(mock_db_path, 'nvgre_tunnel') + db = Db() + runner = CliRunner() + + # add + result = runner.invoke( + config.config.commands["nvgre-tunnel-map"].commands["add"], + ["tunnel_1", "Vlan1000", "--vlan-id", "1000", "--vsid", "5000"], obj=db + ) + + logger.debug("\n" + result.output) + logger.debug(result.exit_code) + assert result.exit_code == SUCCESS + + result = runner.invoke( + config.config.commands["nvgre-tunnel-map"].commands["add"], + ["tunnel_1", "Vlan2000", "--vlan-id", "2000", "--vsid", "6000"], obj=db + ) + + logger.debug("\n" + result.output) + logger.debug(result.exit_code) + assert result.exit_code == SUCCESS + + # verify + self.verify_output(db, runner, "nvgre-tunnel-map", assert_show_output.show_nvgre_tunnel_maps) + + # delete + result = runner.invoke( + config.config.commands["nvgre-tunnel-map"].commands["delete"], + ["tunnel_1", "Vlan1000"], obj=db + ) + + logger.debug("\n" + result.output) + logger.debug(result.exit_code) + assert result.exit_code == SUCCESS + + result = runner.invoke( + config.config.commands["nvgre-tunnel-map"].commands["delete"], + ["tunnel_1", "Vlan2000"], obj=db + ) + + logger.debug("\n" + result.output) + logger.debug(result.exit_code) + assert result.exit_code == SUCCESS + + # verify + self.verify_output(db, runner, "nvgre-tunnel-map", assert_show_output.show_nvgre_tunnel_map_empty) + + + def test_nvgre_tunnel_map_add_invalid_vlan(self): + db = Db() + runner = CliRunner() + + # add + result = runner.invoke( + config.config.commands["nvgre-tunnel-map"].commands["add"], + ["tunnel_1", "Vlan5000", "--vlan-id", "5000", "--vsid", "5000"], obj=db + ) + + logger.debug("\n" + result.output) + logger.debug(result.exit_code) + assert result.exit_code == ERROR + + # verify + self.verify_output(db, runner, "nvgre-tunnel-map", assert_show_output.show_nvgre_tunnel_map_empty) + + + def test_nvgre_tunnel_map_add_invalid_vsid(self): + dbconnector.dedicated_dbs['CONFIG_DB'] = os.path.join(mock_db_path, 'nvgre_tunnel') + db = Db() + runner = CliRunner() + + # add + result = runner.invoke( + config.config.commands["nvgre-tunnel-map"].commands["add"], + ["tunnel_1", "Vlan1000", "--vlan-id", "1000", "--vsid", "999999999"], obj=db + ) + + logger.debug("\n" + result.output) + logger.debug(result.exit_code) + assert result.exit_code == ERROR + + # verify + self.verify_output(db, runner, "nvgre-tunnel-map", assert_show_output.show_nvgre_tunnel_map_empty) +