Пример #1
0
    def create_destination_flexvol(self, src_backend_name, dest_backend_name,
                                   src_flexvol_name, dest_flexvol_name):
        """Create a SnapMirror mirror target FlexVol for a given source."""
        dest_backend_config = config_utils.get_backend_configuration(
            dest_backend_name)
        dest_vserver = dest_backend_config.netapp_vserver
        dest_client = config_utils.get_client_for_backend(
            dest_backend_name, vserver_name=dest_vserver)

        source_backend_config = config_utils.get_backend_configuration(
            src_backend_name)
        src_vserver = source_backend_config.netapp_vserver
        src_client = config_utils.get_client_for_backend(
            src_backend_name, vserver_name=src_vserver)

        provisioning_options = (
            src_client.get_provisioning_options_from_flexvol(src_flexvol_name))

        # If the source is encrypted then the destination needs to be
        # encrypted too. Using is_flexvol_encrypted because it includes
        # a simple check to ensure that the NVE feature is supported.
        if src_client.is_flexvol_encrypted(src_flexvol_name, src_vserver):
            provisioning_options['encrypt'] = 'true'

        # Remove size and volume_type
        size = provisioning_options.pop('size', None)
        if not size:
            msg = _("Unable to read the size of the source FlexVol (%s) "
                    "to create a SnapMirror destination.")
            raise exception.NetAppDriverException(msg % src_flexvol_name)
        provisioning_options.pop('volume_type', None)

        source_aggregate = provisioning_options.pop('aggregate')
        aggregate_map = self._get_replication_aggregate_map(
            src_backend_name, dest_backend_name)

        if not aggregate_map.get(source_aggregate):
            msg = _("Unable to find configuration matching the source "
                    "aggregate (%s) and the destination aggregate. Option "
                    "netapp_replication_aggregate_map may be incorrect.")
            raise exception.NetAppDriverException(message=msg %
                                                  source_aggregate)

        destination_aggregate = aggregate_map[source_aggregate]

        # NOTE(gouthamr): The volume is intentionally created as a Data
        # Protection volume; junction-path will be added on breaking
        # the mirror.
        dest_client.create_flexvol(dest_flexvol_name,
                                   destination_aggregate,
                                   size,
                                   volume_type='dp',
                                   **provisioning_options)
Пример #2
0
    def _invoke(self, method, path, data=None, use_system=True,
                timeout=None, verify=False, **kwargs):
        """Invokes end point for resource on path."""
        url = self._get_resource_url(path, use_system, **kwargs)
        if self._content_type == 'json':
            headers = {'Accept': 'application/json',
                       'Content-Type': 'application/json'}
            if cinder_utils.TRACE_API:
                self._log_http_request(method, url, headers, data)
            data = json.dumps(data) if data else None
            res = self.invoke_service(method, url, data=data,
                                      headers=headers,
                                      timeout=timeout, verify=verify)

            try:
                res_dict = res.json() if res.text else None
            # This should only occur if we expected JSON, but were sent
            # something else
            except scanner.JSONDecodeError:
                res_dict = None

            if cinder_utils.TRACE_API:
                self._log_http_response(res.status_code, dict(res.headers),
                                        res_dict)

            self._eval_response(res)
            return res_dict
        else:
            raise exception.NetAppDriverException(
                _("Content type not supported."))
Пример #3
0
 def _check_host_type(self):
     host_type = (self.configuration.netapp_host_type
                  or self.DEFAULT_HOST_TYPE)
     self.host_type = self.HOST_TYPES.get(host_type)
     if not self.host_type:
         raise exception.NetAppDriverException(
             _('Configured host type is not supported.'))
