~ttx/nova/diablo-rbp-merge

« back to all changes in this revision

Viewing changes to nova/volume/driver.py

  • Committer: Thierry Carrez
  • Date: 2011-09-09 09:24:28 UTC
  • mfrom: (1130.337.61 nova)
  • Revision ID: thierry@openstack.org-20110909092428-slkw8ue1y2geqb0j
Merge RBP development from trunk (rev1541)

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
 
23
23
import time
24
24
import os
 
25
from xml.etree import ElementTree
25
26
 
26
27
from nova import exception
27
28
from nova import flags
28
29
from nova import log as logging
29
30
from nova import utils
 
31
from nova.volume import volume_types
30
32
 
31
33
 
32
34
LOG = logging.getLogger("nova.volume.driver")
212
214
        """Make sure volume is exported."""
213
215
        raise NotImplementedError()
214
216
 
 
217
    def get_volume_stats(self, refresh=False):
 
218
        """Return the current state of the volume service. If 'refresh' is
 
219
           True, run the update first."""
 
220
        return None
 
221
 
215
222
 
216
223
class AOEDriver(VolumeDriver):
217
224
    """Implements AOE specific volume commands."""
802
809
            if match:
803
810
                matches.append(entry)
804
811
        return matches
 
812
 
 
813
 
 
814
class ZadaraBEDriver(ISCSIDriver):
 
815
    """Performs actions to configure Zadara BE module."""
 
816
 
 
817
    def _is_vsa_volume(self, volume):
 
818
        return volume_types.is_vsa_volume(volume['volume_type_id'])
 
819
 
 
820
    def _is_vsa_drive(self, volume):
 
821
        return volume_types.is_vsa_drive(volume['volume_type_id'])
 
822
 
 
823
    def _not_vsa_volume_or_drive(self, volume):
 
824
        """Returns True if volume is not VSA BE volume."""
 
825
        if not volume_types.is_vsa_object(volume['volume_type_id']):
 
826
            LOG.debug(_("\tVolume %s is NOT VSA volume"), volume['name'])
 
827
            return True
 
828
        else:
 
829
            return False
 
830
 
 
831
    def check_for_setup_error(self):
 
832
        """No setup necessary for Zadara BE."""
 
833
        pass
 
834
 
 
835
    """ Volume Driver methods """
 
836
    def create_volume(self, volume):
 
837
        """Creates BE volume."""
 
838
        if self._not_vsa_volume_or_drive(volume):
 
839
            return super(ZadaraBEDriver, self).create_volume(volume)
 
840
 
 
841
        if self._is_vsa_volume(volume):
 
842
            LOG.debug(_("\tFE VSA Volume %s creation - do nothing"),
 
843
                        volume['name'])
 
844
            return
 
845
 
 
846
        if int(volume['size']) == 0:
 
847
            sizestr = '0'   # indicates full-partition
 
848
        else:
 
849
            sizestr = '%s' % (int(volume['size']) << 30)    # size in bytes
 
850
 
 
851
        # Set the qos-str to default type sas
 
852
        qosstr = 'SAS_1000'
 
853
        volume_type = volume_types.get_volume_type(None,
 
854
                                            volume['volume_type_id'])
 
855
        if volume_type is not None:
 
856
            qosstr = volume_type['extra_specs']['drive_type'] + \
 
857
                     ("_%s" % volume_type['extra_specs']['drive_size'])
 
858
 
 
859
        vsa_id = None
 
860
        for i in volume.get('volume_metadata'):
 
861
            if i['key'] == 'to_vsa_id':
 
862
                vsa_id = i['value']
 
863
                break
 
864
 
 
865
        try:
 
866
            self._sync_exec('/var/lib/zadara/bin/zadara_sncfg',
 
867
                            'create_qospart',
 
868
                            '--qos', qosstr,
 
869
                            '--pname', volume['name'],
 
870
                            '--psize', sizestr,
 
871
                            '--vsaid', vsa_id,
 
872
                            run_as_root=True,
 
873
                            check_exit_code=0)
 
874
        except exception.ProcessExecutionError:
 
875
            LOG.debug(_("VSA BE create_volume for %s failed"), volume['name'])
 
876
            raise
 
877
 
 
878
        LOG.debug(_("VSA BE create_volume for %s succeeded"), volume['name'])
 
879
 
 
880
    def delete_volume(self, volume):
 
881
        """Deletes BE volume."""
 
