Merge "Add source_ip_prefix and destination_ip_prefix to metering label rules"

This commit is contained in:
Zuul 2020-09-30 12:20:12 +00:00 committed by Gerrit Code Review
commit bcc530bd26
11 changed files with 573 additions and 110 deletions

View File

@ -17,7 +17,9 @@ from neutron_lib import constants
from neutron_lib.db import api as db_api
from neutron_lib.db import utils as db_utils
from neutron_lib.exceptions import metering as metering_exc
from oslo_db import exception as db_exc
from oslo_log import log as logging
from oslo_utils import uuidutils
from neutron.api.rpc.agentnotifiers import metering_rpc_agent_api
@ -27,6 +29,8 @@ from neutron.objects import base as base_obj
from neutron.objects import metering as metering_objs
from neutron.objects import router as l3_obj
LOG = logging.getLogger(__name__)
class MeteringDbMixin(metering.MeteringPluginBase):
@ -84,7 +88,10 @@ class MeteringDbMixin(metering.MeteringPluginBase):
res = {'id': metering_label_rule['id'],
'metering_label_id': metering_label_rule['metering_label_id'],
'direction': metering_label_rule['direction'],
'remote_ip_prefix': metering_label_rule['remote_ip_prefix'],
'remote_ip_prefix': metering_label_rule.get('remote_ip_prefix'),
'source_ip_prefix': metering_label_rule.get('source_ip_prefix'),
'destination_ip_prefix': metering_label_rule.get(
'destination_ip_prefix'),
'excluded': metering_label_rule['excluded']}
return db_utils.resource_fields(res, fields)
@ -109,45 +116,33 @@ class MeteringDbMixin(metering.MeteringPluginBase):
return self._make_metering_label_rule_dict(
self._get_metering_label_rule(context, rule_id), fields)
def _validate_cidr(self, context, label_id, remote_ip_prefix,
direction, excluded):
r_ips = self.get_metering_label_rules(context,
filters={'metering_label_id':
[label_id],
'direction':
[direction],
'excluded':
[excluded]},
fields=['remote_ip_prefix'])
cidrs = [r['remote_ip_prefix'] for r in r_ips]
new_cidr_ipset = netaddr.IPSet([remote_ip_prefix])
if (netaddr.IPSet(cidrs) & new_cidr_ipset):
raise metering_exc.MeteringLabelRuleOverlaps(
remote_ip_prefix=remote_ip_prefix)
def create_metering_label_rule(self, context, metering_label_rule):
m = metering_label_rule['metering_label_rule']
label_id = metering_label_rule['metering_label_id']
try:
with db_api.CONTEXT_WRITER.using(context):
label_id = m['metering_label_id']
ip_prefix = m['remote_ip_prefix']
direction = m['direction']
excluded = m['excluded']
self._validate_cidr(context, label_id, ip_prefix, direction,
excluded)
rule = metering_objs.MeteringLabelRule(
context, id=uuidutils.generate_uuid(),
metering_label_id=label_id, direction=direction,
excluded=m['excluded'],
remote_ip_prefix=netaddr.IPNetwork(ip_prefix))
metering_label_id=label_id,
direction=metering_label_rule['direction'],
excluded=metering_label_rule['excluded'],
)
if metering_label_rule.get('remote_ip_prefix'):
rule.remote_ip_prefix = netaddr.IPNetwork(
metering_label_rule['remote_ip_prefix'])
if metering_label_rule.get('source_ip_prefix'):
rule.source_ip_prefix = netaddr.IPNetwork(
metering_label_rule['source_ip_prefix'])
if metering_label_rule.get('destination_ip_prefix'):
rule.destination_ip_prefix = netaddr.IPNetwork(
metering_label_rule['destination_ip_prefix'])
rule.create()
return self._make_metering_label_rule_dict(rule)
except db_exc.DBReferenceError:
raise metering_exc.MeteringLabelNotFound(label_id=label_id)
return self._make_metering_label_rule_dict(rule)
def delete_metering_label_rule(self, context, rule_id):
with db_api.CONTEXT_WRITER.using(context):
rule = self._get_metering_label_rule(context, rule_id)

View File

@ -1 +1 @@
49d8622c5221
I38991de2b4

View File