Пример #4
0
    def create_group_snapshot(self, context, group_snapshot, snapshots):
        """Creates a Cinder group snapshot object.

        The Cinder group snapshot object is created by making use of an ONTAP
        consistency group snapshot in order to provide write-order consistency
        for a set of flexvols snapshots. First, a list of the flexvols backing
        the given Cinder group must be gathered. An ONTAP group-snapshot of
        these flexvols will create a snapshot copy of all the Cinder volumes in
        the generic volume group. For each Cinder volume in the group, it is
        then necessary to clone its backing file from the ONTAP cg-snapshot.
        The naming convention used to for the clones is what indicates the
        clone's role as a Cinder snapshot and its inclusion in a Cinder group.
        The ONTAP cg-snapshot of the flexvols is deleted after the cloning
        operation is completed.

        :returns: An implicit update for the group snapshot and snapshot models
                 that is then used by the manager to set the models to
                 available.
        """
        try:
            if volume_utils.is_group_a_cg_snapshot_type(group_snapshot):
                self._create_consistent_group_snapshot(group_snapshot,
                                                       snapshots)
            else:
                for snapshot in snapshots:
                    self._clone_backing_file_for_volume(
                        snapshot['volume_name'], snapshot['name'],
                        snapshot['volume_id'], is_snapshot=True)
        except Exception as ex:
            err_msg = (_("Create group snapshot failed (%s).") % ex)
            LOG.exception(err_msg, resource=group_snapshot)
            raise exception.NetAppDriverException(err_msg)

        return None, None
Пример #5
0
 def invoke_service(self,
                    method='GET',
                    url=None,
                    params=None,
                    data=None,
                    headers=None,
                    timeout=None,
                    verify=False):
     url = url or self._endpoint
     try:
         response = self.conn.request(method,
                                      url,
                                      params,
                                      data,
                                      headers=headers,
                                      timeout=timeout,
                                      verify=verify)
     # Catching error conditions other than the perceived ones.
     # Helps propagating only known exceptions back to the caller.
     except Exception as e:
         LOG.exception(
             _LE("Unexpected error while invoking web service."
                 " Error - %s."), e)
         raise exception.NetAppDriverException(
             _("Invoking web service failed."))
     self._eval_response(response)
     return response
Пример #6
0
 def _get_latest_volume(self, uid):
     label = utils.convert_uuid_to_es_fmt(uid)
     for vol in self._client.list_volumes():
         if vol.get('label') == label:
             self._cache_volume(vol)
             return self._get_cached_volume(label)
     raise exception.NetAppDriverException(_("Volume %s not found."), uid)
Пример #7
0
    def _create_volume(self, eseries_pool_label, eseries_volume_label,
                       size_gb):
        """Creates volume with given label and size."""

        target_pool = None

        pools = self._client.list_storage_pools()
        for pool in pools:
            if pool["label"] == eseries_pool_label:
                target_pool = pool
                break

        if not target_pool:
            msg = _("Pools %s does not exist")
            raise exception.NetAppDriverException(msg % eseries_pool_label)

        try:
            vol = self._client.create_volume(target_pool['volumeGroupRef'],
                                             eseries_volume_label, size_gb)
            LOG.info(_("Created volume with label %s."), eseries_volume_label)
        except exception.NetAppDriverException as e:
            with excutils.save_and_reraise_exception():
                LOG.error(_("Error creating volume. Msg - %s."),
                          six.text_type(e))

        return vol
 def _copy_volume_high_prior_readonly(self, src_vol, dst_vol):
     """Copies src volume to dest volume."""
     LOG.info(_LI("Copying src vol %(src)s to dest vol %(dst)s."),
              {'src': src_vol['label'], 'dst': dst_vol['label']})
     try:
         job = None
         job = self._client.create_volume_copy_job(src_vol['id'],
                                                   dst_vol['volumeRef'])
         while True:
             j_st = self._client.list_vol_copy_job(job['volcopyRef'])
             if (j_st['status'] == 'inProgress' or j_st['status'] ==
                     'pending' or j_st['status'] == 'unknown'):
                 time.sleep(self.SLEEP_SECS)
                 continue
             if j_st['status'] == 'failed' or j_st['status'] == 'halted':
                 LOG.error(_LE("Vol copy job status %s."), j_st['status'])
                 raise exception.NetAppDriverException(
                     _("Vol copy job for dest %s failed.") %
                     dst_vol['label'])
             LOG.info(_LI("Vol copy job completed for dest %s."),
                      dst_vol['label'])
             break
     finally:
         if job:
             try:
                 self._client.delete_vol_copy_job(job['volcopyRef'])
             except exception.NetAppDriverException:
                 LOG.warning(_LW("Failure deleting "
                                 "job %s."), job['volcopyRef'])
         else:
             LOG.warning(_LW('Volume copy job for src vol %s not found.'),
                         src_vol['id'])
     LOG.info(_LI('Copy job to dest vol %s completed.'), dst_vol['label'])