882
        if self._not_vsa_volume_or_drive(volume):
 
883
            return super(ZadaraBEDriver, self).delete_volume(volume)
 
884
 
 
885
        if self._is_vsa_volume(volume):
 
886
            LOG.debug(_("\tFE VSA Volume %s deletion - do nothing"),
 
887
                        volume['name'])
 
888
            return
 
889
 
 
890
        try:
 
891
            self._sync_exec('/var/lib/zadara/bin/zadara_sncfg',
 
892
                            'delete_partition',
 
893
                            '--pname', volume['name'],
 
894
                            run_as_root=True,
 
895
                            check_exit_code=0)
 
896
        except exception.ProcessExecutionError:
 
897
            LOG.debug(_("VSA BE delete_volume for %s failed"), volume['name'])
 
898
            return
 
899
 
 
900
        LOG.debug(_("VSA BE delete_volume for %s suceeded"), volume['name'])
 
901
 
 
902
    def local_path(self, volume):
 
903
        if self._not_vsa_volume_or_drive(volume):
 
904
            return super(ZadaraBEDriver, self).local_path(volume)
 
905
 
 
906
        if self._is_vsa_volume(volume):
 
907
            LOG.debug(_("\tFE VSA Volume %s local path call - call discover"),
 
908
                        volume['name'])
 
909
            return super(ZadaraBEDriver, self).discover_volume(None, volume)
 
910
 
 
911
        raise exception.Error(_("local_path not supported"))
 
912
 
 
913
    def ensure_export(self, context, volume):
 
914
        """ensure BE export for a volume"""
 
915
        if self._not_vsa_volume_or_drive(volume):
 
916
            return super(ZadaraBEDriver, self).ensure_export(context, volume)
 
917
 
 
918
        if self._is_vsa_volume(volume):
 
919
            LOG.debug(_("\tFE VSA Volume %s ensure export - do nothing"),
 
920
                        volume['name'])
 
921
            return
 
922
 
 
923
        try:
 
924
            iscsi_target = self.db.volume_get_iscsi_target_num(context,
 
925
                                                           volume['id'])
 
926
        except exception.NotFound:
 
927
            LOG.info(_("Skipping ensure_export. No iscsi_target " +
 
928
                       "provisioned for volume: %d"), volume['id'])
 
929
            return
 
930
 
 
931
        try:
 
932
            ret = self._common_be_export(context, volume, iscsi_target)
 
933
        except exception.ProcessExecutionError:
 
934
            return
 
935
        return ret
 
936
 
 
937
    def create_export(self, context, volume):
 
938
        """create BE export for a volume"""
 
939
        if self._not_vsa_volume_or_drive(volume):
 
940
            return super(ZadaraBEDriver, self).create_export(context, volume)
 
941
 
 
942
        if self._is_vsa_volume(volume):
 
943
            LOG.debug(_("\tFE VSA Volume %s create export - do nothing"),
 
944
                        volume['name'])
 
945
            return
 
946
 
 
947
        self._ensure_iscsi_targets(context, volume['host'])
 
948
        iscsi_target = self.db.volume_allocate_iscsi_target(context,
 
949
                                                          volume['id'],
 
950
                                                          volume['host'])
 
951
        try:
 
952
            ret = self._common_be_export(context, volume, iscsi_target)
 
953
        except:
 
954
            raise exception.ProcessExecutionError
 
955
        return ret
 
956
 
 
957
    def remove_export(self, context, volume):
 
958
        """Removes BE export for a volume."""
 
959
        if self._not_vsa_volume_or_drive(volume):
 
960
            return super(ZadaraBEDriver, self).remove_export(context, volume)
 
961
 
 
962
        if self._is_vsa_volume(volume):
 
963
            LOG.debug(_("\tFE VSA Volume %s remove export - do nothing"),
 
964
                        volume['name'])
 
965
            return
 
966
 
 
967
        try:
 
968
            iscsi_target = self.db.volume_get_iscsi_target_num(context,
 
969
                                                           volume['id'])
 
970
        except exception.NotFound:
 
971
            LOG.info(_("Skipping remove_export. No iscsi_target " +
 
972
                       "provisioned for volume: %d"), volume['id'])
 
973
            return
 
974
 
 
975
        try:
 
976
            self._sync_exec('/var/lib/zadara/bin/zadara_sncfg',
 
977
                        'remove_export',
 
978
                        '--pname', volume['name'],
 
979
                        '--tid', iscsi_target,
 
980
                        run_as_root=True,
 
981
                        check_exit_code=0)
 
