Merge "Fixes LP#902358 - Test case for Floating IPs"
diff --git a/tempest/openstack.py b/tempest/openstack.py
index a3fe71d..3504589 100644
--- a/tempest/openstack.py
+++ b/tempest/openstack.py
@@ -7,6 +7,7 @@
 from tempest.services.nova.json.extensions_client import ExtensionsClient
 from tempest.services.nova.json.security_groups_client \
 import SecurityGroupsClient
+from tempest.services.nova.json.floating_ips_client import FloatingIPsClient
 
 from tempest.services.nova.json.keypairs_client import KeyPairsClient
 
@@ -55,6 +56,11 @@
                                               self.config.nova.api_key,
                                               self.config.nova.auth_url,
                                               self.config.nova.tenant_name)
+            self.floating_ips_client = FloatingIPsClient(self.config,
+                                              self.config.nova.username,
+                                              self.config.nova.api_key,
+                                              self.config.nova.auth_url,
+                                              self.config.nova.tenant_name)
         else:
             #Assuming basic/native authentication
             self.servers_client = ServersClient(self.config,
@@ -85,6 +91,10 @@
                                               self.config.nova.username,
                                               self.config.nova.api_key,
                                               self.config.nova.auth_url)
+            self.floating_ips_client = FloatingIPsClient(self.config,
+                                              self.config.nova.username,
+                                              self.config.nova.api_key,
+                                              self.config.nova.auth_url)
 
 
 class ServiceManager(object):
