Set ns_name in RouterInfo as attribute
Closes-Bug: #1302007 Change-Id: I02a9b92eea06010569d3d9c5a987e69f89b23be5
This commit is contained in:
parent
c55afe363e
commit
a47f1e6352
@ -108,11 +108,11 @@ class RouterInfo(object):
|
|||||||
self.use_namespaces = use_namespaces
|
self.use_namespaces = use_namespaces
|
||||||
# Invoke the setter for establishing initial SNAT action
|
# Invoke the setter for establishing initial SNAT action
|
||||||
self.router = router
|
self.router = router
|
||||||
|
self.ns_name = NS_PREFIX + router_id if use_namespaces else None
|
||||||
self.iptables_manager = iptables_manager.IptablesManager(
|
self.iptables_manager = iptables_manager.IptablesManager(
|
||||||
root_helper=root_helper,
|
root_helper=root_helper,
|
||||||
#FIXME(danwent): use_ipv6=True,
|
#FIXME(danwent): use_ipv6=True,
|
||||||
namespace=self.ns_name())
|
namespace=self.ns_name)
|
||||||
|
|
||||||
self.routes = []
|
self.routes = []
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -134,10 +134,6 @@ class RouterInfo(object):
|
|||||||
# Gateway port was removed, remove rules
|
# Gateway port was removed, remove rules
|
||||||
self._snat_action = 'remove_rules'
|
self._snat_action = 'remove_rules'
|
||||||
|
|
||||||
def ns_name(self):
|
|
||||||
if self.use_namespaces:
|
|
||||||
return NS_PREFIX + self.router_id
|
|
||||||
|
|
||||||
def perform_snat_action(self, snat_callback, *args):
|
def perform_snat_action(self, snat_callback, *args):
|
||||||
# Process SNAT rules for attached subnets
|
# Process SNAT rules for attached subnets
|
||||||
if self._snat_action:
|
if self._snat_action:
|
||||||
@ -308,7 +304,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, manager.Manager):
|
|||||||
|
|
||||||
def _create_router_namespace(self, ri):
|
def _create_router_namespace(self, ri):
|
||||||
ip_wrapper_root = ip_lib.IPWrapper(self.root_helper)
|
ip_wrapper_root = ip_lib.IPWrapper(self.root_helper)
|
||||||
ip_wrapper = ip_wrapper_root.ensure_namespace(ri.ns_name())
|
ip_wrapper = ip_wrapper_root.ensure_namespace(ri.ns_name)
|
||||||
ip_wrapper.netns.execute(['sysctl', '-w', 'net.ipv4.ip_forward=1'])
|
ip_wrapper.netns.execute(['sysctl', '-w', 'net.ipv4.ip_forward=1'])
|
||||||
|
|
||||||
def _fetch_external_net_id(self, force=False):
|
def _fetch_external_net_id(self, force=False):
|
||||||
@ -352,7 +348,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, manager.Manager):
|
|||||||
ri.iptables_manager.apply()
|
ri.iptables_manager.apply()
|
||||||
super(L3NATAgent, self).process_router_add(ri)
|
super(L3NATAgent, self).process_router_add(ri)
|
||||||
if self.conf.enable_metadata_proxy:
|
if self.conf.enable_metadata_proxy:
|
||||||
self._spawn_metadata_proxy(ri.router_id, ri.ns_name())
|
self._spawn_metadata_proxy(ri.router_id, ri.ns_name)
|
||||||
|
|
||||||
def _router_removed(self, router_id):
|
def _router_removed(self, router_id):
|
||||||
ri = self.router_info.get(router_id)
|
ri = self.router_info.get(router_id)
|
||||||
@ -370,9 +366,9 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, manager.Manager):
|
|||||||
ri.iptables_manager.ipv4['nat'].remove_rule(c, r)
|
ri.iptables_manager.ipv4['nat'].remove_rule(c, r)
|
||||||
ri.iptables_manager.apply()
|
ri.iptables_manager.apply()
|
||||||
if self.conf.enable_metadata_proxy:
|
if self.conf.enable_metadata_proxy:
|
||||||
self._destroy_metadata_proxy(ri.router_id, ri.ns_name())
|
self._destroy_metadata_proxy(ri.router_id, ri.ns_name)
|
||||||
del self.router_info[router_id]
|
del self.router_info[router_id]
|
||||||
self._destroy_router_namespace(ri.ns_name())
|
self._destroy_router_namespace(ri.ns_name)
|
||||||
|
|
||||||
def _spawn_metadata_proxy(self, router_id, ns_name):
|
def _spawn_metadata_proxy(self, router_id, ns_name):
|
||||||
def callback(pid_file):
|
def callback(pid_file):
|
||||||
@ -415,7 +411,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, manager.Manager):
|
|||||||
|
|
||||||
def _get_existing_devices(self, ri):
|
def _get_existing_devices(self, ri):
|
||||||
ip_wrapper = ip_lib.IPWrapper(root_helper=self.root_helper,
|
ip_wrapper = ip_lib.IPWrapper(root_helper=self.root_helper,
|
||||||
namespace=ri.ns_name())
|
namespace=ri.ns_name)
|
||||||
ip_devs = ip_wrapper.get_devices(exclude_loopback=True)
|
ip_devs = ip_wrapper.get_devices(exclude_loopback=True)
|
||||||
return [ip_dev.name for ip_dev in ip_devs]
|
return [ip_dev.name for ip_dev in ip_devs]
|
||||||
|
|
||||||
@ -451,7 +447,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, manager.Manager):
|
|||||||
LOG.debug(_('Deleting stale internal router device: %s'),
|
LOG.debug(_('Deleting stale internal router device: %s'),
|
||||||
stale_dev)
|
stale_dev)
|
||||||
self.driver.unplug(stale_dev,
|
self.driver.unplug(stale_dev,
|
||||||
namespace=ri.ns_name(),
|
namespace=ri.ns_name,
|
||||||
prefix=INTERNAL_DEV_PREFIX)
|
prefix=INTERNAL_DEV_PREFIX)
|
||||||
|
|
||||||
internal_cidrs = [p['ip_cidr'] for p in ri.internal_ports]
|
internal_cidrs = [p['ip_cidr'] for p in ri.internal_ports]
|
||||||
@ -479,7 +475,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, manager.Manager):
|
|||||||
stale_dev)
|
stale_dev)
|
||||||
self.driver.unplug(stale_dev,
|
self.driver.unplug(stale_dev,
|
||||||
bridge=self.conf.external_network_bridge,
|
bridge=self.conf.external_network_bridge,
|
||||||
namespace=ri.ns_name(),
|
namespace=ri.ns_name,
|
||||||
prefix=EXTERNAL_DEV_PREFIX)
|
prefix=EXTERNAL_DEV_PREFIX)
|
||||||
|
|
||||||
# Process static routes for router
|
# Process static routes for router
|
||||||
@ -568,7 +564,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, manager.Manager):
|
|||||||
fip_statuses = {}
|
fip_statuses = {}
|
||||||
interface_name = self.get_external_device_name(ex_gw_port['id'])
|
interface_name = self.get_external_device_name(ex_gw_port['id'])
|
||||||
device = ip_lib.IPDevice(interface_name, self.root_helper,
|
device = ip_lib.IPDevice(interface_name, self.root_helper,
|
||||||
namespace=ri.ns_name())
|
namespace=ri.ns_name)
|
||||||
existing_cidrs = set([addr['cidr'] for addr in device.addr.list()])
|
existing_cidrs = set([addr['cidr'] for addr in device.addr.list()])
|
||||||
new_cidrs = set()
|
new_cidrs = set()
|
||||||
|
|
||||||
@ -616,7 +612,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, manager.Manager):
|
|||||||
ip_address]
|
ip_address]
|
||||||
try:
|
try:
|
||||||
ip_wrapper = ip_lib.IPWrapper(self.root_helper,
|
ip_wrapper = ip_lib.IPWrapper(self.root_helper,
|
||||||
namespace=ri.ns_name())
|
namespace=ri.ns_name)
|
||||||
ip_wrapper.netns.execute(arping_cmd, check_exit_code=True)
|
ip_wrapper.netns.execute(arping_cmd, check_exit_code=True)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.error(_("Failed sending gratuitous ARP: %s"), str(e))
|
LOG.error(_("Failed sending gratuitous ARP: %s"), str(e))
|
||||||
@ -638,7 +634,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, manager.Manager):
|
|||||||
ex_gw_port['id'], interface_name,
|
ex_gw_port['id'], interface_name,
|
||||||
ex_gw_port['mac_address'],
|
ex_gw_port['mac_address'],
|
||||||
bridge=self.conf.external_network_bridge,
|
bridge=self.conf.external_network_bridge,
|
||||||
namespace=ri.ns_name(),
|
namespace=ri.ns_name,
|
||||||
prefix=EXTERNAL_DEV_PREFIX)
|
prefix=EXTERNAL_DEV_PREFIX)
|
||||||
|
|
||||||
# Compute a list of addresses this router is supposed to have.
|
# Compute a list of addresses this router is supposed to have.
|
||||||
@ -649,7 +645,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, manager.Manager):
|
|||||||
for ip in floating_ips]
|
for ip in floating_ips]
|
||||||
|
|
||||||
self.driver.init_l3(interface_name, [ex_gw_port['ip_cidr']],
|
self.driver.init_l3(interface_name, [ex_gw_port['ip_cidr']],
|
||||||
namespace=ri.ns_name(),
|
namespace=ri.ns_name,
|
||||||
preserve_ips=preserve_ips)
|
preserve_ips=preserve_ips)
|
||||||
ip_address = ex_gw_port['ip_cidr'].split('/')[0]
|
ip_address = ex_gw_port['ip_cidr'].split('/')[0]
|
||||||
self._send_gratuitous_arp_packet(ri, interface_name, ip_address)
|
self._send_gratuitous_arp_packet(ri, interface_name, ip_address)
|
||||||
@ -658,7 +654,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, manager.Manager):
|
|||||||
if ex_gw_port['subnet']['gateway_ip']:
|
if ex_gw_port['subnet']['gateway_ip']:
|
||||||
cmd = ['route', 'add', 'default', 'gw', gw_ip]
|
cmd = ['route', 'add', 'default', 'gw', gw_ip]
|
||||||
ip_wrapper = ip_lib.IPWrapper(self.root_helper,
|
ip_wrapper = ip_lib.IPWrapper(self.root_helper,
|
||||||
namespace=ri.ns_name())
|
namespace=ri.ns_name)
|
||||||
ip_wrapper.netns.execute(cmd, check_exit_code=False)
|
ip_wrapper.netns.execute(cmd, check_exit_code=False)
|
||||||
|
|
||||||
def external_gateway_removed(self, ri, ex_gw_port,
|
def external_gateway_removed(self, ri, ex_gw_port,
|
||||||
@ -666,7 +662,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, manager.Manager):
|
|||||||
|
|
||||||
self.driver.unplug(interface_name,
|
self.driver.unplug(interface_name,
|
||||||
bridge=self.conf.external_network_bridge,
|
bridge=self.conf.external_network_bridge,
|
||||||
namespace=ri.ns_name(),
|
namespace=ri.ns_name,
|
||||||
prefix=EXTERNAL_DEV_PREFIX)
|
prefix=EXTERNAL_DEV_PREFIX)
|
||||||
|
|
||||||
def metadata_filter_rules(self):
|
def metadata_filter_rules(self):
|
||||||
@ -700,13 +696,13 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, manager.Manager):
|
|||||||
interface_name = self.get_internal_device_name(port_id)
|
interface_name = self.get_internal_device_name(port_id)
|
||||||
if not ip_lib.device_exists(interface_name,
|
if not ip_lib.device_exists(interface_name,
|
||||||
root_helper=self.root_helper,
|
root_helper=self.root_helper,
|
||||||
namespace=ri.ns_name()):
|
namespace=ri.ns_name):
|
||||||
self.driver.plug(network_id, port_id, interface_name, mac_address,
|
self.driver.plug(network_id, port_id, interface_name, mac_address,
|
||||||
namespace=ri.ns_name(),
|
namespace=ri.ns_name,
|
||||||
prefix=INTERNAL_DEV_PREFIX)
|
prefix=INTERNAL_DEV_PREFIX)
|
||||||
|
|
||||||
self.driver.init_l3(interface_name, [internal_cidr],
|
self.driver.init_l3(interface_name, [internal_cidr],
|
||||||
namespace=ri.ns_name())
|
namespace=ri.ns_name)
|
||||||
ip_address = internal_cidr.split('/')[0]
|
ip_address = internal_cidr.split('/')[0]
|
||||||
self._send_gratuitous_arp_packet(ri, interface_name, ip_address)
|
self._send_gratuitous_arp_packet(ri, interface_name, ip_address)
|
||||||
|
|
||||||
@ -714,8 +710,8 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, manager.Manager):
|
|||||||
interface_name = self.get_internal_device_name(port_id)
|
interface_name = self.get_internal_device_name(port_id)
|
||||||
if ip_lib.device_exists(interface_name,
|
if ip_lib.device_exists(interface_name,
|
||||||
root_helper=self.root_helper,
|
root_helper=self.root_helper,
|
||||||
namespace=ri.ns_name()):
|
namespace=ri.ns_name):
|
||||||
self.driver.unplug(interface_name, namespace=ri.ns_name(),
|
self.driver.unplug(interface_name, namespace=ri.ns_name,
|
||||||
prefix=INTERNAL_DEV_PREFIX)
|
prefix=INTERNAL_DEV_PREFIX)
|
||||||
|
|
||||||
def internal_network_nat_rules(self, ex_gw_ip, internal_cidr):
|
def internal_network_nat_rules(self, ex_gw_ip, internal_cidr):
|
||||||
@ -873,7 +869,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, manager.Manager):
|
|||||||
cmd = ['ip', 'route', operation, 'to', route['destination'],
|
cmd = ['ip', 'route', operation, 'to', route['destination'],
|
||||||
'via', route['nexthop']]
|
'via', route['nexthop']]
|
||||||
ip_wrapper = ip_lib.IPWrapper(self.root_helper,
|
ip_wrapper = ip_lib.IPWrapper(self.root_helper,
|
||||||
namespace=ri.ns_name())
|
namespace=ri.ns_name)
|
||||||
ip_wrapper.netns.execute(cmd, check_exit_code=False)
|
ip_wrapper.netns.execute(cmd, check_exit_code=False)
|
||||||
|
|
||||||
def routes_updated(self, ri):
|
def routes_updated(self, ri):
|
||||||
|
@ -102,7 +102,7 @@ class FWaaSL3AgentRpcCallback(api.FWaaSAgentRpcCallbackMixin):
|
|||||||
if rid not in self.router_info:
|
if rid not in self.router_info:
|
||||||
continue
|
continue
|
||||||
if self.router_info[rid].use_namespaces:
|
if self.router_info[rid].use_namespaces:
|
||||||
router_ns = self.router_info[rid].ns_name()
|
router_ns = self.router_info[rid].ns_name
|
||||||
if router_ns in local_ns_list:
|
if router_ns in local_ns_list:
|
||||||
router_info_list.append(self.router_info[rid])
|
router_info_list.append(self.router_info[rid])
|
||||||
else:
|
else:
|
||||||
|
@ -307,15 +307,15 @@ class vArmourL3NATAgent(l3_agent.L3NATAgent,
|
|||||||
|
|
||||||
if not ip_lib.device_exists(interface_name,
|
if not ip_lib.device_exists(interface_name,
|
||||||
root_helper=self.root_helper,
|
root_helper=self.root_helper,
|
||||||
namespace=ri.ns_name()):
|
namespace=ri.ns_name):
|
||||||
self.driver.plug(ex_gw_port['network_id'],
|
self.driver.plug(ex_gw_port['network_id'],
|
||||||
ex_gw_port['id'], interface_name,
|
ex_gw_port['id'], interface_name,
|
||||||
ex_gw_port['mac_address'],
|
ex_gw_port['mac_address'],
|
||||||
bridge=self.conf.external_network_bridge,
|
bridge=self.conf.external_network_bridge,
|
||||||
namespace=ri.ns_name(),
|
namespace=ri.ns_name,
|
||||||
prefix=l3_agent.EXTERNAL_DEV_PREFIX)
|
prefix=l3_agent.EXTERNAL_DEV_PREFIX)
|
||||||
self.driver.init_l3(interface_name, [ex_gw_port['ip_cidr']],
|
self.driver.init_l3(interface_name, [ex_gw_port['ip_cidr']],
|
||||||
namespace=ri.ns_name())
|
namespace=ri.ns_name)
|
||||||
|
|
||||||
def _update_routing_table(self, ri, operation, route):
|
def _update_routing_table(self, ri, operation, route):
|
||||||
return
|
return
|
||||||
|
@ -70,16 +70,13 @@ class RouterWithMetering(object):
|
|||||||
self.id = router['id']
|
self.id = router['id']
|
||||||
self.router = router
|
self.router = router
|
||||||
self.root_helper = config.get_root_helper(self.conf)
|
self.root_helper = config.get_root_helper(self.conf)
|
||||||
|
self.ns_name = NS_PREFIX + self.id if conf.use_namespaces else None
|
||||||
self.iptables_manager = iptables_manager.IptablesManager(
|
self.iptables_manager = iptables_manager.IptablesManager(
|
||||||
root_helper=self.root_helper,
|
root_helper=self.root_helper,
|
||||||
namespace=self.ns_name(),
|
namespace=self.ns_name,
|
||||||
binary_name=WRAP_NAME)
|
binary_name=WRAP_NAME)
|
||||||
self.metering_labels = {}
|
self.metering_labels = {}
|
||||||
|
|
||||||
def ns_name(self):
|
|
||||||
if self.conf.use_namespaces:
|
|
||||||
return NS_PREFIX + self.router['id']
|
|
||||||
|
|
||||||
|
|
||||||
class IptablesMeteringDriver(abstract_driver.MeteringAbstractDriver):
|
class IptablesMeteringDriver(abstract_driver.MeteringAbstractDriver):
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ class VPNAgent(l3_agent.L3NATAgentWithStateReport):
|
|||||||
router_info = self.router_info.get(router_id)
|
router_info = self.router_info.get(router_id)
|
||||||
if not router_info:
|
if not router_info:
|
||||||
return
|
return
|
||||||
return router_info.ns_name()
|
return router_info.ns_name
|
||||||
|
|
||||||
def add_nat_rule(self, router_id, chain, rule, top=False):
|
def add_nat_rule(self, router_id, chain, rule, top=False):
|
||||||
"""Add nat rule in namespace.
|
"""Add nat rule in namespace.
|
||||||
|
@ -321,7 +321,7 @@ class TestFwaasL3AgentRpcCallback(base.BaseTestCase):
|
|||||||
self.api.router_info = {ri.router_id: ri}
|
self.api.router_info = {ri.router_id: ri}
|
||||||
with mock.patch.object(ip_lib.IPWrapper,
|
with mock.patch.object(ip_lib.IPWrapper,
|
||||||
'get_namespaces') as mock_get_namespaces:
|
'get_namespaces') as mock_get_namespaces:
|
||||||
mock_get_namespaces.return_value = ri.ns_name()
|
mock_get_namespaces.return_value = ri.ns_name
|
||||||
router_info_list = self.api._get_router_info_list_for_tenant(
|
router_info_list = self.api._get_router_info_list_for_tenant(
|
||||||
routers,
|
routers,
|
||||||
ri.router['tenant_id'])
|
ri.router['tenant_id'])
|
||||||
@ -376,7 +376,7 @@ class TestFwaasL3AgentRpcCallback(base.BaseTestCase):
|
|||||||
ri_expected.append(ri)
|
ri_expected.append(ri)
|
||||||
with mock.patch.object(ip_lib.IPWrapper,
|
with mock.patch.object(ip_lib.IPWrapper,
|
||||||
'get_namespaces') as mock_get_namespaces:
|
'get_namespaces') as mock_get_namespaces:
|
||||||
mock_get_namespaces.return_value = ri.ns_name()
|
mock_get_namespaces.return_value = ri.ns_name
|
||||||
router_info_list = self.api._get_router_info_list_for_tenant(
|
router_info_list = self.api._get_router_info_list_for_tenant(
|
||||||
routers,
|
routers,
|
||||||
ri.router['tenant_id'])
|
ri.router['tenant_id'])
|
||||||
|
@ -96,7 +96,7 @@ class TestBasicRouterOperations(base.BaseTestCase):
|
|||||||
ri = l3_agent.RouterInfo(id, self.conf.root_helper,
|
ri = l3_agent.RouterInfo(id, self.conf.root_helper,
|
||||||
self.conf.use_namespaces, None)
|
self.conf.use_namespaces, None)
|
||||||
|
|
||||||
self.assertTrue(ri.ns_name().endswith(id))
|
self.assertTrue(ri.ns_name.endswith(id))
|
||||||
|
|
||||||
def test_router_info_create_with_router(self):
|
def test_router_info_create_with_router(self):
|
||||||
id = _uuid()
|
id = _uuid()
|
||||||
@ -113,7 +113,7 @@ class TestBasicRouterOperations(base.BaseTestCase):
|
|||||||
'gw_port': ex_gw_port}
|
'gw_port': ex_gw_port}
|
||||||
ri = l3_agent.RouterInfo(id, self.conf.root_helper,
|
ri = l3_agent.RouterInfo(id, self.conf.root_helper,
|
||||||
self.conf.use_namespaces, router)
|
self.conf.use_namespaces, router)
|
||||||
self.assertTrue(ri.ns_name().endswith(id))
|
self.assertTrue(ri.ns_name.endswith(id))
|
||||||
self.assertEqual(ri.router, router)
|
self.assertEqual(ri.router, router)
|
||||||
|
|
||||||
def test_agent_create(self):
|
def test_agent_create(self):
|
||||||
@ -838,7 +838,7 @@ class TestBasicRouterOperations(base.BaseTestCase):
|
|||||||
self.assertEqual(self.mock_driver.unplug.call_count,
|
self.assertEqual(self.mock_driver.unplug.call_count,
|
||||||
len(stale_devnames))
|
len(stale_devnames))
|
||||||
calls = [mock.call(stale_devname,
|
calls = [mock.call(stale_devname,
|
||||||
namespace=ri.ns_name(),
|
namespace=ri.ns_name,
|
||||||
prefix=l3_agent.INTERNAL_DEV_PREFIX)
|
prefix=l3_agent.INTERNAL_DEV_PREFIX)
|
||||||
for stale_devname in stale_devnames]
|
for stale_devname in stale_devnames]
|
||||||
self.mock_driver.unplug.assert_has_calls(calls, any_order=True)
|
self.mock_driver.unplug.assert_has_calls(calls, any_order=True)
|
||||||
@ -867,7 +867,7 @@ class TestBasicRouterOperations(base.BaseTestCase):
|
|||||||
self.mock_driver.unplug.assert_called_with(
|
self.mock_driver.unplug.assert_called_with(
|
||||||
stale_devnames[0],
|
stale_devnames[0],
|
||||||
bridge="br-ex",
|
bridge="br-ex",
|
||||||
namespace=ri.ns_name(),
|
namespace=ri.ns_name,
|
||||||
prefix=l3_agent.EXTERNAL_DEV_PREFIX)
|
prefix=l3_agent.EXTERNAL_DEV_PREFIX)
|
||||||
|
|
||||||
def test_routers_with_admin_state_down(self):
|
def test_routers_with_admin_state_down(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user