982
        except exception.ProcessExecutionError:
 
983
            LOG.debug(_("VSA BE remove_export for %s failed"), volume['name'])
 
984
            return
 
985
 
 
986
    def create_snapshot(self, snapshot):
 
987
        """Nothing required for snapshot"""
 
988
        if self._not_vsa_volume_or_drive(volume):
 
989
            return super(ZadaraBEDriver, self).create_snapshot(volume)
 
990
 
 
991
        pass
 
992
 
 
993
    def delete_snapshot(self, snapshot):
 
994
        """Nothing required to delete a snapshot"""
 
995
        if self._not_vsa_volume_or_drive(volume):
 
996
            return super(ZadaraBEDriver, self).delete_snapshot(volume)
 
997
 
 
998
        pass
 
999
 
 
1000
    """ Internal BE Volume methods """
 
1001
    def _common_be_export(self, context, volume, iscsi_target):
 
1002
        """
 
1003
        Common logic that asks zadara_sncfg to setup iSCSI target/lun for
 
1004
        this volume
 
1005
        """
 
1006
        (out, err) = self._sync_exec(
 
1007
                                '/var/lib/zadara/bin/zadara_sncfg',
 
1008
                                'create_export',
 
1009
                                '--pname', volume['name'],
 
1010
                                '--tid', iscsi_target,
 
1011
                                run_as_root=True,
 
1012
                                check_exit_code=0)
 
1013
 
 
1014
        result_xml = ElementTree.fromstring(out)
 
1015
        response_node = result_xml.find("Sn")
 
1016
        if response_node is None:
 
1017
            msg = "Malformed response from zadara_sncfg"
 
1018
            raise exception.Error(msg)
 
1019
 
 
1020
        sn_ip = response_node.findtext("SnIp")
 
1021
        sn_iqn = response_node.findtext("IqnName")
 
1022
        iscsi_portal = sn_ip + ":3260," + ("%s" % iscsi_target)
 
1023
 
 
1024
        model_update = {}
 
1025
        model_update['provider_location'] = ("%s %s" %
 
1026
                                             (iscsi_portal,
 
1027
                                              sn_iqn))
 
1028
        return model_update
 
1029
 
 
1030
    def _get_qosgroup_summary(self):
 
1031
        """gets the list of qosgroups from Zadara BE"""
 
1032
        try:
 
1033
            (out, err) = self._sync_exec(
 
1034
                                        '/var/lib/zadara/bin/zadara_sncfg',
 
1035
                                        'get_qosgroups_xml',
 
1036
                                        run_as_root=True,
 
1037
                                        check_exit_code=0)
 
1038
        except exception.ProcessExecutionError:
 
1039
            LOG.debug(_("Failed to retrieve QoS info"))
 
1040
            return {}
 
1041
 
 
1042
        qos_groups = {}
 
1043
        result_xml = ElementTree.fromstring(out)
 
1044
        for element in result_xml.findall('QosGroup'):
 
1045
            qos_group = {}
 
1046
            # get the name of the group.
 
1047
            # If we cannot find it, forget this element
 
1048
            group_name = element.findtext("Name")
 
1049
            if not group_name:
 
1050
                continue
 
1051
 
 
1052
            # loop through all child nodes & fill up attributes of this group
 
1053
            for child in element.getchildren():
 
1054
                # two types of elements -  property of qos-group & sub property
 
1055
                # classify them accordingly
 
1056
                if child.text:
 
1057
                    qos_group[child.tag] = int(child.text) \
 
1058
                        if child.text.isdigit() else child.text
 
1059
                else:
 
1060
                    subelement = {}
 
1061
                    for subchild in child.getchildren():
 
1062
                        subelement[subchild.tag] = int(subchild.text) \
 
1063
                            if subchild.text.isdigit() else subchild.text
 
1064
                    qos_group[child.tag] = subelement
 
1065
 
 
1066
            # Now add this group to the master qos_groups
 
1067
            qos_groups[group_name] = qos_group
 
1068
 
 
1069
        return qos_groups
 
1070
 
 
1071
    def get_volume_stats(self, refresh=False):
 
1072
        """Return the current state of the volume service. If 'refresh' is
 
1073
           True, run the update first."""
 
1074
 
 
1075
        drive_info = self._get_qosgroup_summary()
 
1076
        return {'drive_qos_info': drive_info}