"Fossies" - the Fresh Open Source Software Archive

Member "manila-8.1.4/manila/share/drivers/dell_emc/plugins/unity/connection.py" (19 Nov 2020, 29076 Bytes) of package /linux/misc/openstack/manila-8.1.4.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "connection.py": 8.1.3_vs_8.1.4.

    1 # Copyright (c) 2016 EMC Corporation.
    2 # All Rights Reserved.
    3 #
    4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
    5 #    not use this file except in compliance with the License. You may obtain
    6 #    a copy of the License at
    7 #
    8 #         http://www.apache.org/licenses/LICENSE-2.0
    9 #
   10 #    Unless required by applicable law or agreed to in writing, software
   11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   13 #    License for the specific language governing permissions and limitations
   14 #    under the License.
   15 """Unity backend for the EMC Manila driver."""
   16 import random
   17 
   18 from oslo_config import cfg
   19 from oslo_log import log
   20 from oslo_utils import excutils
   21 from oslo_utils import importutils
   22 from oslo_utils import netutils
   23 
   24 storops = importutils.try_import('storops')
   25 if storops:
   26     # pylint: disable=import-error
   27     from storops import exception as storops_ex
   28     from storops.unity import enums
   29 
   30 from manila.common import constants as const
   31 from manila import exception
   32 from manila.i18n import _
   33 from manila.share.drivers.dell_emc.common.enas import utils as enas_utils
   34 from manila.share.drivers.dell_emc.plugins import base as driver
   35 from manila.share.drivers.dell_emc.plugins.unity import client
   36 from manila.share.drivers.dell_emc.plugins.unity import utils as unity_utils
   37 from manila.share import utils as share_utils
   38 from manila import utils
   39 
   40 """Version history:
   41      6.1.1 - Fix parsing management IPv6 address
   42      6.1.2 - Bugfix: failed to delete CIFS share if wrong access was set
   43 """
   44 
   45 VERSION = "6.1.2"
   46 
   47 LOG = log.getLogger(__name__)
   48 SUPPORTED_NETWORK_TYPES = (None, 'flat', 'vlan')
   49 
   50 UNITY_OPTS = [
   51     cfg.StrOpt('unity_server_meta_pool',
   52                required=True,
   53                deprecated_name='emc_nas_server_pool',
   54                help='Pool to persist the meta-data of NAS server.'),
   55     cfg.ListOpt('unity_share_data_pools',
   56                 deprecated_name='emc_nas_pool_names',
   57                 help='Comma separated list of pools that can be used to '
   58                      'persist share data.'),
   59     cfg.ListOpt('unity_ethernet_ports',
   60                 deprecated_name='emc_interface_ports',
   61                 help='Comma separated list of ports that can be used for '
   62                      'share server interfaces. Members of the list '
   63                      'can be Unix-style glob expressions.'),
   64     cfg.StrOpt('emc_nas_server_container',
   65                deprecated_for_removal=True,
   66                deprecated_reason='Unity driver supports nas server auto load '
   67                                  'balance.',
   68                help='Storage processor to host the NAS server. Obsolete.'),
   69 ]
   70 
   71 CONF = cfg.CONF
   72 CONF.register_opts(UNITY_OPTS)
   73 
   74 
   75 @enas_utils.decorate_all_methods(enas_utils.log_enter_exit,
   76                                  debug_only=True)
   77 class UnityStorageConnection(driver.StorageConnection):
   78     """Implements Unity specific functionality for EMC Manila driver."""
   79 
   80     IP_ALLOCATIONS = 1
   81 
   82     @enas_utils.log_enter_exit
   83     def __init__(self, *args, **kwargs):
   84         super(UnityStorageConnection, self).__init__(*args, **kwargs)
   85         if 'configuration' in kwargs:
   86             kwargs['configuration'].append_config_values(UNITY_OPTS)
   87 
   88         self.client = None
   89         self.pool_set = None
   90         self.nas_server_pool = None
   91         self.reserved_percentage = None
   92         self.max_over_subscription_ratio = None
   93         self.port_ids_conf = None
   94         self.ipv6_implemented = True
   95         self.revert_to_snap_support = True
   96         self.shrink_share_support = True
   97 
   98         # props from super class.
   99         self.driver_handles_share_servers = True
  100 
  101     def connect(self, emc_share_driver, context):
  102         """Connect to Unity storage."""
  103         config = emc_share_driver.configuration
  104         storage_ip = enas_utils.convert_ipv6_format_if_needed(
  105             config.emc_nas_server)
  106         username = config.emc_nas_login
  107         password = config.emc_nas_password
  108         self.client = client.UnityClient(storage_ip, username, password)
  109 
  110         pool_conf = config.safe_get('unity_share_data_pools')
  111         self.pool_set = self._get_managed_pools(pool_conf)
  112 
  113         self.reserved_percentage = config.safe_get(
  114             'reserved_share_percentage')
  115         if self.reserved_percentage is None:
  116             self.reserved_percentage = 0
  117 
  118         self.max_over_subscription_ratio = config.safe_get(
  119             'max_over_subscription_ratio')
  120         self.port_ids_conf = config.safe_get('unity_ethernet_ports')
  121         self.validate_port_configuration(self.port_ids_conf)
  122         pool_name = config.unity_server_meta_pool
  123         self._config_pool(pool_name)
  124 
  125     def validate_port_configuration(self, port_ids_conf):
  126         """Initializes the SP and ports based on the port option."""
  127 
  128         ports = self.client.get_file_ports()
  129 
  130         sp_ports_map, unmanaged_port_ids = unity_utils.match_ports(
  131             ports, port_ids_conf)
  132 
  133         if not sp_ports_map:
  134             msg = (_("All the specified storage ports to be managed "
  135                      "do not exist. Please check your configuration "
  136                      "unity_ethernet_ports in manila.conf. "
  137                      "The available ports in the backend are %s.") %
  138                    ",".join([port.get_id() for port in ports]))
  139             raise exception.BadConfigurationException(reason=msg)
  140 
  141         if unmanaged_port_ids:
  142             LOG.info("The following specified ports are not managed by "
  143                      "the backend: %(unmanaged)s. This host will only "
  144                      "manage the storage ports: %(exist)s",
  145                      {'unmanaged': ",".join(unmanaged_port_ids),
  146                       'exist': ",".join(map(",".join,
  147                                             sp_ports_map.values()))})
  148         else:
  149             LOG.debug("Ports: %s will be managed.",
  150                       ",".join(map(",".join, sp_ports_map.values())))
  151 
  152         if len(sp_ports_map) == 1:
  153             LOG.info("Only ports of %s are configured. Configure ports "
  154                      "of both SPA and SPB to use both of the SPs.",
  155                      list(sp_ports_map)[0])
  156 
  157         return sp_ports_map
  158 
  159     def check_for_setup_error(self):
  160         """Check for setup error."""
  161 
  162     def create_share(self, context, share, share_server=None):
  163         """Create a share and export it based on protocol used."""
  164         share_name = share['id']
  165         size = share['size']
  166 
  167         # Check share's protocol.
  168         # Throw an exception immediately if it is an invalid protocol.
  169         share_proto = share['share_proto'].upper()
  170         proto_enum = self._get_proto_enum(share_proto)
  171 
  172         # Get pool name from share host field
  173         pool_name = self._get_pool_name_from_host(share['host'])
  174         # Get share server name from share server
  175         server_name = self._get_server_name(share_server)
  176 
  177         pool = self.client.get_pool(pool_name)
  178         try:
  179             nas_server = self.client.get_nas_server(server_name)
  180         except storops_ex.UnityResourceNotFoundError:
  181             message = (_("Failed to get NAS server %(server)s when "
  182                          "creating the share %(share)s.") %
  183                        {'server': server_name, 'share': share_name})
  184             LOG.exception(message)
  185             raise exception.EMCUnityError(err=message)
  186 
  187         locations = None
  188         if share_proto == 'CIFS':
  189             filesystem = self.client.create_filesystem(
  190                 pool, nas_server, share_name,
  191                 size, proto=proto_enum)
  192             self.client.create_cifs_share(filesystem, share_name)
  193 
  194             locations = self._get_cifs_location(
  195                 nas_server.file_interface, share_name)
  196         elif share_proto == 'NFS':
  197             self.client.create_nfs_filesystem_and_share(
  198                 pool, nas_server, share_name, size)
  199 
  200             locations = self._get_nfs_location(
  201                 nas_server.file_interface, share_name)
  202 
  203         return locations
  204 
  205     def create_share_from_snapshot(self, context, share, snapshot,
  206                                    share_server=None):
  207         """Create a share from a snapshot - clone a snapshot."""
  208         share_name = share['id']
  209 
  210         # Check share's protocol.
  211         # Throw an exception immediately if it is an invalid protocol.
  212         share_proto = share['share_proto'].upper()
  213         self._validate_share_protocol(share_proto)
  214 
  215         # Get share server name from share server
  216         server_name = self._get_server_name(share_server)
  217 
  218         try:
  219             nas_server = self.client.get_nas_server(server_name)
  220         except storops_ex.UnityResourceNotFoundError:
  221             message = (_("Failed to get NAS server %(server)s when "
  222                          "creating the share %(share)s.") %
  223                        {'server': server_name, 'share': share_name})
  224             LOG.exception(message)
  225             raise exception.EMCUnityError(err=message)
  226 
  227         backend_snap = self.client.create_snap_of_snap(snapshot['id'],
  228                                                        share_name)
  229 
  230         locations = None
  231         if share_proto == 'CIFS':
  232             self.client.create_cifs_share(backend_snap, share_name)
  233 
  234             locations = self._get_cifs_location(
  235                 nas_server.file_interface, share_name)
  236         elif share_proto == 'NFS':
  237             self.client.create_nfs_share(backend_snap, share_name)
  238 
  239             locations = self._get_nfs_location(
  240                 nas_server.file_interface, share_name)
  241 
  242         return locations
  243 
  244     def delete_share(self, context, share, share_server=None):
  245         """Delete a share."""
  246         share_name = share['id']
  247         try:
  248             backend_share = self.client.get_share(share_name,
  249                                                   share['share_proto'])
  250         except storops_ex.UnityResourceNotFoundError:
  251             LOG.warning("Share %s is not found when deleting the share",
  252                         share_name)
  253             return
  254 
  255         # Share created by the API create_share_from_snapshot()
  256         if self._is_share_from_snapshot(backend_share):
  257             filesystem = backend_share.snap.filesystem
  258             self.client.delete_snapshot(backend_share.snap)
  259         else:
  260             filesystem = backend_share.filesystem
  261             self.client.delete_share(backend_share)
  262 
  263         if self._is_isolated_filesystem(filesystem):
  264             self.client.delete_filesystem(filesystem)
  265 
  266     def extend_share(self, share, new_size, share_server=None):
  267         backend_share = self.client.get_share(share['id'],
  268                                               share['share_proto'])
  269 
  270         if not self._is_share_from_snapshot(backend_share):
  271             self.client.extend_filesystem(backend_share.filesystem,
  272                                           new_size)
  273         else:
  274             share_id = share['id']
  275             reason = ("Driver does not support extending a "
  276                       "snapshot based share.")
  277             raise exception.ShareExtendingError(share_id=share_id,
  278                                                 reason=reason)
  279 
  280     def shrink_share(self, share, new_size, share_server=None):
  281         """Shrinks a share to new size.
  282 
  283         :param share: Share that will be shrunk.
  284         :param new_size: New size of share.
  285         :param share_server: Data structure with share server information.
  286             Not used by this driver.
  287         """
  288         share_id = share['id']
  289         backend_share = self.client.get_share(share_id,
  290                                               share['share_proto'])
  291         if self._is_share_from_snapshot(backend_share):
  292             reason = ("Driver does not support shrinking a "
  293                       "snapshot based share.")
  294             raise exception.ShareShrinkingError(share_id=share_id,
  295                                                 reason=reason)
  296         self.client.shrink_filesystem(share_id, backend_share.filesystem,
  297                                       new_size)
  298         LOG.info("Share %(shr_id)s successfully shrunk to "
  299                  "%(shr_size)sG.",
  300                  {'shr_id': share_id,
  301                   'shr_size': new_size})
  302 
  303     def create_snapshot(self, context, snapshot, share_server=None):
  304         """Create snapshot from share."""
  305         share_name = snapshot['share_id']
  306         share_proto = snapshot['share']['share_proto']
  307         backend_share = self.client.get_share(share_name, share_proto)
  308 
  309         snapshot_name = snapshot['id']
  310         if self._is_share_from_snapshot(backend_share):
  311             self.client.create_snap_of_snap(backend_share.snap, snapshot_name)
  312         else:
  313             self.client.create_snapshot(backend_share.filesystem,
  314                                         snapshot_name)
  315 
  316     def delete_snapshot(self, context, snapshot, share_server=None):
  317         """Delete a snapshot."""
  318         snap = self.client.get_snapshot(snapshot['id'])
  319         self.client.delete_snapshot(snap)
  320 
  321     def update_access(self, context, share, access_rules, add_rules,
  322                       delete_rules, share_server=None):
  323         # adding rules
  324         if add_rules:
  325             for rule in add_rules:
  326                 self.allow_access(context, share, rule, share_server)
  327 
  328         # deleting rules
  329         if delete_rules:
  330             for rule in delete_rules:
  331                 self.deny_access(context, share, rule, share_server)
  332 
  333         # recovery mode
  334         if not (add_rules or delete_rules):
  335             white_list = []
  336             for rule in access_rules:
  337                 self.allow_access(context, share, rule, share_server)
  338                 white_list.append(rule['access_to'])
  339             self.clear_access(share, white_list)
  340 
  341     def clear_access(self, share, white_list=None):
  342         share_proto = share['share_proto'].upper()
  343         share_name = share['id']
  344         if share_proto == 'CIFS':
  345             self.client.cifs_clear_access(share_name, white_list)
  346         elif share_proto == 'NFS':
  347             self.client.nfs_clear_access(share_name, white_list)
  348 
  349     def allow_access(self, context, share, access, share_server=None):
  350         """Allow access to a share."""
  351         access_level = access['access_level']
  352         if access_level not in const.ACCESS_LEVELS:
  353             raise exception.InvalidShareAccessLevel(level=access_level)
  354 
  355         share_proto = share['share_proto'].upper()
  356 
  357         self._validate_share_protocol(share_proto)
  358         self._validate_share_access_type(share, access)
  359 
  360         if share_proto == 'CIFS':
  361             self._cifs_allow_access(share, access)
  362         elif share_proto == 'NFS':
  363             self._nfs_allow_access(share, access)
  364 
  365     def deny_access(self, context, share, access, share_server):
  366         """Deny access to a share."""
  367         share_proto = share['share_proto'].upper()
  368 
  369         self._validate_share_protocol(share_proto)
  370         self._validate_share_access_type(share, access)
  371 
  372         if share_proto == 'CIFS':
  373             self._cifs_deny_access(share, access)
  374         elif share_proto == 'NFS':
  375             self._nfs_deny_access(share, access)
  376 
  377     def ensure_share(self, context, share, share_server):
  378         """Ensure that the share is exported."""
  379         share_name = share['id']
  380         share_proto = share['share_proto']
  381 
  382         backend_share = self.client.get_share(share_name, share_proto)
  383         if not backend_share.existed:
  384             raise exception.ShareNotFound(share_id=share_name)
  385 
  386     def update_share_stats(self, stats_dict):
  387         """Communicate with EMCNASClient to get the stats."""
  388         stats_dict['driver_version'] = VERSION
  389         stats_dict['pools'] = []
  390 
  391         for pool in self.client.get_pool():
  392             if pool.name in self.pool_set:
  393                 # the unit of following numbers are GB
  394                 total_size = float(pool.size_total)
  395                 used_size = float(pool.size_used)
  396 
  397                 pool_stat = {
  398                     'pool_name': pool.name,
  399                     'thin_provisioning': True,
  400                     'total_capacity_gb': enas_utils.bytes_to_gb(total_size),
  401                     'free_capacity_gb':
  402                         enas_utils.bytes_to_gb(total_size - used_size),
  403                     'allocated_capacity_gb': enas_utils.bytes_to_gb(used_size),
  404                     'provisioned_capacity_gb':
  405                         enas_utils.bytes_to_gb(pool.size_subscribed),
  406                     'qos': False,
  407                     'reserved_percentage': self.reserved_percentage,
  408                     'max_over_subscription_ratio':
  409                         self.max_over_subscription_ratio,
  410                 }
  411                 stats_dict['pools'].append(pool_stat)
  412 
  413         if not stats_dict.get('pools'):
  414             message = _("Failed to update storage pool.")
  415             LOG.error(message)
  416             raise exception.EMCUnityError(err=message)
  417 
  418     def get_pool(self, share):
  419         """Get the pool name of the share."""
  420         backend_share = self.client.get_share(
  421             share['id'], share['share_proto'])
  422 
  423         return backend_share.filesystem.pool.name
  424 
  425     def get_network_allocations_number(self):
  426         """Returns number of network allocations for creating VIFs."""
  427         return self.IP_ALLOCATIONS
  428 
  429     def setup_server(self, network_info, metadata=None):
  430         """Set up and configures share server with given network parameters."""
  431         server_name = network_info['server_id']
  432         segmentation_id = network_info['segmentation_id']
  433         network = self.validate_network(network_info)
  434         mtu = network['mtu']
  435         tenant = self.client.get_tenant(network_info['server_id'],
  436                                         segmentation_id)
  437 
  438         sp_ports_map = unity_utils.find_ports_by_mtu(
  439             self.client.get_file_ports(),
  440             self.port_ids_conf, mtu)
  441 
  442         sp = self._choose_sp(sp_ports_map)
  443         nas_server = self.client.create_nas_server(server_name,
  444                                                    sp,
  445                                                    self.nas_server_pool,
  446                                                    tenant=tenant)
  447         sp = nas_server.home_sp
  448         port_id = self._choose_port(sp_ports_map, sp)
  449         try:
  450             self._create_network_interface(nas_server, network, port_id)
  451 
  452             self._handle_security_services(
  453                 nas_server, network_info['security_services'])
  454 
  455             return {'share_server_name': server_name}
  456 
  457         except Exception:
  458             with excutils.save_and_reraise_exception():
  459                 LOG.exception('Could not setup server.')
  460                 server_details = {'share_server_name': server_name}
  461                 self.teardown_server(
  462                     server_details, network_info['security_services'])
  463 
  464     def teardown_server(self, server_details, security_services=None):
  465         """Teardown share server."""
  466         if not server_details:
  467             LOG.debug('Server details are empty.')
  468             return
  469 
  470         server_name = server_details.get('share_server_name')
  471         if not server_name:
  472             LOG.debug('No share server found for server %s.',
  473                       server_details.get('instance_id'))
  474             return
  475 
  476         username = None
  477         password = None
  478         for security_service in security_services:
  479             if security_service['type'] == 'active_directory':
  480                 username = security_service['user']
  481                 password = security_service['password']
  482                 break
  483 
  484         self.client.delete_nas_server(server_name, username, password)
  485 
  486     def _cifs_allow_access(self, share, access):
  487         """Allow access to CIFS share."""
  488         self.client.cifs_allow_access(
  489             share['id'], access['access_to'], access['access_level'])
  490 
  491     def _cifs_deny_access(self, share, access):
  492         """Deny access to CIFS share."""
  493         self.client.cifs_deny_access(share['id'], access['access_to'])
  494 
  495     def _config_pool(self, pool_name):
  496         try:
  497             self.nas_server_pool = self.client.get_pool(pool_name)
  498         except storops_ex.UnityResourceNotFoundError:
  499             message = (_("The storage pools %s to store NAS server "
  500                          "configuration do not exist.") % pool_name)
  501             LOG.exception(message)
  502             raise exception.BadConfigurationException(reason=message)
  503 
  504     @staticmethod
  505     def validate_network(network_info):
  506         network = network_info['network_allocations'][0]
  507         if network['network_type'] not in SUPPORTED_NETWORK_TYPES:
  508             msg = _('The specified network type %s is unsupported by '
  509                     'the EMC Unity driver')
  510             raise exception.NetworkBadConfigurationException(
  511                 reason=msg % network['network_type'])
  512         return network
  513 
  514     def _create_network_interface(self, nas_server, network, port_id):
  515         kargs = {'ip_addr': network['ip_address'],
  516                  'gateway': network['gateway'],
  517                  'vlan_id': network['segmentation_id'],
  518                  'port_id': port_id}
  519 
  520         if netutils.is_valid_ipv6_cidr(kargs['ip_addr']):
  521             kargs['netmask'] = None
  522             kargs['prefix_length'] = str(utils.cidr_to_prefixlen(
  523                 network['cidr']))
  524         else:
  525             kargs['netmask'] = utils.cidr_to_netmask(network['cidr'])
  526 
  527         # Create the interfaces on NAS server
  528         self.client.create_interface(nas_server, **kargs)
  529 
  530     def _choose_sp(self, sp_ports_map):
  531         sp = None
  532         if len(sp_ports_map.keys()) == 1:
  533             # Only one storage processor has usable ports,
  534             # create NAS server on that SP.
  535             sp = self.client.get_storage_processor(
  536                 sp_id=list(sp_ports_map.keys())[0])
  537             LOG.debug('All the usable ports belong to  %s. '
  538                       'Creating NAS server on this SP without '
  539                       'load balance.', sp.get_id())
  540         return sp
  541 
  542     @staticmethod
  543     def _choose_port(sp_ports_map, sp):
  544         ports = sp_ports_map[sp.get_id()]
  545         return random.choice(list(ports))
  546 
  547     @staticmethod
  548     def _get_cifs_location(file_interfaces, share_name):
  549         return [
  550             {'path': r'\\%(interface)s\%(share_name)s' % {
  551                 'interface': enas_utils.export_unc_path(interface.ip_address),
  552                 'share_name': share_name}
  553              }
  554             for interface in file_interfaces
  555         ]
  556 
  557     def _get_managed_pools(self, pool_conf):
  558         # Get the real pools from the backend storage
  559         real_pools = set(pool.name for pool in self.client.get_pool())
  560 
  561         if not pool_conf:
  562             LOG.debug("No storage pool is specified, so all pools in storage "
  563                       "system will be managed.")
  564             return real_pools
  565 
  566         matched_pools, unmanaged_pools = unity_utils.do_match(real_pools,
  567                                                               pool_conf)
  568 
  569         if not matched_pools:
  570             msg = (_("All the specified storage pools to be managed "
  571                      "do not exist. Please check your configuration "
  572                      "emc_nas_pool_names in manila.conf. "
  573                      "The available pools in the backend are %s") %
  574                    ",".join(real_pools))
  575             raise exception.BadConfigurationException(reason=msg)
  576 
  577         if unmanaged_pools:
  578             LOG.info("The following specified storage pools "
  579                      "are not managed by the backend: "
  580                      "%(un_managed)s. This host will only manage "
  581                      "the storage pools: %(exist)s",
  582                      {'un_managed': ",".join(unmanaged_pools),
  583                       'exist': ",".join(matched_pools)})
  584         else:
  585             LOG.debug("Storage pools: %s will be managed.",
  586                       ",".join(matched_pools))
  587 
  588         return matched_pools
  589 
  590     @staticmethod
  591     def _get_nfs_location(file_interfaces, share_name):
  592         return [
  593             {'path': '%(interface)s:/%(share_name)s' % {
  594                 'interface': enas_utils.convert_ipv6_format_if_needed(
  595                     interface.ip_address),
  596                 'share_name': share_name}
  597              }
  598             for interface in file_interfaces
  599         ]
  600 
  601     @staticmethod
  602     def _get_pool_name_from_host(host):
  603         pool_name = share_utils.extract_host(host, level='pool')
  604         if not pool_name:
  605             message = (_("Pool is not available in the share host %s.") %
  606                        host)
  607             raise exception.InvalidHost(reason=message)
  608 
  609         return pool_name
  610 
  611     @staticmethod
  612     def _get_proto_enum(share_proto):
  613         share_proto = share_proto.upper()
  614         UnityStorageConnection._validate_share_protocol(share_proto)
  615 
  616         if share_proto == 'CIFS':
  617             return enums.FSSupportedProtocolEnum.CIFS
  618         elif share_proto == 'NFS':
  619             return enums.FSSupportedProtocolEnum.NFS
  620 
  621     @staticmethod
  622     def _get_server_name(share_server):
  623         if not share_server:
  624             msg = _('Share server not provided.')
  625             raise exception.InvalidInput(reason=msg)
  626 
  627         server_name = share_server.get(
  628             'backend_details', {}).get('share_server_name')
  629 
  630         if server_name is None:
  631             msg = (_("Name of the share server %s not found.")
  632                    % share_server['id'])
  633             LOG.error(msg)
  634             raise exception.InvalidInput(reason=msg)
  635 
  636         return server_name
  637 
  638     def _handle_security_services(self, nas_server, security_services):
  639         kerberos_enabled = False
  640         # Support 'active_directory' and 'kerberos'
  641         for security_service in security_services:
  642             service_type = security_service['type']
  643             if service_type == 'active_directory':
  644                 # Create DNS server for NAS server
  645                 domain = security_service['domain']
  646                 dns_ip = security_service['dns_ip']
  647                 self.client.create_dns_server(nas_server,
  648                                               domain,
  649                                               dns_ip)
  650 
  651                 # Enable CIFS service
  652                 username = security_service['user']
  653                 password = security_service['password']
  654                 self.client.enable_cifs_service(nas_server,
  655                                                 domain=domain,
  656                                                 username=username,
  657                                                 password=password)
  658             elif service_type == 'kerberos':
  659                 # Enable NFS service with kerberos
  660                 kerberos_enabled = True
  661                 # TODO(jay.xu): enable nfs service with kerberos
  662                 LOG.warning('Kerberos is not supported by '
  663                             'EMC Unity manila driver plugin.')
  664             elif service_type == 'ldap':
  665                 LOG.warning('LDAP is not supported by '
  666                             'EMC Unity manila driver plugin.')
  667             else:
  668                 LOG.warning('Unknown security service type: %s.',
  669                             service_type)
  670 
  671         if not kerberos_enabled:
  672             # Enable NFS service without kerberos
  673             self.client.enable_nfs_service(nas_server)
  674 
  675     def _nfs_allow_access(self, share, access):
  676         """Allow access to NFS share."""
  677         self.client.nfs_allow_access(
  678             share['id'], access['access_to'], access['access_level'])
  679 
  680     def _nfs_deny_access(self, share, access):
  681         """Deny access to NFS share."""
  682         self.client.nfs_deny_access(share['id'], access['access_to'])
  683 
  684     @staticmethod
  685     def _is_isolated_filesystem(filesystem):
  686         filesystem.update()
  687         return (
  688             not filesystem.has_snap() and
  689             not (filesystem.cifs_share or filesystem.nfs_share)
  690         )
  691 
  692     @staticmethod
  693     def _is_share_from_snapshot(share):
  694         return True if share.snap else False
  695 
  696     @staticmethod
  697     def _validate_share_access_type(share, access):
  698         reason = None
  699         share_proto = share['share_proto'].upper()
  700 
  701         if share_proto == 'CIFS' and access['access_type'] != 'user':
  702             reason = _('Only user access type allowed for CIFS share.')
  703         elif share_proto == 'NFS' and access['access_type'] != 'ip':
  704             reason = _('Only IP access type allowed for NFS share.')
  705 
  706         if reason:
  707             raise exception.InvalidShareAccess(reason=reason)
  708 
  709     @staticmethod
  710     def _validate_share_protocol(share_proto):
  711         if share_proto not in ('NFS', 'CIFS'):
  712             raise exception.InvalidShare(
  713                 reason=(_('Invalid NAS protocol supplied: %s.') %
  714                         share_proto))
  715 
  716     def revert_to_snapshot(self, context, snapshot, share_access_rules,
  717                            snapshot_access_rules, share_server=None):
  718         """Reverts a share (in place) to the specified snapshot."""
  719         return self.client.restore_snapshot(snapshot['id'])