add extend volume tests:

- test_volume_extend
- test_volume_extend_with_size_smaller_than_original_size
- test_volume_extend_with_non_number_size
- test_volume_extend_with_None_size
- test_volume_extend_with_nonexistent_volume_id
- test_volume_extend_without_passing_volume_id

Closes-Bug: #1214251

Change-Id: I05d96109fdbdae47949126e852db6f274e3c2c4f
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index 09131e2..f12d4bb 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -16,7 +16,7 @@
 #    under the License.
 
 from tempest.api.volume.base import BaseVolumeTest
-from tempest.common.utils.data_utils import rand_name
+from tempest.common.utils import data_utils
 from tempest.test import attr
 from tempest.test import services
 from tempest.test import stresstest
@@ -32,8 +32,8 @@
         cls.image_client = cls.os.image_client
 
         # Create a test shared instance and volume for attach/detach tests
-        srv_name = rand_name(cls.__name__ + '-Instance-')
-        vol_name = rand_name(cls.__name__ + '-Volume-')
+        srv_name = data_utils.rand_name(cls.__name__ + '-Instance-')
+        vol_name = data_utils.rand_name(cls.__name__ + '-Volume-')
         resp, cls.server = cls.servers_client.create_server(srv_name,
                                                             cls.image_ref,
                                                             cls.flavor_ref)
@@ -102,7 +102,7 @@
         # it is shared with the other tests. After it is uploaded in Glance,
         # there is no way to delete it from Cinder, so we delete it from Glance
         # using the Glance image_client and from Cinder via tearDownClass.
-        image_name = rand_name('Image-')
+        image_name = data_utils.rand_name('Image-')
         resp, body = self.client.upload_volume(self.volume['id'],
                                                image_name,
                                                self.config.volume.disk_format)
@@ -112,6 +112,17 @@
         self.image_client.wait_for_image_status(image_id, 'active')
         self.client.wait_for_volume_status(self.volume['id'], 'available')
 
+    @attr(type='gate')
+    def test_volume_extend(self):
+        # Extend Volume Test.
+        extend_size = int(self.volume['size']) + 1
+        resp, body = self.client.extend_volume(self.volume['id'], extend_size)
+        self.assertEqual(202, resp.status)
+        self.client.wait_for_volume_status(self.volume['id'], 'available')
+        resp, volume = self.client.get_volume(self.volume['id'])
+        self.assertEqual(200, resp.status)
+        self.assertEqual(int(volume['size']), extend_size)
+
 
 class VolumesActionsTestXML(VolumesActionsTest):
     _interface = "xml"
diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py
index 3e2b6ad..4e95145 100644
--- a/tempest/api/volume/test_volumes_negative.py
+++ b/tempest/api/volume/test_volumes_negative.py
@@ -150,6 +150,41 @@
                           self.client.detach_volume,
                           'xxx')
 
+    @attr(type=['negative', 'gate'])
+    def test_volume_extend_with_size_smaller_than_original_size(self):
+        # Extend volume with smaller size than original size.
+        extend_size = 0
+        self.assertRaises(exceptions.BadRequest, self.client.extend_volume,
+                          self.volume['id'], extend_size)
+
+    @attr(type=['negative', 'gate'])
+    def test_volume_extend_with_non_number_size(self):
+        # Extend volume when size is non number.
+        extend_size = 'abc'
+        self.assertRaises(exceptions.BadRequest, self.client.extend_volume,
+                          self.volume['id'], extend_size)
+
+    @attr(type=['negative', 'gate'])
+    def test_volume_extend_with_None_size(self):
+        # Extend volume with None size.
+        extend_size = None
+        self.assertRaises(exceptions.BadRequest, self.client.extend_volume,
+                          self.volume['id'], extend_size)
+
+    @attr(type=['negative', 'gate'])
+    def test_volume_extend_with_nonexistent_volume_id(self):
+        # Extend volume size when volume is nonexistent.
+        extend_size = int(self.volume['size']) + 1
+        self.assertRaises(exceptions.NotFound, self.client.extend_volume,
+                          str(uuid.uuid4()), extend_size)
+
+    @attr(type=['negative', 'gate'])
+    def test_volume_extend_without_passing_volume_id(self):
+        # Extend volume size when passing volume id is None.
+        extend_size = int(self.volume['size']) + 1
+        self.assertRaises(exceptions.NotFound, self.client.extend_volume,
+                          None, extend_size)
+
 
 class VolumesNegativeTestXML(VolumesNegativeTest):
     _interface = 'xml'
diff --git a/tempest/services/volume/json/volumes_client.py b/tempest/services/volume/json/volumes_client.py
index 62a6e24..f054a2b 100644
--- a/tempest/services/volume/json/volumes_client.py
+++ b/tempest/services/volume/json/volumes_client.py
@@ -154,3 +154,13 @@
         except exceptions.NotFound:
             return True
         return False
+
+    def extend_volume(self, volume_id, extend_size):
+        """Extend a volume."""
+        post_body = {
+            'new_size': extend_size
+        }
+        post_body = json.dumps({'os-extend': post_body})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body, self.headers)
+        return resp, body
diff --git a/tempest/services/volume/xml/volumes_client.py b/tempest/services/volume/xml/volumes_client.py
index b59ec03..f5d8beb 100644
--- a/tempest/services/volume/xml/volumes_client.py
+++ b/tempest/services/volume/xml/volumes_client.py
@@ -227,3 +227,13 @@
         resp, body = self.post(url, str(Document(post_body)), self.headers)
         volume = xml_to_json(etree.fromstring(body))
         return resp, volume
+
+    def extend_volume(self, volume_id, extend_size):
+        """Extend a volume."""
+        post_body = Element("os-extend",
+                            new_size=extend_size)
+        url = 'volumes/%s/action' % str(volume_id)
+        resp, body = self.post(url, str(Document(post_body)), self.headers)
+        if body:
+            body = xml_to_json(etree.fromstring(body))
+        return resp, body