114 lines
4.3 KiB
Python
114 lines
4.3 KiB
Python
|
# Copyright 2015 Cloudbase Solutions Srl
|
||
|
# All Rights Reserved.
|
||
|
#
|
||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||
|
# not use this file except in compliance with the License. You may obtain
|
||
|
# a copy of the License at
|
||
|
#
|
||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||
|
#
|
||
|
# Unless required by applicable law or agreed to in writing, software
|
||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||
|
# License for the specific language governing permissions and limitations
|
||
|
# under the License.
|
||
|
|
||
|
import os
|
||
|
import socket
|
||
|
|
||
|
from oslo_log import log as logging
|
||
|
|
||
|
from os_win._i18n import _
|
||
|
from os_win import _utils
|
||
|
from os_win import exceptions
|
||
|
from os_win.utils import baseutils
|
||
|
from os_win.utils import win32utils
|
||
|
|
||
|
LOG = logging.getLogger(__name__)
|
||
|
|
||
|
|
||
|
class SMBUtils(baseutils.BaseUtils):
|
||
|
_loopback_share_map = {}
|
||
|
|
||
|
def __init__(self):
|
||
|
self._win32_utils = win32utils.Win32Utils()
|
||
|
self._smb_conn = self._get_wmi_conn(r"root\Microsoft\Windows\SMB")
|
||
|
|
||
|
def check_smb_mapping(self, share_path, remove_unavailable_mapping=False):
|
||
|
mappings = self._smb_conn.Msft_SmbMapping(RemotePath=share_path)
|
||
|
|
||
|
if not mappings:
|
||
|
return False
|
||
|
|
||
|
if os.path.exists(share_path):
|
||
|
LOG.debug('Share already mounted: %s', share_path)
|
||
|
return True
|
||
|
else:
|
||
|
LOG.debug('Share exists but is unavailable: %s ', share_path)
|
||
|
if remove_unavailable_mapping:
|
||
|
self.unmount_smb_share(share_path, force=True)
|
||
|
return False
|
||
|
|
||
|
def mount_smb_share(self, share_path, username=None, password=None):
|
||
|
try:
|
||
|
LOG.debug('Mounting share: %s', share_path)
|
||
|
self._smb_conn.Msft_SmbMapping.Create(RemotePath=share_path,
|
||
|
UserName=username,
|
||
|
Password=password)
|
||
|
except exceptions.x_wmi as exc:
|
||
|
err_msg = (_(
|
||
|
'Unable to mount SMBFS share: %(share_path)s '
|
||
|
'WMI exception: %(wmi_exc)s') % {'share_path': share_path,
|
||
|
'wmi_exc': exc})
|
||
|
raise exceptions.SMBException(err_msg)
|
||
|
|
||
|
def unmount_smb_share(self, share_path, force=False):
|
||
|
mappings = self._smb_conn.Msft_SmbMapping(RemotePath=share_path)
|
||
|
if not mappings:
|
||
|
LOG.debug('Share %s is not mounted. Skipping unmount.',
|
||
|
share_path)
|
||
|
|
||
|
for mapping in mappings:
|
||
|
# Due to a bug in the WMI module, getting the output of
|
||
|
# methods returning None will raise an AttributeError
|
||
|
try:
|
||
|
mapping.Remove(Force=force)
|
||
|
except AttributeError:
|
||
|
pass
|
||
|
except exceptions.x_wmi:
|
||
|
# If this fails, a 'Generic Failure' exception is raised.
|
||
|
# This happens even if we unforcefully unmount an in-use
|
||
|
# share, for which reason we'll simply ignore it in this
|
||
|
# case.
|
||
|
if force:
|
||
|
raise exceptions.SMBException(
|
||
|
_("Could not unmount share: %s") % share_path)
|
||
|
|
||
|
def get_smb_share_path(self, share_name):
|
||
|
shares = self._smb_conn.Msft_SmbShare(Name=share_name)
|
||
|
share_path = shares[0].Path if shares else None
|
||
|
if not shares:
|
||
|
LOG.debug("Could not find any local share named %s.", share_name)
|
||
|
return share_path
|
||
|
|
||
|
def is_local_share(self, share_path):
|
||
|
# In case of Scale-Out File Servers, we'll get the Distributed Node
|
||
|
# Name of the share. We have to check whether this resolves to a
|
||
|
# local ip, which would happen in a hyper converged scenario.
|
||
|
#
|
||
|
# In this case, mounting the share is not supported and we have to
|
||
|
# use the local share path.
|
||
|
if share_path in self._loopback_share_map:
|
||
|
return self._loopback_share_map[share_path]
|
||
|
|
||
|
addr = share_path.lstrip('\\').split('\\', 1)[0]
|
||
|
|
||
|
local_ips = _utils.get_ips(socket.gethostname())
|
||
|
local_ips += _utils.get_ips('localhost')
|
||
|
|
||
|
dest_ips = _utils.get_ips(addr)
|
||
|
is_local = bool(set(local_ips).intersection(set(dest_ips)))
|
||
|
|
||
|
self._loopback_share_map[share_path] = is_local
|
||
|
return is_local
|