Merge "Add source_ip_prefix and destination_ip_prefix to metering label rules"
This commit is contained in:
commit
bcc530bd26
|
@ -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)
|
||||
|
|
|
@ -1 +1 @@
|
|||
49d8622c5221
|
||||
I38991de2b4
|
||||
|
|
|
@ -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)))
|
|
@ -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"),
|
||||
|
|
|
@ -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
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue