"Fossies" - the Fresh Open Source Software Archive

Member "manila-8.1.4/manila/share/drivers/dell_emc/plugins/vnx/connection.py" (19 Nov 2020, 35507 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) 2014 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 """VNX backend for the EMC Manila driver."""
   16 
   17 import copy
   18 import random
   19 import six
   20 
   21 from oslo_config import cfg
   22 from oslo_log import log
   23 from oslo_utils import excutils
   24 from oslo_utils import units
   25 
   26 from manila.common import constants as const
   27 from manila import exception
   28 from manila.i18n import _
   29 from manila.share.drivers.dell_emc.common.enas import constants
   30 from manila.share.drivers.dell_emc.common.enas import utils as enas_utils
   31 from manila.share.drivers.dell_emc.plugins import base as driver
   32 from manila.share.drivers.dell_emc.plugins.vnx import object_manager as manager
   33 from manila.share import utils as share_utils
   34 from manila import utils
   35 
   36 """Version history:
   37     1.0.0 - Initial version (Liberty)
   38     2.0.0 - Bumped the version for Mitaka
   39     3.0.0 - Bumped the version for Ocata
   40     4.0.0 - Bumped the version for Pike
   41     5.0.0 - Bumped the version for Queens
   42 """
   43 VERSION = "5.0.0"
   44 
   45 LOG = log.getLogger(__name__)
   46 
   47 VNX_OPTS = [
   48     cfg.StrOpt('vnx_server_container',
   49                deprecated_name='emc_nas_server_container',
   50                help='Data mover to host the NAS server.'),
   51     cfg.ListOpt('vnx_share_data_pools',
   52                 deprecated_name='emc_nas_pool_names',
   53                 help='Comma separated list of pools that can be used to '
   54                      'persist share data.'),
   55     cfg.ListOpt('vnx_ethernet_ports',
   56                 deprecated_name='emc_interface_ports',
   57                 help='Comma separated list of ports that can be used for '
   58                      'share server interfaces. Members of the list '
   59                      'can be Unix-style glob expressions.')
   60 ]
   61 
   62 CONF = cfg.CONF
   63 CONF.register_opts(VNX_OPTS)
   64 
   65 
   66 @enas_utils.decorate_all_methods(enas_utils.log_enter_exit,
   67                                  debug_only=True)
   68 class VNXStorageConnection(driver.StorageConnection):
   69     """Implements VNX specific functionality for EMC Manila driver."""
   70 
   71     @enas_utils.log_enter_exit
   72     def __init__(self, *args, **kwargs):
   73         super(VNXStorageConnection, self).__init__(*args, **kwargs)
   74         if 'configuration' in kwargs:
   75             kwargs['configuration'].append_config_values(VNX_OPTS)
   76 
   77         self.mover_name = None
   78         self.pools = None
   79         self.manager = None
   80         self.pool_conf = None
   81         self.reserved_percentage = None
   82         self.driver_handles_share_servers = True
   83         self.port_conf = None
   84         self.ipv6_implemented = True
   85 
   86     def create_share(self, context, share, share_server=None):
   87         """Create a share and export it based on protocol used."""
   88         share_name = share['id']
   89         size = share['size'] * units.Ki
   90 
   91         share_proto = share['share_proto']
   92 
   93         # Validate the share protocol
   94         if share_proto.upper() not in ('NFS', 'CIFS'):
   95             raise exception.InvalidShare(
   96                 reason=(_('Invalid NAS protocol supplied: %s.')
   97                         % share_proto))
   98 
   99         # Get the pool name from share host field
  100         pool_name = share_utils.extract_host(share['host'], level='pool')
  101         if not pool_name:
  102             message = (_("Pool is not available in the share host %s.") %
  103                        share['host'])
  104             raise exception.InvalidHost(reason=message)
  105 
  106         # Validate share server
  107         self._share_server_validation(share_server)
  108 
  109         if share_proto == 'CIFS':
  110             vdm_name = self._get_share_server_name(share_server)
  111             server_name = vdm_name
  112 
  113             # Check if CIFS server exists.
  114             status, server = self._get_context('CIFSServer').get(server_name,
  115                                                                  vdm_name)
  116             if status != constants.STATUS_OK:
  117                 message = (_("CIFS server %s not found.") % server_name)
  118                 LOG.error(message)
  119                 raise exception.EMCVnxXMLAPIError(err=message)
  120 
  121         self._allocate_container(share_name, size, share_server, pool_name)
  122 
  123         if share_proto == 'NFS':
  124             location = self._create_nfs_share(share_name, share_server)
  125         elif share_proto == 'CIFS':
  126             location = self._create_cifs_share(share_name, share_server)
  127 
  128         return location
  129 
  130     def _share_server_validation(self, share_server):
  131         """Validate the share server."""
  132         if not share_server:
  133             msg = _('Share server not provided')
  134             raise exception.InvalidInput(reason=msg)
  135 
  136         backend_details = share_server.get('backend_details')
  137         vdm = backend_details.get(
  138             'share_server_name') if backend_details else None
  139 
  140         if vdm is None:
  141             message = _("No share server found.")
  142             LOG.error(message)
  143             raise exception.EMCVnxXMLAPIError(err=message)
  144 
  145     def _allocate_container(self, share_name, size, share_server, pool_name):
  146         """Allocate file system for share."""
  147         vdm_name = self._get_share_server_name(share_server)
  148 
  149         self._get_context('FileSystem').create(
  150             share_name, size, pool_name, vdm_name)
  151 
  152     def _allocate_container_from_snapshot(self, share, snapshot, share_server,
  153                                           pool_name):
  154         """Allocate file system from snapshot."""
  155         vdm_name = self._get_share_server_name(share_server)
  156 
  157         interconn_id = self._get_context('Mover').get_interconnect_id(
  158             self.mover_name, self.mover_name)
  159 
  160         self._get_context('FileSystem').create_from_snapshot(
  161             share['id'], snapshot['id'], snapshot['share_id'],
  162             pool_name, vdm_name, interconn_id)
  163 
  164         nwe_size = share['size'] * units.Ki
  165         self._get_context('FileSystem').extend(share['id'], pool_name,
  166                                                nwe_size)
  167 
  168     @enas_utils.log_enter_exit
  169     def _create_cifs_share(self, share_name, share_server):
  170         """Create CIFS share."""
  171         vdm_name = self._get_share_server_name(share_server)
  172         server_name = vdm_name
  173 
  174         # Get available CIFS Server and interface (one CIFS server per VDM)
  175         status, server = self._get_context('CIFSServer').get(server_name,
  176                                                              vdm_name)
  177 
  178         if 'interfaces' not in server or len(server['interfaces']) == 0:
  179             message = (_("CIFS server %s doesn't have interface, "
  180                          "so the share is inaccessible.")
  181                        % server['compName'])
  182             LOG.error(message)
  183             raise exception.EMCVnxXMLAPIError(err=message)
  184 
  185         interface = enas_utils.export_unc_path(server['interfaces'][0])
  186 
  187         self._get_context('CIFSShare').create(share_name, server['name'],
  188                                               vdm_name)
  189 
  190         self._get_context('CIFSShare').disable_share_access(share_name,
  191                                                             vdm_name)
  192 
  193         location = (r'\\%(interface)s\%(name)s' %
  194                     {'interface': interface, 'name': share_name})
  195 
  196         return location
  197 
  198     @enas_utils.log_enter_exit
  199     def _create_nfs_share(self, share_name, share_server):
  200         """Create NFS share."""
  201         vdm_name = self._get_share_server_name(share_server)
  202 
  203         self._get_context('NFSShare').create(share_name, vdm_name)
  204 
  205         nfs_if = enas_utils.convert_ipv6_format_if_needed(
  206             share_server['backend_details']['nfs_if'])
  207 
  208         return ('%(nfs_if)s:/%(share_name)s'
  209                 % {'nfs_if': nfs_if,
  210                    'share_name': share_name})
  211 
  212     def create_share_from_snapshot(self, context, share, snapshot,
  213                                    share_server=None):
  214         """Create a share from a snapshot - clone a snapshot."""
  215         share_name = share['id']
  216 
  217         share_proto = share['share_proto']
  218 
  219         # Validate the share protocol
  220         if share_proto.upper() not in ('NFS', 'CIFS'):
  221             raise exception.InvalidShare(
  222                 reason=(_('Invalid NAS protocol supplied: %s.')
  223                         % share_proto))
  224 
  225         # Get the pool name from share host field
  226         pool_name = share_utils.extract_host(share['host'], level='pool')
  227         if not pool_name:
  228             message = (_("Pool is not available in the share host %s.") %
  229                        share['host'])
  230             raise exception.InvalidHost(reason=message)
  231 
  232         self._share_server_validation(share_server)
  233 
  234         self._allocate_container_from_snapshot(
  235             share, snapshot, share_server, pool_name)
  236 
  237         nfs_if = enas_utils.convert_ipv6_format_if_needed(
  238             share_server['backend_details']['nfs_if'])
  239 
  240         if share_proto == 'NFS':
  241             self._create_nfs_share(share_name, share_server)
  242             location = ('%(nfs_if)s:/%(share_name)s'
  243                         % {'nfs_if': nfs_if,
  244                            'share_name': share_name})
  245         elif share_proto == 'CIFS':
  246             location = self._create_cifs_share(share_name, share_server)
  247 
  248         return location
  249 
  250     def create_snapshot(self, context, snapshot, share_server=None):
  251         """Create snapshot from share."""
  252         share_name = snapshot['share_id']
  253         status, filesystem = self._get_context('FileSystem').get(share_name)
  254         if status != constants.STATUS_OK:
  255             message = (_("File System %s not found.") % share_name)
  256             LOG.error(message)
  257             raise exception.EMCVnxXMLAPIError(err=message)
  258 
  259         pool_id = filesystem['pools_id'][0]
  260 
  261         self._get_context('Snapshot').create(snapshot['id'],
  262                                              snapshot['share_id'],
  263                                              pool_id)
  264 
  265     def delete_share(self, context, share, share_server=None):
  266         """Delete a share."""
  267         if share_server is None:
  268             LOG.warning("Driver does not support share deletion without "
  269                         "share network specified. Return directly because "
  270                         "there is nothing to clean.")
  271             return
  272 
  273         share_proto = share['share_proto']
  274 
  275         if share_proto == 'NFS':
  276             self._delete_nfs_share(share, share_server)
  277         elif share_proto == 'CIFS':
  278             self._delete_cifs_share(share, share_server)
  279         else:
  280             raise exception.InvalidShare(
  281                 reason='Unsupported share type')
  282 
  283     @enas_utils.log_enter_exit
  284     def _delete_cifs_share(self, share, share_server):
  285         """Delete CIFS share."""
  286         vdm_name = self._get_share_server_name(share_server)
  287 
  288         name = share['id']
  289 
  290         self._get_context('CIFSShare').delete(name, vdm_name)
  291 
  292         self._deallocate_container(name, vdm_name)
  293 
  294     @enas_utils.log_enter_exit
  295     def _delete_nfs_share(self, share, share_server):
  296         """Delete NFS share."""
  297         vdm_name = self._get_share_server_name(share_server)
  298 
  299         name = share['id']
  300 
  301         self._get_context('NFSShare').delete(name, vdm_name)
  302 
  303         self._deallocate_container(name, vdm_name)
  304 
  305     @enas_utils.log_enter_exit
  306     def _deallocate_container(self, share_name, vdm_name):
  307         """Delete underneath objects of the share."""
  308         path = '/' + share_name
  309 
  310         try:
  311             # Delete mount point
  312             self._get_context('MountPoint').delete(path, vdm_name)
  313         except Exception:
  314             LOG.debug("Skip the failure of mount point %s deletion.", path)
  315 
  316         try:
  317             # Delete file system
  318             self._get_context('FileSystem').delete(share_name)
  319         except Exception:
  320             LOG.debug("Skip the failure of file system %s deletion.",
  321                       share_name)
  322 
  323     def delete_snapshot(self, context, snapshot, share_server=None):
  324         """Delete a snapshot."""
  325         self._get_context('Snapshot').delete(snapshot['id'])
  326 
  327     def ensure_share(self, context, share, share_server=None):
  328         """Ensure that the share is exported."""
  329 
  330     def extend_share(self, share, new_size, share_server=None):
  331         # Get the pool name from share host field
  332         pool_name = share_utils.extract_host(share['host'], level='pool')
  333         if not pool_name:
  334             message = (_("Pool is not available in the share host %s.") %
  335                        share['host'])
  336             raise exception.InvalidHost(reason=message)
  337 
  338         share_name = share['id']
  339 
  340         self._get_context('FileSystem').extend(
  341             share_name, pool_name, new_size * units.Ki)
  342 
  343     def allow_access(self, context, share, access, share_server=None):
  344         """Allow access to a share."""
  345         access_level = access['access_level']
  346         if access_level not in const.ACCESS_LEVELS:
  347             raise exception.InvalidShareAccessLevel(level=access_level)
  348 
  349         share_proto = share['share_proto']
  350 
  351         if share_proto == 'NFS':
  352             self._nfs_allow_access(context, share, access, share_server)
  353         elif share_proto == 'CIFS':
  354             self._cifs_allow_access(context, share, access, share_server)
  355         else:
  356             raise exception.InvalidShare(
  357                 reason=(_('Invalid NAS protocol supplied: %s.')
  358                         % share_proto))
  359 
  360     @enas_utils.log_enter_exit
  361     def _cifs_allow_access(self, context, share, access, share_server):
  362         """Allow access to CIFS share."""
  363         vdm_name = self._get_share_server_name(share_server)
  364         share_name = share['id']
  365 
  366         if access['access_type'] != 'user':
  367             reason = _('Only user access type allowed for CIFS share')
  368             raise exception.InvalidShareAccess(reason=reason)
  369 
  370         user_name = access['access_to']
  371 
  372         access_level = access['access_level']
  373         if access_level == const.ACCESS_LEVEL_RW:
  374             cifs_access = constants.CIFS_ACL_FULLCONTROL
  375         else:
  376             cifs_access = constants.CIFS_ACL_READ
  377 
  378         # Check if CIFS server exists.
  379         server_name = vdm_name
  380         status, server = self._get_context('CIFSServer').get(server_name,
  381                                                              vdm_name)
  382         if status != constants.STATUS_OK:
  383             message = (_("CIFS server %s not found.") % server_name)
  384             LOG.error(message)
  385             raise exception.EMCVnxXMLAPIError(err=message)
  386 
  387         self._get_context('CIFSShare').allow_share_access(
  388             vdm_name,
  389             share_name,
  390             user_name,
  391             server['domain'],
  392             access=cifs_access)
  393 
  394     @enas_utils.log_enter_exit
  395     def _nfs_allow_access(self, context, share, access, share_server):
  396         """Allow access to NFS share."""
  397         vdm_name = self._get_share_server_name(share_server)
  398 
  399         access_type = access['access_type']
  400         if access_type != 'ip':
  401             reason = _('Only ip access type allowed.')
  402             raise exception.InvalidShareAccess(reason=reason)
  403 
  404         host_ip = access['access_to']
  405         access_level = access['access_level']
  406 
  407         self._get_context('NFSShare').allow_share_access(
  408             share['id'], host_ip, vdm_name, access_level)
  409 
  410     def update_access(self, context, share, access_rules, add_rules,
  411                       delete_rules, share_server=None):
  412         # deleting rules
  413         for rule in delete_rules:
  414             self.deny_access(context, share, rule, share_server)
  415 
  416         # adding rules
  417         for rule in add_rules:
  418             self.allow_access(context, share, rule, share_server)
  419 
  420         # recovery mode
  421         if not (add_rules or delete_rules):
  422             white_list = []
  423             for rule in access_rules:
  424                 self.allow_access(context, share, rule, share_server)
  425                 white_list.append(
  426                     enas_utils.convert_ipv6_format_if_needed(
  427                         rule['access_to']))
  428             self.clear_access(share, share_server, white_list)
  429 
  430     def clear_access(self, share, share_server, white_list):
  431         share_proto = share['share_proto'].upper()
  432         share_name = share['id']
  433         if share_proto == 'CIFS':
  434             self._cifs_clear_access(share_name, share_server, white_list)
  435         elif share_proto == 'NFS':
  436             self._nfs_clear_access(share_name, share_server, white_list)
  437 
  438     @enas_utils.log_enter_exit
  439     def _cifs_clear_access(self, share_name, share_server, white_list):
  440         """Clear access for CIFS share except hosts in the white list."""
  441         vdm_name = self._get_share_server_name(share_server)
  442 
  443         # Check if CIFS server exists.
  444         server_name = vdm_name
  445         status, server = self._get_context('CIFSServer').get(server_name,
  446                                                              vdm_name)
  447         if status != constants.STATUS_OK:
  448             message = (_("CIFS server %(server_name)s has issue. "
  449                          "Detail: %(status)s") %
  450                        {'server_name': server_name, 'status': status})
  451             raise exception.EMCVnxXMLAPIError(err=message)
  452 
  453         self._get_context('CIFSShare').clear_share_access(
  454             share_name=share_name,
  455             mover_name=vdm_name,
  456             domain=server['domain'],
  457             white_list_users=white_list)
  458 
  459     @enas_utils.log_enter_exit
  460     def _nfs_clear_access(self, share_name, share_server, white_list):
  461         """Clear access for NFS share except hosts in the white list."""
  462         self._get_context('NFSShare').clear_share_access(
  463             share_name=share_name,
  464             mover_name=self._get_share_server_name(share_server),
  465             white_list_hosts=white_list)
  466 
  467     def deny_access(self, context, share, access, share_server=None):
  468         """Deny access to a share."""
  469         share_proto = share['share_proto']
  470 
  471         if share_proto == 'NFS':
  472             self._nfs_deny_access(share, access, share_server)
  473         elif share_proto == 'CIFS':
  474             self._cifs_deny_access(share, access, share_server)
  475         else:
  476             raise exception.InvalidShare(
  477                 reason=_('Unsupported share type'))
  478 
  479     @enas_utils.log_enter_exit
  480     def _cifs_deny_access(self, share, access, share_server):
  481         """Deny access to CIFS share."""
  482         vdm_name = self._get_share_server_name(share_server)
  483         share_name = share['id']
  484 
  485         if access['access_type'] != 'user':
  486             reason = _('Only user access type allowed for CIFS share')
  487             raise exception.InvalidShareAccess(reason=reason)
  488 
  489         user_name = access['access_to']
  490 
  491         access_level = access['access_level']
  492         if access_level == const.ACCESS_LEVEL_RW:
  493             cifs_access = constants.CIFS_ACL_FULLCONTROL
  494         else:
  495             cifs_access = constants.CIFS_ACL_READ
  496 
  497         # Check if CIFS server exists.
  498         server_name = vdm_name
  499         status, server = self._get_context('CIFSServer').get(server_name,
  500                                                              vdm_name)
  501         if status != constants.STATUS_OK:
  502             message = (_("CIFS server %s not found.") % server_name)
  503             LOG.error(message)
  504             raise exception.EMCVnxXMLAPIError(err=message)
  505 
  506         self._get_context('CIFSShare').deny_share_access(
  507             vdm_name,
  508             share_name,
  509             user_name,
  510             server['domain'],
  511             access=cifs_access)
  512 
  513     @enas_utils.log_enter_exit
  514     def _nfs_deny_access(self, share, access, share_server):
  515         """Deny access to NFS share."""
  516         vdm_name = self._get_share_server_name(share_server)
  517 
  518         access_type = access['access_type']
  519         if access_type != 'ip':
  520             reason = _('Only ip access type allowed.')
  521             raise exception.InvalidShareAccess(reason=reason)
  522 
  523         host_ip = enas_utils.convert_ipv6_format_if_needed(access['access_to'])
  524 
  525         self._get_context('NFSShare').deny_share_access(share['id'], host_ip,
  526                                                         vdm_name)
  527 
  528     def check_for_setup_error(self):
  529         """Check for setup error."""
  530         # To verify the input from Manila configuration
  531         status, out = self._get_context('Mover').get_ref(self.mover_name,
  532                                                          True)
  533         if constants.STATUS_ERROR == status:
  534             message = (_("Could not find Data Mover by name: %s.") %
  535                        self.mover_name)
  536             LOG.error(message)
  537             raise exception.InvalidParameterValue(err=message)
  538 
  539         self.pools = self._get_managed_storage_pools(self.pool_conf)
  540 
  541     def _get_managed_storage_pools(self, pools):
  542         matched_pools = set()
  543         if pools:
  544             # Get the real pools from the backend storage
  545             status, backend_pools = self._get_context('StoragePool').get_all()
  546             if status != constants.STATUS_OK:
  547                 message = (_("Failed to get storage pool information. "
  548                              "Reason: %s") % backend_pools)
  549                 LOG.error(message)
  550                 raise exception.EMCVnxXMLAPIError(err=message)
  551 
  552             real_pools = set([item for item in backend_pools])
  553             conf_pools = set([item.strip() for item in pools])
  554             matched_pools, unmatched_pools = enas_utils.do_match_any(
  555                 real_pools, conf_pools)
  556 
  557             if not matched_pools:
  558                 msg = (_("None of the specified storage pools to be managed "
  559                          "exist. Please check your configuration "
  560                          "vnx_share_data_pools in manila.conf. "
  561                          "The available pools in the backend are %s.") %
  562                        ",".join(real_pools))
  563                 raise exception.InvalidParameterValue(err=msg)
  564 
  565             LOG.info("Storage pools: %s will be managed.",
  566                      ",".join(matched_pools))
  567         else:
  568             LOG.debug("No storage pool is specified, so all pools "
  569                       "in storage system will be managed.")
  570         return matched_pools
  571 
  572     def connect(self, emc_share_driver, context):
  573         """Connect to VNX NAS server."""
  574         config = emc_share_driver.configuration
  575         config.append_config_values(VNX_OPTS)
  576         self.mover_name = config.vnx_server_container
  577 
  578         self.pool_conf = config.safe_get('vnx_share_data_pools')
  579 
  580         self.reserved_percentage = config.safe_get('reserved_share_percentage')
  581         if self.reserved_percentage is None:
  582             self.reserved_percentage = 0
  583 
  584         self.manager = manager.StorageObjectManager(config)
  585         self.port_conf = config.safe_get('vnx_ethernet_ports')
  586 
  587     def get_managed_ports(self):
  588         # Get the real ports(devices) list from the backend storage
  589         real_ports = self._get_physical_devices(self.mover_name)
  590 
  591         if not self.port_conf:
  592             LOG.debug("No ports are specified, so any of the ports on the "
  593                       "Data Mover can be used.")
  594             return real_ports
  595 
  596         matched_ports, unmanaged_ports = enas_utils.do_match_any(
  597             real_ports, self.port_conf)
  598 
  599         if not matched_ports:
  600             msg = (_("None of the specified network ports exist. "
  601                      "Please check your configuration vnx_ethernet_ports "
  602                      "in manila.conf. The available ports on the Data Mover "
  603                      "are %s.") %
  604                    ",".join(real_ports))
  605             raise exception.BadConfigurationException(reason=msg)
  606 
  607         LOG.debug("Ports: %s can be used.", ",".join(matched_ports))
  608 
  609         return list(matched_ports)
  610 
  611     def update_share_stats(self, stats_dict):
  612         """Communicate with EMCNASClient to get the stats."""
  613         stats_dict['driver_version'] = VERSION
  614 
  615         self._get_context('Mover').get_ref(self.mover_name, True)
  616 
  617         stats_dict['pools'] = []
  618 
  619         status, pools = self._get_context('StoragePool').get_all()
  620         for name, pool in pools.items():
  621             if not self.pools or pool['name'] in self.pools:
  622                 total_size = float(pool['total_size'])
  623                 used_size = float(pool['used_size'])
  624 
  625                 pool_stat = dict(
  626                     pool_name=pool['name'],
  627                     total_capacity_gb=enas_utils.mb_to_gb(total_size),
  628                     free_capacity_gb=enas_utils.mb_to_gb(
  629                         total_size - used_size),
  630                     qos=False,
  631                     reserved_percentage=self.reserved_percentage,
  632                 )
  633                 stats_dict['pools'].append(pool_stat)
  634 
  635         if not stats_dict['pools']:
  636             message = _("Failed to update storage pool.")
  637             LOG.error(message)
  638             raise exception.EMCVnxXMLAPIError(err=message)
  639 
  640     def get_pool(self, share):
  641         """Get the pool name of the share."""
  642         share_name = share['id']
  643         status, filesystem = self._get_context('FileSystem').get(share_name)
  644         if status != constants.STATUS_OK:
  645             message = (_("File System %(name)s not found. "
  646                          "Reason: %(err)s") %
  647                        {'name': share_name, 'err': filesystem})
  648             LOG.error(message)
  649             raise exception.EMCVnxXMLAPIError(err=message)
  650 
  651         pool_id = filesystem['pools_id'][0]
  652 
  653         # Get the real pools from the backend storage
  654         status, backend_pools = self._get_context('StoragePool').get_all()
  655         if status != constants.STATUS_OK:
  656             message = (_("Failed to get storage pool information. "
  657                          "Reason: %s") % backend_pools)
  658             LOG.error(message)
  659             raise exception.EMCVnxXMLAPIError(err=message)
  660 
  661         for name, pool_info in backend_pools.items():
  662             if pool_info['id'] == pool_id:
  663                 return name
  664 
  665         available_pools = [item for item in backend_pools]
  666         message = (_("No matched pool name for share: %(share)s. "
  667                      "Available pools: %(pools)s") %
  668                    {'share': share_name, 'pools': available_pools})
  669         raise exception.EMCVnxXMLAPIError(err=message)
  670 
  671     def get_network_allocations_number(self):
  672         """Returns number of network allocations for creating VIFs."""
  673         return constants.IP_ALLOCATIONS
  674 
  675     def setup_server(self, network_info, metadata=None):
  676         """Set up and configures share server with given network parameters."""
  677         # Only support single security service with type 'active_directory'
  678         vdm_name = network_info['server_id']
  679         vlan_id = network_info['segmentation_id']
  680         active_directory = None
  681         allocated_interfaces = []
  682 
  683         if network_info.get('security_services'):
  684             is_valid, active_directory = self._get_valid_security_service(
  685                 network_info['security_services'])
  686 
  687             if not is_valid:
  688                 raise exception.EMCVnxXMLAPIError(err=active_directory)
  689 
  690         try:
  691             if not self._vdm_exist(vdm_name):
  692                 LOG.debug('Share server %s not found, creating '
  693                           'share server...', vdm_name)
  694                 self._get_context('VDM').create(vdm_name, self.mover_name)
  695 
  696             devices = self.get_managed_ports()
  697 
  698             for net_info in network_info['network_allocations']:
  699                 random.shuffle(devices)
  700 
  701                 ip_version = net_info['ip_version']
  702 
  703                 interface = {
  704                     'name': net_info['id'][-12:],
  705                     'device_name': devices[0],
  706                     'ip': net_info['ip_address'],
  707                     'mover_name': self.mover_name,
  708                     'vlan_id': vlan_id if vlan_id else -1,
  709                 }
  710 
  711                 if ip_version == 6:
  712                     interface['ip_version'] = ip_version
  713                     interface['net_mask'] = six.text_type(
  714                         utils.cidr_to_prefixlen(network_info['cidr']))
  715                 else:
  716                     interface['net_mask'] = utils.cidr_to_netmask(
  717                         network_info['cidr'])
  718 
  719                 self._get_context('MoverInterface').create(interface)
  720 
  721                 allocated_interfaces.append(interface)
  722 
  723             cifs_interface = allocated_interfaces[0]
  724             nfs_interface = allocated_interfaces[1]
  725             if active_directory:
  726                 self._configure_active_directory(
  727                     active_directory, vdm_name, cifs_interface)
  728 
  729             self._get_context('VDM').attach_nfs_interface(
  730                 vdm_name, nfs_interface['name'])
  731 
  732             return {
  733                 'share_server_name': vdm_name,
  734                 'cifs_if': cifs_interface['ip'],
  735                 'nfs_if': nfs_interface['ip'],
  736             }
  737 
  738         except Exception:
  739             with excutils.save_and_reraise_exception():
  740                 LOG.exception('Could not setup server.')
  741                 server_details = self._construct_backend_details(
  742                     vdm_name, allocated_interfaces)
  743                 self.teardown_server(
  744                     server_details, network_info['security_services'])
  745 
  746     def _construct_backend_details(self, vdm_name, interfaces):
  747         if_number = len(interfaces)
  748         cifs_if = interfaces[0]['ip'] if if_number > 0 else None
  749         nfs_if = interfaces[1]['ip'] if if_number > 1 else None
  750 
  751         return {
  752             'share_server_name': vdm_name,
  753             'cifs_if': cifs_if,
  754             'nfs_if': nfs_if,
  755         }
  756 
  757     @enas_utils.log_enter_exit
  758     def _vdm_exist(self, name):
  759         status, out = self._get_context('VDM').get(name)
  760         if constants.STATUS_OK != status:
  761             return False
  762 
  763         return True
  764 
  765     def _get_physical_devices(self, mover_name):
  766         """Get a proper network device to create interface."""
  767         devices = self._get_context('Mover').get_physical_devices(mover_name)
  768         if not devices:
  769             message = (_("Could not get physical device port on mover %s.") %
  770                        self.mover_name)
  771             LOG.error(message)
  772             raise exception.EMCVnxXMLAPIError(err=message)
  773 
  774         return devices
  775 
  776     def _configure_active_directory(
  777             self, security_service, vdm_name, interface):
  778 
  779         domain = security_service['domain']
  780         server = security_service['dns_ip']
  781 
  782         self._get_context('DNSDomain').create(self.mover_name, domain, server)
  783 
  784         cifs_server_args = {
  785             'name': vdm_name,
  786             'interface_ip': interface['ip'],
  787             'domain_name': security_service['domain'],
  788             'user_name': security_service['user'],
  789             'password': security_service['password'],
  790             'mover_name': vdm_name,
  791             'is_vdm': True,
  792         }
  793 
  794         self._get_context('CIFSServer').create(cifs_server_args)
  795 
  796     def teardown_server(self, server_details, security_services=None):
  797         """Teardown share server."""
  798         if not server_details:
  799             LOG.debug('Server details are empty.')
  800             return
  801 
  802         vdm_name = server_details.get('share_server_name')
  803         if not vdm_name:
  804             LOG.debug('No share server found in server details.')
  805             return
  806 
  807         cifs_if = server_details.get('cifs_if')
  808         nfs_if = server_details.get('nfs_if')
  809 
  810         status, vdm = self._get_context('VDM').get(vdm_name)
  811         if constants.STATUS_OK != status:
  812             LOG.debug('Share server %s not found.', vdm_name)
  813             return
  814 
  815         interfaces = self._get_context('VDM').get_interfaces(vdm_name)
  816 
  817         for if_name in interfaces['nfs']:
  818             self._get_context('VDM').detach_nfs_interface(vdm_name, if_name)
  819 
  820         if security_services:
  821             # Only support single security service with type 'active_directory'
  822             is_valid, active_directory = self._get_valid_security_service(
  823                 security_services)
  824             if is_valid:
  825                 status, servers = self._get_context('CIFSServer').get_all(
  826                     vdm_name)
  827                 if constants.STATUS_OK != status:
  828                     LOG.error('Could not find CIFS server by name: %s.',
  829                               vdm_name)
  830                 else:
  831                     cifs_servers = copy.deepcopy(servers)
  832                     for name, server in cifs_servers.items():
  833                         # Unjoin CIFS Server from domain
  834                         cifs_server_args = {
  835                             'name': server['name'],
  836                             'join_domain': False,
  837                             'user_name': active_directory['user'],
  838                             'password': active_directory['password'],
  839                             'mover_name': vdm_name,
  840                             'is_vdm': True,
  841                         }
  842 
  843                         try:
  844                             self._get_context('CIFSServer').modify(
  845                                 cifs_server_args)
  846                         except exception.EMCVnxXMLAPIError as expt:
  847                             LOG.debug("Failed to modify CIFS server "
  848                                       "%(server)s. Reason: %(err)s.",
  849                                       {'server': server, 'err': expt})
  850 
  851                         self._get_context('CIFSServer').delete(name, vdm_name)
  852 
  853         # Delete interface from Data Mover
  854         if cifs_if:
  855             self._get_context('MoverInterface').delete(cifs_if,
  856                                                        self.mover_name)
  857 
  858         if nfs_if:
  859             self._get_context('MoverInterface').delete(nfs_if,
  860                                                        self.mover_name)
  861 
  862         # Delete Virtual Data Mover
  863         self._get_context('VDM').delete(vdm_name)
  864 
  865     def _get_valid_security_service(self, security_services):
  866         """Validate security services and return a supported security service.
  867 
  868         :param security_services:
  869         :returns: (<is_valid>, <data>) -- <is_valid> is true to indicate
  870             security_services includes zero or single security service for
  871             active directory. Otherwise, it would return false. <data> return
  872             error message when <is_valid> is false. Otherwise, it will
  873             return zero or single security service for active directory.
  874         """
  875 
  876         # Only support single security service with type 'active_directory'
  877         service_number = len(security_services)
  878 
  879         if (service_number > 1 or
  880                 security_services[0]['type'] != 'active_directory'):
  881             return False, _("Unsupported security services. "
  882                             "Only support single security service and "
  883                             "only support type 'active_directory'")
  884 
  885         return True, security_services[0]
  886 
  887     def _get_share_server_name(self, share_server):
  888         try:
  889             return share_server['backend_details']['share_server_name']
  890         except Exception:
  891             LOG.debug("Didn't get share server name from share_server %s.",
  892                       share_server)
  893         return share_server['id']
  894 
  895     def _get_context(self, type):
  896         return self.manager.getStorageContext(type)