@ -0,0 +1,38 @@
# Copyright 2020 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
from alembic import op
import sqlalchemy as sa
"""Add source and destination IP prefixes to neutron metering system
Revision ID: I38991de2b4
Revises: fd6107509ccd
Create Date: 2020-08-20 10:00:00.000000
"""
# revision identifiers, used by Alembic.
revision = 'I38991de2b4'
down_revision = '49d8622c5221'
metering_label_rules_table_name = 'meteringlabelrules'
def upgrade():
op.add_column(metering_label_rules_table_name,
sa.Column('source_ip_prefix', sa.String(64)))
op.add_column(metering_label_rules_table_name,
sa.Column('destination_ip_prefix', sa.String(64)))

View File

@ -23,6 +23,8 @@ class MeteringLabelRule(model_base.BASEV2, model_base.HasId):
direction = sa.Column(sa.Enum('ingress', 'egress',
name='meteringlabels_direction'))
remote_ip_prefix = sa.Column(sa.String(64))
source_ip_prefix = sa.Column(sa.String(64))
destination_ip_prefix = sa.Column(sa.String(64))
metering_label_id = sa.Column(sa.String(36),
sa.ForeignKey("meteringlabels.id",
ondelete="CASCADE"),

View File

@ -0,0 +1,42 @@
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from neutron_lib.api.definitions import metering_source_and_destination_filters
from neutron_lib.api import extensions
class Metering_source_and_destination_fields(
extensions.APIExtensionDescriptor):
api_definition = metering_source_and_destination_filters
@classmethod
def get_extended_resources(cls, version):
sub_resource_map = super(Metering_source_and_destination_fields, cls
).get_extended_resources(version)
processed_sub_resource_map = {}
for value in sub_resource_map.values():
parent_def = value['parent']
collection_name = parent_def['collection_name']
member_name = parent_def['member_name']
if collection_name == member_name:
processed_sub_resource_map[
collection_name] = value['parameters']
else:
processed_sub_resource_map[
collection_name] = {member_name: value['parameters']}
return processed_sub_resource_map

View File

@ -23,7 +23,8 @@ from neutron.objects import base
@base.NeutronObjectRegistry.register
class MeteringLabelRule(base.NeutronDbObject):
# Version 1.0: Initial version
VERSION = '1.0'
# Version 2.0: Source and destination field for the metering label rule
VERSION = '2.0'
db_model = metering_models.MeteringLabelRule
@ -33,6 +34,8 @@ class MeteringLabelRule(base.NeutronDbObject):
'id': common_types.UUIDField(),
'direction': common_types.FlowDirectionEnumField(nullable=True),
'remote_ip_prefix': common_types.IPNetworkField(nullable=True),
'source_ip_prefix': common_types.IPNetworkField(nullable=True),
'destination_ip_prefix': common_types.IPNetworkField(nullable=True),
'metering_label_id': common_types.UUIDField(),
'excluded': obj_fields.BooleanField(default=False),
}
@ -42,19 +45,34 @@ class MeteringLabelRule(base.NeutronDbObject):
@classmethod
def modify_fields_from_db(cls, db_obj):
result = super(MeteringLabelRule, cls).modify_fields_from_db(db_obj)
if 'remote_ip_prefix' in result:
result['remote_ip_prefix'] = net_utils.AuthenticIPNetwork(
result['remote_ip_prefix'])
cls.ip_field_from_db(result, "remote_ip_prefix")
cls.ip_field_from_db(result, "source_ip_prefix")
cls.ip_field_from_db(result, "destination_ip_prefix")
return result
@classmethod
def ip_field_from_db(cls, result, attribute_name):
if attribute_name in result:
result[attribute_name] = net_utils.AuthenticIPNetwork(
result[attribute_name])
@classmethod
def modify_fields_to_db(cls, fields):
result = super(MeteringLabelRule, cls).modify_fields_to_db(fields)
if 'remote_ip_prefix' in result:
result['remote_ip_prefix'] = cls.filter_to_str(
result['remote_ip_prefix'])
cls.ip_field_to_db(result, "remote_ip_prefix")
cls.ip_field_to_db(result, "source_ip_prefix")
cls.ip_field_to_db(result, "destination_ip_prefix")
return result
@classmethod
def ip_field_to_db(cls, result, attribute_name):
if attribute_name in result:
result[attribute_name] = cls.filter_to_str(result[attribute_name])
@base.NeutronObjectRegistry.register
class MeteringLabel(base.NeutronDbObject):

View File

