javelin: implement floating-ip support

Consider multi-node environment when use_floatingip_for_ssh is True, so
we can run Javelin in multi-node environment.

* create: If CONF.use_floatingip_for_ssh is at True, we create and associate a
  floating IP to a server.
* check: if the server has a floating IP, we
  validate the server connectivity with it.
* destroy: disassociate the floating IP from the server.

This feature is useful when namespaces are
not reachable from the node where we run Javelin2.

Change-Id: I6c6d998588621b503bda941b54cd5c752eefb684
diff --git a/tempest/cmd/javelin.py b/tempest/cmd/javelin.py
index 4c09bd2..4f88823 100755
--- a/tempest/cmd/javelin.py
+++ b/tempest/cmd/javelin.py
@@ -118,6 +118,7 @@
 from tempest.openstack.common import log as logging
 from tempest.openstack.common import timeutils
 from tempest.services.compute.json import flavors_client
+from tempest.services.compute.json import floating_ips_client
 from tempest.services.compute.json import security_groups_client
 from tempest.services.compute.json import servers_client
 from tempest.services.identity.v2.json import identity_client
@@ -194,6 +195,8 @@
                                                         **compute_params)
         self.flavors = flavors_client.FlavorsClientJSON(_auth,
                                                         **compute_params)
+        self.floating_ips = floating_ips_client.FloatingIPsClientJSON(
+            _auth, **compute_params)
         self.secgroups = security_groups_client.SecurityGroupsClientJSON(
             _auth, **compute_params)
         self.objects = object_client.ObjectClient(_auth,
@@ -451,15 +454,31 @@
             # validate neutron is enabled and ironic disabled:
             if (CONF.service_available.neutron and
                     not CONF.baremetal.driver_enabled):
+                _floating_is_alive = False
                 for network_name, body in found['addresses'].items():
                     for addr in body:
                         ip = addr['addr']
-                        if addr.get('OS-EXT-IPS:type', 'fixed') == 'fixed':
+                        # If floatingip_for_ssh is at True, it's assumed
+                        # you want to use the floating IP to reach the server,
+                        # fallback to fixed IP, then other type.
+                        # This is useful in multi-node environment.
+                        if CONF.use_floatingip_for_ssh:
+                            if addr.get('OS-EXT-IPS:type',
+                                        'floating') == 'floating':
+                                self._ping_ip(ip, 60)
+                                _floating_is_alive = True
+                        elif addr.get('OS-EXT-IPS:type', 'fixed') == 'fixed':
                             namespace = _get_router_namespace(client,
                                                               network_name)
                             self._ping_ip(ip, 60, namespace)
                         else:
                             self._ping_ip(ip, 60)
+                # if floatingip_for_ssh is at True, validate found a
+                # floating IP and ping worked.
+                if CONF.use_floatingip_for_ssh:
+                    self.assertTrue(_floating_is_alive,
+                                    "Server %s has no floating IP." %
+                                    server['name'])
             else:
                 addr = found['addresses']['private'][0]['addr']
                 self._ping_ip(addr, 60)
@@ -838,6 +857,10 @@
         # create to security group(s) after server spawning
         for secgroup in server['secgroups']:
             client.servers.add_security_group(server_id, secgroup)
+        if CONF.compute.use_floatingip_for_ssh:
+            floating_ip = client.floating_ips.create_floating_ip()
+            client.floating_ips.associate_floating_ip_to_server(
+                floating_ip['ip'], server_id)
 
 
 def destroy_servers(servers):
@@ -847,13 +870,31 @@
     for server in servers:
         client = client_for_user(server['owner'])
 
-        response = _get_server_by_name(client, server['name'])
-        if not response:
+        res = _get_server_by_name(client, server['name'])
+        if not res:
             LOG.info("Server '%s' does not exist" % server['name'])
             continue
 
-        client.servers.delete_server(response['id'])
-        client.servers.wait_for_server_termination(response['id'],
+        # we iterate all interfaces until we find a floating IP
+        # and stop looping after dropping it.
+        def _find_first_floating():
+            if (CONF.service_available.neutron and
+                    not CONF.baremetal.driver_enabled and
+                    CONF.use_floatingip_for_ssh):
+                for body in res['addresses'].items():
+                    for addr in body:
+                        ip = addr['addr']
+                        if addr.get('OS-EXT-IPS:type',
+                                    'floating') == 'floating':
+                            (client.floating_ips.
+                             disassociate_floating_ip_from_server(
+                                 ip, res['id']))
+                            client.floating_ips.delete_floating_ip(ip)
+                            return
+
+        _find_first_floating()
+        client.servers.delete_server(res['id'])
+        client.servers.wait_for_server_termination(res['id'],
                                                    ignore_error=True)