Пример #9
0
    def _invoke(self, method, path, data=None, use_system=True,
                timeout=None, verify=False, **kwargs):
        """Invokes end point for resource on path."""
        scrubbed_data = copy.deepcopy(data)
        if scrubbed_data:
            if 'password' in scrubbed_data:
                scrubbed_data['password'] = "******"
            if 'storedPassword' in scrubbed_data:
                scrubbed_data['storedPassword'] = "******"

        params = {'m': method, 'p': path, 'd': scrubbed_data,
                  'sys': use_system, 't': timeout, 'v': verify, 'k': kwargs}
        LOG.debug("Invoking rest with method: %(m)s, path: %(p)s,"
                  " data: %(d)s, use_system: %(sys)s, timeout: %(t)s,"
                  " verify: %(v)s, kwargs: %(k)s." % (params))
        url = self._get_resource_url(path, use_system, **kwargs)
        if self._content_type == 'json':
            headers = {'Accept': 'application/json',
                       'Content-Type': 'application/json'}
            data = json.dumps(data) if data else None
            res = self.invoke_service(method, url, data=data,
                                      headers=headers,
                                      timeout=timeout, verify=verify)
            return res.json() if res.text else None
        else:
            raise exception.NetAppDriverException(
                _("Content type not supported."))
Пример #10
0
 def _get_iscsi_service_details(self):
     """Gets iscsi iqn, ip and port information."""
     ports = []
     hw_inventory = self._client.list_hardware_inventory()
     iscsi_ports = hw_inventory.get('iscsiPorts')
     if iscsi_ports:
         for port in iscsi_ports:
             if (port.get('ipv4Enabled') and port.get('iqn')
                     and port.get('ipv4Data')
                     and port['ipv4Data'].get('ipv4AddressData') and
                     port['ipv4Data']['ipv4AddressData'].get('ipv4Address')
                     and
                     port['ipv4Data']['ipv4AddressData'].get('configState')
                     == 'configured'):
                 iscsi_det = {}
                 iscsi_det['ip'] =\
                     port['ipv4Data']['ipv4AddressData']['ipv4Address']
                 iscsi_det['iqn'] = port['iqn']
                 iscsi_det['tcp_port'] = port.get('tcpListenPort')
                 iscsi_det['controller'] = port.get('controllerId')
                 ports.append(iscsi_det)
     if not ports:
         msg = _('No good iscsi portals found for %s.')
         raise exception.NetAppDriverException(msg %
                                               self._client.get_system_id())
     return ports
Пример #11
0
 def _check_host_type(self):
     self.host_type =\
         self.HOST_TYPES.get(self.configuration.netapp_eseries_host_type,
                             None)
     if not self.host_type:
         raise exception.NetAppDriverException(
             _('Configured host type is not supported.'))
Пример #12
0
 def check_for_setup_error(self):
     self.host_type =\
         self.HOST_TYPES.get(self.configuration.netapp_eseries_host_type,
                             None)
     if not self.host_type:
         raise exception.NetAppDriverException(
             _('Configured host type is not supported.'))
     self._check_storage_system()
     self._populate_system_objects()
Пример #13
0
 def _get_free_lun(self, host):
     """Gets free lun for given host."""
     luns = self._get_vol_mapping_for_host_frm_array(host['hostRef'])
     used_luns = set(map(lambda lun: int(lun['lun']), luns))
     for lun in xrange(self.MAX_LUNS_PER_HOST):
         if lun not in used_luns:
             return lun
     msg = _("No free luns. Host might exceeded max luns.")
     raise exception.NetAppDriverException(msg)
