]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
CoraidDriver: support users that are not admin
authorJean-Baptiste RANSY <jean-baptiste.ransy@alyseo.com>
Thu, 21 Mar 2013 13:19:54 +0000 (14:19 +0100)
committerJean-Baptiste RANSY <jean-baptiste.ransy@alyseo.com>
Sun, 24 Mar 2013 13:37:33 +0000 (14:37 +0100)
Add support to the CoraidDriver to specify a subgroup (with admin
privileges).
Coraid storage orchestration allows you to allocate portions of your
SAN to specific users/groups.
The driver should support this and not assume logging in
as "admin" (equiv to "root").

Will add another parameter to cinder.conf, "coraid_group",
and if present, the driver will authenticate using the specified group
as well as the already available coraid_user parameter.

Fixes bug 1157354

Change-Id: I0d165955126adabce788ebdacd3a14a08edc8d8f

cinder/tests/test_coraid.py
cinder/volume/drivers/coraid.py

index 847d651d172332006a92cc2400d10504a1e7b0d0..52297d7462fefd952cb3c018eab3e59941caad2e 100644 (file)
@@ -23,6 +23,7 @@ from cinder import test
 from cinder.volume import configuration as conf
 from cinder.volume.drivers import coraid
 from cinder.volume.drivers.coraid import CoraidDriver
+from cinder.volume.drivers.coraid import CoraidESMException
 from cinder.volume.drivers.coraid import CoraidRESTClient
 
 import cookielib
@@ -32,7 +33,8 @@ LOG = logging.getLogger(__name__)
 
 
 fake_esm_ipaddress = "192.168.0.1"
-fake_esm_username = "admin"
+fake_esm_username = "darmok"
+fake_esm_group = "tanagra"
 fake_esm_password = "12345678"
 
 fake_volume_name = "volume-12345678-1234-1234-1234-1234567890ab"
@@ -87,6 +89,24 @@ fake_esm_success = {"category": "provider",
                     "metaCROp": "noAction",
                     "message": None}
 
+fake_group_fullpath = "admin group:%s" % (fake_esm_group)
+fake_group_id = 4
+fake_login_reply = {"values": [
+                    {"fullPath": fake_group_fullpath,
+                     "groupId": fake_group_id}],
+                    "message": "",
+                    "state": "adminSucceed",
+                    "metaCROp": "noAction"}
+
+fake_group_fail_fullpath = "fail group:%s" % (fake_esm_group)
+fake_group_fail_id = 5
+fake_login_reply_group_fail = {"values": [
+                               {"fullPath": fake_group_fail_fullpath,
+                                "groupId": fake_group_fail_id}],
+                               "message": "",
+                               "state": "adminSucceed",
+                               "metaCROp": "noAction"}
+
 
 class TestCoraidDriver(test.TestCase):
     def setUp(self):
@@ -98,6 +118,7 @@ class TestCoraidDriver(test.TestCase):
         configuration.append_config_values(mox.IgnoreArg())
         configuration.coraid_esm_address = fake_esm_ipaddress
         configuration.coraid_user = fake_esm_username
+        configuration.coraid_group = fake_esm_group
         configuration.coraid_password = fake_esm_password
 
         self.drv = CoraidDriver(configuration=configuration)
@@ -149,8 +170,32 @@ class TestCoraidRESTClient(test.TestCase):
                        lambda *_, **__: self.rest_mock)
         self.drv = CoraidRESTClient(fake_esm_ipaddress,
                                     fake_esm_username,
+                                    fake_esm_group,
                                     fake_esm_password)
 
