You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ch...@apache.org on 2015/12/16 23:28:40 UTC

qpid-dispatch git commit: Break utility code into separate source module.

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 <cr...@redhat.com>
Authored: Wed Dec 16 17:27:57 2015 -0500
Committer: Chuck Rolke <cr...@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-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org