Пример #14
0
    def check_for_setup_error(self):
        """Check that the driver is working and can communicate.

        Discovers the LUNs on the NetApp server.
        """
        if self.lun_ostype not in self.ALLOWED_LUN_OS_TYPES:
            msg = _("Invalid value for NetApp configuration"
                    " option netapp_lun_ostype.")
            LOG.error(msg)
            raise exception.NetAppDriverException(msg)
        if self.host_type not in self.ALLOWED_IGROUP_HOST_TYPES:
            msg = _("Invalid value for NetApp configuration"
                    " option netapp_host_type.")
            LOG.error(msg)
            raise exception.NetAppDriverException(msg)
        lun_list = self.zapi_client.get_lun_list()
        self._extract_and_populate_luns(lun_list)
        LOG.debug("Success getting list of LUNs from server.")
Пример #15
0
 def check_for_setup_error(self):
     """Check that the driver is working and can communicate."""
     if not self._get_flexvol_to_pool_map():
         msg = _('No pools are available for provisioning volumes. '
                 'Ensure that the configuration option '
                 'netapp_pool_name_search_pattern is set correctly.')
         raise exception.NetAppDriverException(msg)
     self._add_looping_tasks()
     super(NetAppBlockStorageCmodeLibrary, self).check_for_setup_error()
Пример #16
0
 def _check_storage_system(self):
     """Checks whether system is registered and has good status."""
     try:
         system = self._client.list_storage_system()
     except exception.NetAppDriverException:
         with excutils.save_and_reraise_exception():
             LOG.info(
                 _LI("System with controller addresses [%s] is not "
                     "registered with web service."),
                 self.configuration.netapp_controller_ips)
     password_not_in_sync = False
     if system.get('status', '').lower() == 'passwordoutofsync':
         password_not_in_sync = True
         new_pwd = self.configuration.netapp_sa_password
         self._client.update_stored_system_password(new_pwd)
         time.sleep(self.SLEEP_SECS)
     sa_comm_timeout = 60
     comm_time = 0
     while True:
         system = self._client.list_storage_system()
         status = system.get('status', '').lower()
         # wait if array not contacted or
         # password was not in sync previously.
         if ((status == 'nevercontacted') or
             (password_not_in_sync and status == 'passwordoutofsync')):
             LOG.info(_LI('Waiting for web service array communication.'))
             time.sleep(self.SLEEP_SECS)
             comm_time = comm_time + self.SLEEP_SECS
             if comm_time >= sa_comm_timeout:
                 msg = _("Failure in communication between web service and"
                         " array. Waited %s seconds. Verify array"
                         " configuration parameters.")
                 raise exception.NetAppDriverException(msg %
                                                       sa_comm_timeout)
         else:
             break
     msg_dict = {'id': system.get('id'), 'status': status}
     if (status == 'passwordoutofsync' or status == 'notsupported'
             or status == 'offline'):
         raise exception.NetAppDriverException(
             _("System %(id)s found with bad status - "
               "%(status)s.") % msg_dict)
     LOG.info(_LI("System %(id)s has %(status)s status."), msg_dict)
     return True
Пример #17
0
    def move_volume_mapping_via_symbol(self, map_ref, to_ref, lun_id):
        """Moves a map from one host/host_group object to another."""

        path = "/storage-systems/{system-id}/symbol/moveLUNMapping"
        data = {'lunMappingRef': map_ref, 'lun': int(lun_id), 'mapRef': to_ref}
        return_code = self._invoke('POST', path, data)
        if return_code == 'ok':
            return {'lun': lun_id}
        msg = _("Failed to move LUN mapping.  Return code: %s") % return_code
        raise exception.NetAppDriverException(msg)
Пример #18
0
 def wait_for_quiesced():
     snapmirror = dest_client.get_snapmirrors(
         src_vserver,
         src_flexvol_name,
         dest_vserver,
         dest_flexvol_name,
         desired_attributes=['relationship-status', 'mirror-state'])[0]
     if snapmirror.get('relationship-status') != 'quiesced':
         msg = _("SnapMirror relationship is not quiesced.")
         raise exception.NetAppDriverException(reason=msg)
Пример #19
0
 def _get_iscsi_portal_for_vol(self, volume, portals, anyController=True):
     """Get the iscsi portal info relevant to volume."""
     for portal in portals:
         if portal.get('controller') == volume.get('currentManager'):
             return portal
     if anyController and portals:
         return portals[0]
     msg = _('No good iscsi portal found in supplied list for %s.')
     raise exception.NetAppDriverException(msg %
                                           self._client.get_system_id())