@ -213,6 +213,10 @@ class IptablesMeteringDriver(abstract_driver.MeteringAbstractDriver):
def _add_rule_to_chain(self, ext_dev, rule, im,
label_chain, rules_chain):
ipt_rule = self._prepare_rule(ext_dev, rule, label_chain)
LOG.debug("Adding IPtables rule [%s] for configurations [%s].",
ipt_rule, rule)
if rule['excluded']:
im.ipv4['filter'].add_rule(rules_chain, ipt_rule,
wrap=False, top=True)
@ -223,6 +227,10 @@ class IptablesMeteringDriver(abstract_driver.MeteringAbstractDriver):
def _remove_rule_from_chain(self, ext_dev, rule, im,
label_chain, rules_chain):
ipt_rule = self._prepare_rule(ext_dev, rule, label_chain)
LOG.debug("Removing IPtables rule [%s] for configurations [%s].",
ipt_rule, rule)
if rule['excluded']:
im.ipv4['filter'].remove_rule(rules_chain, ipt_rule,
wrap=False, top=True)
@ -231,16 +239,43 @@ class IptablesMeteringDriver(abstract_driver.MeteringAbstractDriver):
wrap=False, top=False)
def _prepare_rule(self, ext_dev, rule, label_chain):
remote_ip = rule['remote_ip_prefix']
if rule['direction'] == 'egress':
dir_opt = '-s %s -o %s' % (remote_ip, ext_dev)
if rule.get('remote_ip_prefix'):
ipt_rule = IptablesMeteringDriver.\
prepare_source_and_destination_rule_legacy(ext_dev, rule)
else:
dir_opt = '-d %s -i %s' % (remote_ip, ext_dev)
ipt_rule = IptablesMeteringDriver.\
prepare_source_and_destination_rule(ext_dev, rule)
if rule['excluded']:
ipt_rule = '%s -j RETURN' % dir_opt
ipt_rule = '%s -j RETURN' % ipt_rule
else:
ipt_rule = '%s -j %s' % (dir_opt, label_chain)
ipt_rule = '%s -j %s' % (ipt_rule, label_chain)
return ipt_rule
@staticmethod
def prepare_source_and_destination_rule(ext_dev, rule):
if rule['direction'] == 'egress':
iptables_rule = '-o %s' % ext_dev
else:
iptables_rule = '-i %s' % ext_dev
source_ip_prefix = rule.get('source_ip_prefix')
if source_ip_prefix:
iptables_rule = "-s %s %s" % (source_ip_prefix, iptables_rule)
destination_ip_prefix = rule.get('destination_ip_prefix')
if destination_ip_prefix:
iptables_rule = "-d %s %s" % (destination_ip_prefix, iptables_rule)
return iptables_rule
@staticmethod
def prepare_source_and_destination_rule_legacy(ext_dev, rule):
remote_ip = rule['remote_ip_prefix']
if rule['direction'] == 'egress':
ipt_rule = '-s %s -o %s' % (remote_ip, ext_dev)
else:
ipt_rule = '-d %s -i %s' % (remote_ip, ext_dev)
return ipt_rule
def _process_ns_specific_metering_label(self, router, ext_dev, im):

View File

