class EMCViPRDriverCommon(object):

    OPENSTACK_TAG = 'OpenStack'

    def __init__(self, protocol, default_backend_name, configuration=None):
        self.protocol = protocol
        self.configuration = configuration
        self.configuration.append_config_values(volume_opts)

        self.check_for_vipr_cli_path()
        self.init_vipr_cli_components()

        self.stats = {
            'driver_version':
            '1.0',
            'free_capacity_gb':
            'unknown',
            'reserved_percentage':
            '0',
            'storage_protocol':
            protocol,
            'total_capacity_gb':
            'unknown',
            'vendor_name':
            'EMC',
            'volume_backend_name':
            self.configuration.volume_backend_name or default_backend_name
        }

        self.volume_api = cinder_volume.API()

    def check_for_vipr_cli_path(self):
        if (self.configuration.vipr_cli_path is None):
            message = "vipr_cli_path is not set in cinder configuration"
            raise exception.VolumeBackendAPIException(data=message)

        if (os.path.exists(self.configuration.vipr_cli_path)):
            sys.path.append(self.configuration.vipr_cli_path)
        else:
            message = self.configuration.vipr_cli_path + " path does not" + \
                " exist in the system. Please check/add/change" + \
                " vipr_cli_path" + \
                " in cinder configuration with valid ViPR" + \
                " CLI installation path"
            raise exception.VolumeBackendAPIException(data=message)

    def init_vipr_cli_components(self):
        import common as vipr_utils
        vipr_utils.COOKIE = None

        from exportgroup import ExportGroup
        from host import Host
        from hostinitiators import HostInitiator
        from snapshot import Snapshot
        from virtualarray import VirtualArray
        from volume import Volume

        # instantiate a few vipr cli objects for later use
        self.volume_obj = Volume(self.configuration.vipr_hostname,
                                 self.configuration.vipr_port)
        self.exportgroup_obj = ExportGroup(self.configuration.vipr_hostname,
                                           self.configuration.vipr_port)
        self.host_obj = Host(self.configuration.vipr_hostname,
                             self.configuration.vipr_port)
        self.hostinitiator_obj = HostInitiator(
            self.configuration.vipr_hostname, self.configuration.vipr_port)
        self.varray_obj = VirtualArray(self.configuration.vipr_hostname,
                                       self.configuration.vipr_port)
        self.snapshot_obj = Snapshot(self.configuration.vipr_hostname,
                                     self.configuration.vipr_port)

    def check_for_setup_error(self):
        # validate all of the vipr_* configuration values
        if (self.configuration.vipr_hostname is None):
            message = "vipr_hostname is not set in cinder configuration"
            raise exception.VolumeBackendAPIException(data=message)

        if (self.configuration.vipr_port is None):
            message = "vipr_port is not set in cinder configuration"
            raise exception.VolumeBackendAPIException(data=message)

        if (self.configuration.vipr_username is None):
            message = "vipr_username is not set in cinder configuration"
            raise exception.VolumeBackendAPIException(data=message)

        if (self.configuration.vipr_password is None):
            message = "vipr_password is not set in cinder configuration"
            raise exception.VolumeBackendAPIException(data=message)

        if (self.configuration.vipr_tenant is None):
            message = "vipr_tenant is not set in cinder configuration"
            raise exception.VolumeBackendAPIException(data=message)

        if (self.configuration.vipr_project is None):
            message = "vipr_project is not set in cinder configuration"
            raise exception.VolumeBackendAPIException(data=message)

        if (self.configuration.vipr_varray is None):
            message = "vipr_varray is not set in cinder configuration"
            raise exception.VolumeBackendAPIException(data=message)

    def authenticate_user(self):
        global AUTHENTICATED
        from authentication import Authentication
        # we should check to see if we are already authenticated before blindly
        # doing it again
        if (AUTHENTICATED is False):
            obj = Authentication(self.configuration.vipr_hostname,
                                 self.configuration.vipr_port)
            # cookiedir = os.getcwd()
            cookiedir = self.configuration.vipr_cookiedir
            obj.authenticate_user(self.configuration.vipr_username,
                                  self.configuration.vipr_password, cookiedir,
                                  None)
            AUTHENTICATED = True

    @retry_wrapper
    def create_volume(self, vol):
        self.authenticate_user()
        name = self._get_volume_name(vol)
        size = int(vol['size']) * 1073741824

        from common import SOSError
        vpool = self._get_vpool(vol)
        self.vpool = vpool['ViPR:VPOOL']

        try:
            res = self.volume_obj.create(
                self.configuration.vipr_tenant + "/" +
                self.configuration.vipr_project,
                name,
                size,
                self.configuration.vipr_varray,
                self.vpool,
                protocol=None,
                # no longer specified in volume
                # creation
                sync=True,
                number_of_volumes=1,
                thin_provisioned=None,
                consistencygroup=None)
        except SOSError as e:
            if (e.err_code == SOSError.SOS_FAILURE_ERR):
                raise SOSError(
                    SOSError.SOS_FAILURE_ERR,
                    "Volume " + name + ": Tag failed\n" + e.err_text)
            else:
                raise e

    @retry_wrapper
    def setTags(self, vol):

        self.authenticate_user()
        name = self._get_volume_name(vol)
        from common import SOSError

        # first, get the current tags that start with the OPENSTACK_TAG
        # eyecatcher
        removeTags = []
        currentTags = self.volume_obj.getTags(self.configuration.vipr_tenant +
                                              "/" +
                                              self.configuration.vipr_project +
                                              "/" + name)
        for cTag in currentTags:
            if (cTag.startswith(self.OPENSTACK_TAG)):
                removeTags.append(cTag)

        try:
            if (len(removeTags) > 0):
                self.volume_obj.tag(
                    self.configuration.vipr_tenant + "/" +
                    self.configuration.vipr_project + "/" + name, None,
                    removeTags)
        except SOSError as e:
            if (e.err_code == SOSError.SOS_FAILURE_ERR):
                LOG.debug("SOSError adding the tag: " + e.err_text)

        # now add the tags for the volume
        addTags = []
        # put all the openstack volume properties into the ViPR volume
        try:
            for prop, value in vars(vol).iteritems():
                try:
                    # don't put the status in, it's always the status before
                    # the current transaction
                    if (not prop.startswith("status")):
                        addTags.append(self.OPENSTACK_TAG + ":" + prop + ":" +
                                       value)
                except Exception:
                    pass
        except Exception:
            pass

        try:
            self.volume_obj.tag(
                self.configuration.vipr_tenant + "/" +
                self.configuration.vipr_project + "/" + name, addTags, None)
        except SOSError as e:
            if (e.err_code == SOSError.SOS_FAILURE_ERR):
                LOG.debug("SOSError adding the tag: " + e.err_text)

        return (self.volume_obj.getTags(self.configuration.vipr_tenant + "/" +
                                        self.configuration.vipr_project + "/" +
                                        name))

    @retry_wrapper
    def create_cloned_volume(self, vol, src_vref):
        """Creates a clone of the specified volume."""
        self.authenticate_user()
        name = self._get_volume_name(vol)
        srcname = self._get_volume_name(src_vref)
        number_of_volumes = 1
        from common import SOSError

        try:
            res = self.volume_obj.clone(self.configuration.vipr_tenant + "/" +
                                        self.configuration.vipr_project,
                                        name,
                                        number_of_volumes,
                                        srcname,
                                        None,
                                        sync=True)
        except SOSError as e:
            if (e.err_code == SOSError.SOS_FAILURE_ERR):
                raise SOSError(
                    SOSError.SOS_FAILURE_ERR,
                    "Volume " + name + ": clone failed\n" + e.err_text)
            else:
                raise e

    @retry_wrapper
    def expand_volume(self, vol, new_size):
        """expands the volume to new_size specified."""
        self.authenticate_user()
        volume_name = self._get_volume_name(vol)
        import common as vipr_utils
        size_in_bytes = vipr_utils.to_bytes(str(new_size) + "G")
        from common import SOSError

        try:
            self.volume_obj.expand(
                self.configuration.vipr_tenant + "/" +
                self.configuration.vipr_project + "/" + volume_name,
                size_in_bytes, True)
        except SOSError as e:
            if (e.err_code == SOSError.SOS_FAILURE_ERR):
                raise SOSError(
                    SOSError.SOS_FAILURE_ERR,
                    "Volume " + volume_name + ": expand failed\n" + e.err_text)
            else:
                raise e

    @retry_wrapper
    def create_volume_from_snapshot(self, snapshot, volume):
        """Creates volume from given snapshot ( snapshot clone to volume )."""
        self.authenticate_user()
        src_snapshot_name = snapshot['name']
        src_vol_ref = self.volume_api.get(context.get_admin_context(),
                                          snapshot['volume_id'])
        src_vol_name = self._get_volume_name(src_vol_ref)
        new_volume_name = self._get_volume_name(volume)
        number_of_volumes = 1
        from common import SOSError

        try:
            self.volume_obj.clone(self.configuration.vipr_tenant + "/" +
                                  self.configuration.vipr_project,
                                  new_volume_name,
                                  number_of_volumes,
                                  src_vol_name,
                                  src_snapshot_name,
                                  sync=True)
        except SOSError as e:
            if (e.err_code == SOSError.SOS_FAILURE_ERR):
                raise SOSError(
                    SOSError.SOS_FAILURE_ERR, "Snapshot " + src_snapshot_name +
                    ": clone failed\n" + e.err_text)
            else:
                raise e

    @retry_wrapper
    def delete_volume(self, vol):
        self.authenticate_user()
        name = self._get_volume_name(vol)
        from common import SOSError
        try:
            self.volume_obj.delete(self.configuration.vipr_tenant + "/" +
                                   self.configuration.vipr_project + "/" +
                                   name,
                                   volume_name_list=None,
                                   sync=True)
        except SOSError as e:
            if e.err_code == SOSError.NOT_FOUND_ERR:
                LOG.info("Volume " + name +
                         " no longer exists; volume deletion is" +
                         " considered success.")
            elif e.err_code == SOSError.SOS_FAILURE_ERR:
                raise SOSError(
                    SOSError.SOS_FAILURE_ERR,
                    "Volume " + name + ": Delete failed\n" + e.err_text)
            else:
                raise e

    @retry_wrapper
    def list_volume(self):
        import common as vipr_utils
        from common import SOSError
        try:
            uris = self.volume_obj.list_volumes(
                self.configuration.vipr_tenant + "/" +
                self.configuration.vipr_project)
            if (len(uris) > 0):
                output = []
                for uri in uris:
                    output.append(self.volume_obj.show_by_uri(uri))

                return vipr_utils.format_json_object(output)
            else:
                return
        except SOSError as e:
            raise e

    @retry_wrapper
    def create_snapshot(self, snapshot):
        self.authenticate_user()
        from common import SOSError
        try:
            snapshotname = snapshot['name']
            vol = snapshot['volume']
            volumename = self._get_volume_name(vol)
            projectname = self.configuration.vipr_project
            tenantname = self.configuration.vipr_tenant
            storageresType = 'block'
            storageresTypename = 'volumes'
            resourceUri = self.snapshot_obj.storageResource_query(
                storageresType,
                fileshareName=None,
                volumeName=volumename,
                cgName=None,
                project=projectname,
                tenant=tenantname)
            inactive = False
            rptype = None
            sync = True
            self.snapshot_obj.snapshot_create(storageresType,
                                              storageresTypename, resourceUri,
                                              snapshotname, inactive, rptype,
                                              sync)
            return

        except SOSError as e:
            if (e.err_code == SOSError.SOS_FAILURE_ERR):
                raise SOSError(
                    SOSError.SOS_FAILURE_ERR, "Snapshot: " + snapshotname +
                    ", Create Failed\n" + e.err_text)
            else:
                raise e

    @retry_wrapper
    def delete_snapshot(self, snapshot):
        self.authenticate_user()
        snapshotname = snapshot['name']
        from common import SOSError
        try:
            vol = snapshot['volume']
            volumename = self._get_volume_name(vol)
            projectname = self.configuration.vipr_project
            tenantname = self.configuration.vipr_tenant
            storageresType = 'block'
            storageresTypename = 'volumes'
            resourceUri = self.snapshot_obj.storageResource_query(
                storageresType,
                fileshareName=None,
                volumeName=volumename,
                cgName=None,
                project=projectname,
                tenant=tenantname)
            if resourceUri is None:
                LOG.info("Snapshot " + snapshotname +
                         " is not found; snapshot deletion" +
                         " is considered successful.")
            else:
                self.snapshot_obj.snapshot_delete(storageresType,
                                                  storageresTypename,
                                                  resourceUri,
                                                  snapshotname,
                                                  sync=True)
            return
        except SOSError as e:
            if (e.err_code == SOSError.SOS_FAILURE_ERR):
                raise SOSError(
                    SOSError.SOS_FAILURE_ERR,
                    "Snapshot " + snapshotname + ": Delete Failed\n")
            else:
                raise e

    @retry_wrapper
    def initialize_connection(self, volume, protocol, initiatorNodes,
                              initiatorPorts, hostname):

        from common import SOSError

        try:
            self.authenticate_user()
            volumename = self._get_volume_name(volume)
            foundgroupname = self._find_exportgroup(initiatorPorts)
            if (foundgroupname is None):
                for i in xrange(len(initiatorPorts)):
                    # check if this initiator is contained in any ViPR Host
                    # object
                    LOG.debug("checking for initiator port:" +
                              initiatorPorts[i])
                    foundhostname = self._find_host(initiatorPorts[i])
                    if (foundhostname is None):
                        hostfound = self._host_exists(hostname)
                        if (hostfound is None):
                            # create a host so it can be added to the export
                            # group
                            hostfound = hostname
                            self.host_obj.create(
                                hostname,
                                platform.system(),
                                hostname,
                                self.configuration.vipr_tenant,
                                port=None,
                                username=None,
                                passwd=None,
                                usessl=None,
                                osversion=None,
                                cluster=None,
                                datacenter=None,
                                vcenter=None,
                                autodiscovery=True)
                            LOG.info("Created host " + hostname)
                        # add the initiator to the host
                        self.hostinitiator_obj.create(hostfound, protocol,
                                                      initiatorNodes[i],
                                                      initiatorPorts[i])
                        LOG.info("Initiator " + initiatorPorts[i] +
                                 " added to host " + hostfound)
                        foundhostname = hostfound
                    else:
                        LOG.info("Found host " + foundhostname)
                    # create an export group for this host
                    foundgroupname = foundhostname + 'SG'
                    # create a unique name
                    foundgroupname = foundgroupname + '-' + \
                        ''.join(random.choice(string.ascii_uppercase
                                              + string.digits)
                                for x in range(6))
                    res = self.exportgroup_obj.exportgroup_create(
                        foundgroupname, self.configuration.vipr_project,
                        self.configuration.vipr_tenant,
                        self.configuration.vipr_varray, 'Host', foundhostname)
            LOG.debug("adding the volume to the exportgroup : " + volumename)
            res = self.exportgroup_obj.exportgroup_add_volumes(
                True, foundgroupname, self.configuration.vipr_tenant,
                self.configuration.vipr_project, [volumename], None, None)
            return self._find_device_info(volume, initiatorPorts)

        except SOSError as e:
            raise SOSError(
                SOSError.SOS_FAILURE_ERR,
                "Attach volume (" + self._get_volume_name(volume) +
                ") to host (" + hostname + ") initiator (" +
                initiatorPorts[0] + ") failed: " + e.err_text)

    @retry_wrapper
    def terminate_connection(self, volume, protocol, initiatorNodes,
                             initiatorPorts, hostname):
        from common import SOSError
        try:
            self.authenticate_user()
            volumename = self._get_volume_name(volume)
            tenantproject = self.configuration.vipr_tenant + \
                '/' + self.configuration.vipr_project
            voldetails = self.volume_obj.show(tenantproject + '/' + volumename)
            volid = voldetails['id']

            # find the exportgroups
            exports = self.volume_obj.get_exports_by_uri(volid)
            exportgroups = set()
            itls = exports['itl']
            for itl in itls:
                itl_port = itl['initiator']['port']
                if (itl_port in initiatorPorts):
                    exportgroups.add(itl['export']['id'])

            for exportgroup in exportgroups:
                res = self.exportgroup_obj.exportgroup_remove_volumes_by_uri(
                    exportgroup, volid, True, None, None, None, None)
            else:
                LOG.info("No export group found for the host: " + hostname +
                         "; this is considered already detached.")

            return itls

        except SOSError as e:
            raise SOSError(
                SOSError.SOS_FAILURE_ERR, "Detaching volume " + volumename +
                " from host " + hostname + " failed: " + e.err_text)

    @retry_wrapper
    def _find_device_info(self, volume, initiator_ports):
        '''Returns the device_info in a list of itls that have
        the matched initiator
        (there could be multiple targets, hence a list):
                [
                 {
                  "hlu":9,
                  "initiator":{...,"port":"20:00:00:25:B5:49:00:22"},
                  "export":{...},
                  "device":{...,"wwn":"600601602B802D00B62236585D0BE311"},
                  "target":{...,"port":"50:06:01:6A:46:E0:72:EF"},
                  "san_zone_name":"..."
                 },
                 {
                  "hlu":9,
                  "initiator":{...,"port":"20:00:00:25:B5:49:00:22"},
                  "export":{...},
                  "device":{...,"wwn":"600601602B802D00B62236585D0BE311"},
                  "target":{...,"port":"50:06:01:62:46:E0:72:EF"},
                  "san_zone_name":"..."
                 }
                ]
        '''
        volumename = self._get_volume_name(volume)
        fullname = self.configuration.vipr_project + '/' + volumename
        vol_uri = self.volume_obj.volume_query(fullname)
        '''
        The itl info shall be available at the first try since now export is a
        synchronous call.  We are trying a few more times to accommodate any
        delay on filling in the itl info after the export task is completed.
        '''
        itls = []
        for x in xrange(10):
            exports = self.volume_obj.get_exports_by_uri(vol_uri)
            LOG.debug(_("Volume exports: %s") % exports)
            for itl in exports['itl']:
                itl_port = itl['initiator']['port']
                if (itl_port in initiator_ports):
                    found_device_number = itl['hlu']
                    if (found_device_number is not None
                            and found_device_number != '-1'):
                        # 0 is a valid number for found_device_number.
                        # Only loop if it is None or -1
                        LOG.debug("Found Device Number: " +
                                  str(found_device_number))
                        itls.append(itl)

            if itls:
                break
            else:
                LOG.debug("Device Number not found yet." +
                          " Retrying after 10 seconds...")
                time.sleep(10)

        if itls is None:
            # No device number found after 10 tries; return an empty itl
            LOG.info("No device number has been found after 10 tries;" +
                     "this likely indicates an unsuccessful attach of" +
                     "volume " + volumename + " to" + " initiator " +
                     str(initiator_ports))

        return itls

    def _get_volume_name(self, vol):
        try:
            name = vol['display_name']
        except Exception as exp:
            name = None

        if (name is None or len(name) == 0):
            name = vol['name']

        return name

    def _get_vpool(self, volume):
        vpool = {}
        ctxt = context.get_admin_context()
        type_id = volume['volume_type_id']
        if type_id is not None:
            volume_type = volume_types.get_volume_type(ctxt, type_id)
            specs = volume_type.get('extra_specs')
            for key, value in specs.iteritems():
                vpool[key] = value

        return vpool

    @retry_wrapper
    def _find_exportgroup(self, initiator_ports):
        '''Find the export group to which the given initiator ports are the
        same as the initiators in the group
        '''
        foundgroupname = None
        grouplist = self.exportgroup_obj.exportgroup_list(
            self.configuration.vipr_project, self.configuration.vipr_tenant)
        for groupid in grouplist:
            groupdetails = self.exportgroup_obj.exportgroup_show(
                groupid, self.configuration.vipr_project,
                self.configuration.vipr_tenant)
            if groupdetails is not None:
                if (groupdetails['inactive']):
                    continue
                initiators = groupdetails['initiators']
                if initiators is not None:
                    inits_eg = set()
                    for initiator in initiators:
                        inits_eg.add(initiator['initiator_port'])

                    if (inits_eg == set(initiator_ports)):
                        foundgroupname = groupdetails['name']
                    if foundgroupname is not None:
                        # Check the associated varray
                        if groupdetails['varray']:
                            varray_uri = groupdetails['varray']['id']
                            varray_details = self.varray_obj.varray_show(
                                varray_uri)
                            if (varray_details['name'] ==
                                    self.configuration.vipr_varray):
                                LOG.debug("Found exportgroup " +
                                          foundgroupname)
                                break

                        # Not the right varray
                        foundgroupname = None

        return foundgroupname

    @retry_wrapper
    def _find_host(self, initiator_port):
        '''Find the host, if exists, to which the given initiator belong.'''
        foundhostname = None
        hosts = self.host_obj.list_by_tenant(self.configuration.vipr_tenant)
        for host in hosts:
            initiators = self.host_obj.list_initiators(host['id'])
            for initiator in initiators:
                if (initiator_port == initiator['name']):
                    foundhostname = host['name']
                    break

            if foundhostname is not None:
                break

        return foundhostname

    @retry_wrapper
    def _host_exists(self, host_name):
        '''Check if a Host object with the given
        hostname already exists in ViPR
        '''
        hosts = self.host_obj.search_by_name(host_name)

        if (len(hosts) > 0):
            for host in hosts:
                hostname = host['match']
                if (host_name == hostname):
                    return hostname
            return hostname
        LOG.debug("no host found for:" + host_name)
        return None

    @retry_wrapper
    def update_volume_stats(self):
        """Retrieve stats info."""
        LOG.debug(_("Updating volume stats"))
        self.authenticate_user()
        import common as vipr_utils
        from common import SOSError

        try:
            vols = self.volume_obj.list_volumes(
                self.configuration.vipr_tenant + "/" +
                self.configuration.vipr_project)
            vpairs = set()
            if (len(vols) > 0):
                for vol in vols:
                    if (vol):
                        vpair = (vol["vpool"]["id"], vol["varray"]["id"])
                        if (vpair not in vpairs):
                            vpairs.add(vpair)

            if (len(vpairs) > 0):
                free_gb = 0.0
                used_gb = 0.0
                provisioned_gb = 0.0
                precent_used = 0.0
                percent_provisioned = 0.0
                for vpair in vpairs:
                    if (vpair):
                        (s, h) = vipr_utils.service_json_request(
                            self.configuration.vipr_hostname,
                            self.configuration.vipr_port,
                            "GET",
                            URI_VPOOL_VARRAY_CAPACITY.format(
                                vpair[0], vpair[1]),
                            body=None)
                        capacity = vipr_utils.json_decode(s)

                        free_gb += float(capacity["free_gb"])
                        used_gb += float(capacity["used_gb"])
                        provisioned_gb += float(capacity["provisioned_gb"])

                self.stats['free_capacity_gb'] = free_gb
                self.stats['total_capacity_gb'] = free_gb + used_gb
                self.stats['reserved_percentage'] = 100 * \
                    provisioned_gb / (free_gb + used_gb)

            return self.stats

        except SOSError as e:
            raise e