From 9b0de9bbff162316f39f955c12d211345860972a Mon Sep 17 00:00:00 2001 From: James Page Date: Mon, 10 Jun 2019 12:15:40 +0100 Subject: [PATCH] Add support for FWaaS v2 logging Enable support for configuration of FWaaS v2 firewall group logging. Configuration options mirror those for neutron-openvswitch for security group logging. This feature is currently only enabled for FWaaS v2 at Stein for the charms (but is supported back to Queens in Neutron). Change-Id: Ic60ee47078089c59ccb09b8659422e7ad7081149 Partial-Bug: 1831972 --- config.yaml | 26 +++++ .../contrib/openstack/amulet/deployment.py | 3 + .../contrib/openstack/amulet/utils.py | 14 ++- .../charmhelpers/contrib/openstack/context.py | 107 ++++++++++++++++++ .../charmhelpers/contrib/openstack/neutron.py | 5 + .../templates/section-oslo-notifications | 3 + .../openstack/templates/vendor_data.json | 1 + hooks/charmhelpers/contrib/openstack/utils.py | 14 +++ hooks/charmhelpers/contrib/python/__init__.py | 0 .../contrib/storage/linux/ceph.py | 4 +- hooks/charmhelpers/fetch/ubuntu.py | 16 ++- hooks/neutron_ovs_context.py | 54 +++++++++ hooks/neutron_ovs_utils.py | 6 +- templates/newton/fwaas_driver.ini | 9 ++ templates/stein/fwaas_driver.ini | 9 ++ templates/stein/l3_agent.ini | 29 +++++ unit_tests/test_neutron_ovs_context.py | 94 ++++++++++++++- unit_tests/test_neutron_ovs_hooks.py | 1 + unit_tests/test_neutron_ovs_utils.py | 6 + 19 files changed, 385 insertions(+), 16 deletions(-) create mode 100644 hooks/charmhelpers/contrib/openstack/templates/vendor_data.json create mode 100644 hooks/charmhelpers/contrib/python/__init__.py create mode 100644 templates/newton/fwaas_driver.ini create mode 100644 templates/stein/fwaas_driver.ini create mode 100644 templates/stein/l3_agent.ini diff --git a/config.yaml b/config.yaml index 169d7cef..236bd795 100644 --- a/config.yaml +++ b/config.yaml @@ -312,3 +312,29 @@ options: description: | YAML-formatted associative array of sysctl key/value pairs to be set persistently e.g. '{ kernel.pid_max : 4194303 }'. + 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) diff --git a/hooks/charmhelpers/contrib/openstack/amulet/deployment.py b/hooks/charmhelpers/contrib/openstack/amulet/deployment.py index 8e57467b..e6d5de63 100644 --- a/hooks/charmhelpers/contrib/openstack/amulet/deployment.py +++ b/hooks/charmhelpers/contrib/openstack/amulet/deployment.py @@ -294,8 +294,10 @@ class OpenStackAmuletDeployment(AmuletDeployment): ('bionic', None): self.bionic_queens, ('bionic', 'cloud:bionic-rocky'): self.bionic_rocky, ('bionic', 'cloud:bionic-stein'): self.bionic_stein, + ('bionic', 'cloud:bionic-train'): self.bionic_train, ('cosmic', None): self.cosmic_rocky, ('disco', None): self.disco_stein, + ('eoan', None): self.eoan_train, } return releases[(self.series, self.openstack)] @@ -313,6 +315,7 @@ class OpenStackAmuletDeployment(AmuletDeployment): ('bionic', 'queens'), ('cosmic', 'rocky'), ('disco', 'stein'), + ('eoan', 'train'), ]) if self.openstack: os_origin = self.openstack.split(':')[1] diff --git a/hooks/charmhelpers/contrib/openstack/amulet/utils.py b/hooks/charmhelpers/contrib/openstack/amulet/utils.py index 53fa6506..0a5f81bd 100644 --- a/hooks/charmhelpers/contrib/openstack/amulet/utils.py +++ b/hooks/charmhelpers/contrib/openstack/amulet/utils.py @@ -54,11 +54,15 @@ NOVA_CLIENT_VERSION = "2" OPENSTACK_RELEASES_PAIRS = [ 'trusty_icehouse', 'trusty_kilo', 'trusty_liberty', - 'trusty_mitaka', 'xenial_mitaka', 'xenial_newton', - 'yakkety_newton', 'xenial_ocata', 'zesty_ocata', - 'xenial_pike', 'artful_pike', 'xenial_queens', - 'bionic_queens', 'bionic_rocky', 'cosmic_rocky', - 'bionic_stein', 'disco_stein'] + 'trusty_mitaka', 'xenial_mitaka', + 'xenial_newton', 'yakkety_newton', + 'xenial_ocata', 'zesty_ocata', + 'xenial_pike', 'artful_pike', + 'xenial_queens', 'bionic_queens', + 'bionic_rocky', 'cosmic_rocky', + 'bionic_stein', 'disco_stein', + 'bionic_train', 'eoan_train', +] class OpenStackAmuletUtils(AmuletUtils): diff --git a/hooks/charmhelpers/contrib/openstack/context.py b/hooks/charmhelpers/contrib/openstack/context.py index 8742e3c1..5df59efc 100644 --- a/hooks/charmhelpers/contrib/openstack/context.py +++ b/hooks/charmhelpers/contrib/openstack/context.py @@ -521,6 +521,86 @@ class IdentityCredentialsContext(IdentityServiceContext): return {} +class NovaVendorMetadataContext(OSContextGenerator): + """Context used for configuring nova vendor metadata on nova.conf file.""" + + def __init__(self, os_release_pkg, interfaces=None): + """Initialize the NovaVendorMetadataContext object. + + :param os_release_pkg: the package name to extract the OpenStack + release codename from. + :type os_release_pkg: str + :param interfaces: list of string values to be used as the Context's + relation interfaces. + :type interfaces: List[str] + """ + self.os_release_pkg = os_release_pkg + if interfaces is not None: + self.interfaces = interfaces + + def __call__(self): + cmp_os_release = CompareOpenStackReleases( + os_release(self.os_release_pkg)) + ctxt = {'vendor_data': False} + + vdata_providers = [] + vdata = config('vendor-data') + vdata_url = config('vendor-data-url') + + if vdata: + try: + # validate the JSON. If invalid, we do not set anything here + json.loads(vdata) + except (TypeError, ValueError) as e: + log('Error decoding vendor-data. {}'.format(e), level=ERROR) + else: + ctxt['vendor_data'] = True + # Mitaka does not support DynamicJSON + # so vendordata_providers is not needed + if cmp_os_release > 'mitaka': + vdata_providers.append('StaticJSON') + + if vdata_url: + if cmp_os_release > 'mitaka': + ctxt['vendor_data_url'] = vdata_url + vdata_providers.append('DynamicJSON') + else: + log('Dynamic vendor data unsupported' + ' for {}.'.format(cmp_os_release), level=ERROR) + if vdata_providers: + ctxt['vendordata_providers'] = ','.join(vdata_providers) + + return ctxt + + +class NovaVendorMetadataJSONContext(OSContextGenerator): + """Context used for writing nova vendor metadata json file.""" + + def __init__(self, os_release_pkg): + """Initialize the NovaVendorMetadataJSONContext object. + + :param os_release_pkg: the package name to extract the OpenStack + release codename from. + :type os_release_pkg: str + """ + self.os_release_pkg = os_release_pkg + + def __call__(self): + ctxt = {'vendor_data_json': '{}'} + + vdata = config('vendor-data') + if vdata: + try: + # validate the JSON. If invalid, we return empty. + json.loads(vdata) + except (TypeError, ValueError) as e: + log('Error decoding vendor-data. {}'.format(e), level=ERROR) + else: + ctxt['vendor_data_json'] = vdata + + return ctxt + + class AMQPContext(OSContextGenerator): def __init__(self, ssl_dir=None, rel_name='amqp', relation_prefix=None, @@ -647,6 +727,10 @@ class AMQPContext(OSContextGenerator): if notification_format: ctxt['notification_format'] = notification_format + send_notifications_to_logs = conf.get('send-notifications-to-logs', None) + if send_notifications_to_logs: + ctxt['send_notifications_to_logs'] = send_notifications_to_logs + if not self.complete: return {} @@ -698,6 +782,25 @@ class CephContext(OSContextGenerator): ensure_packages(['ceph-common']) return ctxt + def context_complete(self, ctxt): + """Overridden here to ensure the context is actually complete. + + We set `key` and `auth` to None here, by default, to ensure + that the context will always evaluate to incomplete until the + Ceph relation has actually sent these details; otherwise, + there is a potential race condition between the relation + appearing and the first unit actually setting this data on the + relation. + + :param ctxt: The current context members + :type ctxt: Dict[str, ANY] + :returns: True if the context is complete + :rtype: bool + """ + if 'auth' not in ctxt or 'key' not in ctxt: + return False + return super(CephContext, self).context_complete(ctxt) + class HAProxyContext(OSContextGenerator): """Provides half a context for the haproxy template, which describes @@ -1607,6 +1710,10 @@ class NeutronAPIContext(OSContextGenerator): 'rel_key': 'enable-nsg-logging', 'default': False, }, + 'enable_nfg_logging': { + 'rel_key': 'enable-nfg-logging', + 'default': False, + }, 'global_physnet_mtu': { 'rel_key': 'global-physnet-mtu', 'default': 1500, diff --git a/hooks/charmhelpers/contrib/openstack/neutron.py b/hooks/charmhelpers/contrib/openstack/neutron.py index 0f847f56..fb5607f3 100644 --- a/hooks/charmhelpers/contrib/openstack/neutron.py +++ b/hooks/charmhelpers/contrib/openstack/neutron.py @@ -217,6 +217,11 @@ def neutron_plugins(): plugins['nsx']['config'] = '/etc/neutron/nsx.ini' plugins['vsp']['driver'] = ( 'nuage_neutron.plugins.nuage.plugin.NuagePlugin') + if CompareOpenStackReleases(release) >= 'newton': + plugins['vsp']['config'] = '/etc/neutron/plugins/ml2/ml2_conf.ini' + plugins['vsp']['driver'] = 'neutron.plugins.ml2.plugin.Ml2Plugin' + plugins['vsp']['server_packages'] = ['neutron-server', + 'neutron-plugin-ml2'] return plugins diff --git a/hooks/charmhelpers/contrib/openstack/templates/section-oslo-notifications b/hooks/charmhelpers/contrib/openstack/templates/section-oslo-notifications index 7bb43d4f..71c7eb06 100644 --- a/hooks/charmhelpers/contrib/openstack/templates/section-oslo-notifications +++ b/hooks/charmhelpers/contrib/openstack/templates/section-oslo-notifications @@ -2,6 +2,9 @@ [oslo_messaging_notifications] driver = {{ oslo_messaging_driver }} transport_url = {{ transport_url }} +{% if send_notifications_to_logs %} +driver = log +{% endif %} {% if notification_topics -%} topics = {{ notification_topics }} {% endif -%} diff --git a/hooks/charmhelpers/contrib/openstack/templates/vendor_data.json b/hooks/charmhelpers/contrib/openstack/templates/vendor_data.json new file mode 100644 index 00000000..904f612a --- /dev/null +++ b/hooks/charmhelpers/contrib/openstack/templates/vendor_data.json @@ -0,0 +1 @@ +{{ vendor_data_json }} \ No newline at end of file diff --git a/hooks/charmhelpers/contrib/openstack/utils.py b/hooks/charmhelpers/contrib/openstack/utils.py index 1914ab84..d43a4d20 100644 --- a/hooks/charmhelpers/contrib/openstack/utils.py +++ b/hooks/charmhelpers/contrib/openstack/utils.py @@ -120,6 +120,7 @@ OPENSTACK_RELEASES = ( 'queens', 'rocky', 'stein', + 'train', ) UBUNTU_OPENSTACK_RELEASE = OrderedDict([ @@ -139,6 +140,7 @@ UBUNTU_OPENSTACK_RELEASE = OrderedDict([ ('bionic', 'queens'), ('cosmic', 'rocky'), ('disco', 'stein'), + ('eoan', 'train'), ]) @@ -159,6 +161,7 @@ OPENSTACK_CODENAMES = OrderedDict([ ('2018.1', 'queens'), ('2018.2', 'rocky'), ('2019.1', 'stein'), + ('2019.2', 'train'), ]) # The ugly duckling - must list releases oldest to newest @@ -195,6 +198,8 @@ SWIFT_CODENAMES = OrderedDict([ ['2.18.0', '2.19.0']), ('stein', ['2.20.0', '2.21.0']), + ('train', + ['2.22.0']), ]) # >= Liberty version->codename mapping @@ -208,6 +213,7 @@ PACKAGE_CODENAMES = { ('17', 'queens'), ('18', 'rocky'), ('19', 'stein'), + ('20', 'train'), ]), 'neutron-common': OrderedDict([ ('7', 'liberty'), @@ -218,6 +224,7 @@ PACKAGE_CODENAMES = { ('12', 'queens'), ('13', 'rocky'), ('14', 'stein'), + ('15', 'train'), ]), 'cinder-common': OrderedDict([ ('7', 'liberty'), @@ -228,6 +235,7 @@ PACKAGE_CODENAMES = { ('12', 'queens'), ('13', 'rocky'), ('14', 'stein'), + ('15', 'train'), ]), 'keystone': OrderedDict([ ('8', 'liberty'), @@ -238,6 +246,7 @@ PACKAGE_CODENAMES = { ('13', 'queens'), ('14', 'rocky'), ('15', 'stein'), + ('16', 'train'), ]), 'horizon-common': OrderedDict([ ('8', 'liberty'), @@ -248,6 +257,7 @@ PACKAGE_CODENAMES = { ('13', 'queens'), ('14', 'rocky'), ('15', 'stein'), + ('16', 'train'), ]), 'ceilometer-common': OrderedDict([ ('5', 'liberty'), @@ -258,6 +268,7 @@ PACKAGE_CODENAMES = { ('10', 'queens'), ('11', 'rocky'), ('12', 'stein'), + ('13', 'train'), ]), 'heat-common': OrderedDict([ ('5', 'liberty'), @@ -268,6 +279,7 @@ PACKAGE_CODENAMES = { ('10', 'queens'), ('11', 'rocky'), ('12', 'stein'), + ('13', 'train'), ]), 'glance-common': OrderedDict([ ('11', 'liberty'), @@ -278,6 +290,7 @@ PACKAGE_CODENAMES = { ('16', 'queens'), ('17', 'rocky'), ('18', 'stein'), + ('19', 'train'), ]), 'openstack-dashboard': OrderedDict([ ('8', 'liberty'), @@ -288,6 +301,7 @@ PACKAGE_CODENAMES = { ('13', 'queens'), ('14', 'rocky'), ('15', 'stein'), + ('16', 'train'), ]), } diff --git a/hooks/charmhelpers/contrib/python/__init__.py b/hooks/charmhelpers/contrib/python/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hooks/charmhelpers/contrib/storage/linux/ceph.py b/hooks/charmhelpers/contrib/storage/linux/ceph.py index 2c62092c..bbfe1933 100644 --- a/hooks/charmhelpers/contrib/storage/linux/ceph.py +++ b/hooks/charmhelpers/contrib/storage/linux/ceph.py @@ -1488,7 +1488,7 @@ def is_broker_action_done(action, rid=None, unit=None): @param action: name of action to be performed @returns True if action complete otherwise False """ - rdata = relation_get(rid, unit) or {} + rdata = relation_get(rid=rid, unit=unit) or {} broker_rsp = rdata.get(get_broker_rsp_key()) if not broker_rsp: return False @@ -1510,7 +1510,7 @@ def mark_broker_action_done(action, rid=None, unit=None): @param action: name of action to be performed @returns None """ - rdata = relation_get(rid, unit) or {} + rdata = relation_get(rid=rid, unit=unit) or {} broker_rsp = rdata.get(get_broker_rsp_key()) if not broker_rsp: return diff --git a/hooks/charmhelpers/fetch/ubuntu.py b/hooks/charmhelpers/fetch/ubuntu.py index c6d9341e..24c76e34 100644 --- a/hooks/charmhelpers/fetch/ubuntu.py +++ b/hooks/charmhelpers/fetch/ubuntu.py @@ -173,6 +173,14 @@ CLOUD_ARCHIVE_POCKETS = { 'stein/proposed': 'bionic-proposed/stein', 'bionic-stein/proposed': 'bionic-proposed/stein', 'bionic-proposed/stein': 'bionic-proposed/stein', + # Train + 'train': 'bionic-updates/train', + 'bionic-train': 'bionic-updates/train', + 'bionic-train/updates': 'bionic-updates/train', + 'bionic-updates/train': 'bionic-updates/train', + 'train/proposed': 'bionic-proposed/train', + 'bionic-train/proposed': 'bionic-proposed/train', + 'bionic-proposed/train': 'bionic-proposed/train', } @@ -522,14 +530,16 @@ def add_source(source, key=None, fail_invalid=False): for r, fn in six.iteritems(_mapping): m = re.match(r, source) if m: - # call the assoicated function with the captured groups - # raises SourceConfigError on error. - fn(*m.groups()) if key: + # Import key before adding the source which depends on it, + # as refreshing packages could fail otherwise. try: import_key(key) except GPGKeyError as e: raise SourceConfigError(str(e)) + # call the associated function with the captured groups + # raises SourceConfigError on error. + fn(*m.groups()) break else: # nothing matched. log an error and maybe sys.exit diff --git a/hooks/neutron_ovs_context.py b/hooks/neutron_ovs_context.py index 1ce34fdd..e8ce2e55 100644 --- a/hooks/neutron_ovs_context.py +++ b/hooks/neutron_ovs_context.py @@ -47,6 +47,9 @@ IPTABLES_HYBRID = 'iptables_hybrid' OPENVSWITCH = 'openvswitch' VALID_FIREWALL_DRIVERS = (IPTABLES_HYBRID, OPENVSWITCH) +NFG_LOG_RATE_LIMIT_MIN = 100 +NFG_LOG_BURST_LIMIT_MIN = 25 + def _get_firewall_driver(ovs_ctxt): ''' @@ -103,6 +106,33 @@ def get_nsg_log_path(desired_nsg_log_path): return desired_nsg_log_path +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 OVSPluginContext(context.NeutronContext): interfaces = [] @@ -278,6 +308,30 @@ class L3AgentContext(OSContextGenerator): else: ctxt['agent_mode'] = 'legacy' + ctxt['enable_nfg_logging'] = ( + neutron_api_settings['enable_nfg_logging'] + ) + + 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 diff --git a/hooks/neutron_ovs_utils.py b/hooks/neutron_ovs_utils.py index 66655149..bbd1bb16 100644 --- a/hooks/neutron_ovs_utils.py +++ b/hooks/neutron_ovs_utils.py @@ -106,7 +106,10 @@ ML2_CONF = '%s/plugins/ml2/ml2_conf.ini' % NEUTRON_CONF_DIR OVS_CONF = '%s/plugins/ml2/openvswitch_agent.ini' % NEUTRON_CONF_DIR EXT_PORT_CONF = '/etc/init/ext-port.conf' NEUTRON_METADATA_AGENT_CONF = "/etc/neutron/metadata_agent.ini" -DVR_PACKAGES = ['neutron-l3-agent'] +DVR_PACKAGES = [ + 'neutron-l3-agent', + 'libnetfilter-log1', +] DHCP_PACKAGES = ['neutron-dhcp-agent'] # haproxy is required for isolated provider networks # ns-metadata-proxy LP#1831935 @@ -116,6 +119,7 @@ L3HA_PACKAGES = ['keepalived'] PY3_PACKAGES = [ 'python3-neutron', + 'python3-zmq', # fwaas_v2_log ] PURGE_PACKAGES = [ diff --git a/templates/newton/fwaas_driver.ini b/templates/newton/fwaas_driver.ini new file mode 100644 index 00000000..3b3f3338 --- /dev/null +++ b/templates/newton/fwaas_driver.ini @@ -0,0 +1,9 @@ +# newton +############################################################################### +# [ WARNING ] +# Configuration file maintained by Juju. Local changes may be overwritten. +############################################################################### +[fwaas] +agent_version = v1 +driver = iptables +enabled = True diff --git a/templates/stein/fwaas_driver.ini b/templates/stein/fwaas_driver.ini new file mode 100644 index 00000000..cc33f770 --- /dev/null +++ b/templates/stein/fwaas_driver.ini @@ -0,0 +1,9 @@ +# stein +############################################################################### +# [ WARNING ] +# Configuration file maintained by Juju. Local changes may be overwritten. +############################################################################### +[fwaas] +agent_version = v2 +driver = iptables_v2 +enabled = True diff --git a/templates/stein/l3_agent.ini b/templates/stein/l3_agent.ini new file mode 100644 index 00000000..8da1b9a3 --- /dev/null +++ b/templates/stein/l3_agent.ini @@ -0,0 +1,29 @@ +# stein +############################################################################### +# [ WARNING ] +# Configuration file maintained by Juju. Local changes may be overwritten. +# {{ restart_trigger_l3agent }} +############################################################################### + +[DEFAULT] +interface_driver = openvswitch +agent_mode = {{ agent_mode }} +{% if external_configuration_new -%} +gateway_external_network_id = +external_network_bridge = +{% endif %} + +[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 +{% endif -%} \ No newline at end of file diff --git a/unit_tests/test_neutron_ovs_context.py b/unit_tests/test_neutron_ovs_context.py index 16e59ec6..e58ec3dc 100644 --- a/unit_tests/test_neutron_ovs_context.py +++ b/unit_tests/test_neutron_ovs_context.py @@ -435,8 +435,80 @@ class L3AgentContextTest(CharmTestCase): } _rget.side_effect = lambda *args, **kwargs: rdata self.assertEqual( - context.L3AgentContext()(), {'agent_mode': 'dvr', - 'external_configuration_new': True} + context.L3AgentContext()(), { + 'agent_mode': 'dvr', + 'external_configuration_new': True, + 'enable_nfg_logging': False, + 'nfg_log_burst_limit': 25, + 'nfg_log_output_base': None, + 'nfg_log_rate_limit': None, + } + ) + + @patch.object(context, 'validate_nfg_log_path') + @patch.object(charmhelpers.contrib.openstack.context, 'relation_get') + @patch.object(charmhelpers.contrib.openstack.context, 'relation_ids') + @patch.object(charmhelpers.contrib.openstack.context, 'related_units') + def test_dvr_nfg_enabled(self, _runits, _rids, _rget, + _validate_nfg_log_path): + _runits.return_value = ['unit1'] + _rids.return_value = ['rid2'] + rdata = { + 'neutron-security-groups': 'True', + 'enable-dvr': 'True', + 'l2-population': 'True', + 'overlay-network-type': 'vxlan', + 'network-device-mtu': 1500, + 'enable-nfg-logging': 'True', + } + _rget.side_effect = lambda *args, **kwargs: rdata + _validate_nfg_log_path.side_effect = lambda x: x + self.test_config.set('firewall-group-log-output-base', + '/var/log/neutron/firewall.log') + self.test_config.set('firewall-group-log-rate-limit', 200) + self.test_config.set('firewall-group-log-burst-limit', 30) + self.assertEqual( + context.L3AgentContext()(), { + 'agent_mode': 'dvr', + 'external_configuration_new': True, + 'enable_nfg_logging': True, + 'nfg_log_burst_limit': 30, + 'nfg_log_output_base': '/var/log/neutron/firewall.log', + 'nfg_log_rate_limit': 200, + } + ) + + @patch.object(context, 'validate_nfg_log_path') + @patch.object(charmhelpers.contrib.openstack.context, 'relation_get') + @patch.object(charmhelpers.contrib.openstack.context, 'relation_ids') + @patch.object(charmhelpers.contrib.openstack.context, 'related_units') + def test_dvr_nfg_enabled_mins(self, _runits, _rids, _rget, + _validate_nfg_log_path): + _runits.return_value = ['unit1'] + _rids.return_value = ['rid2'] + rdata = { + 'neutron-security-groups': 'True', + 'enable-dvr': 'True', + 'l2-population': 'True', + 'overlay-network-type': 'vxlan', + 'network-device-mtu': 1500, + 'enable-nfg-logging': 'True', + } + _rget.side_effect = lambda *args, **kwargs: rdata + _validate_nfg_log_path.side_effect = lambda x: x + self.test_config.set('firewall-group-log-output-base', + '/var/log/neutron/firewall.log') + self.test_config.set('firewall-group-log-rate-limit', 90) + self.test_config.set('firewall-group-log-burst-limit', 20) + self.assertEqual( + context.L3AgentContext()(), { + 'agent_mode': 'dvr', + 'external_configuration_new': True, + 'enable_nfg_logging': True, + 'nfg_log_burst_limit': 25, + 'nfg_log_output_base': '/var/log/neutron/firewall.log', + 'nfg_log_rate_limit': 100, + } ) @patch.object(charmhelpers.contrib.openstack.context, 'relation_get') @@ -455,8 +527,14 @@ class L3AgentContextTest(CharmTestCase): } _rget.side_effect = lambda *args, **kwargs: rdata self.assertEqual( - context.L3AgentContext()(), {'agent_mode': 'dvr_snat', - 'external_configuration_new': True} + context.L3AgentContext()(), { + 'agent_mode': 'dvr_snat', + 'external_configuration_new': True, + 'enable_nfg_logging': False, + 'nfg_log_burst_limit': 25, + 'nfg_log_output_base': None, + 'nfg_log_rate_limit': None, + } ) @patch.object(charmhelpers.contrib.openstack.context, 'relation_get') @@ -473,7 +551,13 @@ class L3AgentContextTest(CharmTestCase): 'network-device-mtu': 1500, } _rget.side_effect = lambda *args, **kwargs: rdata - self.assertEqual(context.L3AgentContext()(), {'agent_mode': 'legacy'}) + self.assertEqual(context.L3AgentContext()(), { + 'agent_mode': 'legacy', + 'enable_nfg_logging': False, + 'nfg_log_burst_limit': 25, + 'nfg_log_output_base': None, + 'nfg_log_rate_limit': None, + }) class SharedSecretContext(CharmTestCase): diff --git a/unit_tests/test_neutron_ovs_hooks.py b/unit_tests/test_neutron_ovs_hooks.py index 462d5b4c..0f7ba2e1 100644 --- a/unit_tests/test_neutron_ovs_hooks.py +++ b/unit_tests/test_neutron_ovs_hooks.py @@ -169,6 +169,7 @@ class NeutronOVSHooksTests(CharmTestCase): self.assertTrue(self.CONFIGS.write_all.called) _plugin_joined.assert_called_with(relation_id='rid') self.purge_packages.assert_called_with(['neutron-l3-agent', + 'libnetfilter-log1', 'keepalived']) def test_neutron_plugin_joined_dvr_dhcp(self): diff --git a/unit_tests/test_neutron_ovs_utils.py b/unit_tests/test_neutron_ovs_utils.py index f9386cb5..666a493b 100644 --- a/unit_tests/test_neutron_ovs_utils.py +++ b/unit_tests/test_neutron_ovs_utils.py @@ -209,6 +209,7 @@ class TestNeutronOVSUtils(CharmTestCase): head_pkg, 'neutron-plugin-openvswitch-agent', 'neutron-l3-agent', + 'libnetfilter-log1', ] self.assertEqual(pkg_list, expect) @@ -227,8 +228,10 @@ class TestNeutronOVSUtils(CharmTestCase): expect = [ head_pkg, 'neutron-l3-agent', + 'libnetfilter-log1', 'neutron-openvswitch-agent', 'python3-neutron', + 'python3-zmq', 'python3-neutron-fwaas', ] self.assertEqual(pkg_list, expect) @@ -249,6 +252,7 @@ class TestNeutronOVSUtils(CharmTestCase): expect = [ head_pkg, 'neutron-l3-agent', + 'libnetfilter-log1', 'keepalived', 'neutron-openvswitch-agent', ] @@ -270,6 +274,7 @@ class TestNeutronOVSUtils(CharmTestCase): expect = [ head_pkg, 'neutron-l3-agent', + 'libnetfilter-log1', 'neutron-openvswitch-agent', ] self.assertEqual(pkg_list, expect) @@ -290,6 +295,7 @@ class TestNeutronOVSUtils(CharmTestCase): expect = [ head_pkg, 'neutron-l3-agent', + 'libnetfilter-log1', 'neutron-openvswitch-agent', ] self.assertEqual(pkg_list, expect)