@ -12,8 +12,15 @@
# License for the specific language governing permissions and limitations
# under the License.
import ipaddress
import netaddr
from neutron_lib.agent import topics
from neutron_lib.api.definitions import metering as metering_apidef
from neutron_lib.api.definitions import metering_source_and_destination_filters
from neutron_lib.exceptions import metering as metering_exc
from neutron_lib import exceptions as neutron_exc
from neutron_lib import rpc as n_rpc
from neutron.api.rpc.agentnotifiers import metering_rpc_agent_api
@ -28,7 +35,8 @@ LOG = logging.getLogger(__name__)
class MeteringPlugin(metering_db.MeteringDbMixin):
"""Implementation of the Neutron Metering Service Plugin."""
supported_extension_aliases = [metering_apidef.ALIAS]
supported_extension_aliases = [
metering_apidef.ALIAS, metering_source_and_destination_filters.ALIAS]
path_prefix = "/metering"
__filter_validation_support = True
@ -66,6 +74,10 @@ class MeteringPlugin(metering_db.MeteringDbMixin):
return label
def create_metering_label_rule(self, context, metering_label_rule):
metering_label_rule = metering_label_rule['metering_label_rule']
MeteringPlugin.validate_metering_label_rule(metering_label_rule)
self.check_for_rule_overlaps(context, metering_label_rule)
rule = super(MeteringPlugin, self).create_metering_label_rule(
context, metering_label_rule)
@ -82,6 +94,80 @@ class MeteringPlugin(metering_db.MeteringDbMixin):
return rule
@staticmethod
def validate_metering_label_rule(metering_label_rule):
MeteringPlugin.validate_metering_rule_ip_address(
metering_label_rule, "remote_ip_prefix")
MeteringPlugin.validate_metering_rule_ip_address(
metering_label_rule, "source_ip_prefix")
MeteringPlugin.validate_metering_rule_ip_address(
metering_label_rule, "destination_ip_prefix")
if metering_label_rule.get("remote_ip_prefix"):
if metering_label_rule.get("source_ip_prefix") or \
metering_label_rule.get("destination_ip_prefix"):
raise neutron_exc.Invalid(
"Cannot use 'remote-ip-prefix' in conjunction "
"with 'source-ip-prefix' or 'destination-ip-prefix'.")
none_ip_prefix_informed = not metering_label_rule.get(
'remote_ip_prefix') and not metering_label_rule.get(
'source_ip_prefix') and not metering_label_rule.get(
'destination_ip_prefix')
if none_ip_prefix_informed:
raise neutron_exc.Invalid(
"You must define at least one of the following parameters "
"'remote_ip_prefix', or 'source_ip_prefix' or "
"'destination_ip_prefix'.")
@staticmethod
def validate_metering_rule_ip_address(metering_label_rule,
ip_address_field):
try:
if metering_label_rule.get(ip_address_field):
ipaddress.ip_interface(
metering_label_rule.get(ip_address_field))
except ValueError as exception:
raise neutron_exc.Invalid(
"%s: %s is invalid [%s]." %
(ip_address_field,
metering_label_rule.get(ip_address_field),
exception))
def check_for_rule_overlaps(self, context, metering_label_rule):
label_id = metering_label_rule['metering_label_id']
direction = metering_label_rule['direction']
excluded = metering_label_rule['excluded']
db_metering_rules = self.get_metering_label_rules(
context, filters={
'metering_label_id': [label_id],
'direction': [direction],
'excluded': [excluded]}
)
for db_metering_rule in db_metering_rules:
MeteringPlugin.verify_rule_overlap(
db_metering_rule, metering_label_rule, "remote_ip_prefix")
@staticmethod
def verify_rule_overlap(db_metering_rule, metering_label_rule,
attribute_name):
if db_metering_rule.get(
attribute_name) and metering_label_rule.get(attribute_name):
remote_ip_prefix = metering_label_rule[attribute_name]
cidr = [db_metering_rule.get(attribute_name)]
new_cidr_ipset = netaddr.IPSet([remote_ip_prefix])
if netaddr.IPSet(cidr) & new_cidr_ipset:
LOG.warning("The metering rule [%s] overlaps with"
" previously created rule [%s]. It is not an"
" expected use case, and people should use"
" it wisely.", metering_label_rule,
db_metering_rule)
raise metering_exc.MeteringLabelRuleOverlaps(
remote_ip_prefix=remote_ip_prefix)
def delete_metering_label_rule(self, context, rule_id):
rule = super(MeteringPlugin, self).delete_metering_label_rule(
context, rule_id)

View File