Пример #20
0
def _get_free_lun(client, host, multiattach_enabled, mappings):
    """Returns least used LUN ID available on the given host."""
    if not _is_host_full(client, host):
        unused_luns = _get_unused_lun_ids(mappings)
        if unused_luns:
            chosen_lun = random.sample(unused_luns, 1)
            return chosen_lun[0]
        elif multiattach_enabled:
            msg = _("No unused LUN IDs are available on the host; "
                    "multiattach is enabled which requires that all LUN IDs "
                    "to be unique across the entire host group.")
            raise exception.NetAppDriverException(msg)
        used_lun_counts = _get_used_lun_id_counter(mappings)
        # most_common returns an arbitrary tuple of members with same frequency
        for lun_id, __ in reversed(used_lun_counts.most_common()):
            if _is_lun_id_available_on_host(client, host, lun_id):
                return lun_id
    msg = _("No free LUN IDs left. Maximum number of volumes that can be "
            "attached to host (%s) has been exceeded.")
    raise exception.NetAppDriverException(msg % utils.MAX_LUNS_PER_HOST)
Пример #21
0
 def _eval_response(self, response):
     """Evaluates response before passing result to invoker."""
     status_code = int(response.status_code)
     # codes >= 300 are not ok and to be treated as errors
     if status_code >= 300:
         # Response code 422 returns error code and message
         if status_code == 422:
             msg = _("Response error - %s.") % response.text
         else:
             msg = _("Response error code - %s.") % status_code
         raise exception.NetAppDriverException(msg)
Пример #22
0
    def create_volume(self, pool, label, size, unit='gb', seg_size=0,
                      read_cache=None, write_cache=None, flash_cache=None,
                      data_assurance=None, thin_provision=False):
        """Creates a volume on array with the configured attributes

        Note: if read_cache, write_cache, flash_cache, or data_assurance
        are not provided, the default will be utilized by the Webservice.

        :param pool: The pool unique identifier
        :param label: The unqiue label for the volume
        :param size: The capacity in units
        :param unit: The unit for capacity
        :param seg_size: The segment size for the volume, expressed in KB.
                         Default will allow the Webservice to choose.
        :param read_cache: If true, enable read caching, if false,
                           explicitly disable it.
        :param write_cache: If true, enable write caching, if false,
                            explicitly disable it.
        :param flash_cache: If true, add the volume to a Flash Cache
        :param data_assurance: If true, enable the Data Assurance capability
        :returns: The created volume
        """

        # Utilize the new API if it is available
        if self.features.SSC_API_V2:
            path = "/storage-systems/{system-id}/ssc/volumes"
            data = {'poolId': pool, 'name': label, 'sizeUnit': unit,
                    'size': int(size), 'dataAssuranceEnable': data_assurance,
                    'flashCacheEnable': flash_cache,
                    'readCacheEnable': read_cache,
                    'writeCacheEnable': write_cache,
                    'thinProvision': thin_provision}
        # Use the old API
        else:
            # Determine if there are were extra specs provided that are not
            # supported
            extra_specs = [read_cache, write_cache]
            unsupported_spec = any([spec is not None for spec in extra_specs])
            if(unsupported_spec):
                msg = _("E-series proxy API version %(current_version)s does "
                        "not support full set of SSC extra specs. The proxy"
                        " version must be at at least %(min_version)s.")
                min_version = self.features.SSC_API_V2.minimum_version
                raise exception.NetAppDriverException(msg %
                                                      {'current_version':
                                                       self.api_version,
                                                       'min_version':
                                                       min_version})

            path = "/storage-systems/{system-id}/volumes"
            data = {'poolId': pool, 'name': label, 'sizeUnit': unit,
                    'size': int(size), 'segSize': seg_size}
        return self._invoke('POST', path, data)
