From 3116958c4d5f72a7d54600ecba3bb5dcd0db7f21 Mon Sep 17 00:00:00 2001 From: matiAlfaro Date: Thu, 2 May 2024 10:06:36 +0300 Subject: [PATCH 1/7] lag mtu set Signed-off-by: matiAlfaro --- config/main.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/config/main.py b/config/main.py index ea2a5154e31..0f86e246b17 100644 --- a/config/main.py +++ b/config/main.py @@ -2208,7 +2208,6 @@ def add_portchannel(ctx, portchannel_name, min_links, fallback, fast_rate): fvs = { 'admin_status': 'up', - 'mtu': '9100', 'lacp_key': 'auto', 'fast_rate': fast_rate.lower(), } @@ -2333,16 +2332,19 @@ def add_portchannel_member(ctx, portchannel_name, port_name): # Dont allow a port to be member of port channel if its MTU does not match with portchannel portchannel_entry = db.get_entry('PORTCHANNEL', portchannel_name) - if portchannel_entry and portchannel_entry.get(PORT_MTU) is not None : + if portchannel_entry: port_entry = db.get_entry('PORT', port_name) - if port_entry and port_entry.get(PORT_MTU) is not None: port_mtu = port_entry.get(PORT_MTU) - - portchannel_mtu = portchannel_entry.get(PORT_MTU) # TODO: MISSING CONSTRAINT IN YANG MODEL - if portchannel_mtu != port_mtu: - ctx.fail("Port MTU of {} is different than the {} MTU size" - .format(port_name, portchannel_name)) + portchannel_mtu = portchannel_entry.get(PORT_MTU) + # If portchannel MTU is not set, set it to the first port MTU + if not portchannel_mtu: + portchannel_mtu = port_mtu + db.mod_entry('PORTCHANNEL', portchannel_name, {PORT_MTU: port_mtu}) + + if (not isinstance(portchannel_mtu, type(port_mtu))) or (portchannel_mtu != port_mtu): # TODO: MISSING CONSTRAINT IN YANG MODEL + ctx.fail("Port MTU of {} is different than the {} MTU size " + .format(port_name, portchannel_name)) # Dont allow a port to be member of port channel if its TPID is not at default 0x8100 # If TPID is supported at LAG level, when member is added, the LAG's TPID is applied to the @@ -2403,6 +2405,11 @@ def del_portchannel_member(ctx, portchannel_name, port_name): try: db.set_entry('PORTCHANNEL_MEMBER', portchannel_name + '|' + port_name, None) + # If this was the last port in the portchannel, set the portchannel MTU to None + portchannel_list = db.get_table(CFG_PORTCHANNEL_PREFIX) + if (portchannel_list is None) or (portchannel_list == {}): + db.mod_entry('PORTCHANNEL', portchannel_name, {PORT_MTU: None}) + except JsonPatchConflict: ctx.fail("Invalid or nonexistent portchannel or interface. Please ensure existence of portchannel member.") From b65c45291078b10583a18b112af31107fe257486 Mon Sep 17 00:00:00 2001 From: matiAlfaro Date: Thu, 2 May 2024 18:08:56 +0300 Subject: [PATCH 2/7] add test Signed-off-by: matiAlfaro --- tests/config_int_mtu_test.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/config_int_mtu_test.py b/tests/config_int_mtu_test.py index c2037bcbe3a..afc74df2259 100644 --- a/tests/config_int_mtu_test.py +++ b/tests/config_int_mtu_test.py @@ -24,3 +24,21 @@ def test_interface_invalid_mtu_check(self): result1 = runner.invoke(config.config.commands["interface"].commands["mtu"], ["Ethernet0", "9217"], obj=db) assert "Error: Invalid value" in result1.output + + def test_portchannel_mtu_check(self): + config.ADHOC_VALIDATION = False + runner = CliRunner() + db = Db() + obj = {'db':db.cfgdb} + # Ethernet32 is already member of PortChannel1001, try (fail) to change mtu + result = runner.invoke(config.config.commands["interface"].commands["mtu"], ["Ethernet32", "1000"], obj=obj) + assert result.exit_code!=0 + # remove port from portchannel + result = runner.invoke(config.config.commands["portchannel"].commands["member"].commands["del"], ["PortChannel1001", "Ethernet32"], obj=obj) + assert result.exit_code==0 + # Set mtu for port interface + result = runner.invoke(config.config.commands["interface"].commands["mtu"], ["Ethernet32", "1000"], obj=obj) + assert result.exit_code!=0 + # Add port back to portchannel + result = runner.invoke(config.config.commands["portchannel"].commands["member"].commands["add"], ["PortChannel1001", "Ethernet32"], obj=obj) + assert result.exit_code==0 From 995b7aedc621d766a3813e632abb3cd807801b19 Mon Sep 17 00:00:00 2001 From: matiAlfaro Date: Sun, 5 May 2024 11:11:31 +0300 Subject: [PATCH 3/7] fix test - pass db_wrap to allow setting LAG Signed-off-by: matiAlfaro --- tests/config_int_mtu_test.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/tests/config_int_mtu_test.py b/tests/config_int_mtu_test.py index afc74df2259..869b5313140 100644 --- a/tests/config_int_mtu_test.py +++ b/tests/config_int_mtu_test.py @@ -2,6 +2,7 @@ import config.main as config from click.testing import CliRunner from utilities_common.db import Db +import time class TestConfigInterfaceMtu(object): def test_interface_mtu_check(self): @@ -24,21 +25,22 @@ def test_interface_invalid_mtu_check(self): result1 = runner.invoke(config.config.commands["interface"].commands["mtu"], ["Ethernet0", "9217"], obj=db) assert "Error: Invalid value" in result1.output - def test_portchannel_mtu_check(self): - config.ADHOC_VALIDATION = False runner = CliRunner() db = Db() - obj = {'db':db.cfgdb} + obj = {'db':db.cfgdb, 'db_wrap':db, 'namespace':''} # Ethernet32 is already member of PortChannel1001, try (fail) to change mtu - result = runner.invoke(config.config.commands["interface"].commands["mtu"], ["Ethernet32", "1000"], obj=obj) + result = runner.invoke(config.config.commands["interface"].commands["mtu"], + ["Ethernet32", "1000"], obj=obj) assert result.exit_code!=0 # remove port from portchannel - result = runner.invoke(config.config.commands["portchannel"].commands["member"].commands["del"], ["PortChannel1001", "Ethernet32"], obj=obj) - assert result.exit_code==0 + result1 = runner.invoke(config.config.commands["portchannel"].commands["member"].commands["del"], + ["PortChannel1001", "Ethernet32"], obj=obj) + assert result1.exit_code==0 # Set mtu for port interface - result = runner.invoke(config.config.commands["interface"].commands["mtu"], ["Ethernet32", "1000"], obj=obj) - assert result.exit_code!=0 + result2 = runner.invoke(config.config.commands["interface"].commands["mtu"], ["Ethernet32", "1000"], obj=obj) + assert result2.exit_code!=0 # Add port back to portchannel - result = runner.invoke(config.config.commands["portchannel"].commands["member"].commands["add"], ["PortChannel1001", "Ethernet32"], obj=obj) - assert result.exit_code==0 + result3 = runner.invoke(config.config.commands["portchannel"].commands["member"].commands["add"], + ["PortChannel1001", "Ethernet32"], obj=obj) + assert result3.exit_code==0 From 659de17cdc123d51cd60e7d4caea663a0c757231 Mon Sep 17 00:00:00 2001 From: matiAlfaro Date: Sun, 5 May 2024 11:44:57 +0300 Subject: [PATCH 4/7] remove Etheret32 from ACLs in cofig_db.json Signed-off-by: matiAlfaro --- config/main.py | 2 +- tests/config_int_mtu_test.py | 2 +- tests/mock_tables/config_db.json | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/config/main.py b/config/main.py index 0f86e246b17..52427cc3037 100644 --- a/config/main.py +++ b/config/main.py @@ -2342,7 +2342,7 @@ def add_portchannel_member(ctx, portchannel_name, port_name): portchannel_mtu = port_mtu db.mod_entry('PORTCHANNEL', portchannel_name, {PORT_MTU: port_mtu}) - if (not isinstance(portchannel_mtu, type(port_mtu))) or (portchannel_mtu != port_mtu): # TODO: MISSING CONSTRAINT IN YANG MODEL + if (portchannel_mtu != port_mtu): # TODO: MISSING CONSTRAINT IN YANG MODEL ctx.fail("Port MTU of {} is different than the {} MTU size " .format(port_name, portchannel_name)) diff --git a/tests/config_int_mtu_test.py b/tests/config_int_mtu_test.py index 869b5313140..87690410aa2 100644 --- a/tests/config_int_mtu_test.py +++ b/tests/config_int_mtu_test.py @@ -2,7 +2,6 @@ import config.main as config from click.testing import CliRunner from utilities_common.db import Db -import time class TestConfigInterfaceMtu(object): def test_interface_mtu_check(self): @@ -43,4 +42,5 @@ def test_portchannel_mtu_check(self): # Add port back to portchannel result3 = runner.invoke(config.config.commands["portchannel"].commands["member"].commands["add"], ["PortChannel1001", "Ethernet32"], obj=obj) + print(result3.output) assert result3.exit_code==0 diff --git a/tests/mock_tables/config_db.json b/tests/mock_tables/config_db.json index b2bf54c995a..2d1cc6f14b3 100644 --- a/tests/mock_tables/config_db.json +++ b/tests/mock_tables/config_db.json @@ -516,13 +516,13 @@ }, "ACL_TABLE|BMC_ACL_NORTHBOUND": { "policy_desc": "BMC_ACL_NORTHBOUND", - "ports@": "Ethernet8,Ethernet0,Ethernet17,Ethernet16,Ethernet37,Ethernet4,Ethernet13,Ethernet45,Ethernet19,Ethernet24,Ethernet31,Ethernet30,Ethernet39,Ethernet40,Ethernet27,Ethernet43,Ethernet7,Ethernet3,Ethernet20,Ethernet6,Ethernet12,Ethernet34,Ethernet26,Ethernet11,Ethernet42,Ethernet5,Ethernet32,Ethernet36,Ethernet15,Ethernet33,Ethernet35,Ethernet9,Ethernet29,Ethernet21,Ethernet18,Ethernet38,Ethernet23,Ethernet41,Ethernet14,Ethernet2,Ethernet28,Ethernet22,Ethernet10,Ethernet25,Ethernet44,Ethernet1", + "ports@": "Ethernet8,Ethernet0,Ethernet17,Ethernet16,Ethernet37,Ethernet4,Ethernet13,Ethernet45,Ethernet19,Ethernet24,Ethernet31,Ethernet30,Ethernet39,Ethernet40,Ethernet27,Ethernet43,Ethernet7,Ethernet3,Ethernet20,Ethernet6,Ethernet12,Ethernet34,Ethernet26,Ethernet11,Ethernet42,Ethernet5,Ethernet36,Ethernet15,Ethernet33,Ethernet35,Ethernet9,Ethernet29,Ethernet21,Ethernet18,Ethernet38,Ethernet23,Ethernet41,Ethernet14,Ethernet2,Ethernet28,Ethernet22,Ethernet10,Ethernet25,Ethernet44,Ethernet1", "stage": "ingress", "type": "BMCDATA" }, "ACL_TABLE|BMC_ACL_NORTHBOUND_V6": { "policy_desc": "BMC_ACL_NORTHBOUND_V6", - "ports@": "Ethernet13,Ethernet3,Ethernet32,Ethernet33,Ethernet34,Ethernet8,Ethernet6,Ethernet44,Ethernet39,Ethernet18,Ethernet15,Ethernet1,Ethernet19,Ethernet7,Ethernet14,Ethernet43,Ethernet40,Ethernet27,Ethernet4,Ethernet36,Ethernet41,Ethernet10,Ethernet31,Ethernet5,Ethernet9,Ethernet12,Ethernet16,Ethernet25,Ethernet24,Ethernet17,Ethernet35,Ethernet11,Ethernet38,Ethernet42,Ethernet29,Ethernet20,Ethernet45,Ethernet26,Ethernet21,Ethernet37,Ethernet0,Ethernet28,Ethernet23,Ethernet22,Ethernet2,Ethernet30", + "ports@": "Ethernet13,Ethernet3,Ethernet33,Ethernet34,Ethernet8,Ethernet6,Ethernet44,Ethernet39,Ethernet18,Ethernet15,Ethernet1,Ethernet19,Ethernet7,Ethernet14,Ethernet43,Ethernet40,Ethernet27,Ethernet4,Ethernet36,Ethernet41,Ethernet10,Ethernet31,Ethernet5,Ethernet9,Ethernet12,Ethernet16,Ethernet25,Ethernet24,Ethernet17,Ethernet35,Ethernet11,Ethernet38,Ethernet42,Ethernet29,Ethernet20,Ethernet45,Ethernet26,Ethernet21,Ethernet37,Ethernet0,Ethernet28,Ethernet23,Ethernet22,Ethernet2,Ethernet30", "stage": "ingress", "type": "BMCDATAV6" }, @@ -577,7 +577,7 @@ }, "ACL_TABLE|EVERFLOWV6": { "policy_desc": "EVERFLOWV6", - "ports@": "Ethernet0,Ethernet1,Ethernet2,Ethernet3,Ethernet4,Ethernet5,Ethernet6,Ethernet7,Ethernet8,Ethernet9,Ethernet10,Ethernet11,Ethernet12,Ethernet13,Ethernet14,Ethernet15,Ethernet16,Ethernet17,Ethernet18,Ethernet19,Ethernet20,Ethernet21,Ethernet22,Ethernet23,Ethernet24,Ethernet25,Ethernet26,Ethernet27,Ethernet28,Ethernet29,Ethernet30,Ethernet31,Ethernet32,Ethernet33,Ethernet34,Ethernet35,Ethernet36,Ethernet37,Ethernet38,Ethernet39,Ethernet40,Ethernet41,Ethernet42,Ethernet43,Ethernet44,Ethernet45,Ethernet46,Ethernet47", + "ports@": "Ethernet0,Ethernet1,Ethernet2,Ethernet3,Ethernet4,Ethernet5,Ethernet6,Ethernet7,Ethernet8,Ethernet9,Ethernet10,Ethernet11,Ethernet12,Ethernet13,Ethernet14,Ethernet15,Ethernet16,Ethernet17,Ethernet18,Ethernet19,Ethernet20,Ethernet21,Ethernet22,Ethernet23,Ethernet24,Ethernet25,Ethernet26,Ethernet27,Ethernet28,Ethernet29,Ethernet30,Ethernet31,Ethernet33,Ethernet34,Ethernet35,Ethernet36,Ethernet37,Ethernet38,Ethernet39,Ethernet40,Ethernet41,Ethernet42,Ethernet43,Ethernet44,Ethernet45,Ethernet46,Ethernet47", "type": "MIRRORV6", "stage": "ingress" }, From e3b919d40a4a67a669e21103797ec4172f29bafd Mon Sep 17 00:00:00 2001 From: matiAlfaro Date: Sun, 5 May 2024 12:02:04 +0300 Subject: [PATCH 5/7] flake8 fixes Signed-off-by: matiAlfaro --- config/main.py | 6 +++--- tests/config_int_mtu_test.py | 19 ++++++++++--------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/config/main.py b/config/main.py index 52427cc3037..d260d16ca45 100644 --- a/config/main.py +++ b/config/main.py @@ -2336,15 +2336,15 @@ def add_portchannel_member(ctx, portchannel_name, port_name): port_entry = db.get_entry('PORT', port_name) if port_entry and port_entry.get(PORT_MTU) is not None: port_mtu = port_entry.get(PORT_MTU) - portchannel_mtu = portchannel_entry.get(PORT_MTU) + portchannel_mtu = portchannel_entry.get(PORT_MTU) # If portchannel MTU is not set, set it to the first port MTU if not portchannel_mtu: portchannel_mtu = port_mtu db.mod_entry('PORTCHANNEL', portchannel_name, {PORT_MTU: port_mtu}) - + if (portchannel_mtu != port_mtu): # TODO: MISSING CONSTRAINT IN YANG MODEL ctx.fail("Port MTU of {} is different than the {} MTU size " - .format(port_name, portchannel_name)) + .format(port_name, portchannel_name)) # Dont allow a port to be member of port channel if its TPID is not at default 0x8100 # If TPID is supported at LAG level, when member is added, the LAG's TPID is applied to the diff --git a/tests/config_int_mtu_test.py b/tests/config_int_mtu_test.py index 87690410aa2..98b4fee6839 100644 --- a/tests/config_int_mtu_test.py +++ b/tests/config_int_mtu_test.py @@ -24,23 +24,24 @@ def test_interface_invalid_mtu_check(self): result1 = runner.invoke(config.config.commands["interface"].commands["mtu"], ["Ethernet0", "9217"], obj=db) assert "Error: Invalid value" in result1.output + def test_portchannel_mtu_check(self): runner = CliRunner() db = Db() - obj = {'db':db.cfgdb, 'db_wrap':db, 'namespace':''} + obj = {'db': db.cfgdb, 'db_wrap': db, 'namespace': ''} # Ethernet32 is already member of PortChannel1001, try (fail) to change mtu - result = runner.invoke(config.config.commands["interface"].commands["mtu"], - ["Ethernet32", "1000"], obj=obj) - assert result.exit_code!=0 + result = runner.invoke(config.config.commands["interface"].commands["mtu"], + ["Ethernet32", "1000"], obj=obj) + assert result.exit_code != 0 # remove port from portchannel - result1 = runner.invoke(config.config.commands["portchannel"].commands["member"].commands["del"], + result1 = runner.invoke(config.config.commands["portchannel"].commands["member"].commands["del"], ["PortChannel1001", "Ethernet32"], obj=obj) - assert result1.exit_code==0 + assert result1.exit_code == 0 # Set mtu for port interface result2 = runner.invoke(config.config.commands["interface"].commands["mtu"], ["Ethernet32", "1000"], obj=obj) - assert result2.exit_code!=0 + assert result2.exit_code != 0 # Add port back to portchannel - result3 = runner.invoke(config.config.commands["portchannel"].commands["member"].commands["add"], + result3 = runner.invoke(config.config.commands["portchannel"].commands["member"].commands["add"], ["PortChannel1001", "Ethernet32"], obj=obj) print(result3.output) - assert result3.exit_code==0 + assert result3.exit_code == 0 From 43caf5ef4fb8cc67b7bb2f17f54259e128b005d3 Mon Sep 17 00:00:00 2001 From: matiAlfaro Date: Sun, 5 May 2024 12:10:18 +0300 Subject: [PATCH 6/7] more flake8 fixes Signed-off-by: matiAlfaro --- config/main.py | 2 +- tests/config_int_mtu_test.py | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/config/main.py b/config/main.py index d260d16ca45..209fd7f6a61 100644 --- a/config/main.py +++ b/config/main.py @@ -2409,7 +2409,7 @@ def del_portchannel_member(ctx, portchannel_name, port_name): portchannel_list = db.get_table(CFG_PORTCHANNEL_PREFIX) if (portchannel_list is None) or (portchannel_list == {}): db.mod_entry('PORTCHANNEL', portchannel_name, {PORT_MTU: None}) - + except JsonPatchConflict: ctx.fail("Invalid or nonexistent portchannel or interface. Please ensure existence of portchannel member.") diff --git a/tests/config_int_mtu_test.py b/tests/config_int_mtu_test.py index 98b4fee6839..9b013a901ae 100644 --- a/tests/config_int_mtu_test.py +++ b/tests/config_int_mtu_test.py @@ -30,18 +30,21 @@ def test_portchannel_mtu_check(self): db = Db() obj = {'db': db.cfgdb, 'db_wrap': db, 'namespace': ''} # Ethernet32 is already member of PortChannel1001, try (fail) to change mtu - result = runner.invoke(config.config.commands["interface"].commands["mtu"], - ["Ethernet32", "1000"], obj=obj) + result = runner.invoke( + config.config.commands["interface"].commands["mtu"], + ["Ethernet32", "1000"], obj=obj) assert result.exit_code != 0 # remove port from portchannel - result1 = runner.invoke(config.config.commands["portchannel"].commands["member"].commands["del"], - ["PortChannel1001", "Ethernet32"], obj=obj) + result1 = runner.invoke( + config.config.commands["portchannel"].commands["member"].commands["del"], + ["PortChannel1001", "Ethernet32"], obj=obj) assert result1.exit_code == 0 # Set mtu for port interface result2 = runner.invoke(config.config.commands["interface"].commands["mtu"], ["Ethernet32", "1000"], obj=obj) assert result2.exit_code != 0 # Add port back to portchannel - result3 = runner.invoke(config.config.commands["portchannel"].commands["member"].commands["add"], - ["PortChannel1001", "Ethernet32"], obj=obj) + result3 = runner.invoke( + config.config.commands["portchannel"].commands["member"].commands["add"], + ["PortChannel1001", "Ethernet32"], obj=obj) print(result3.output) assert result3.exit_code == 0 From b111f96130d19c4d468156b4019c692135d87863 Mon Sep 17 00:00:00 2001 From: matiAlfaro Date: Sun, 5 May 2024 14:51:51 +0300 Subject: [PATCH 7/7] fix condition Signed-off-by: matiAlfaro --- config/main.py | 9 ++++----- tests/config_int_mtu_test.py | 14 +++++++------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/config/main.py b/config/main.py index 209fd7f6a61..b0f1ebefb02 100644 --- a/config/main.py +++ b/config/main.py @@ -2338,13 +2338,13 @@ def add_portchannel_member(ctx, portchannel_name, port_name): port_mtu = port_entry.get(PORT_MTU) portchannel_mtu = portchannel_entry.get(PORT_MTU) # If portchannel MTU is not set, set it to the first port MTU - if not portchannel_mtu: + if (str(portchannel_mtu) == 'None'): portchannel_mtu = port_mtu db.mod_entry('PORTCHANNEL', portchannel_name, {PORT_MTU: port_mtu}) if (portchannel_mtu != port_mtu): # TODO: MISSING CONSTRAINT IN YANG MODEL - ctx.fail("Port MTU of {} is different than the {} MTU size " - .format(port_name, portchannel_name)) + ctx.fail("Port MTU of {} is {} different than the {} MTU size which is {}" + .format(port_name, port_mtu, portchannel_name, portchannel_mtu)) # Dont allow a port to be member of port channel if its TPID is not at default 0x8100 # If TPID is supported at LAG level, when member is added, the LAG's TPID is applied to the @@ -2406,8 +2406,7 @@ def del_portchannel_member(ctx, portchannel_name, port_name): try: db.set_entry('PORTCHANNEL_MEMBER', portchannel_name + '|' + port_name, None) # If this was the last port in the portchannel, set the portchannel MTU to None - portchannel_list = db.get_table(CFG_PORTCHANNEL_PREFIX) - if (portchannel_list is None) or (portchannel_list == {}): + if len([(k, v) for k, v in db.get_table('PORTCHANNEL_MEMBER') if k == portchannel_name]) == 0: db.mod_entry('PORTCHANNEL', portchannel_name, {PORT_MTU: None}) except JsonPatchConflict: diff --git a/tests/config_int_mtu_test.py b/tests/config_int_mtu_test.py index 9b013a901ae..7fc88f07d50 100644 --- a/tests/config_int_mtu_test.py +++ b/tests/config_int_mtu_test.py @@ -35,16 +35,16 @@ def test_portchannel_mtu_check(self): ["Ethernet32", "1000"], obj=obj) assert result.exit_code != 0 # remove port from portchannel - result1 = runner.invoke( + result = runner.invoke( config.config.commands["portchannel"].commands["member"].commands["del"], ["PortChannel1001", "Ethernet32"], obj=obj) - assert result1.exit_code == 0 + assert result.exit_code == 0 # Set mtu for port interface - result2 = runner.invoke(config.config.commands["interface"].commands["mtu"], ["Ethernet32", "1000"], obj=obj) - assert result2.exit_code != 0 + result = runner.invoke(config.config.commands["interface"].commands["mtu"], ["Ethernet32", "1000"], obj=obj) + assert result.exit_code != 0 # Add port back to portchannel - result3 = runner.invoke( + result = runner.invoke( config.config.commands["portchannel"].commands["member"].commands["add"], ["PortChannel1001", "Ethernet32"], obj=obj) - print(result3.output) - assert result3.exit_code == 0 + print(result.output) + assert result.exit_code == 0