@ -64,13 +64,29 @@ class MeteringPluginDbTestCaseMixin(object):
return self.deserialize(fmt, res)
def _create_metering_label_rule(self, fmt, metering_label_id, direction,
remote_ip_prefix, excluded, **kwargs):
data = {'metering_label_rule':
{'metering_label_id': metering_label_id,
'tenant_id': kwargs.get('tenant_id', 'test-tenant'),
'direction': direction,
'excluded': excluded,
'remote_ip_prefix': remote_ip_prefix}}
excluded, remote_ip_prefix=None,
source_ip_prefix=None,
destination_ip_prefix=None,
**kwargs):
data = {
'metering_label_rule': {
'metering_label_id': metering_label_id,
'tenant_id': kwargs.get('tenant_id', 'test-tenant'),
'direction': direction,
'excluded': excluded,
}
}
if remote_ip_prefix:
data['metering_label_rule']['remote_ip_prefix'] = remote_ip_prefix
if source_ip_prefix:
data['metering_label_rule']['source_ip_prefix'] = source_ip_prefix
if destination_ip_prefix:
data['metering_label_rule']['destination_ip_prefix'] =\
destination_ip_prefix
req = self.new_create_request('metering-label-rules',
data, fmt)
@ -82,10 +98,9 @@ class MeteringPluginDbTestCaseMixin(object):
return req.get_response(self.ext_api)
def _make_metering_label_rule(self, fmt, metering_label_id, direction,
remote_ip_prefix, excluded, **kwargs):
excluded, **kwargs):
res = self._create_metering_label_rule(fmt, metering_label_id,
direction, remote_ip_prefix,
excluded, **kwargs)
direction, excluded, **kwargs)
if res.status_int >= 400:
raise webob.exc.HTTPClientError(code=res.status_int)
return self.deserialize(fmt, res)
@ -101,15 +116,14 @@ class MeteringPluginDbTestCaseMixin(object):
@contextlib.contextmanager
def metering_label_rule(self, metering_label_id=None, direction='ingress',
remote_ip_prefix='10.0.0.0/24',
excluded='false', fmt=None):
excluded=False, fmt=None, **kwargs):
if not fmt:
fmt = self.fmt
metering_label_rule = self._make_metering_label_rule(fmt,
metering_label_id,
direction,
remote_ip_prefix,
excluded)
excluded,
**kwargs)
yield metering_label_rule
@ -212,10 +226,9 @@ class TestMetering(MeteringPluginDbTestCase):
('direction', direction),
('excluded', excluded),
('remote_ip_prefix', remote_ip_prefix)]
with self.metering_label_rule(metering_label_id,
direction,
remote_ip_prefix,
excluded) as label_rule:
with self.metering_label_rule(
metering_label_id, direction, excluded,
remote_ip_prefix=remote_ip_prefix) as label_rule:
for k, v, in keys:
self.assertEqual(label_rule['metering_label_rule'][k], v)
@ -224,11 +237,9 @@ class TestMetering(MeteringPluginDbTestCase):
remote_ip_prefix = '192.168.0.0/24'
excluded = True
res = self._create_metering_label_rule(self.fmt,
_fake_uuid(),
direction,
remote_ip_prefix,
excluded)
res = self._create_metering_label_rule(
self.fmt, _fake_uuid(), direction, excluded,
remote_ip_prefix=remote_ip_prefix)
self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int)
def test_update_metering_label_rule(self):
@ -239,8 +250,8 @@ class TestMetering(MeteringPluginDbTestCase):
data = {'metering_label_rule': {}}
with self.metering_label(name, description) as metering_label, \
self.metering_label_rule(
metering_label['metering_label']['id'],
direction, remote_ip_prefix) as label_rule:
metering_label['metering_label']['id'], direction,
remote_ip_prefix=remote_ip_prefix) as label_rule:
rule_id = label_rule['metering_label_rule']['id']
self._update('metering-label-rules', rule_id, data,
webob.exc.HTTPNotImplemented.code)
@ -256,10 +267,9 @@ class TestMetering(MeteringPluginDbTestCase):
remote_ip_prefix = '192.168.0.0/24'
excluded = True
with self.metering_label_rule(metering_label_id,
direction,
remote_ip_prefix,
excluded) as label_rule:
with self.metering_label_rule(
metering_label_id, direction, excluded,
remote_ip_prefix=remote_ip_prefix) as label_rule:
rule_id = label_rule['metering_label_rule']['id']
self._delete('metering-label-rules', rule_id, 204)
@ -274,14 +284,12 @@ class TestMetering(MeteringPluginDbTestCase):
remote_ip_prefix = '192.168.0.0/24'
excluded = True
with self.metering_label_rule(metering_label_id,
direction,
remote_ip_prefix,
excluded) as v1,\
self.metering_label_rule(metering_label_id,
'ingress',
remote_ip_prefix,
excluded) as v2:
with self.metering_label_rule(
metering_label_id, direction, excluded,
remote_ip_prefix=remote_ip_prefix) as v1,\
self.metering_label_rule(
metering_label_id, 'ingress', excluded,
remote_ip_prefix=remote_ip_prefix) as v2:
metering_label_rule = (v1, v2)
self._test_list_resources('metering-label-rule',
@ -298,14 +306,12 @@ class TestMetering(MeteringPluginDbTestCase):
remote_ip_prefix = '192.168.0.0/24'
excluded = True
with self.metering_label_rule(metering_label_id,
direction,
remote_ip_prefix,
excluded) as v1,\
self.metering_label_rule(metering_label_id,
direction,
n_consts.IPv4_ANY,
False) as v2:
with self.metering_label_rule(
metering_label_id, direction, excluded,
remote_ip_prefix=remote_ip_prefix) as v1,\
self.metering_label_rule(
metering_label_id, direction, False,
remote_ip_prefix=n_consts.IPv4_ANY) as v2:
metering_label_rule = (v1, v2)
self._test_list_resources('metering-label-rule',
@ -323,15 +329,12 @@ class TestMetering(MeteringPluginDbTestCase):
remote_ip_prefix2 = '192.168.0.0/16'
excluded = True
with self.metering_label_rule(metering_label_id,
direction,
remote_ip_prefix1,
excluded):
res = self._create_metering_label_rule(self.fmt,
metering_label_id,
direction,
remote_ip_prefix2,
excluded)
with self.metering_label_rule(
metering_label_id, direction, excluded,
remote_ip_prefix=remote_ip_prefix1):
res = self._create_metering_label_rule(
self.fmt, metering_label_id, direction, excluded,
remote_ip_prefix=remote_ip_prefix2)
self.assertEqual(webob.exc.HTTPConflict.code, res.status_int)
def test_create_metering_label_rule_two_labels(self):
@ -349,14 +352,12 @@ class TestMetering(MeteringPluginDbTestCase):
remote_ip_prefix = '192.168.0.0/24'
excluded = True
with self.metering_label_rule(metering_label_id1,
direction,
remote_ip_prefix,
excluded) as v1,\
self.metering_label_rule(metering_label_id2,
direction,
remote_ip_prefix,
excluded) as v2:
with self.metering_label_rule(
metering_label_id1, direction, excluded,
remote_ip_prefix=remote_ip_prefix) as v1,\
self.metering_label_rule(
metering_label_id2, direction, excluded,
remote_ip_prefix=remote_ip_prefix) as v2:
metering_label_rule = (v1, v2)
self._test_list_resources('metering-label-rule',

View File

@ -59,7 +59,7 @@ object_data = {
'L3HARouterNetwork': '1.0-87acea732853f699580179a94d2baf91',
'L3HARouterVRIdAllocation': '1.0-37502aebdbeadc4f9e3bd5e9da714ab9',
'MeteringLabel': '1.0-cc4b620a3425222447cbe459f62de533',
'MeteringLabelRule': '1.0-b5c5717e7bab8d1af1623156012a5842',
'MeteringLabelRule': '2.0-0ad09894c62e1ce6e868f725158959ba',
'Log': '1.0-6391351c0f34ed34375a19202f361d24',
'Network': '1.1-c3e9ecc0618ee934181d91b143a48901',
'NetworkDhcpAgentBinding': '1.1-d9443c88809ffa4c45a0a5a48134b54a',

View File

@ -22,6 +22,8 @@ from neutron_lib.plugins import constants
from neutron_lib.plugins import directory
from neutron_lib.tests import tools
from neutron_lib.utils import net as net_utils
from oslo_serialization import jsonutils
from oslo_utils import uuidutils
from neutron.api.rpc.agentnotifiers import metering_rpc_agent_api
@ -275,6 +277,8 @@ class TestMeteringPlugin(test_db_base_plugin_v2.NeutronDbPluginV2TestCase,
'remote_ip_prefix':
net_utils.AuthenticIPNetwork(
'10.0.0.0/24'),
'destination_ip_prefix': None,
'source_ip_prefix': None,
'direction': 'ingress',
'metering_label_id': self.uuid,
'excluded': False,
@ -293,6 +297,8 @@ class TestMeteringPlugin(test_db_base_plugin_v2.NeutronDbPluginV2TestCase,
'remote_ip_prefix':
net_utils.AuthenticIPNetwork(
'10.0.0.0/24'),
'destination_ip_prefix': None,
'source_ip_prefix': None,
'direction': 'ingress',
'metering_label_id': self.uuid,
'excluded': False,
@ -300,18 +306,258 @@ class TestMeteringPlugin(test_db_base_plugin_v2.NeutronDbPluginV2TestCase,
'id': self.uuid}],
'id': self.uuid}]
remote_ip_prefix = {'remote_ip_prefix': '10.0.0.0/24'}
with self.router(tenant_id=self.tenant_id, set_context=True):
with self.metering_label(tenant_id=self.tenant_id,
set_context=True) as label:
la = label['metering_label']
self.mock_uuid.return_value = second_uuid
with self.metering_label_rule(la['id']):
with self.metering_label_rule(la['id'], **remote_ip_prefix):
self.mock_add_rule.assert_called_with(self.ctx,
expected_add)
self._delete('metering-label-rules', second_uuid)
self.mock_remove_rule.assert_called_with(self.ctx,
expected_del)
def test_add_and_remove_metering_label_rule_source_ip_only(self):
second_uuid = 'e27fe2df-376e-4ac7-ae13-92f050a21f84'
expected_add = [{'status': 'ACTIVE',
'name': 'router1',
'gw_port_id': None,
'admin_state_up': True,
'distributed': False,
'tenant_id': self.tenant_id,
'_metering_labels': [
{'rule': {
'source_ip_prefix':
net_utils.AuthenticIPNetwork(
'10.0.0.0/24'),
'destination_ip_prefix': None,
'remote_ip_prefix': None,
'direction': 'ingress',
'metering_label_id': self.uuid,
'excluded': False,
'id': second_uuid},
'id': self.uuid}],
'id': self.uuid}]
expected_del = [{'status': 'ACTIVE',
'name': 'router1',
'gw_port_id': None,
'admin_state_up': True,
'distributed': False,
'tenant_id': self.tenant_id,
'_metering_labels': [
{'rule': {
'source_ip_prefix':
net_utils.AuthenticIPNetwork(
'10.0.0.0/24'),
'destination_ip_prefix': None,
'remote_ip_prefix': None,
'direction': 'ingress',
'metering_label_id': self.uuid,
'excluded': False,
'id': second_uuid},
'id': self.uuid}],
'id': self.uuid}]
source_ip_prefix = {'source_ip_prefix': '10.0.0.0/24'}
with self.router(tenant_id=self.tenant_id, set_context=True):
with self.metering_label(tenant_id=self.tenant_id,
set_context=True) as label:
la = label['metering_label']
self.mock_uuid.return_value = second_uuid
with self.metering_label_rule(la['id'],
**source_ip_prefix):
self.mock_add_rule.assert_called_with(self.ctx,
expected_add)
self._delete('metering-label-rules', second_uuid)
self.mock_remove_rule.assert_called_with(self.ctx,
expected_del)
def test_add_and_remove_metering_label_rule_dest_ip_only(self):
second_uuid = 'e27fe2df-376e-4ac7-ae13-92f050a21f84'
expected_add = [{'status': 'ACTIVE',
'name': 'router1',
'gw_port_id': None,
'admin_state_up': True,
'distributed': False,
'tenant_id': self.tenant_id,
'_metering_labels': [
{'rule': {
'destination_ip_prefix':
net_utils.AuthenticIPNetwork(
'10.0.0.0/24'),
'source_ip_prefix': None,
'remote_ip_prefix': None,
'direction': 'ingress',
'metering_label_id': self.uuid,
'excluded': False,
'id': second_uuid},
'id': self.uuid}],
'id': self.uuid}]
expected_del = [{'status': 'ACTIVE',
'name': 'router1',
'gw_port_id': None,
'admin_state_up': True,
'distributed': False,
'tenant_id': self.tenant_id,
'_metering_labels': [
{'rule': {
'destination_ip_prefix':
net_utils.AuthenticIPNetwork(
'10.0.0.0/24'),
'source_ip_prefix': None,
'remote_ip_prefix': None,
'direction': 'ingress',
'metering_label_id': self.uuid,
'excluded': False,
'id': second_uuid},
'id': self.uuid}],
'id': self.uuid}]
source_ip_prefix = {'destination_ip_prefix': '10.0.0.0/24'}
with self.router(tenant_id=self.tenant_id, set_context=True):
with self.metering_label(tenant_id=self.tenant_id,
set_context=True) as label:
la = label['metering_label']
self.mock_uuid.return_value = second_uuid
with self.metering_label_rule(la['id'],
**source_ip_prefix):
self.mock_add_rule.assert_called_with(self.ctx,
expected_add)
self._delete('metering-label-rules', second_uuid)
self.mock_remove_rule.assert_called_with(self.ctx,
expected_del)
def test_add_and_remove_metering_label_rule_src_and_dest_ip_only(self):
second_uuid = 'e27fe2df-376e-4ac7-ae13-92f050a21f84'
expected_add = [{'status': 'ACTIVE',
'name': 'router1',
'gw_port_id': None,
'admin_state_up': True,
'distributed': False,
'tenant_id': self.tenant_id,
'_metering_labels': [
{'rule': {
'destination_ip_prefix':
net_utils.AuthenticIPNetwork('0.0.0.0/0'),
'source_ip_prefix':
net_utils.AuthenticIPNetwork(
'10.0.0.0/24'),
'remote_ip_prefix': None,
'direction': 'ingress',
'metering_label_id': self.uuid,
'excluded': False,
'id': second_uuid},
'id': self.uuid}],
'id': self.uuid}]
expected_del = [{'status': 'ACTIVE',
'name': 'router1',
'gw_port_id': None,
'admin_state_up': True,
'distributed': False,
'tenant_id': self.tenant_id,
'_metering_labels': [
{'rule': {
'destination_ip_prefix':
net_utils.AuthenticIPNetwork('0.0.0.0/0'),
'source_ip_prefix':
net_utils.AuthenticIPNetwork(
'10.0.0.0/24'),
'remote_ip_prefix': None,
'direction': 'ingress',
'metering_label_id': self.uuid,
'excluded': False,
'id': second_uuid},
'id': self.uuid}],
'id': self.uuid}]
ip_prefixes = {'source_ip_prefix': '10.0.0.0/24',
'destination_ip_prefix': '00.0.0.0/0'}
with self.router(tenant_id=self.tenant_id, set_context=True):
with self.metering_label(tenant_id=self.tenant_id,
set_context=True) as label:
la = label['metering_label']
self.mock_uuid.return_value = second_uuid
with self.metering_label_rule(la['id'],
**ip_prefixes):
self.mock_add_rule.assert_called_with(self.ctx,
expected_add)
self._delete('metering-label-rules', second_uuid)
self.mock_remove_rule.assert_called_with(self.ctx,
expected_del)
def test_add_and_remove_metering_label_rule_src_and_remote_ip(self):
with self.router(tenant_id=self.tenant_id, set_context=True):
with self.metering_label(tenant_id=self.tenant_id,
set_context=True) as label:
la = label['metering_label']
res = self._create_metering_label_rule(
self.fmt, la['id'], 'ingress', False,
remote_ip_prefix='0.0.0.0/0',
source_ip_prefix='10.0.0.0/24')
expected_error_code = 500
self.assertEqual(expected_error_code, res.status_int)
expected_error_message = "Cannot use 'remote-ip-prefix' in " \
"conjunction with " \
"'source-ip-prefix' or " \
"'destination-ip-prefix'."
self.assertEqual(
expected_error_message, jsonutils.loads(res.body)[
"NeutronError"]["message"])
def test_add_and_remove_metering_label_rule_dest_and_remote_ip(self):
with self.router(tenant_id=self.tenant_id, set_context=True):
with self.metering_label(tenant_id=self.tenant_id,
set_context=True) as label:
la = label['metering_label']
res = self._create_metering_label_rule(
self.fmt, la['id'], 'ingress', False,
remote_ip_prefix='0.0.0.0/0',
destination_ip_prefix='8.8.8.8/32')
expected_error_code = 500
self.assertEqual(expected_error_code, res.status_int)
expected_error_message = "Cannot use 'remote-ip-prefix' in " \
"conjunction with " \
"'source-ip-prefix' or " \
"'destination-ip-prefix'."
self.assertEqual(
expected_error_message, jsonutils.loads(res.body)[
"NeutronError"]["message"])
def test_add_and_remove_metering_label_rule_no_ip_prefix_entered(self):
with self.router(tenant_id=self.tenant_id, set_context=True):
with self.metering_label(tenant_id=self.tenant_id,
set_context=True) as label:
la = label['metering_label']
res = self._create_metering_label_rule(
self.fmt, la['id'], 'ingress', False)
expected_error_code = 500
self.assertEqual(expected_error_code, res.status_int)
expected_error_message = "You must define at least one of " \
"the following parameters " \
"'remote_ip_prefix', or " \
"'source_ip_prefix' or " \
"'destination_ip_prefix'."
self.assertEqual(
expected_error_message, jsonutils.loads(res.body)[
"NeutronError"]["message"])
def test_delete_metering_label_does_not_clear_router_tenant_id(self):
tenant_id = '654f6b9d-0f36-4ae5-bd1b-01616794ca60'
with self.metering_label(tenant_id=tenant_id) as metering_label: