Repository: qpid-dispatch Updated Branches: refs/heads/crolke-DISPATCH-188-1 6faf3da50 -> 3256a51c8
Break utility code into separate source module. Project: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/repo Commit: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/commit/3256a51c Tree: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/tree/3256a51c Diff: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/diff/3256a51c Branch: refs/heads/crolke-DISPATCH-188-1 Commit: 3256a51c8cd00bfbe1fc4a17ea8f72eb841cb49f Parents: 6faf3da Author: Chuck Rolke <cro...@redhat.com> Authored: Wed Dec 16 17:27:57 2015 -0500 Committer: Chuck Rolke <cro...@redhat.com> Committed: Wed Dec 16 17:27:57 2015 -0500 ---------------------------------------------------------------------- .../qpid_dispatch_internal/management/policy.py | 298 +---------------- .../management/policy_util.py | 335 +++++++++++++++++++ tests/system_tests_policy.py | 46 ++- 3 files changed, 370 insertions(+), 309 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/3256a51c/python/qpid_dispatch_internal/management/policy.py ---------------------------------------------------------------------- diff --git a/python/qpid_dispatch_internal/management/policy.py b/python/qpid_dispatch_internal/management/policy.py index c07b891..185b60c 100644 --- a/python/qpid_dispatch_internal/management/policy.py +++ b/python/qpid_dispatch_internal/management/policy.py @@ -18,17 +18,15 @@ # """ -Utilities for command-line programs. + """ -import sys, optparse, os +import sys, os import ConfigParser -from collections import Sequence, Mapping, namedtuple -from qpid_dispatch_site import VERSION +import optparse +from policy_util import PolicyError, HostStruct, HostAddr, PolicyAppConnectionMgr import pdb #; pdb.set_trace() import ast -import socket -import binascii @@ -98,195 +96,6 @@ Internal Policy: # # -class PolicyError(Exception): - def __init__(self, value): - self.value = value - def __str__(self): - return repr(self.value) - -# -# -class HostStruct(): - """ - HostStruct represents a single, binary socket address from getaddrinfo - - name : name given to constructor, numeric IP or host name - - saddr : net name resolved by getaddrinfo, numeric IP - - family : saddr.family, int - - binary : saddr packed binary address, binary string - """ - families = [socket.AF_INET] - famnames = ["IPv4"] - if socket.has_ipv6: - families.append(socket.AF_INET6) - famnames.append("IPv6") - - def __init__(self, hostname): - """ - Given a host name text string, return the socket info for it. - @param[in] hostname host IP address to parse - """ - try: - res = socket.getaddrinfo(hostname, 0) - if len(res) == 0: - raise PolicyError("HostStruct: '%s' did not resolve to an IP address" % hostname) - foundFirst = False - saddr = "" - sfamily = socket.AF_UNSPEC - for i0 in range(0, len(res)): - family, dum0, dum1, dum2, sockaddr = res[i0] - if not foundFirst: - if family in self.families: - saddr = sockaddr[0] - sfamily = family - foundFirst = True - else: - if family in self.families: - if not saddr == sockaddr[0] or not sfamily == family: - raise PolicyError("HostStruct: '%s' resolves to multiple IP addresses" % - hostname) - - if not foundFirst: - raise PolicyError("HostStruct: '%s' did not resolve to one of the supported address family" % - hostname) - self.name = hostname - self.saddr = saddr - self.family = sfamily - self.binary = socket.inet_pton(family, saddr) - return - except Exception, e: - raise PolicyError("HostStruct: '%s' failed to resolve: '%s'" % - (hostname, e)) - - def __str__(self): - return self.name - - def __repr__(self): - return self.__str__() - - def dump(self): - return ("(%s, %s, %s, %s)" % - (self.name, - self.saddr, - "AF_INET" if self.family == socket.AF_INET else "AF_INET6", - binascii.hexlify(self.binary))) - -# -# -class HostAddr(): - """ - Provide HostIP address ranges and comparison functions. - A HostIP may be: - - single address: 10.10.1.1 - - a pair of addresses: 10.10.0.0,10.10.255.255 - Only IPv4 and IPv6 are supported. - - No unix sockets. - HostIP names must resolve to a single IP address. - Address pairs define a range. - - The second address must be numerically larger than the first address. - - The addresses must be of the same address 'family', IPv4 or IPv6. - IPv6 support is conditional based on underlying OS network options. - Raises a PolicyError on validation error in constructor. - """ - - def has_ipv6(self): - return socket.has_ipv6 - - def __init__(self, hostspec): - """ - Parse host spec into binary structures to use for comparisons. - Validate the hostspec to enforce usage rules. - """ - self.hoststructs = [] - - if hostspec == "*": - self.wildcard = True - else: - self.wildcard = False - - hosts = [x.strip() for x in hostspec.split(",")] - - # hosts must contain one or two host specs - if len(hosts) not in [1, 2]: - raise PolicyError("hostspec must contain 1 or 2 host names") - self.hoststructs.append(HostStruct(hosts[0])) - if len(hosts) > 1: - self.hoststructs.append(HostStruct(hosts[1])) - if not self.hoststructs[0].family == self.hoststructs[1].family: - raise PolicyError("mixed IPv4 and IPv6 host specs in range not allowed") - c0 = self.memcmp(self.hoststructs[0].binary, self.hoststructs[1].binary) - if c0 > 0: - raise PolicyError("host specs in range must have lower numeric address first") - - def __str__(self): - if self.wildcard: - return "*" - res = self.hoststructs[0].name - if len(self.hoststructs) > 1: - res += "," + self.hoststructs[1].name - return res - - def __repr__(self): - return self.__str__() - - def dump(self): - if self.wildcard: - return "(*)" - res = "(" + self.hoststructs[0].dump() - if len(self.hoststructs) > 1: - res += "," + self.hoststructs[1].dump() - res += ")" - return res - - def memcmp(self, a, b): - res = 0 - for i in range(0,len(a)): - if a[i] > b[i]: - res = 1 - break; - elif a[i] < b[i]: - res = -1 - break - return res - - def match_bin(self, cstruct): - """ - Does the candidate hoststruct match the IP or range of IP addresses represented by this? - @param[in] cstruct the IP address to be tested - @return candidate matches this or not - """ - if self.wildcard: - return True - try: - if not cstruct.family == self.hoststructs[0].family: - # sorry, wrong AF_INET family - return False - c0 = self.memcmp(cstruct.binary, self.hoststructs[0].binary) - if len(self.hoststructs) == 1: - return c0 == 0 - c1 = self.memcmp(cstruct.binary, self.hoststructs[1].binary) - return c0 >= 0 and c1 <= 0 - except PolicyError: - return False - except Exception, e: - assert isinstance(cstruct, HostStruct), \ - ("Wrong type. Expected HostStruct but received %s" % cstruct.__class__.__name__) - return False - - def match_str(self, candidate): - """ - Does the candidate string match the IP or range represented by this? - @param[in] candidate the IP address to be tested - @return candidate matches this or not - """ - try: - hoststruct = HostStruct(candidate) - except PolicyError: - return False - return self.match_bin(hoststruct) - - -# -# class PolicyKeys(): # Internal policy key words KW_POLICY_VERSION = "policyVersion" @@ -544,103 +353,6 @@ class PolicyCompiler(): return False return True -class PolicyConnStatsPerApp(): - """ - Track policy user/host connection statistics for one app - connections : 5 - max_t : 20 - max_u : 5 - max_h : 10 - host_aggr : { 'host1' : [conn1, conn2, conn3], - 'host2' : [conn4, conn5] } - user_aggr : { 'user1' : [conn1, conn2, conn3], - 'user2' : [conn4, conn5] } - """ - def __init__(self, maxconn, maxconnperuser, maxconnperhost): - """ - The object is constructed with the policy limits - for total, total per user, and total per host counts. - As connections are allowed they are tracked in - aggregation maps. - """ - self.connections = 0 - if maxconn < 0 or maxconnperuser < 0 or maxconnperhost < 0: - raise PolicyError("PolicyConnStatsPerApp settings must be >= 0") - self.max_t = maxconn - self.max_u = maxconnperuser - self.max_h = maxconnperhost - self.user_aggr = {} - self.host_aggr = {} - - def __str__(self): - pdb.set_trace() - res = ("Connection Limits: Total: %s, Per User: %s, Per Host: %s\n" % - (self.max_t, self.max_u, self.max_h)) - res += ("User counts: %s\n" % self.user_aggr) - res += ("Host counts: %s" % self.host_aggr) - return res - - def __repr__(self): - return self.__str__() - - def update(self, maxconn, maxconnperuser, maxconnperhost): - if maxconn < 0 or maxconnperuser < 0 or maxconnperhost < 0: - raise PolicyError("PolicyConnStatsPerApp settings must be >= 0") - self.max_t = maxconn - self.max_u = maxconnperuser - self.max_h = maxconnperhost - - def can_connect(self, conn_id, user, host, diags): - """ - Register a connection attempt. - If all the connection rules pass then add the - user/host to the connection tables - """ - n_user = 0 - if user in self.user_aggr: - n_user = len(self.user_aggr[user]) - n_host = 0 - if host in self.host_aggr: - n_host = len(self.host_aggr[host]) - - allowbytotal = self.max_t == 0 or self.connections < self.max_t - allowbyuser = self.max_u == 0 or n_user < self.max_u - allowbyhost = self.max_h == 0 or n_host < self.max_h - - if allowbytotal and allowbyuser and allowbyhost: - self.connections += 1 - if not user in self.user_aggr: - self.user_aggr[user] = [] - self.user_aggr[user].append(conn_id) - if not host in self.host_aggr: - self.host_aggr[host] = [] - self.host_aggr[host].append(conn_id) - return True - else: - if not allowbytotal: - diags.append("LogMe: INFO user '%s' from host '%s' denied connection by total connection limit" % - (user, host)) - if not allowbyuser: - diags.append("LogMe: INFO user '%s' from host '%s' denied connection by per user limit" % - (user, host)) - if not allowbyhost: - diags.append("LogMe: INFO user '%s' from host '%s' denied connection by per host limit" % - (user, host)) - return False - - def disconnect(self, conn_id, user, host): - """ - Unregister a connection - """ - assert(self.connections > 0) - assert(user in self.user_aggr) - assert(conn_id in self.user_aggr[user]) - assert(host in self.host_aggr) - assert(conn_id in self.host_aggr[host]) - self.connections -= 1 - self.user_aggr[user].remove(conn_id) - self.host_aggr[host].remove(conn_id) - class Policy(): """ The policy database. @@ -736,7 +448,7 @@ class Policy(): if c in self.stats: self.stats[c].update(c_max, c_max_u, c_max_h) else: - self.stats[c] = PolicyConnStatsPerApp(c_max, c_max_u, c_max_h) + self.stats[c] = PolicyAppConnectionMgr(c_max, c_max_u, c_max_h) self.data.update(newpolicies) http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/3256a51c/python/qpid_dispatch_internal/management/policy_util.py ---------------------------------------------------------------------- diff --git a/python/qpid_dispatch_internal/management/policy_util.py b/python/qpid_dispatch_internal/management/policy_util.py new file mode 100644 index 0000000..681743d --- /dev/null +++ b/python/qpid_dispatch_internal/management/policy_util.py @@ -0,0 +1,335 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# + +import sys, os +import socket +import binascii + + +# +# +class PolicyError(Exception): + def __init__(self, value): + self.value = value + def __str__(self): + return repr(self.value) + +# +# +class HostStruct(): + """ + HostStruct represents a single, binary socket address from getaddrinfo + - name : name given to constructor; numeric IP or host name + - saddr : net name resolved by getaddrinfo; numeric IP + - family : saddr.family; int + - binary : saddr packed binary address; binary string + """ + families = [socket.AF_INET] + famnames = ["IPv4"] + if socket.has_ipv6: + families.append(socket.AF_INET6) + famnames.append("IPv6") + + def __init__(self, hostname): + """ + Given a host name text string, return the socket info for it. + @param[in] hostname host IP address to parse + """ + try: + res = socket.getaddrinfo(hostname, 0) + if len(res) == 0: + raise PolicyError("HostStruct: '%s' did not resolve to an IP address" % hostname) + foundFirst = False + saddr = "" + sfamily = socket.AF_UNSPEC + for i0 in range(0, len(res)): + family, dum0, dum1, dum2, sockaddr = res[i0] + if not foundFirst: + if family in self.families: + saddr = sockaddr[0] + sfamily = family + foundFirst = True + else: + if family in self.families: + if not saddr == sockaddr[0] or not sfamily == family: + raise PolicyError("HostStruct: '%s' resolves to multiple IP addresses" % + hostname) + + if not foundFirst: + raise PolicyError("HostStruct: '%s' did not resolve to one of the supported address family" % + hostname) + self.name = hostname + self.saddr = saddr + self.family = sfamily + self.binary = socket.inet_pton(family, saddr) + return + except Exception, e: + raise PolicyError("HostStruct: '%s' failed to resolve: '%s'" % + (hostname, e)) + + def __str__(self): + return self.name + + def __repr__(self): + return self.__str__() + + def dump(self): + return ("(%s, %s, %s, %s)" % + (self.name, + self.saddr, + "AF_INET" if self.family == socket.AF_INET else "AF_INET6", + binascii.hexlify(self.binary))) + +# +# +class HostAddr(): + """ + Provide HostIP address ranges and comparison functions. + A HostIP may be: + - single address: 10.10.1.1 + - a pair of addresses: 10.10.0.0,10.10.255.255 + - a wildcard: * + Only IPv4 and IPv6 are supported. + - No unix sockets. + HostIP names must resolve to a single IP address. + Address pairs define a range. + - The second address must be numerically larger than the first address. + - The addresses must be of the same address 'family', IPv4 or IPv6. + The wildcard '*' matches all address IPv4 or IPv6. + IPv6 support is conditional based on underlying OS network options. + Raises a PolicyError on validation error in constructor. + """ + + def has_ipv6(self): + return socket.has_ipv6 + + def __init__(self, hostspec): + """ + Parse host spec into binary structures to use for comparisons. + Validate the hostspec to enforce usage rules. + """ + self.hoststructs = [] + + if hostspec == "*": + self.wildcard = True + else: + self.wildcard = False + + hosts = [x.strip() for x in hostspec.split(",")] + + # hosts must contain one or two host specs + if len(hosts) not in [1, 2]: + raise PolicyError("hostspec must contain 1 or 2 host names") + self.hoststructs.append(HostStruct(hosts[0])) + if len(hosts) > 1: + self.hoststructs.append(HostStruct(hosts[1])) + if not self.hoststructs[0].family == self.hoststructs[1].family: + raise PolicyError("mixed IPv4 and IPv6 host specs in range not allowed") + c0 = self.memcmp(self.hoststructs[0].binary, self.hoststructs[1].binary) + if c0 > 0: + raise PolicyError("host specs in range must have lower numeric address first") + + def __str__(self): + if self.wildcard: + return "*" + res = self.hoststructs[0].name + if len(self.hoststructs) > 1: + res += "," + self.hoststructs[1].name + return res + + def __repr__(self): + return self.__str__() + + def dump(self): + if self.wildcard: + return "(*)" + res = "(" + self.hoststructs[0].dump() + if len(self.hoststructs) > 1: + res += "," + self.hoststructs[1].dump() + res += ")" + return res + + def memcmp(self, a, b): + res = 0 + for i in range(0,len(a)): + if a[i] > b[i]: + res = 1 + break; + elif a[i] < b[i]: + res = -1 + break + return res + + def match_bin(self, candidate): + """ + Does the candidate hoststruct match the IP or range of IP addresses represented by this? + @param[in] candidate the IP address to be tested + @return candidate matches this or not + """ + if self.wildcard: + return True + try: + if not candidate.family == self.hoststructs[0].family: + # sorry, wrong AF_INET family + return False + c0 = self.memcmp(candidate.binary, self.hoststructs[0].binary) + if len(self.hoststructs) == 1: + return c0 == 0 + c1 = self.memcmp(candidate.binary, self.hoststructs[1].binary) + return c0 >= 0 and c1 <= 0 + except PolicyError: + return False + except Exception, e: + assert isinstance(candidate, HostStruct), \ + ("Wrong type. Expected HostStruct but received %s" % candidate.__class__.__name__) + return False + + def match_str(self, candidate): + """ + Does the candidate string match the IP or range represented by this? + @param[in] candidate the IP address to be tested + @return candidate matches this or not + """ + try: + hoststruct = HostStruct(candidate) + except PolicyError: + return False + return self.match_bin(hoststruct) + +# +# +class PolicyAppConnectionMgr(): + """ + Track policy user/host connection limits and statistics for one app. + # limits - set at creation and by update() + max_total : 20 + max_per_user : 5 + max_per_host : 10 + # statistics - maintained for the lifetime of corresponding application + connections_approved : N + connections_denied : N + # live state - maintained for the lifetime of corresponding application + connections_active : 5 + per_host_state : { 'host1' : [conn1, conn2, conn3], + 'host2' : [conn4, conn5] } + per_user_state : { 'user1' : [conn1, conn2, conn3], + 'user2' : [conn4, conn5] } + """ + def __init__(self, maxconn, maxconnperuser, maxconnperhost): + """ + The object is constructed with the policy limits and zeroed counts. + @param[in] maxconn maximum total concurrent connections + @param[in] maxconnperuser maximum total conncurrent connections for each user + @param[in] maxconnperuser maximum total conncurrent connections for each host + """ + if maxconn < 0 or maxconnperuser < 0 or maxconnperhost < 0: + raise PolicyError("PolicyAppConnectionMgr settings must be >= 0") + self.max_total = maxconn + self.max_per_user = maxconnperuser + self.max_per_host = maxconnperhost + self.connections_approved = 0 + self.connections_denied = 0 + self.connections_active = 0 + self.per_host_state = {} + self.per_user_state = {} + + def __str__(self): + res = ("Connection Limits: total: %s, per user: %s, per host: %s\n" % + (self.max_total, self.max_per_user, self.max_per_host)) + res += ("Connections Statistics: total approved: %s, total denied: %s" % + (self.connections_approved, self.connections_denied)) + res += ("Connection State: total current: %s" % self.connections_active) + res += ("User state: %s\n" % self.per_user_state) + res += ("Host state: %s" % self.per_host_state) + return res + + def __repr__(self): + return self.__str__() + + def update(self, maxconn, maxconnperuser, maxconnperhost): + """ + Reset connection limits + @param[in] maxconn maximum total concurrent connections + @param[in] maxconnperuser maximum total conncurrent connections for each user + @param[in] maxconnperuser maximum total conncurrent connections for each host + """ + if maxconn < 0 or maxconnperuser < 0 or maxconnperhost < 0: + raise PolicyError("PolicyAppConnectionMgr settings must be >= 0") + self.max_total = maxconn + self.max_per_user = maxconnperuser + self.max_per_host = maxconnperhost + + def can_connect(self, conn_id, user, host, diags): + """ + Register a connection attempt. + If all the connection limit rules pass then add the + user/host to the connection tables. + @param[in] conn_id unique ID for connection, usually IP:port + @param[in] user authenticated user ID + @param[in] host IP address of host + @param[out] diags on failure holds 1, 2, or 3 error strings + @return connection is allowed and tracked in state tables + """ + n_user = 0 + if user in self.per_user_state: + n_user = len(self.per_user_state[user]) + n_host = 0 + if host in self.per_host_state: + n_host = len(self.per_host_state[host]) + + allowbytotal = self.max_total == 0 or self.connections_active < self.max_total + allowbyuser = self.max_per_user == 0 or n_user < self.max_per_user + allowbyhost = self.max_per_host == 0 or n_host < self.max_per_host + + if allowbytotal and allowbyuser and allowbyhost: + if not user in self.per_user_state: + self.per_user_state[user] = [] + self.per_user_state[user].append(conn_id) + if not host in self.per_host_state: + self.per_host_state[host] = [] + self.per_host_state[host].append(conn_id) + self.connections_active += 1 + self.connections_approved += 1 + return True + else: + if not allowbytotal: + diags.append("LogMe: INFO user '%s' from host '%s' denied connection by total connection limit" % + (user, host)) + if not allowbyuser: + diags.append("LogMe: INFO user '%s' from host '%s' denied connection by per user limit" % + (user, host)) + if not allowbyhost: + diags.append("LogMe: INFO user '%s' from host '%s' denied connection by per host limit" % + (user, host)) + self.connections_denied += 1 + return False + + def disconnect(self, conn_id, user, host): + """ + Unregister a connection + """ + assert(self.connections_active > 0) + assert(user in self.per_user_state) + assert(conn_id in self.per_user_state[user]) + assert(host in self.max_per_host) + assert(conn_id in self.max_per_host[host]) + self.connections_active -= 1 + self.per_user_state[user].remove(conn_id) + self.per_host_state[host].remove(conn_id) + http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/3256a51c/tests/system_tests_policy.py ---------------------------------------------------------------------- diff --git a/tests/system_tests_policy.py b/tests/system_tests_policy.py index 6f195b3..0ef7356 100644 --- a/tests/system_tests_policy.py +++ b/tests/system_tests_policy.py @@ -30,8 +30,10 @@ from proton.utils import BlockingConnection, LinkDetached from qpid_dispatch.management.client import Node from system_test import TIMEOUT +from qpid_dispatch_internal.management.policy_util import \ + HostAddr, PolicyError, HostStruct, PolicyAppConnectionMgr from qpid_dispatch_internal.management.policy import \ - Policy, HostAddr, PolicyError, HostStruct, PolicyConnStatsPerApp + Policy class AbsoluteConnectionCountLimit(TestCase): """ @@ -233,34 +235,34 @@ class PolicyFile(TestCase): for s in addrs: self.assertTrue(s in upolicy['targets']) for s in addrs: self.assertTrue(s in upolicy['sources']) -class PolicyConnStatsPerAppTests(TestCase): +class PolicyAppConnectionMgrTests(TestCase): - def test_policy_app_conn_stats_fail_by_total(self): - stats = PolicyConnStatsPerApp(1, 2, 2) + def test_policy_app_conn_mgr_fail_by_total(self): + stats = PolicyAppConnectionMgr(1, 2, 2) diags = [] self.assertTrue(stats.can_connect('10.10.10.10:10000', 'chuck', '10.10.10.10', diags)) self.assertFalse(stats.can_connect('10.10.10.10:10001', 'chuck', '10.10.10.10', diags)) self.assertTrue(len(diags) == 1) self.assertTrue('by total' in diags[0]) - def test_policy_app_conn_stats_fail_by_user(self): - stats = PolicyConnStatsPerApp(3, 1, 2) + def test_policy_app_conn_mgr_fail_by_user(self): + stats = PolicyAppConnectionMgr(3, 1, 2) diags = [] self.assertTrue(stats.can_connect('10.10.10.10:10000', 'chuck', '10.10.10.10', diags)) self.assertFalse(stats.can_connect('10.10.10.10:10001', 'chuck', '10.10.10.10', diags)) self.assertTrue(len(diags) == 1) self.assertTrue('per user' in diags[0]) - def test_policy_app_conn_stats_fail_by_hosts(self): - stats = PolicyConnStatsPerApp(3, 2, 1) + def test_policy_app_conn_mgr_fail_by_hosts(self): + stats = PolicyAppConnectionMgr(3, 2, 1) diags = [] self.assertTrue(stats.can_connect('10.10.10.10:10000', 'chuck', '10.10.10.10', diags)) self.assertFalse(stats.can_connect('10.10.10.10:10001', 'chuck', '10.10.10.10', diags)) self.assertTrue(len(diags) == 1) self.assertTrue('per host' in diags[0]) - def test_policy_app_conn_stats_fail_by_user_hosts(self): - stats = PolicyConnStatsPerApp(3, 1, 1) + def test_policy_app_conn_mgr_fail_by_user_hosts(self): + stats = PolicyAppConnectionMgr(3, 1, 1) diags = [] self.assertTrue(stats.can_connect('10.10.10.10:10000', 'chuck', '10.10.10.10', diags)) self.assertFalse(stats.can_connect('10.10.10.10:10001', 'chuck', '10.10.10.10', diags)) @@ -268,8 +270,8 @@ class PolicyConnStatsPerAppTests(TestCase): self.assertTrue('per user' in diags[0] or 'per user' in diags[1]) self.assertTrue('per host' in diags[0] or 'per host' in diags[1]) - def test_policy_app_conn_stats_update(self): - stats = PolicyConnStatsPerApp(3, 1, 2) + def test_policy_app_conn_mgr_update(self): + stats = PolicyAppConnectionMgr(3, 1, 2) diags = [] self.assertTrue(stats.can_connect('10.10.10.10:10000', 'chuck', '10.10.10.10', diags)) self.assertFalse(stats.can_connect('10.10.10.10:10001', 'chuck', '10.10.10.10', diags)) @@ -279,18 +281,18 @@ class PolicyConnStatsPerAppTests(TestCase): stats.update(3, 2, 2) self.assertTrue(stats.can_connect('10.10.10.10:10001', 'chuck', '10.10.10.10', diags)) - def test_policy_app_conn_stats_create_bad_settings(self): + def test_policy_app_conn_mgr_create_bad_settings(self): denied = False try: - stats = PolicyConnStatsPerApp(-3, 1, 2) + stats = PolicyAppConnectionMgr(-3, 1, 2) except PolicyError: denied = True self.assertTrue(denied, "Failed to detect negative setting value.") - def test_policy_app_conn_stats_update_bad_settings(self): + def test_policy_app_conn_mgr_update_bad_settings(self): denied = False try: - stats = PolicyConnStatsPerApp(0, 0, 0) + stats = PolicyAppConnectionMgr(0, 0, 0) except PolicyError: denied = True self.assertFalse(denied, "Should allow all zeros.") @@ -300,5 +302,17 @@ class PolicyConnStatsPerAppTests(TestCase): denied = True self.assertTrue(denied, "Failed to detect negative setting value.") + def test_policy_app_conn_mgr_larger_counts(self): + stats = PolicyAppConnectionMgr(10000, 10000, 10000) + diags = [] + for i in range(0, 10000): + self.assertTrue(stats.can_connect('1.1.1.1:' + str(i), 'chuck', '1.1.1.1', diags)) + self.assertTrue(len(diags) == 0) + self.assertFalse(stats.can_connect('1.1.1.1:10000', 'chuck', '1.1.1.1', diags)) + self.assertTrue(len(diags) == 3) + self.assertTrue(stats.connections_active == 10000) + self.assertTrue(stats.connections_approved == 10000) + self.assertTrue(stats.connections_denied == 1) + if __name__ == '__main__': unittest.main(main_module()) --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@qpid.apache.org For additional commands, e-mail: commits-h...@qpid.apache.org