Merge "Add support for FWaaS v2 logging"
This commit is contained in:
commit
4e8b989994
26
config.yaml
26
config.yaml
@ -308,3 +308,29 @@ options:
|
|||||||
for Neutron agents (DHCP and L3 agents). This option overrides the
|
for Neutron agents (DHCP and L3 agents). This option overrides the
|
||||||
default-availability-zone charm config setting only when the Juju
|
default-availability-zone charm config setting only when the Juju
|
||||||
provider sets JUJU_AVAILABILITY_ZONE.
|
provider sets JUJU_AVAILABILITY_ZONE.
|
||||||
|
firewall-group-log-output-base:
|
||||||
|
type: string
|
||||||
|
default:
|
||||||
|
description: |
|
||||||
|
This option allows setting a path for Firewall Group logs.
|
||||||
|
A valid file system path must be provided. If this option is not
|
||||||
|
provided Neutron will use syslog as a destination.
|
||||||
|
(Available from Stein)
|
||||||
|
firewall-group-log-rate-limit:
|
||||||
|
type: int
|
||||||
|
default:
|
||||||
|
description: |
|
||||||
|
Log entries are queued for writing to a log file when a packet rate
|
||||||
|
exceeds the limit set by this option.
|
||||||
|
Possible values: null (no rate limitation), integer values greater than 100.
|
||||||
|
WARNING: Should be NOT LESS than 100, if set (if null logging will not be
|
||||||
|
rate limited).
|
||||||
|
(Available from Stein)
|
||||||
|
firewall-group-log-burst-limit:
|
||||||
|
type: int
|
||||||
|
default: 25
|
||||||
|
description: |
|
||||||
|
This option sets the maximum queue size for log entries.
|
||||||
|
Can be used to avoid excessive memory consumption.
|
||||||
|
WARNING: Should be NOT LESS than 25.
|
||||||
|
(Available from Stein)
|
||||||
|
@ -217,19 +217,35 @@ def full_restart():
|
|||||||
service('force-reload-kmod', 'openvswitch-switch')
|
service('force-reload-kmod', 'openvswitch-switch')
|
||||||
|
|
||||||
|
|
||||||
def enable_ipfix(bridge, target):
|
def enable_ipfix(bridge, target,
|
||||||
'''Enable IPfix on bridge to target.
|
cache_active_timeout=60,
|
||||||
|
cache_max_flows=128,
|
||||||
|
sampling=64):
|
||||||
|
'''Enable IPFIX on bridge to target.
|
||||||
:param bridge: Bridge to monitor
|
:param bridge: Bridge to monitor
|
||||||
:param target: IPfix remote endpoint
|
:param target: IPFIX remote endpoint
|
||||||
|
:param cache_active_timeout: The maximum period in seconds for
|
||||||
|
which an IPFIX flow record is cached
|
||||||
|
and aggregated before being sent
|
||||||
|
:param cache_max_flows: The maximum number of IPFIX flow records
|
||||||
|
that can be cached at a time
|
||||||
|
:param sampling: The rate at which packets should be sampled and
|
||||||
|
sent to each target collector
|
||||||
'''
|
'''
|
||||||
cmd = ['ovs-vsctl', 'set', 'Bridge', bridge, 'ipfix=@i', '--',
|
cmd = [
|
||||||
'--id=@i', 'create', 'IPFIX', 'targets="{}"'.format(target)]
|
'ovs-vsctl', 'set', 'Bridge', bridge, 'ipfix=@i', '--',
|
||||||
|
'--id=@i', 'create', 'IPFIX',
|
||||||
|
'targets="{}"'.format(target),
|
||||||
|
'sampling={}'.format(sampling),
|
||||||
|
'cache_active_timeout={}'.format(cache_active_timeout),
|
||||||
|
'cache_max_flows={}'.format(cache_max_flows),
|
||||||
|
]
|
||||||
log('Enabling IPfix on {}.'.format(bridge))
|
log('Enabling IPfix on {}.'.format(bridge))
|
||||||
subprocess.check_call(cmd)
|
subprocess.check_call(cmd)
|
||||||
|
|
||||||
|
|
||||||
def disable_ipfix(bridge):
|
def disable_ipfix(bridge):
|
||||||
'''Diable IPfix on target bridge.
|
'''Diable IPFIX on target bridge.
|
||||||
:param bridge: Bridge to modify
|
:param bridge: Bridge to modify
|
||||||
'''
|
'''
|
||||||
cmd = ['ovs-vsctl', 'clear', 'Bridge', bridge, 'ipfix']
|
cmd = ['ovs-vsctl', 'clear', 'Bridge', bridge, 'ipfix']
|
||||||
|
@ -126,7 +126,11 @@ def _config_ini(path):
|
|||||||
:returns: Configuration contained in path
|
:returns: Configuration contained in path
|
||||||
:rtype: Dict
|
:rtype: Dict
|
||||||
"""
|
"""
|
||||||
conf = configparser.ConfigParser()
|
# When strict is enabled, duplicate options are not allowed in the
|
||||||
|
# parsed INI; however, Oslo allows duplicate values. This change
|
||||||
|
# causes us to ignore the duplicate values which is acceptable as
|
||||||
|
# long as we don't validate any multi-value options
|
||||||
|
conf = configparser.ConfigParser(strict=False)
|
||||||
conf.read(path)
|
conf.read(path)
|
||||||
return dict(conf)
|
return dict(conf)
|
||||||
|
|
||||||
@ -204,7 +208,7 @@ def validate_file_ownership(config):
|
|||||||
"Invalid ownership configuration: {}".format(key))
|
"Invalid ownership configuration: {}".format(key))
|
||||||
owner = options.get('owner', config.get('owner', 'root'))
|
owner = options.get('owner', config.get('owner', 'root'))
|
||||||
group = options.get('group', config.get('group', 'root'))
|
group = options.get('group', config.get('group', 'root'))
|
||||||
optional = options.get('optional', config.get('optional', 'False'))
|
optional = options.get('optional', config.get('optional', False))
|
||||||
if '*' in file_name:
|
if '*' in file_name:
|
||||||
for file in glob.glob(file_name):
|
for file in glob.glob(file_name):
|
||||||
if file not in files.keys():
|
if file not in files.keys():
|
||||||
@ -226,7 +230,7 @@ def validate_file_permissions(config):
|
|||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"Invalid ownership configuration: {}".format(key))
|
"Invalid ownership configuration: {}".format(key))
|
||||||
mode = options.get('mode', config.get('permissions', '600'))
|
mode = options.get('mode', config.get('permissions', '600'))
|
||||||
optional = options.get('optional', config.get('optional', 'False'))
|
optional = options.get('optional', config.get('optional', False))
|
||||||
if '*' in file_name:
|
if '*' in file_name:
|
||||||
for file in glob.glob(file_name):
|
for file in glob.glob(file_name):
|
||||||
if file not in files.keys():
|
if file not in files.keys():
|
||||||
|
@ -106,9 +106,11 @@ class CertRequest(object):
|
|||||||
sans = sorted(list(set(entry['addresses'])))
|
sans = sorted(list(set(entry['addresses'])))
|
||||||
request[entry['cn']] = {'sans': sans}
|
request[entry['cn']] = {'sans': sans}
|
||||||
if self.json_encode:
|
if self.json_encode:
|
||||||
return {'cert_requests': json.dumps(request, sort_keys=True)}
|
req = {'cert_requests': json.dumps(request, sort_keys=True)}
|
||||||
else:
|
else:
|
||||||
return {'cert_requests': request}
|
req = {'cert_requests': request}
|
||||||
|
req['unit_name'] = local_unit().replace('/', '_')
|
||||||
|
return req
|
||||||
|
|
||||||
|
|
||||||
def get_certificate_request(json_encode=True):
|
def get_certificate_request(json_encode=True):
|
||||||
|
@ -1710,6 +1710,10 @@ class NeutronAPIContext(OSContextGenerator):
|
|||||||
'rel_key': 'enable-nsg-logging',
|
'rel_key': 'enable-nsg-logging',
|
||||||
'default': False,
|
'default': False,
|
||||||
},
|
},
|
||||||
|
'enable_nfg_logging': {
|
||||||
|
'rel_key': 'enable-nfg-logging',
|
||||||
|
'default': False,
|
||||||
|
},
|
||||||
'global_physnet_mtu': {
|
'global_physnet_mtu': {
|
||||||
'rel_key': 'global-physnet-mtu',
|
'rel_key': 'global-physnet-mtu',
|
||||||
'default': 1500,
|
'default': 1500,
|
||||||
|
@ -110,17 +110,19 @@ def is_device_mounted(device):
|
|||||||
return bool(re.search(r'MOUNTPOINT=".+"', out))
|
return bool(re.search(r'MOUNTPOINT=".+"', out))
|
||||||
|
|
||||||
|
|
||||||
def mkfs_xfs(device, force=False):
|
def mkfs_xfs(device, force=False, inode_size=1024):
|
||||||
"""Format device with XFS filesystem.
|
"""Format device with XFS filesystem.
|
||||||
|
|
||||||
By default this should fail if the device already has a filesystem on it.
|
By default this should fail if the device already has a filesystem on it.
|
||||||
:param device: Full path to device to format
|
:param device: Full path to device to format
|
||||||
:ptype device: tr
|
:ptype device: tr
|
||||||
:param force: Force operation
|
:param force: Force operation
|
||||||
:ptype: force: boolean"""
|
:ptype: force: boolean
|
||||||
|
:param inode_size: XFS inode size in bytes
|
||||||
|
:ptype inode_size: int"""
|
||||||
cmd = ['mkfs.xfs']
|
cmd = ['mkfs.xfs']
|
||||||
if force:
|
if force:
|
||||||
cmd.append("-f")
|
cmd.append("-f")
|
||||||
|
|
||||||
cmd += ['-i', 'size=1024', device]
|
cmd += ['-i', "size={}".format(inode_size), device]
|
||||||
check_call(cmd)
|
check_call(cmd)
|
||||||
|
@ -8,6 +8,7 @@ from charmhelpers.core.hookenv import (
|
|||||||
related_units,
|
related_units,
|
||||||
unit_get,
|
unit_get,
|
||||||
network_get_primary_address,
|
network_get_primary_address,
|
||||||
|
log,
|
||||||
)
|
)
|
||||||
from charmhelpers.contrib.openstack.context import (
|
from charmhelpers.contrib.openstack.context import (
|
||||||
OSContextGenerator,
|
OSContextGenerator,
|
||||||
@ -49,6 +50,9 @@ CORE_PLUGIN = {
|
|||||||
OVS_ODL: NEUTRON_OVS_ODL_PLUGIN,
|
OVS_ODL: NEUTRON_OVS_ODL_PLUGIN,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NFG_LOG_RATE_LIMIT_MIN = 100
|
||||||
|
NFG_LOG_BURST_LIMIT_MIN = 25
|
||||||
|
|
||||||
|
|
||||||
def _get_availability_zone():
|
def _get_availability_zone():
|
||||||
from neutron_utils import get_availability_zone as get_az
|
from neutron_utils import get_availability_zone as get_az
|
||||||
@ -107,6 +111,33 @@ class L3AgentContext(OSContextGenerator):
|
|||||||
return ctxt
|
return ctxt
|
||||||
|
|
||||||
|
|
||||||
|
def validate_nfg_log_path(desired_nfg_log_path):
|
||||||
|
if not desired_nfg_log_path:
|
||||||
|
# None means "we need to use syslog" - no need
|
||||||
|
# to check anything on filesystem
|
||||||
|
return None
|
||||||
|
|
||||||
|
dst_dir, _ = os.path.split(desired_nfg_log_path)
|
||||||
|
path_exists = os.path.exists(dst_dir)
|
||||||
|
if not path_exists:
|
||||||
|
log(
|
||||||
|
"Desired NFG log directory {} not exists! "
|
||||||
|
"falling back to syslog".format(dst_dir),
|
||||||
|
"WARN"
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
if path_exists and os.path.isdir(desired_nfg_log_path):
|
||||||
|
log(
|
||||||
|
"Desired NFG log path {} should be file, not directory! "
|
||||||
|
"falling back to syslog".format(desired_nfg_log_path),
|
||||||
|
"WARN"
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
return desired_nfg_log_path
|
||||||
|
|
||||||
|
|
||||||
class NeutronGatewayContext(NeutronAPIContext):
|
class NeutronGatewayContext(NeutronAPIContext):
|
||||||
|
|
||||||
def __call__(self):
|
def __call__(self):
|
||||||
@ -131,6 +162,7 @@ class NeutronGatewayContext(NeutronAPIContext):
|
|||||||
'enable_metadata_network': config('enable-metadata-network'),
|
'enable_metadata_network': config('enable-metadata-network'),
|
||||||
'enable_isolated_metadata': config('enable-isolated-metadata'),
|
'enable_isolated_metadata': config('enable-isolated-metadata'),
|
||||||
'availability_zone': _get_availability_zone(),
|
'availability_zone': _get_availability_zone(),
|
||||||
|
'enable_nfg_logging': api_settings['enable_nfg_logging'],
|
||||||
}
|
}
|
||||||
|
|
||||||
ctxt['local_ip'] = get_local_ip()
|
ctxt['local_ip'] = get_local_ip()
|
||||||
@ -161,6 +193,26 @@ class NeutronGatewayContext(NeutronAPIContext):
|
|||||||
ctxt['enable_metadata_network'] = True
|
ctxt['enable_metadata_network'] = True
|
||||||
ctxt['enable_isolated_metadata'] = True
|
ctxt['enable_isolated_metadata'] = True
|
||||||
|
|
||||||
|
ctxt['nfg_log_output_base'] = validate_nfg_log_path(
|
||||||
|
config('firewall-group-log-output-base')
|
||||||
|
)
|
||||||
|
ctxt['nfg_log_rate_limit'] = config(
|
||||||
|
'firewall-group-log-rate-limit'
|
||||||
|
)
|
||||||
|
if ctxt['nfg_log_rate_limit'] is not None:
|
||||||
|
ctxt['nfg_log_rate_limit'] = max(
|
||||||
|
ctxt['nfg_log_rate_limit'],
|
||||||
|
NFG_LOG_RATE_LIMIT_MIN
|
||||||
|
)
|
||||||
|
ctxt['nfg_log_burst_limit'] = config(
|
||||||
|
'firewall-group-log-burst-limit'
|
||||||
|
)
|
||||||
|
if ctxt['nfg_log_burst_limit'] is not None:
|
||||||
|
ctxt['nfg_log_burst_limit'] = max(
|
||||||
|
ctxt['nfg_log_burst_limit'],
|
||||||
|
NFG_LOG_BURST_LIMIT_MIN
|
||||||
|
)
|
||||||
|
|
||||||
return ctxt
|
return ctxt
|
||||||
|
|
||||||
|
|
||||||
|
@ -155,6 +155,7 @@ GATEWAY_PKGS = {
|
|||||||
"nova-api-metadata",
|
"nova-api-metadata",
|
||||||
"neutron-metering-agent",
|
"neutron-metering-agent",
|
||||||
"neutron-lbaas-agent",
|
"neutron-lbaas-agent",
|
||||||
|
"libnetfilter-log1", # fwaas_v2_log
|
||||||
],
|
],
|
||||||
NSX: [
|
NSX: [
|
||||||
"neutron-dhcp-agent",
|
"neutron-dhcp-agent",
|
||||||
@ -197,6 +198,7 @@ PY3_PACKAGES = [
|
|||||||
'python3-neutron',
|
'python3-neutron',
|
||||||
'python3-neutron-fwaas',
|
'python3-neutron-fwaas',
|
||||||
'python3-neutron-lbaas',
|
'python3-neutron-lbaas',
|
||||||
|
'python3-zmq', # fwaas_v2_log
|
||||||
]
|
]
|
||||||
|
|
||||||
EARLY_PACKAGES = {
|
EARLY_PACKAGES = {
|
||||||
|
@ -30,4 +30,16 @@ ha_vrrp_health_check_interval = 30
|
|||||||
{% endif -%}
|
{% endif -%}
|
||||||
|
|
||||||
[AGENT]
|
[AGENT]
|
||||||
|
{% if enable_nfg_logging -%}
|
||||||
|
extensions = fwaas_v2,fwaas_v2_log
|
||||||
|
[network_log]
|
||||||
|
{% if nfg_log_rate_limit -%}
|
||||||
|
rate_limit = {{ nfg_log_rate_limit }}
|
||||||
|
{% endif -%}
|
||||||
|
burst_limit = {{ nfg_log_burst_limit }}
|
||||||
|
{% if nfg_log_output_base -%}
|
||||||
|
local_output_log_base = {{ nfg_log_output_base }}
|
||||||
|
{% endif -%}
|
||||||
|
{% else %}
|
||||||
extensions = fwaas_v2
|
extensions = fwaas_v2
|
||||||
|
{% endif -%}
|
@ -156,6 +156,7 @@ class TestNeutronGatewayContext(CharmTestCase):
|
|||||||
self.config.side_effect = self.test_config.get
|
self.config.side_effect = self.test_config.get
|
||||||
self.maxDiff = None
|
self.maxDiff = None
|
||||||
|
|
||||||
|
@patch.object(neutron_contexts, 'validate_nfg_log_path', lambda x: x)
|
||||||
@patch('neutron_utils.config')
|
@patch('neutron_utils.config')
|
||||||
@patch('charmhelpers.contrib.openstack.context.relation_get')
|
@patch('charmhelpers.contrib.openstack.context.relation_get')
|
||||||
@patch('charmhelpers.contrib.openstack.context.related_units')
|
@patch('charmhelpers.contrib.openstack.context.related_units')
|
||||||
@ -168,7 +169,8 @@ class TestNeutronGatewayContext(CharmTestCase):
|
|||||||
'enable-l3ha': 'True',
|
'enable-l3ha': 'True',
|
||||||
'enable-qos': 'True',
|
'enable-qos': 'True',
|
||||||
'network-device-mtu': 9000,
|
'network-device-mtu': 9000,
|
||||||
'dns-domain': 'openstack.example.'}
|
'dns-domain': 'openstack.example.',
|
||||||
|
'enable-nfg-logging': 'True'}
|
||||||
self.test_config.set('plugin', 'ovs')
|
self.test_config.set('plugin', 'ovs')
|
||||||
self.test_config.set('debug', False)
|
self.test_config.set('debug', False)
|
||||||
self.test_config.set('verbose', True)
|
self.test_config.set('verbose', True)
|
||||||
@ -179,6 +181,10 @@ class TestNeutronGatewayContext(CharmTestCase):
|
|||||||
self.test_config.set('vlan-ranges',
|
self.test_config.set('vlan-ranges',
|
||||||
'physnet1:1000:2000 physnet2:2001:3000')
|
'physnet1:1000:2000 physnet2:2001:3000')
|
||||||
self.test_config.set('flat-network-providers', 'physnet3 physnet4')
|
self.test_config.set('flat-network-providers', 'physnet3 physnet4')
|
||||||
|
self.test_config.set('firewall-group-log-output-base',
|
||||||
|
'/var/log/firewall-logs')
|
||||||
|
self.test_config.set('firewall-group-log-rate-limit', 100)
|
||||||
|
self.test_config.set('firewall-group-log-burst-limit', 50)
|
||||||
|
|
||||||
def config_side_effect(key):
|
def config_side_effect(key):
|
||||||
return {
|
return {
|
||||||
@ -224,8 +230,13 @@ class TestNeutronGatewayContext(CharmTestCase):
|
|||||||
'dhcp-match': 'set:ipxe,175'
|
'dhcp-match': 'set:ipxe,175'
|
||||||
},
|
},
|
||||||
'availability_zone': 'nova',
|
'availability_zone': 'nova',
|
||||||
|
'enable_nfg_logging': True,
|
||||||
|
'nfg_log_burst_limit': 50,
|
||||||
|
'nfg_log_output_base': '/var/log/firewall-logs',
|
||||||
|
'nfg_log_rate_limit': 100,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@patch.object(neutron_contexts, 'validate_nfg_log_path', lambda x: x)
|
||||||
@patch('neutron_utils.config')
|
@patch('neutron_utils.config')
|
||||||
@patch('charmhelpers.contrib.openstack.context.relation_get')
|
@patch('charmhelpers.contrib.openstack.context.relation_get')
|
||||||
@patch('charmhelpers.contrib.openstack.context.related_units')
|
@patch('charmhelpers.contrib.openstack.context.related_units')
|
||||||
@ -294,6 +305,10 @@ class TestNeutronGatewayContext(CharmTestCase):
|
|||||||
'dhcp-match': 'set:ipxe,175'
|
'dhcp-match': 'set:ipxe,175'
|
||||||
},
|
},
|
||||||
'availability_zone': 'nova',
|
'availability_zone': 'nova',
|
||||||
|
'enable_nfg_logging': False,
|
||||||
|
'nfg_log_burst_limit': 25,
|
||||||
|
'nfg_log_output_base': None,
|
||||||
|
'nfg_log_rate_limit': None,
|
||||||
})
|
})
|
||||||
|
|
||||||
@patch('charmhelpers.contrib.openstack.context.relation_get')
|
@patch('charmhelpers.contrib.openstack.context.relation_get')
|
||||||
@ -401,6 +416,19 @@ class TestNeutronGatewayContext(CharmTestCase):
|
|||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'az1', context()['availability_zone'])
|
'az1', context()['availability_zone'])
|
||||||
|
|
||||||
|
@patch('charmhelpers.contrib.openstack.context.relation_get')
|
||||||
|
@patch('charmhelpers.contrib.openstack.context.related_units')
|
||||||
|
@patch('charmhelpers.contrib.openstack.context.relation_ids')
|
||||||
|
@patch.object(neutron_contexts, 'get_shared_secret')
|
||||||
|
def test_nfg_min_settings(self, _secret, _rids, _runits, _rget):
|
||||||
|
self.test_config.set('firewall-group-log-rate-limit', 90)
|
||||||
|
self.test_config.set('firewall-group-log-burst-limit', 20)
|
||||||
|
self.network_get_primary_address.return_value = '192.168.20.2'
|
||||||
|
self.unit_get.return_value = '10.5.0.1'
|
||||||
|
ctxt = neutron_contexts.NeutronGatewayContext()()
|
||||||
|
self.assertEqual(ctxt['nfg_log_burst_limit'], 25)
|
||||||
|
self.assertEqual(ctxt['nfg_log_rate_limit'], 100)
|
||||||
|
|
||||||
|
|
||||||
class TestSharedSecret(CharmTestCase):
|
class TestSharedSecret(CharmTestCase):
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user