Verify metering label exists before applying rule

If the metering-agent receives a label rule before it
has added the label, it will fail to update the iptables
rules as there are no existing chains.

When the action is "create", check if there is an existing
label, and create one and the corresponding iptables chains,
before trying to add the rule.

Closes-Bug: #1617248

Change-Id: Ia0ec1361188cca53023667d249c2b1e10bc22089
(cherry picked from commit 29652e0aff)
This commit is contained in:
Brian Haley 2017-03-24 17:41:17 -04:00 committed by Ihar Hrachyshka
parent 633b452e28
commit 8275f13dd7
2 changed files with 67 additions and 8 deletions

View File

@ -233,24 +233,22 @@ class IptablesMeteringDriver(abstract_driver.MeteringAbstractDriver):
label_chain = iptables_manager.get_chain_name(
WRAP_NAME + LABEL + label_id, wrap=False)
im.ipv4['filter'].add_chain(label_chain, wrap=False)
rules_chain = iptables_manager.get_chain_name(
WRAP_NAME + RULE + label_id, wrap=False)
im.ipv4['filter'].add_chain(rules_chain, wrap=False)
im.ipv4['filter'].add_rule(
TOP_CHAIN, '-j ' + rules_chain, wrap=False)
im.ipv4['filter'].add_rule(
label_chain, '', wrap=False)
exists = rm.metering_labels.get(label_id)
if not exists:
self._create_metering_label_chain(rm,
label_chain,
rules_chain)
rm.metering_labels[label_id] = label
rules = label.get('rules')
if rules:
self._process_metering_label_rules(
rules, label_chain, rules_chain, ext_dev, im)
rm.metering_labels[label_id] = label
def _process_associate_metering_label(self, router):
self._update_router(router)
rm = self.routers.get(router['id'])
@ -319,9 +317,18 @@ class IptablesMeteringDriver(abstract_driver.MeteringAbstractDriver):
def _remove_metering_label_rule(self, router):
self._process_metering_rule_action(router, 'delete')
def _create_metering_label_chain(self, rm, label_chain, rules_chain):
rm.iptables_manager.ipv4['filter'].add_chain(label_chain, wrap=False)
rm.iptables_manager.ipv4['filter'].add_chain(rules_chain, wrap=False)
rm.iptables_manager.ipv4['filter'].add_rule(
TOP_CHAIN, '-j ' + rules_chain, wrap=False)
rm.iptables_manager.ipv4['filter'].add_rule(
label_chain, '', wrap=False)
def _process_metering_rule_action_based_on_ns(
self, router, action, ext_dev, im):
'''Process metering rule actions based specific namespaces.'''
rm = self.routers.get(router['id'])
with IptablesManagerTransaction(im):
labels = router.get(constants.METERING_LABEL_KEY, [])
for label in labels:
@ -331,6 +338,14 @@ class IptablesMeteringDriver(abstract_driver.MeteringAbstractDriver):
rules_chain = iptables_manager.get_chain_name(
WRAP_NAME + RULE + label_id, wrap=False)
exists = rm.metering_labels.get(label_id)
if action == 'create' and not exists:
self._create_metering_label_chain(rm,
label_chain,
rules_chain)
rm.metering_labels[label_id] = label
rule = label.get('rule')
if rule:
if action == 'create':

View File

@ -104,6 +104,22 @@ TEST_ROUTERS_WITH_ONE_RULE = [
'tenant_id': '6c5f5d2a1fa2441e88e35422926f48e8'},
]
TEST_ROUTERS_WITH_NEW_LABEL = [
{'_metering_labels': [
{'id': 'e27fe2df-376e-4ac7-ae13-92f050a21f84',
'rule': {
'direction': 'ingress',
'excluded': False,
'id': '7f1a261f-2489-4ed1-870c-a62754501379',
'metering_label_id': 'e27fe2df-376e-4ac7-ae13-92f050a21f84',
'remote_ip_prefix': '50.0.0.0/24'}}],
'admin_state_up': True,
'gw_port_id': '6d411f48-ecc7-45e0-9ece-3b5bdb54fcee',
'id': '473ec392-1711-44e3-b008-3251ccfc5099',
'name': 'router1',
'status': 'ACTIVE',
'tenant_id': '6c5f5d2a1fa2441e88e35422926f48e8'}]
class IptablesDriverTestCase(base.BaseTestCase):
def setUp(self):
@ -440,7 +456,35 @@ class IptablesDriverTestCase(base.BaseTestCase):
'-d 40.0.0.0/24 -o qg-7d411f48-ec'
' -j neutron-meter-l-eeef45da-c60',
wrap=False, top=False),
]
self.v4filter_inst.assert_has_calls(calls)
def test_add_metering_label_rule_without_label(self):
new_routers_rules = TEST_ROUTERS_WITH_NEW_LABEL
# clear all the metering labels
for r in TEST_ROUTERS:
rm = iptables_driver.RouterWithMetering(self.metering.conf, r)
rm.metering_labels = {}
self.metering.update_routers(None, TEST_ROUTERS)
self.metering.add_metering_label_rule(None, new_routers_rules)
calls = [
mock.call.add_chain('neutron-meter-l-e27fe2df-376',
wrap=False),
mock.call.add_chain('neutron-meter-r-e27fe2df-376',
wrap=False),
mock.call.add_rule('neutron-meter-FORWARD',
'-j neutron-meter-r-e27fe2df-376',
wrap=False),
mock.call.add_rule('neutron-meter-l-e27fe2df-376',
'',
wrap=False),
mock.call.add_rule('neutron-meter-r-e27fe2df-376',
'-s 50.0.0.0/24 '
'-i qg-6d411f48-ec '
'-j neutron-meter-l-e27fe2df-376',
top=False,
wrap=False)
]
self.v4filter_inst.assert_has_calls(calls)