def wrap(self, *args, **kwargs): server = kwargs.get('share_server') if not server: # For now cmode driver does not support flat networking. raise exception.NetAppException(_('Share sever is not provided.')) vserver_name = server['backend_details'].get('vserver_name') if \ server.get('backend_details') else None if not vserver_name: raise exception.NetAppException( _('Vserver name missing in ' 'backend details.')) if not self._vserver_exists(vserver_name): raise exception.VserverUnavailable(vserver=vserver_name) return f(self, *args, **kwargs)
def wrap(self, *args, **kwargs): server = kwargs.get('share_server') if not server: # For now cmode driver does not support flat networking. raise exception.NetAppException(_('Share server is not provided.')) vserver_name = server['backend_details'].get('vserver_name') if \ server.get('backend_details') else None if not vserver_name: msg = _('Vserver name is absent in backend details. Please ' 'check whether vserver was created properly or not.') raise exception.NetAppException(msg) if not self._vserver_exists(vserver_name): raise exception.VserverUnavailable(vserver=vserver_name) return f(self, *args, **kwargs)
def create_share(self, share, share_name, clear_current_export_policy=True, ensure_share_already_exists=False, replica=False, is_flexgroup=False): """Creates CIFS share if does not exist on Data ONTAP Vserver. The new CIFS share has Everyone access, so it removes all access after creating. :param share: share entity. :param share_name: share name that must be the CIFS share name. :param clear_current_export_policy: ignored, NFS only. :param ensure_share_already_exists: ensures that CIFS share exists. :param replica: it is a replica volume (DP type). :param is_flexgroup: whether the share is a FlexGroup or not. """ cifs_exist = self._client.cifs_share_exists(share_name) if ensure_share_already_exists and not cifs_exist: msg = _("The expected CIFS share %(share_name)s was not found.") msg_args = {'share_name': share_name} raise exception.NetAppException(msg % msg_args) elif not cifs_exist: self._client.create_cifs_share(share_name) self._client.remove_cifs_share_access(share_name, 'Everyone') # Ensure 'ntfs' security style for RW volume. DP volumes cannot set it. if not replica: self._client.set_volume_security_style(share_name, security_style='ntfs') # Return a callback that may be used for generating export paths # for this share. return (lambda export_address, share_name=share_name: r'\\%s\%s' % (export_address, share_name))
def wait_for_snapmirror_release_svm(self, source_vserver, dest_vserver, src_client, timeout=300): interval = 10 retries = (timeout / interval or 1) @utils.retry(exception.NetAppException, interval=interval, retries=retries, backoff_rate=1) def release_snapmirror(): snapmirrors = src_client.get_snapmirror_destinations_svm( source_vserver=source_vserver, dest_vserver=dest_vserver) if not snapmirrors: LOG.debug("No snapmirrors to be released in source location.") else: try: src_client.release_snapmirror_svm(source_vserver, dest_vserver) except netapp_api.NaApiError as e: if (e.code == netapp_api.EOBJECTNOTFOUND or e.code == netapp_api.ESOURCE_IS_DIFFERENT or "(entry doesn't exist)" in e.message): LOG.debug('Snapmirror relationship does not exists ' 'anymore.') msg = _('Snapmirror release sent to source vserver. We will ' 'wait for it to be released.') raise exception.NetAppException(vserver=msg) try: release_snapmirror() except exception.NetAppException: msg = _("Unable to release the snapmirror from source vserver %s. " "Retries exhausted. Aborting") % source_vserver raise exception.NetAppException(message=msg)
def _create_vserver_if_nonexistent(self, vserver_name, network_info): """Creates Vserver with given parameters if it doesn't exist.""" if self._client.vserver_exists(vserver_name): msg = _('Vserver %s already exists.') raise exception.NetAppException(msg % vserver_name) LOG.debug('Vserver %s does not exist, creating.', vserver_name) self._client.create_vserver( vserver_name, self.configuration.netapp_root_volume_aggregate, self.configuration.netapp_root_volume, self._find_matching_aggregates()) vserver_client = self._get_api_client(vserver=vserver_name) try: self._create_vserver_lifs(vserver_name, vserver_client, network_info) except Exception: with excutils.save_and_reraise_exception(): LOG.error(_LE("Failed to create network interface(s).")) self._client.delete_vserver(vserver_name, vserver_client) vserver_client.enable_nfs() security_services = network_info.get('security_services') if security_services: self._client.setup_security_services(security_services, vserver_client, vserver_name)
def get_vserver_root_volume_name(self, vserver_name): """Get the root volume name of the vserver.""" api_args = { 'query': { 'vserver-info': { 'vserver-name': vserver_name, }, }, 'desired-attributes': { 'vserver-info': { 'root-volume': None, }, }, } vserver_info = self.send_request('vserver-get-iter', api_args) try: root_volume_name = vserver_info.get_child_by_name( 'attributes-list').get_child_by_name( 'vserver-info').get_child_content('root-volume') except AttributeError: msg = _('Could not determine root volume name ' 'for Vserver %s.') % vserver_name raise exception.NetAppException(msg) return root_volume_name
def setup_security_services(self, security_services, vserver_client, vserver_name): api_args = { 'name-mapping-switch': { 'nmswitch': 'ldap,file', }, 'name-server-switch': { 'nsswitch': 'ldap,file', }, 'vserver-name': vserver_name, } self.send_request('vserver-modify', api_args) for security_service in security_services: if security_service['type'].lower() == 'ldap': vserver_client.configure_ldap(security_service) elif security_service['type'].lower() == 'active_directory': vserver_client.configure_active_directory( security_service, vserver_name) elif security_service['type'].lower() == 'kerberos': self.create_kerberos_realm(security_service) vserver_client.configure_kerberos(security_service, vserver_name) else: msg = _('Unsupported security service type %s for ' 'Data ONTAP driver') raise exception.NetAppException(msg % security_service['type'])
def get_nfs_export_policy_for_volume(self, volume_name): """Get the name of the export policy for a volume.""" api_args = { 'query': { 'volume-attributes': { 'volume-id-attributes': { 'name': volume_name, }, }, }, 'desired-attributes': { 'volume-attributes': { 'volume-export-attributes': { 'policy': None, }, }, }, } result = self.send_request('volume-get-iter', api_args) attributes_list = result.get_child_by_name( 'attributes-list') or netapp_api.NaElement('none') volume_attributes = attributes_list.get_child_by_name( 'volume-attributes') or netapp_api.NaElement('none') volume_export_attributes = volume_attributes.get_child_by_name( 'volume-export-attributes') or netapp_api.NaElement('none') export_policy = volume_export_attributes.get_child_content('policy') if not export_policy: msg = _('Could not find export policy for volume %s.') raise exception.NetAppException(msg % volume_name) return export_policy
def wait_for_vserver_state(self, vserver_name, client, state=None, operational_state=None, subtype=None, timeout=300): interval = 10 retries = (timeout / interval or 1) expected = {} if state: expected['state'] = state if operational_state: expected['operational_state'] = operational_state if subtype: expected['subtype'] = subtype @utils.retry(exception.VserverNotReady, interval=interval, retries=retries, backoff_rate=1) def wait_for_state(): vserver_info = client.get_vserver_info(vserver_name) if not all(item in vserver_info.items() for item in expected.items()): raise exception.VserverNotReady(vserver=vserver_name) try: wait_for_state() except exception.VserverNotReady: msg = _("Vserver %s did not reach the expected state. Retries " "exhausted. Aborting.") % vserver_name raise exception.NetAppException(message=msg)
def convert_svm_to_default_subtype(self, vserver_name, client, is_dest_path=True, timeout=300): interval = 10 retries = (timeout / interval or 1) @utils.retry(exception.VserverNotReady, interval=interval, retries=retries, backoff_rate=1) def wait_for_state(): vserver_info = client.get_vserver_info(vserver_name) if vserver_info.get('subtype') != 'default': if is_dest_path: client.break_snapmirror_svm(dest_vserver=vserver_name) else: client.break_snapmirror_svm(source_vserver=vserver_name) raise exception.VserverNotReady(vserver=vserver_name) try: wait_for_state() except exception.VserverNotReady: msg = _("Vserver %s did not reach the expected state. Retries " "exhausted. Aborting.") % vserver_name raise exception.NetAppException(message=msg)
def release_snapmirror(): snapmirrors = src_client.get_snapmirror_destinations( source_vserver=src_vserver, dest_vserver=dest_vserver, source_volume=src_volume_name, dest_volume=dest_volume_name) if not snapmirrors: LOG.debug("No snapmirrors to be released in source volume.") else: try: src_client.release_snapmirror_vol( src_vserver, src_volume_name, dest_vserver, dest_volume_name, relationship_info_only=relationship_info_only) except netapp_api.NaApiError as e: if (e.code == netapp_api.EOBJECTNOTFOUND or e.code == netapp_api.ESOURCE_IS_DIFFERENT or "(entry doesn't exist)" in e.message): LOG.debug('Snapmirror relationship does not exist ' 'anymore.') msg = _('Snapmirror release sent to source volume. Waiting ' 'until it has been released.') raise exception.NetAppException(vserver=msg)
def _setup_security_services(self, security_services, vserver_client, vserver_name): modify_args = { 'name-mapping-switch': { 'nmswitch': 'ldap,file' }, 'name-server-switch': { 'nsswitch': 'ldap,file' }, 'vserver-name': vserver_name } self._client.send_request('vserver-modify', modify_args) for security_service in security_services: if security_service['type'].lower() == "ldap": self._configure_ldap(security_service, vserver_client) elif security_service['type'].lower() == "active_directory": self._configure_active_directory(security_service, vserver_client, vserver_name) elif security_service['type'].lower() == "kerberos": self._configure_kerberos(vserver_name, security_service, vserver_client) else: raise exception.NetAppException( _('Unsupported protocol %s for NetApp driver') % security_service['type'])
def _get_helper(self, share): """Returns driver which implements share protocol.""" share_proto = share['share_proto'] if share_proto.lower() not in self._licenses: current_licenses = self._check_licenses() if share_proto not in current_licenses: msg = _("There is no license for %s at Ontap") % share_proto LOG.error(msg) raise exception.NetAppException(msg) for proto in self._helpers.keys(): if share_proto.upper().startswith(proto): return self._helpers[proto] err_msg = _("Invalid NAS protocol supplied: %s. ") % share_proto raise exception.NetAppException(err_msg)
def _check_if_max_files_is_valid(self, share, value): """Check if max_files has a valid value.""" if int(value) < 0: args = {'value': value, 'key': 'netapp:max_files', 'type_id': share['share_type_id'], 'share_id': share['id']} msg = _('Invalid value "%(value)s" for extra_spec "%(key)s" ' 'in share_type %(type_id)s for share %(share_id)s.') raise exception.NetAppException(msg % args)
def _check_vfiler_exists(self): vfiler_status = self._client.send_request( 'vfiler-get-status', {'vfiler': self.configuration.netapp_nas_vfiler}) if vfiler_status.get_child_content('status') != 'running': msg = _("Vfiler %s is not running") \ % self.configuration.netapp_nas_vfiler LOG.error(msg) raise exception.NetAppException(msg)
def allow_access(self, context, share, access): """Allows access to a given CIFS storage for IPs in access.""" if access['access_type'] != 'sid': msg = _('NetApp only supports "sid" access type for CIFS.') raise exception.NetAppException(msg) user = access['access_to'] target, share_name = self._get_export_location(share) self._allow_access_for(user, share_name)
def _check_data_ontap_version(self): # Temporary check to indicate that the Kilo multi-SVM driver does not # support cDOT 8.3 or higher. ontapi_version = self._client.get_ontapi_version() if ontapi_version >= (1, 30): msg = _('Clustered Data ONTAP 8.3.0 or higher is not ' 'supported by this version of the driver when the ' 'configuration option driver_handles_share_servers ' 'is set to True.') raise exception.NetAppException(msg)
def _get_node_data_port(self, node): port_names = self._client.list_node_data_ports(node) pattern = self.configuration.netapp_port_name_search_pattern matched_port_names = [port_name for port_name in port_names if re.match(pattern, port_name)] if not matched_port_names: raise exception.NetAppException( _('Could not find eligible network ports on node %s on which ' 'to create Vserver LIFs.') % node) return matched_port_names[0]
def _delete_vserver(self, vserver_name, vserver_client, security_services=None): """ Delete vserver. Checks if vserver exists and does not have active shares. Offlines and destroys root volumes. Deletes vserver. """ if not self._vserver_exists(vserver_name): LOG.error(_("Vserver %s does not exists.") % vserver_name) return volumes_data = vserver_client.send_request('volume-get-iter') volumes_count = int(volumes_data.get_child_content('num-records')) if volumes_count == 1: try: vserver_client.send_request( 'volume-offline', {'name': self.configuration.netapp_root_volume_name}) except naapi.NaApiError as e: if e.code == '13042': LOG.error( _("Volume %s is already offline.") % self.configuration.netapp_root_volume_name) else: raise e vserver_client.send_request( 'volume-destroy', {'name': self.configuration.netapp_root_volume_name}) elif volumes_count > 1: msg = _("Error deleting vserver. " "Vserver %s has shares.") % vserver_name LOG.error(msg) raise exception.NetAppException(msg) if security_services: for service in security_services: if service['type'] == 'active_directory': args = { 'admin-password': service['password'], 'admin-username': service['sid'] } try: vserver_client.send_request('cifs-server-delete', args) except naapi.NaApiError as e: if e.code == "15661": LOG.error( _("Cifs server does not exists for" " vserver %s") % vserver_name) else: vserver_client.send_request('cifs-server-delete') self._client.send_request('vserver-destroy', {'vserver-name': vserver_name})
def _get_helper(self, share): """Returns driver which implements share protocol.""" share_protocol = share['share_proto'] self._check_license_for_protocol(share_protocol) for protocol in self._helpers.keys(): if share_protocol.upper().startswith(protocol): return self._helpers[protocol] err_msg = _("Invalid NAS protocol supplied: %s. ") % share_protocol raise exception.NetAppException(err_msg)
def _create_vserver(self, vserver_name, network_info): """Creates Vserver with given parameters if it doesn't exist.""" if self._client.vserver_exists(vserver_name): msg = _('Vserver %s already exists.') raise exception.NetAppException(msg % vserver_name) # NOTE(lseki): If there's already an ipspace created for the same VLAN # port, reuse it. It will be named after the previously created share # server's neutron subnet id. node_name = self._client.list_cluster_nodes()[0] port = self._get_node_data_port(node_name) vlan = network_info['segmentation_id'] ipspace_name = self._client.get_ipspace_name_for_vlan_port( node_name, port, vlan) or self._create_ipspace(network_info) LOG.debug('Vserver %s does not exist, creating.', vserver_name) self._client.create_vserver( vserver_name, self.configuration.netapp_root_volume_aggregate, self.configuration.netapp_root_volume, self._find_matching_aggregates(), ipspace_name) vserver_client = self._get_api_client(vserver=vserver_name) security_services = None try: self._create_vserver_lifs(vserver_name, vserver_client, network_info, ipspace_name) self._create_vserver_admin_lif(vserver_name, vserver_client, network_info, ipspace_name) self._create_vserver_routes(vserver_client, network_info) vserver_client.enable_nfs( self.configuration.netapp_enabled_share_protocols) security_services = network_info.get('security_services') if security_services: self._client.setup_security_services(security_services, vserver_client, vserver_name) except Exception: with excutils.save_and_reraise_exception(): LOG.error("Failed to configure Vserver.") self._delete_vserver(vserver_name, security_services=security_services)
def __init__(self, version, vserver=None, *args, **kwargs): self.configuration = kwargs.get('configuration', None) if not self.configuration: raise exception.NetAppException(_("NetApp configuration missing.")) self._client = naapi.NaServer( host=self.configuration.netapp_nas_server_hostname, username=self.configuration.netapp_nas_login, password=self.configuration.netapp_nas_password, transport_type=self.configuration.netapp_nas_transport_type, ) self._client.set_api_version(*version) if vserver: self._client.set_vserver(vserver)
def _create_net_iface(self, ip, netmask, vlan, node, port, vserver_name, allocation_id): """Creates lif on vlan port.""" vlan_iface_name = "%(port)s-%(tag)s" % {'port': port, 'tag': vlan} try: args = { 'vlan-info': { 'parent-interface': port, 'node': node, 'vlanid': vlan } } self._client.send_request('net-vlan-create', args) except naapi.NaApiError as e: if e.code == '13130': LOG.debug("Vlan %(vlan)s already exists on port %(port)s" % { 'vlan': vlan, 'port': port }) else: raise exception.NetAppException( _("Failed to create vlan %(vlan)s on " "port %(port)s. %(err_msg)") % { 'vlan': vlan, 'port': port, 'err_msg': e.message }) iface_name = (self.configuration.netapp_lif_name_template % { 'node': node, 'net_allocation_id': allocation_id }) LOG.debug('Creating LIF %(lif)r for vserver %(vserver)s ' % { 'lif': iface_name, 'vserver': vserver_name }) args = { 'address': ip, 'administrative-status': 'up', 'data-protocols': [{ 'data-protocol': 'nfs' }, { 'data-protocol': 'cifs' }], 'home-node': node, 'home-port': vlan_iface_name, 'netmask': netmask, 'interface-name': iface_name, 'role': 'data', 'vserver': vserver_name, } self._client.send_request('net-interface-create', args)
def _get_helper(self, share): """Returns driver which implements share protocol.""" share_protocol = share['share_proto'].lower() if share_protocol not in self.SUPPORTED_PROTOCOLS: err_msg = _("Invalid NAS protocol supplied: %s.") % share_protocol raise exception.NetAppException(err_msg) self._check_license_for_protocol(share_protocol) if share_protocol == 'nfs': return nfs_cmode.NetAppCmodeNFSHelper() elif share_protocol == 'cifs': return cifs_cmode.NetAppCmodeCIFSHelper()
def get_vserver_aggregate_capacities(self, aggregate_names=None): """Calculates capacity of one or more aggregates for a vserver. Returns dictionary of aggregate capacity metrics. This must be called against a Vserver LIF. """ if aggregate_names is not None and len(aggregate_names) == 0: return {} api_args = { 'desired-attributes': { 'vserver-info': { 'vserver-name': None, 'vserver-aggr-info-list': { 'vserver-aggr-info': { 'aggr-name': None, 'aggr-availsize': None, }, }, }, }, } result = self.send_request('vserver-get', api_args) attributes = result.get_child_by_name('attributes') if not attributes: raise exception.NetAppException('Failed to read Vserver info') vserver_info = attributes.get_child_by_name('vserver-info') vserver_name = vserver_info.get_child_content('vserver-name') vserver_aggr_info_element = vserver_info.get_child_by_name( 'vserver-aggr-info-list') or netapp_api.NaElement('none') vserver_aggr_info_list = vserver_aggr_info_element.get_children() if not vserver_aggr_info_list: LOG.warning(_LW('No aggregates assigned to Vserver %s.'), vserver_name) # Return dict of key-value pair of aggr_name:aggr_size_available. aggr_space_dict = {} for aggr_info in vserver_aggr_info_list: aggr_name = aggr_info.get_child_content('aggr-name') if aggregate_names is None or aggr_name in aggregate_names: aggr_size = int(aggr_info.get_child_content('aggr-availsize')) aggr_space_dict[aggr_name] = {'available': aggr_size} LOG.debug('Found available Vserver aggregates: %s', aggr_space_dict) return aggr_space_dict
def test_delete_vserver_vlan_client_error(self): mock_exception_log = self.mock_object(lib_multi_svm.LOG, 'exception') self.mock_object( self.library._client, 'delete_vlan', mock.Mock(side_effect=exception.NetAppException("fake error"))) self.library._delete_vserver_vlan(c_fake.NETWORK_INTERFACES) for interface in c_fake.NETWORK_INTERFACES: home_port = interface['home-port'] port, vlan = home_port.split('-') node = interface['home-node'] self.library._client.delete_vlan.assert_called_once_with( node, port, vlan) self.assertEqual(1, mock_exception_log.call_count)
def _create_export(self, share, vserver, vserver_client): """Creates NAS storage.""" helper = self._get_helper(share) helper.set_client(vserver_client) share_name = self._get_valid_share_name(share['id']) args = {'query': {'net-interface-info': {'vserver': vserver}}} ifaces = vserver_client.send_request('net-interface-get-iter', args) if not int(ifaces.get_child_content('num-records')): raise exception.NetAppException( _("Cannot find network interfaces for vserver %s.") % vserver) ifaces_list = ifaces.get_child_by_name('attributes-list')\ .get_children() ip_address = ifaces_list[0].get_child_content('address') export_location = helper.create_share(share_name, ip_address) return export_location
def _check_aggregate_extra_specs_validity(self, aggregate_name, specs): for specs_key in ('netapp_disk_type', 'netapp_raid_type'): aggr_value = self._ssc_stats.get(aggregate_name, {}).get(specs_key) specs_value = specs.get(specs_key) if aggr_value and specs_value and aggr_value != specs_value: msg = _('Invalid value "%(value)s" for extra_spec "%(key)s" ' 'in aggregate %(aggr)s.') msg_args = { 'value': specs_value, 'key': specs_key, 'aggr': aggregate_name } raise exception.NetAppException(msg % msg_args)
def _find_match_aggregates(self): """Find all aggregates match pattern.""" pattern = self.configuration.netapp_aggregate_name_search_pattern try: aggrs = self._client.send_request('aggr-get-iter')\ .get_child_by_name('attributes-list').get_children() except AttributeError: msg = _("Have not found aggregates match pattern %s") % pattern LOG.error(msg) raise exception.NetAppException(msg) aggr_list = [ aggr for aggr in aggrs if re.match(pattern, aggr.get_child_content('aggregate-name')) ] return aggr_list
def _check_license_for_protocol(self, share_protocol): """Validates protocol license if cluster APIs are accessible.""" if not self._have_cluster_creds: return if share_protocol.lower() not in self._licenses: current_licenses = self._get_licenses() if share_protocol.lower() not in current_licenses: msg_args = { 'protocol': share_protocol, 'host': self.configuration.netapp_server_hostname } msg = _('The protocol %(protocol)s is not licensed on ' 'controller %(host)s') % msg_args LOG.error(msg) raise exception.NetAppException(msg)