Пример #23
0
 def check_for_setup_error(self):
     """Check that the driver is working and can communicate."""
     ssc_cmode.check_ssc_api_permissions(self.zapi_client)
     ssc_cmode.refresh_cluster_ssc(self, self.zapi_client.get_connection(),
                                   self.vserver, synchronous=True)
     if not self._get_filtered_pools():
         msg = _('No pools are available for provisioning volumes. '
                 'Ensure that the configuration option '
                 'netapp_pool_name_search_pattern is set correctly.')
         raise exception.NetAppDriverException(msg)
     super(NetAppBlockStorageCmodeLibrary, self).check_for_setup_error()
     self._start_periodic_tasks()
Пример #24
0
def get_export_host_junction_path(share):
    if '[' in share and ']' in share:
        try:
            # ipv6
            host = re.search(r'\[(.*)\]', share).group(1)
            junction_path = share.split(':')[-1]
        except AttributeError:
            raise exception.NetAppDriverException(_("Share '%s' is "
                                                    "not in a valid "
                                                    "format.") % share)
    else:
        # ipv4
        path = share.split(':')
        if len(path) == 2:
            host = path[0]
            junction_path = path[1]
        else:
            raise exception.NetAppDriverException(_("Share '%s' is "
                                                    "not in a valid "
                                                    "format.") % share)

    return host, junction_path
Пример #25
0
 def _schedule_and_create_volume(self, label, size_gb):
     """Creates volume with given label and size."""
     avl_pools = self._get_sorted_avl_storage_pools(size_gb)
     for pool in avl_pools:
         try:
             vol = self._client.create_volume(pool['volumeGroupRef'], label,
                                              size_gb)
             LOG.info(_("Created volume with label %s."), label)
             return vol
         except exception.NetAppDriverException as e:
             LOG.error(_("Error creating volume. Msg - %s."), e)
     msg = _("Failure creating volume %s.")
     raise exception.NetAppDriverException(msg % label)
Пример #26
0
    def _create_volume(self, eseries_pool_label, eseries_volume_label,
                       size_gb):
        """Creates volume with given label and size."""

        if self.configuration.netapp_enable_multiattach:
            volumes = self._client.list_volumes()
            # NOTE(ameade): Ensure we do not create more volumes than we could
            # map to the multi attach ESeries host group.
            if len(volumes) > utils.MAX_LUNS_PER_HOST_GROUP:
                msg = (_("Cannot create more than %(req)s volumes on the "
                         "ESeries array when 'netapp_enable_multiattach' is "
                         "set to true.") % {
                             'req': utils.MAX_LUNS_PER_HOST_GROUP
                         })
                raise exception.NetAppDriverException(msg)

        target_pool = None

        pools = self._client.list_storage_pools()
        for pool in pools:
            if pool["label"] == eseries_pool_label:
                target_pool = pool
                break

        if not target_pool:
            msg = _("Pools %s does not exist")
            raise exception.NetAppDriverException(msg % eseries_pool_label)

        try:
            vol = self._client.create_volume(target_pool['volumeGroupRef'],
                                             eseries_volume_label, size_gb)
            LOG.info(_LI("Created volume with "
                         "label %s."), eseries_volume_label)
        except exception.NetAppDriverException as e:
            with excutils.save_and_reraise_exception():
                LOG.error(_LE("Error creating volume. Msg - %s."),
                          six.text_type(e))

        return vol
Пример #27
0
def _get_free_lun(client, host, maps=None):
    """Gets free LUN for given host."""
    ref = host['hostRef']
    luns = maps or _get_vol_mapping_for_host_frm_array(client, ref)
    if host['clusterRef'] != utils.NULL_REF:
        host_group_maps = _get_vol_mapping_for_host_group_frm_array(
            client, host['clusterRef'])
        luns.extend(host_group_maps)
    used_luns = set(map(lambda lun: int(lun['lun']), luns))
    for lun in xrange(utils.MAX_LUNS_PER_HOST):
        if lun not in used_luns:
            return lun
    msg = _("No free LUNs. Host might have exceeded max number of LUNs.")
    raise exception.NetAppDriverException(msg)