+    def test__get_group_id(self):
+        setattr(self.rest_mock, '_get_group_id',
+                lambda *_: True)
+        self.assertEquals(self.drv._get_group_id(fake_esm_group,
+                                                 fake_login_reply),
+                          fake_group_id)
+
+    def test__set_group(self):
+        setattr(self.rest_mock, '_set_group',
+                lambda *_: fake_group_id)
+        self.stubs.Set(CoraidRESTClient, '_esm',
+                       lambda *_: fake_login_reply)
+        self.drv._set_group(fake_login_reply)
+
+    def test__set_group_fails_no_group(self):
+        setattr(self.rest_mock, '_set_group',
+                lambda *_: False)
+        self.stubs.Set(CoraidRESTClient, '_esm',
+                       lambda *_: fake_login_reply_group_fail)
+        self.assertRaises(CoraidESMException,
+                          self.drv._set_group,
+                          fake_login_reply_group_fail)
+
     def test__configure(self):
         setattr(self.rest_mock, '_configure',
                 lambda *_: True)
index 260a48aeae70813d1de01207b689ddf5622245f5..6d3a2109adac98ea4404dd205f83f4f82a929848 100644 (file)
@@ -18,6 +18,7 @@
 Desc    : Driver to store volumes on Coraid Appliances.
 Require : Coraid EtherCloud ESM, Coraid VSX and Coraid SRX.
 Author  : Jean-Baptiste RANSY <openstack@alyseo.com>
+Contrib : Larry Matter <support@coraid.com>
 """
 
 import cookielib
@@ -45,6 +46,9 @@ coraid_opts = [
     cfg.StrOpt('coraid_user',
                default='admin',
                help='User name to connect to Coraid ESM'),
+    cfg.StrOpt('coraid_group',
+               default=False,
+               help='Group name of coraid_user (must have admin privilege)'),
     cfg.StrOpt('coraid_password',
                default='password',
                help='Password to connect to Coraid ESM'),
@@ -74,9 +78,10 @@ class CoraidESMException(CoraidException):
 class CoraidRESTClient(object):
     """Executes volume driver commands on Coraid ESM EtherCloud Appliance."""
 
-    def __init__(self, ipaddress, user, password):
+    def __init__(self, ipaddress, user, group, password):
         self.url = "https://%s:8443/" % ipaddress
         self.user = user
+        self.group = group
         self.password = password
         self.session = False
         self.cookiejar = cookielib.CookieJar()
@@ -96,6 +101,7 @@ class CoraidRESTClient(object):
                 self.session = time.time() + 1100
                 msg = _('Update session cookie %(session)s')
                 LOG.debug(msg % dict(session=self.session))
+                self._set_group(reply)
                 return True
             else:
                 errmsg = response.get('message', '')
@@ -103,6 +109,37 @@ class CoraidRESTClient(object):
                 raise CoraidESMException(msg % dict(message=errmsg))
         return True
 
+    def _set_group(self, reply):
+        """Set effective group."""
+        if self.group:
+            group = self.group
+            groupId = self._get_group_id(group, reply)
+            if groupId:
+                url = ('admin?op=setRbacGroup&groupId=%s' % (groupId))
+                data = 'Group'
+                reply = self._esm(url, data)
+                if reply.get('state') == 'adminSucceed':
+                    return True
+                else:
+                    errmsg = reply.get('message', '')
+                    msg = _('Error while trying to set group: %(message)s')
+                    raise CoraidRESTException(msg % dict(message=errmsg))
+            else:
+                msg = _('Unable to find group: %(group)s')
+                raise CoraidESMException(msg % dict(group=group))
+        return True
+
+    def _get_group_id(self, groupName, loginResult):
+        """Map group name to group ID."""
+        # NOTE(lmatter): All other groups are under the admin group
+        fullName = "admin group:%s" % groupName
+        groupId = False
+        for kid in loginResult['values']:
+            fullPath = kid['fullPath']
+            if fullPath == fullName:
+                return kid['groupId']
+        return False
+
     def _esm(self, url=False, data=None):
         """
         _esm represent the entry point to send requests to ESM Appliance.
@@ -240,6 +277,7 @@ class CoraidDriver(driver.VolumeDriver):
         """Initialize the volume driver."""
         self.esm = CoraidRESTClient(self.configuration.coraid_esm_address,
                                     self.configuration.coraid_user,
+                                    self.configuration.coraid_group,
                                     self.configuration.coraid_password)
 
     def check_for_setup_error(self):