Ensure tempest tests don't assume IP address allocation strategy

With the advent of pluggable IPAM in Neutron, it is not safe to
assume any particular algorithm is used to allocate IP addresses.
This change ensures the fixed IP sent to Neutron when we need a
specific fixed IP doesn't conflict as it is unsafe to simply take
the numerically highest IP address and add 1 to it. This change
introduces a helper method that finds an IP address not in use,
making it so we can safely ask for that specific IP address.

Change-Id: I84195b0eb63b7ca6a4e00becbe09e579ff8b718e
Partial-Bug: #1543094
diff --git a/tempest/common/utils/net_utils.py b/tempest/common/utils/net_utils.py
new file mode 100644
index 0000000..d98fb32
--- /dev/null
+++ b/tempest/common/utils/net_utils.py
@@ -0,0 +1,48 @@
+# Copyright 2016 Hewlett Packard Enterprise Development Company
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import itertools
+import netaddr
+
+from tempest.lib import exceptions as lib_exc
+
+
+def get_unused_ip_addresses(ports_client, subnets_client,
+                            network_id, subnet_id, count):
+
+    """Return a list with the specified number of unused IP addresses
+
+    This method uses the given ports_client to find the specified number of
+    unused IP addresses on the given subnet using the supplied subnets_client
+    """
+
+    ports = ports_client.list_ports(network_id=network_id)['ports']
+    subnet = subnets_client.show_subnet(subnet_id)
+    ip_net = netaddr.IPNetwork(subnet['subnet']['cidr'])
+    subnet_set = netaddr.IPSet(ip_net.iter_hosts())
+    alloc_set = netaddr.IPSet()
+
+    # prune out any addresses already allocated to existing ports
+    for port in ports:
+        for fixed_ip in port.get('fixed_ips'):
+            alloc_set.add(fixed_ip['ip_address'])
+
+    av_set = subnet_set - alloc_set
+    ip_list = [str(ip) for ip in itertools.islice(av_set, count)]
+
+    if len(ip_list) != count:
+        msg = "Insufficient IP addresses available"
+        raise lib_exc.BadRequest(message=msg)
+
+    return ip_list