Пример #28
0
    def expand_volume(self, object_id, new_capacity, capacity_unit='gb'):
        """Increase the capacity of a volume"""
        if not self.features.SSC_API_V2:
            msg = _("E-series proxy API version %(current_version)s does "
                    "not support this expansion API. The proxy"
                    " version must be at at least %(min_version)s")
            min_version = self.features.SSC_API_V2.minimum_version
            msg_args = {'current_version': self.api_version,
                        'min_version': min_version}
            raise exception.NetAppDriverException(msg % msg_args)

        path = self.RESOURCE_PATHS.get('ssc_volume')
        data = {'newSize': new_capacity, 'sizeUnit': capacity_unit}
        return self._invoke('POST', path, data, **{'object-id': object_id})
Пример #29
0
    def send_iter_request(self,
                          api_name,
                          api_args=None,
                          enable_tunneling=True,
                          max_page_length=DEFAULT_MAX_PAGE_LENGTH):
        """Invoke an iterator-style getter API."""

        if not api_args:
            api_args = {}

        api_args['max-records'] = max_page_length

        # Get first page
        result = self.send_request(api_name,
                                   api_args,
                                   enable_tunneling=enable_tunneling)

        # Most commonly, we can just return here if there is no more data
        next_tag = result.get_child_content('next-tag')
        if not next_tag:
            return result

        # Ensure pagination data is valid and prepare to store remaining pages
        num_records = self._get_record_count(result)
        attributes_list = result.get_child_by_name('attributes-list')
        if not attributes_list:
            msg = _('Missing attributes list for API %s.') % api_name
            raise exception.NetAppDriverException(msg)

        # Get remaining pages, saving data into first page
        while next_tag is not None:
            next_api_args = copy.deepcopy(api_args)
            next_api_args['tag'] = next_tag
            next_result = self.send_request(api_name,
                                            next_api_args,
                                            enable_tunneling=enable_tunneling)

            next_attributes_list = next_result.get_child_by_name(
                'attributes-list') or netapp_api.NaElement('none')

            for record in next_attributes_list.get_children():
                attributes_list.add_child_elem(record)

            num_records += self._get_record_count(next_result)
            next_tag = next_result.get_child_content('next-tag')

        result.get_child_by_name('num-records').set_content(
            six.text_type(num_records))
        result.get_child_by_name('next-tag').set_content('')
        return result
Пример #30
0
def map_volume_to_single_host(client, volume, eseries_vol, host, vol_map):
    """Maps the e-series volume to host with initiator."""
    msg = "Attempting to map volume %s to single host."
    LOG.debug(msg % volume['id'])

    # If volume is not mapped on the backend, map directly to host
    if not vol_map:
        mappings = _get_vol_mapping_for_host_frm_array(client, host['hostRef'])
        lun = _get_free_lun(client, host, mappings)
        return client.create_volume_mapping(eseries_vol['volumeRef'],
                                            host['hostRef'], lun)

    # If volume is already mapped to desired host
    if vol_map.get('mapRef') == host['hostRef']:
        return vol_map

    multiattach_cluster_ref = None
    try:
        host_group = client.get_host_group_by_name(
            utils.MULTI_ATTACH_HOST_GROUP_NAME)
        multiattach_cluster_ref = host_group['clusterRef']
    except exception.NotFound:
        pass

    # Volume is mapped to the multiattach host group
    if vol_map.get('mapRef') == multiattach_cluster_ref:
        LOG.debug("Volume %s is mapped to multiattach host group." %
                  volume['id'])

        # If volume is not currently attached according to Cinder, it is
        # safe to delete the mapping
        if not (volume['attach_status'] == 'attached'):
            msg = (_("Volume %(vol)s is not currently attached, "
                     "moving existing mapping to host %(host)s.") % {
                         'vol': volume['id'],
                         'host': host['label']
                     })
            LOG.debug(msg)
            mappings = _get_vol_mapping_for_host_frm_array(
                client, host['hostRef'])
            lun = _get_free_lun(client, host, mappings)
            return client.move_volume_mapping_via_symbol(
                vol_map.get('mapRef'), host['hostRef'], lun)

    # If we got this far, volume must be mapped to something else
    msg = _("Cannot attach already attached volume %s; "
            "multiattach is disabled via the "
            "'netapp_enable_multiattach' configuration option.")
    raise exception.NetAppDriverException(msg % volume['id'])