diff --git a/tempest/services/nova/json/floating_ips_client.py b/tempest/services/nova/json/floating_ips_client.py
new file mode 100644
index 0000000..8bd276f
--- /dev/null
+++ b/tempest/services/nova/json/floating_ips_client.py
@@ -0,0 +1,63 @@
+from tempest.common import rest_client
+from tempest import exceptions
+import json
+
+
+class FloatingIPsClient(object):
+    def __init__(self, config, username, key, auth_url, tenant_name=None):
+        self.config = config
+        self.client = rest_client.RestClient(config, username, key,
+                                             auth_url, tenant_name)
+
+    def list_floating_ips(self, params=None):
+        """Returns a list of all floating IPs filtered by any parameters"""
+        url = 'os-floating-ips'
+        if params != None:
+            param_list = []
+            for param, value in params.iteritems():
+                param_list.append("%s=%s&" % (param, value))
+            url += '?' + ' '.join(param_list)
+        resp, body = self.client.get(url)
+        body = json.loads(body)
+        return resp, body['floating_ips']
+
+    def get_floating_ip_details(self, floating_ip_id):
+        """Get the details of a floating IP"""
+        url = "os-floating-ips/%s" % str(floating_ip_id)
+        resp, body = self.client.get(url)
+        body = json.loads(body)
+        if resp.status == 404:
+            raise exceptions.NotFound(body)
+        return resp, body['floating_ip']
+
+    def create_floating_ip(self):
+        """Allocate a floating IP to the project"""
+        url = 'os-floating-ips'
+        resp, body = self.client.post(url, None, None)
+        body = json.loads(body)
+        return resp, body['floating_ip']
+
+    def delete_floating_ip(self, floating_ip_id):
+        """Deletes the provided floating IP from the project"""
+        url = "os-floating-ips/%s" % str(floating_ip_id)
+        resp, body = self.client.delete(url)
+        return resp, body
+
+    def associate_floating_ip_to_server(self, floating_ip_id, fixed_ip_addr):
+        """Associate the provided floating IP to a specific server"""
+        url = "os-floating-ips/%s/associate" % str(floating_ip_id)
+        post_body = {
+            'associate_address': {
+                'fixed_ip': fixed_ip_addr,
+            }
+        }
+
+        post_body = json.dumps(post_body)
+        resp, body = self.client.post(url, post_body, None)
+        return resp, body
+
+    def disassociate_floating_ip_from_server(self, floating_ip_id):
+        """Disassociate the provided floating IP from a specific server"""
+        url = "os-floating-ips/%s/disassociate" % str(floating_ip_id)
+        resp, body = self.client.post(url, None, None)
+        return resp, body
diff --git a/tempest/tests/test_floating_ips_actions.py b/tempest/tests/test_floating_ips_actions.py
new file mode 100644
index 0000000..ab9e22d
--- /dev/null
+++ b/tempest/tests/test_floating_ips_actions.py
@@ -0,0 +1,160 @@
+from nose.plugins.attrib import attr
+from tempest import openstack
+import unittest2 as unittest
+from tempest import exceptions
+from tempest.common.utils.data_utils import rand_name
+
+
+class FloatingIPsTest(unittest.TestCase):
+    server_id = None
+    floating_ip_id = None
+
+    @classmethod
+    def setUpClass(cls):
+        cls.os = openstack.Manager()
+        cls.client = cls.os.floating_ips_client
+        cls.servers_client = cls.os.servers_client
+        cls.config = cls.os.config
+        cls.image_ref = cls.config.env.image_ref
+        cls.flavor_ref = cls.config.env.flavor_ref
+        #Server creation
+        resp, server = cls.servers_client.create_server('floating-server',
+                                                        cls.image_ref,
+                                                        cls.flavor_ref)
+        cls.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
+        cls.server_id = server['id']
+        resp, body = cls.servers_client.get_server(server['id'])
+        cls.fixed_ip_addr = body['addresses']['private'][0]['addr']
+        #Floating IP creation
+        resp, body = cls.client.create_floating_ip()
+        cls.floating_ip_id = body['id']
+        #Generating a nonexistant floatingIP id
+        cls.floating_ip_ids = []
+        resp, body = cls.client.list_floating_ips()
+        for i in range(len(body)):
+            cls.floating_ip_ids.append(body[i]['id'])
+        while True:
+            cls.non_exist_id = rand_name('999')
+            if cls.non_exist_id not in cls.floating_ip_ids:
+                break
+
+    @classmethod
+    def tearDownClass(cls):
+        #Deleting the server which is created in this method
+        resp, body = cls.servers_client.delete_server(cls.server_id)
+        #Deleting the floating IP which is created in this method
+        resp, body = cls.client.delete_floating_ip(cls.floating_ip_id)
+
+    @attr(type='positive')
+    def test_allocate_floating_ip(self):
+        """
+        Positive test:Allocation of a new floating IP to a project
+        should be succesfull
+        """
+        resp, body = self.client.create_floating_ip()
+        self.assertEqual(200, resp.status)
+        floating_ip_id_allocated = body['id']
+        resp, floating_ip_details = \
+                self.client.get_floating_ip_details(floating_ip_id_allocated)
+        #Checking if the details of allocated IP is in list of floating IP
+        resp, body = self.client.list_floating_ips()
+        self.assertTrue(floating_ip_details in body)
+        #Deleting the floating IP which is created in this method
+        self.client.delete_floating_ip(floating_ip_id_allocated)
+
+    @attr(type='positive')
+    def test_delete_floating_ip(self):
+        """
+        Positive test:Deletion of valid floating IP from project
+        should be succesfull
+        """
+        #Creating the floating IP that is to be deleted in this method
+        resp, floating_ip_body = self.client.create_floating_ip()
+        #Storing the details of floating IP before deleting it
+        resp, floating_ip_details = \
+                self.client.get_floating_ip_details(floating_ip_body['id'])
+        #Deleting the floating IP from the project
+        resp, body = self.client.delete_floating_ip(floating_ip_body['id'])
+        self.assertEqual(202, resp.status)
+        #Listing the floating IPs and checking the existence
+        #of deleted floating IP
+        resp, body = self.client.list_floating_ips()
+        self.assertTrue(floating_ip_details not in body)
+
+    @attr(type='positive')
+    def test_associate_floating_ip(self):
+        """
+        Positive test:Associate the provided floating IP to a specific server
+        should be successfull
+       l"""
+        #Association of floating IP to fixed IP address
+        resp, body =\
+        self.client.associate_floating_ip_to_server(self.floating_ip_id,
+                                                    self.fixed_ip_addr)
+        self.assertEqual(202, resp.status)
+        #Disassociation of floating IP that was associated in this method
+        resp, body = \
+            self.client.disassociate_floating_ip_from_server(floating_ip_id)
+
+    @attr(type='positive')
+    def test_dissociate_floating_ip(self):
+        """
+        Positive test:Dissociate the provided floating IP
+        from a specific server should be successfull
+        """
+        #Association of floating IP to a specific server
+        #so as to check dissociation
+        resp, body = \
+            self.client.associate_floating_ip_to_server(self.floating_ip_id,
+                                                        self.fixed_ip_addr)
+        #Disassociation of floating IP
+        resp, body = \
+        self.client.disassociate_floating_ip_from_server(self.floating_ip_id)
+        self.assertEqual(202, resp.status)
+
+    @attr(type='negative')
+    def test_delete_nonexistant_floating_ip(self):
+        """
+
+        Negative test:Deletion of a nonexistant floating IP
+        from project should fail
+        """
+        #Deleting the non existant floating IP
+        try:
+            resp, body = self.client.delete_floating_ip(self.non_exist_id)
+        except exceptions.NotFound:
+            pass
+        else:
+            self.fail('Should not be able to delete a nonexistant floating IP')
+
+    @attr(type='negative')
+    def test_associate_nonexistant_floating_ip(self):
+        """
+        Negative test:Association of a non existant floating IP
+        to specific server should fail
+        """
+        #Associating non existant floating IP
+        try:
+            resp, body = \
+            self.client.associate_floating_ip_to_server(self.non_exist_id,
+                                                        self.fixed_ip_addr)
+        except exceptions.NotFound:
+            pass
+        else:
+            self.fail('Should not be able to associate'
+                      ' a nonexistant floating IP')
+
+    @attr(type='negative')
+    def test_dissociate_nonexistant_floating_ip(self):
+        """
+        Negative test:Dissociation of a non existant floating IP should fail
+        """
+        #Dissociating non existant floating IP
+        try:
+            resp, body = \
+            self.client.disassociate_floating_ip_from_server(self.non_exist_id)
+        except exceptions.NotFound:
+            pass
+        else:
+            self.fail('Should not be able to dissociate'
+                      ' a nonexistant floating IP')
diff --git a/tempest/tests/test_list_floating_ips.py b/tempest/tests/test_list_floating_ips.py
new file mode 100644
index 0000000..2de6d6b
--- /dev/null
+++ b/tempest/tests/test_list_floating_ips.py
@@ -0,0 +1,82 @@
+from nose.plugins.attrib import attr
+from tempest import openstack
+import unittest2 as unittest
+from tempest import exceptions
+from tempest.common.utils.data_utils import rand_name
+
+
+class FloatingIPDetailsTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.os = openstack.Manager()
+        cls.client = cls.os.floating_ips_client
+        cls.floating_ip = []
+        cls.floating_ip_id = []
+        cls.random_number = 0
+        for i in range(3):
+            resp, body = cls.client.create_floating_ip()
+            cls.floating_ip.append(body)
+            cls.floating_ip_id.append(body['id'])
+
+    @classmethod
+    def tearDownClass(cls):
+        for i in range(3):
+            cls.client.delete_floating_ip(cls.floating_ip_id[i])
+
+    @attr(type='positive')
+    def test_list_floating_ips(self):
+        """Positive test:Should return the list of floating IPs"""
+        resp, body = self.client.list_floating_ips()
+        self.assertEqual(200, resp.status)
+        floating_ips = body
+        self.assertNotEqual(0, len(floating_ips),
+                            "Expected floating IPs. Got zero.")
+        for i in range(3):
+            self.assertTrue(self.floating_ip[i] in floating_ips)
+
+    @attr(type='positive')
+    def test_get_floating_ip_details(self):
+        """Positive test:Should be able to GET the details of floatingIP"""
+        #Creating a floating IP for which details are to be checked
+        resp, body = self.client.create_floating_ip()
+        floating_ip_instance_id = body['instance_id']
+        floating_ip_ip = body['ip']
+        floating_ip_fixed_ip = body['fixed_ip']
+        floating_ip_id = body['id']
+        resp, body = \
+            self.client.get_floating_ip_details(floating_ip_id)
+        self.assertEqual(200, resp.status)
+        #Comparing the details of floating IP
+        self.assertEqual(floating_ip_instance_id,
+                            body['instance_id'])
+        self.assertEqual(floating_ip_ip, body['ip'])
+        self.assertEqual(floating_ip_fixed_ip,
+                            body['fixed_ip'])
+        self.assertEqual(floating_ip_id, body['id'])
+        #Deleting the floating IP created in this method
+        self.client.delete_floating_ip(floating_ip_id)
+
+    @attr(type='negative')
+    def test_get_nonexistant_floating_ip_details(self):
+        """
+        Negative test:Should not be able to GET the details
+        of nonexistant floating IP
+        """
+        floating_ip_id = []
+        resp, body = self.client.list_floating_ips()
+        for i in range(len(body)):
+            floating_ip_id.append(body[i]['id'])
+        #Creating a nonexistant floatingIP id
+        while True:
+            non_exist_id = rand_name('999')
+            if non_exist_id not in floating_ip_id:
+                break
+        try:
+            resp, body = \
+            self.client.get_floating_ip_details(non_exist_id)
+        except exceptions.NotFound:
+            pass
+        else:
+            self.fail('Should not be able to GET the details from a'
+                      'nonexistant floating IP')