Merge "Huawei: Add share server support"
This commit is contained in:
commit
a22f4e94be
@ -40,7 +40,7 @@ The following operations is supported on V3 storage:
|
||||
- Delete CIFS/NFS Share
|
||||
- Allow CIFS/NFS Share access
|
||||
|
||||
* Only IP access type is supported for NFS(ro/rw).
|
||||
* IP and USER access types are supported for NFS(ro/rw).
|
||||
* Only USER access type is supported for CIFS(ro/rw).
|
||||
- Deny CIFS/NFS Share access
|
||||
- Create snapshot
|
||||
@ -70,6 +70,7 @@ storage systems, the driver configuration file is as follows:
|
||||
<Storage>
|
||||
<Product>V3</Product>
|
||||
<LogicalPortIP>x.x.x.x</LogicalPortIP>
|
||||
<Port>abc;CTE0.A.H1</Port>
|
||||
<RestURL>https://x.x.x.x:8088/deviceManager/rest/;
|
||||
https://x.x.x.x:8088/deviceManager/rest/</RestURL>
|
||||
<UserName>xxxxxxxxx</UserName>
|
||||
@ -85,6 +86,10 @@ storage systems, the driver configuration file is as follows:
|
||||
|
||||
- `Product` is a type of a storage product. Set it to `V3`.
|
||||
- `LogicalPortIP` is an IP address of the logical port.
|
||||
- `Port` is a port name list of bond port or ETH port, used to
|
||||
create vlan and logical port. Multi Ports can be configured in
|
||||
<Port>(separated by ";"). If <Port> is not configured, then will choose
|
||||
an online port on the array.
|
||||
- `RestURL` is an access address of the REST interface. Multi RestURLs
|
||||
can be configured in <RestURL>(separated by ";"). When one of the RestURL
|
||||
failed to connect, driver will retry another automatically.
|
||||
@ -105,14 +110,16 @@ Example for configuring a storage system:
|
||||
|
||||
- `share_driver` = manila.share.drivers.huawei.huawei_nas.HuaweiNasDriver
|
||||
- `manila_huawei_conf_file` = /etc/manila/manila_huawei_conf.xml
|
||||
- `driver_handles_share_servers` = False
|
||||
- `driver_handles_share_servers` = True or False
|
||||
|
||||
.. note::
|
||||
As far as Manila requires `share type` for creation of shares, make sure that
|
||||
used `share type` has extra spec `driver_handles_share_servers` set to `False`
|
||||
otherwise Huawei backend will be filtered by `manila-scheduler`.
|
||||
If you do not provide `share type` with share creation request then default
|
||||
`share type` and its extra specs will be used.
|
||||
- If `driver_handles_share_servers` is True, the driver will choose a port
|
||||
in <Port> to create vlan and logical port for each tenant network.
|
||||
And the share type with the DHSS extra spec should be set to True when
|
||||
creating shares.
|
||||
- If `driver_handles_share_servers` is False, then will use the IP in
|
||||
<LogicalPortIP>. Also the share type with the DHSS extra spec should be
|
||||
set to False when creating shares.
|
||||
|
||||
Restart of manila-share service is needed for the configuration changes to take
|
||||
effect.
|
||||
@ -195,10 +202,14 @@ Restrictions
|
||||
|
||||
The Huawei driver has the following restrictions:
|
||||
|
||||
- Only IP access type is supported for NFS.
|
||||
- IP and USER access types are supported for NFS.
|
||||
|
||||
- Only LDAP domain is supported for NFS.
|
||||
|
||||
- Only USER access type is supported for CIFS.
|
||||
|
||||
- Only AD domain is supported for CIFS.
|
||||
|
||||
The :mod:`manila.share.drivers.huawei.huawei_nas` Module
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -51,7 +51,7 @@ Mapping of share drivers and share features support
|
||||
+----------------------------------------+-----------------------------+-----------------------+--------------+--------------+------------------------+----------------------------+
|
||||
| HPE 3PAR | DHSS = True (L) & False (K) | \- | \- | \- | K | K |
|
||||
+----------------------------------------+-----------------------------+-----------------------+--------------+--------------+------------------------+----------------------------+
|
||||
| Huawei | DHSS = False(K) | L | L | L | K | \- |
|
||||
| Huawei | DHSS = True (M) & False(K) | L | L | L | K | \- |
|
||||
+----------------------------------------+-----------------------------+-----------------------+--------------+--------------+------------------------+----------------------------+
|
||||
| IBM GPFS | DHSS = False(K) | \- | L | \- | K | K |
|
||||
+----------------------------------------+-----------------------------+-----------------------+--------------+--------------+------------------------+----------------------------+
|
||||
@ -69,39 +69,39 @@ Mapping of share drivers and share features support
|
||||
Mapping of share drivers and share access rules support
|
||||
-------------------------------------------------------
|
||||
|
||||
+----------------------------------------+----------------------------------------+----------------------------------------+
|
||||
| | Read & Write | Read Only |
|
||||
+ Driver name +--------------+------------+------------+--------------+------------+------------+
|
||||
| | IP | USER | Cert | IP | USER | Cert |
|
||||
+========================================+==============+============+============+==============+============+============+
|
||||
| Generic (Cinder as back-end) | NFS,CIFS (J) | \- | \- | NFS (K) | \- | \- |
|
||||
+----------------------------------------+--------------+------------+------------+--------------+------------+------------+
|
||||
| NetApp Clustered Data ONTAP | NFS (J) | CIFS (J) | \- | NFS (K) | CIFS (M) | \- |
|
||||
+----------------------------------------+--------------+------------+------------+--------------+------------+------------+
|
||||
| EMC VNX | NFS (J) | CIFS (J) | \- | NFS (L) | CIFS (L) | \- |
|
||||
+----------------------------------------+--------------+------------+------------+--------------+------------+------------+
|
||||
| EMC Isilon | NFS,CIFS (K) | \- | \- | \- | \- | \- |
|
||||
+----------------------------------------+--------------+------------+------------+--------------+------------+------------+
|
||||
| Red Hat GlusterFS | NFS (J) | \- | \- | \- | \- | \- |
|
||||
+----------------------------------------+--------------+------------+------------+--------------+------------+------------+
|
||||
| Red Hat GlusterFS-Native | \- | \- | J | \- | \- | \- |
|
||||
+----------------------------------------+--------------+------------+------------+--------------+------------+------------+
|
||||
| HDFS | \- | HDFS(K) | \- | \- | HDFS(K) | \- |
|
||||
+----------------------------------------+--------------+------------+------------+--------------+------------+------------+
|
||||
| Hitachi HNAS | NFS (L) | \- | \- | NFS (L) | \- | \- |
|
||||
+----------------------------------------+--------------+------------+------------+--------------+------------+------------+
|
||||
| HPE 3PAR | NFS,CIFS (K) | CIFS (K) | \- | \- | \- | \- |
|
||||
+----------------------------------------+--------------+------------+------------+--------------+------------+------------+
|
||||
| Huawei | NFS (K) | CIFS (K) | \- | NFS (K) | CIFS (K) | \- |
|
||||
+----------------------------------------+--------------+------------+------------+--------------+------------+------------+
|
||||
| Quobyte | NFS (K) | \- | \- | NFS (K) | \- | \- |
|
||||
+----------------------------------------+--------------+------------+------------+--------------+------------+------------+
|
||||
| Windows SMB | \- | CIFS (L) | \- | \- | CIFS (L) | \- |
|
||||
+----------------------------------------+--------------+------------+------------+--------------+------------+------------+
|
||||
| IBM GPFS | NFS (K) | \- | \- | NFS (K) | \- | \- |
|
||||
+----------------------------------------+--------------+------------+------------+--------------+------------+------------+
|
||||
| ZFS | ? | ? | ? | ? | ? | ? |
|
||||
+----------------------------------------+--------------+------------+------------+--------------+------------+------------+
|
||||
+----------------------------------------+--------------------------------------------+--------------------------------------------+
|
||||
| | Read & Write | Read Only |
|
||||
+ Driver name +--------------+----------------+------------+--------------+----------------+------------+
|
||||
| | IP | USER | Cert | IP | USER | Cert |
|
||||
+========================================+==============+================+============+==============+================+============+
|
||||
| Generic (Cinder as back-end) | NFS,CIFS (J) | \- | \- | NFS (K) | \- | \- |
|
||||
+----------------------------------------+--------------+----------------+------------+--------------+----------------+------------+
|
||||
| NetApp Clustered Data ONTAP | NFS (J) | CIFS (J) | \- | NFS (K) | CIFS (M) | \- |
|
||||
+----------------------------------------+--------------+----------------+------------+--------------+----------------+------------+
|
||||
| EMC VNX | NFS (J) | CIFS (J) | \- | NFS (L) | CIFS (L) | \- |
|
||||
+----------------------------------------+--------------+----------------+------------+--------------+----------------+------------+
|
||||
| EMC Isilon | NFS,CIFS (K) | \- | \- | \- | \- | \- |
|
||||
+----------------------------------------+--------------+----------------+------------+--------------+----------------+------------+
|
||||
| Red Hat GlusterFS | NFS (J) | \- | \- | \- | \- | \- |
|
||||
+----------------------------------------+--------------+----------------+------------+--------------+----------------+------------+
|
||||
| Red Hat GlusterFS-Native | \- | \- | J | \- | \- | \- |
|
||||
+----------------------------------------+--------------+----------------+------------+--------------+----------------+------------+
|
||||
| HDFS | \- | HDFS(K) | \- | \- | HDFS(K) | \- |
|
||||
+----------------------------------------+--------------+----------------+------------+--------------+----------------+------------+
|
||||
| Hitachi HNAS | NFS (L) | \- | \- | NFS (L) | \- | \- |
|
||||
+----------------------------------------+--------------+----------------+------------+--------------+----------------+------------+
|
||||
| HPE 3PAR | NFS,CIFS (K) | CIFS (K) | \- | \- | \- | \- |
|
||||
+----------------------------------------+--------------+----------------+------------+--------------+----------------+------------+
|
||||
| Huawei | NFS (K) |NFS (M),CIFS (K)| \- | NFS (K) |NFS (M),CIFS (K)| \- |
|
||||
+----------------------------------------+--------------+----------------+------------+--------------+----------------+------------+
|
||||
| Quobyte | NFS (K) | \- | \- | NFS (K) | \- | \- |
|
||||
+----------------------------------------+--------------+----------------+------------+--------------+----------------+------------+
|
||||
| Windows SMB | \- | CIFS (L) | \- | \- | CIFS (L) | \- |
|
||||
+----------------------------------------+--------------+----------------+------------+--------------+----------------+------------+
|
||||
| IBM GPFS | NFS (K) | \- | \- | NFS (K) | \- | \- |
|
||||
+----------------------------------------+--------------+----------------+------------+--------------+----------------+------------+
|
||||
| ZFS | ? | ? | ? | ? | ? | ? |
|
||||
+----------------------------------------+--------------+----------------+------------+--------------+----------------+------------+
|
||||
|
||||
Mapping of share drivers and security services support
|
||||
------------------------------------------------------
|
||||
@ -127,7 +127,7 @@ Mapping of share drivers and security services support
|
||||
+----------------------------------------+------------------+-----------------+------------------+
|
||||
| HPE 3PAR | \- | \- | \- |
|
||||
+----------------------------------------+------------------+-----------------+------------------+
|
||||
| Huawei | \- | \- | \- |
|
||||
| Huawei | M | M | \- |
|
||||
+----------------------------------------+------------------+-----------------+------------------+
|
||||
| Quobyte | \- | \- | \- |
|
||||
+----------------------------------------+------------------+-----------------+------------------+
|
||||
|
@ -73,3 +73,11 @@ class HuaweiBase(object):
|
||||
|
||||
def update_share_stats(self, stats_dict):
|
||||
"""Retrieve stats info from share group."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def setup_server(self, network_info, metadata=None):
|
||||
"""Set up share server with given network parameters."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def teardown_server(self, server_details, security_services=None):
|
||||
"""Teardown share server."""
|
||||
|
@ -12,16 +12,22 @@
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
STATUS_ETH_RUNNING = "10"
|
||||
STATUS_FS_HEALTH = "1"
|
||||
STATUS_FS_RUNNING = "27"
|
||||
STATUS_JOIN_DOMAIN = '1'
|
||||
STATUS_EXIT_DOMAIN = '0'
|
||||
STATUS_SERVICE_RUNNING = "2"
|
||||
DEFAULT_WAIT_INTERVAL = 3
|
||||
DEFAULT_TIMEOUT = 60
|
||||
|
||||
MSG_SNAPSHOT_NOT_FOUND = 1073754118
|
||||
IP_ALLOCATIONS = 0
|
||||
IP_ALLOCATIONS_DHSS_FALSE = 0
|
||||
IP_ALLOCATIONS_DHSS_TRUE = 1
|
||||
SOCKET_TIMEOUT = 52
|
||||
LOGIN_SOCKET_TIMEOUT = 4
|
||||
SYSTEM_NAME_PREFIX = "Array-"
|
||||
|
||||
ACCESS_NFS_RW = "1"
|
||||
ACCESS_NFS_RO = "0"
|
||||
@ -30,6 +36,12 @@ ACCESS_CIFS_RO = "0"
|
||||
|
||||
ERROR_CONNECT_TO_SERVER = -403
|
||||
ERROR_UNAUTHORIZED_TO_SERVER = -401
|
||||
ERROR_LOGICAL_PORT_EXIST = 1073813505
|
||||
ERROR_USER_OR_GROUP_NOT_EXIST = 1077939723
|
||||
|
||||
PORT_TYPE_ETH = '1'
|
||||
PORT_TYPE_BOND = '7'
|
||||
PORT_TYPE_VLAN = '8'
|
||||
|
||||
ALLOC_TYPE_THIN_FLAG = "1"
|
||||
ALLOC_TYPE_THICK_FLAG = "0"
|
||||
|
@ -52,12 +52,13 @@ class HuaweiNasDriver(driver.ShareDriver):
|
||||
Add share level(ro).
|
||||
Add smartx capabilities.
|
||||
Support multi pools in one backend.
|
||||
1.2 - Add share server support.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Do initialization."""
|
||||
LOG.debug("Enter into init function.")
|
||||
super(HuaweiNasDriver, self).__init__(False, *args, **kwargs)
|
||||
super(HuaweiNasDriver, self).__init__((True, False), *args, **kwargs)
|
||||
self.configuration = kwargs.get('configuration', None)
|
||||
if self.configuration:
|
||||
self.configuration.append_config_values(huawei_opts)
|
||||
@ -169,10 +170,18 @@ class HuaweiNasDriver(driver.ShareDriver):
|
||||
data = dict(
|
||||
share_backend_name=backend_name or 'HUAWEI_NAS_Driver',
|
||||
vendor_name='Huawei',
|
||||
driver_version='1.1',
|
||||
driver_version='1.2',
|
||||
storage_protocol='NFS_CIFS',
|
||||
total_capacity_gb=0.0,
|
||||
free_capacity_gb=0.0)
|
||||
|
||||
self.plugin.update_share_stats(data)
|
||||
super(HuaweiNasDriver, self)._update_share_stats(data)
|
||||
|
||||
def _setup_server(self, network_info, metadata=None):
|
||||
"""Set up share server with given network parameters."""
|
||||
return self.plugin.setup_server(network_info, metadata)
|
||||
|
||||
def _teardown_server(self, server_details, security_services=None):
|
||||
"""Teardown share server."""
|
||||
return self.plugin.teardown_server(server_details, security_services)
|
||||
|
@ -13,9 +13,12 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import random
|
||||
import string
|
||||
import time
|
||||
|
||||
from oslo_log import log
|
||||
from oslo_serialization import jsonutils
|
||||
from oslo_utils import strutils
|
||||
from oslo_utils import units
|
||||
|
||||
@ -31,7 +34,7 @@ from manila.share.drivers.huawei.v3 import helper
|
||||
from manila.share.drivers.huawei.v3 import smartx
|
||||
from manila.share import share_types
|
||||
from manila.share import utils as share_utils
|
||||
|
||||
from manila import utils
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
@ -108,9 +111,20 @@ class V3StorageConnection(driver.HuaweiBase):
|
||||
reason=(_('Failed to create share %(name)s. Reason: %(err)s.')
|
||||
% {'name': share_name, 'err': err}))
|
||||
|
||||
location = self._get_location_path(share_name, share_proto)
|
||||
ip = self._get_share_ip(share_server)
|
||||
location = self._get_location_path(share_name, share_proto, ip)
|
||||
return location
|
||||
|
||||
def _get_share_ip(self, share_server):
|
||||
""""Get share logical ip."""
|
||||
if share_server:
|
||||
ip = share_server['backend_details'].get('ip')
|
||||
else:
|
||||
root = self.helper._read_xml()
|
||||
ip = root.findtext('Storage/LogicalPortIP').strip()
|
||||
|
||||
return ip
|
||||
|
||||
def extend_share(self, share, new_size, share_server):
|
||||
share_proto = share['share_proto']
|
||||
share_name = share['name']
|
||||
@ -296,7 +310,10 @@ class V3StorageConnection(driver.HuaweiBase):
|
||||
|
||||
def get_network_allocations_number(self):
|
||||
"""Get number of network interfaces to be created."""
|
||||
return constants.IP_ALLOCATIONS
|
||||
if self.configuration.driver_handles_share_servers:
|
||||
return constants.IP_ALLOCATIONS_DHSS_TRUE
|
||||
else:
|
||||
return constants.IP_ALLOCATIONS_DHSS_FALSE
|
||||
|
||||
def _get_capacity(self, pool_name, result):
|
||||
"""Get free capacity and total capacity of the pools."""
|
||||
@ -374,8 +391,9 @@ class V3StorageConnection(driver.HuaweiBase):
|
||||
share_url_type = self.helper._get_share_url_type(share_proto)
|
||||
share_client_type = self.helper._get_share_client_type(share_proto)
|
||||
access_type = access['access_type']
|
||||
if share_proto == 'NFS' and access_type != 'ip':
|
||||
LOG.warning(_LW('Only IP access type is allowed for NFS shares.'))
|
||||
if share_proto == 'NFS' and access_type not in ('ip', 'user'):
|
||||
LOG.warning(_LW('Only IP or USER access types are allowed for '
|
||||
'NFS shares.'))
|
||||
return
|
||||
elif share_proto == 'CIFS' and access_type != 'user':
|
||||
LOG.warning(_LW('Only USER access type is allowed for'
|
||||
@ -404,6 +422,7 @@ class V3StorageConnection(driver.HuaweiBase):
|
||||
share_url_type = self.helper._get_share_url_type(share_proto)
|
||||
access_type = access['access_type']
|
||||
access_level = access['access_level']
|
||||
access_to = access['access_to']
|
||||
|
||||
if access_level not in common_constants.ACCESS_LEVELS:
|
||||
raise exception.InvalidShareAccess(
|
||||
@ -411,14 +430,18 @@ class V3StorageConnection(driver.HuaweiBase):
|
||||
access_level))
|
||||
|
||||
if share_proto == 'NFS':
|
||||
if access_type == 'ip':
|
||||
if access_level == common_constants.ACCESS_LEVEL_RW:
|
||||
access_level = constants.ACCESS_NFS_RW
|
||||
else:
|
||||
access_level = constants.ACCESS_NFS_RO
|
||||
else:
|
||||
message = _('Only IP access type is allowed for NFS shares.')
|
||||
if access_type == 'user':
|
||||
# Use 'user' as 'netgroup' for NFS.
|
||||
# A group name starts with @.
|
||||
access_to = '@' + access_to
|
||||
elif access_type != 'ip':
|
||||
message = _('Only IP or USER access types '
|
||||
'are allowed for NFS shares.')
|
||||
raise exception.InvalidShareAccess(reason=message)
|
||||
if access_level == common_constants.ACCESS_LEVEL_RW:
|
||||
access_level = constants.ACCESS_NFS_RW
|
||||
else:
|
||||
access_level = constants.ACCESS_NFS_RO
|
||||
elif share_proto == 'CIFS':
|
||||
if access_type == 'user':
|
||||
if access_level == common_constants.ACCESS_LEVEL_RW:
|
||||
@ -438,7 +461,6 @@ class V3StorageConnection(driver.HuaweiBase):
|
||||
raise exception.InvalidShareAccess(reason=err_msg)
|
||||
|
||||
share_id = share['ID']
|
||||
access_to = access['access_to']
|
||||
self.helper._allow_access_rest(share_id, access_to,
|
||||
share_proto, access_level)
|
||||
|
||||
@ -723,17 +745,15 @@ class V3StorageConnection(driver.HuaweiBase):
|
||||
"new_compression": new_compression})
|
||||
LOG.info(msg)
|
||||
|
||||
def _get_location_path(self, share_name, share_proto):
|
||||
root = self.helper._read_xml()
|
||||
target_ip = root.findtext('Storage/LogicalPortIP').strip()
|
||||
|
||||
def _get_location_path(self, share_name, share_proto, ip=None):
|
||||
location = None
|
||||
if ip is None:
|
||||
root = self.helper._read_xml()
|
||||
ip = root.findtext('Storage/LogicalPortIP').strip()
|
||||
if share_proto == 'NFS':
|
||||
location = '%s:/%s' % (target_ip,
|
||||
share_name.replace("-", "_"))
|
||||
location = '%s:/%s' % (ip, share_name.replace("-", "_"))
|
||||
elif share_proto == 'CIFS':
|
||||
location = '\\\\%s\\%s' % (target_ip,
|
||||
share_name.replace("-", "_"))
|
||||
location = '\\\\%s\\%s' % (ip, share_name.replace("-", "_"))
|
||||
else:
|
||||
raise exception.InvalidShareAccess(
|
||||
reason=(_('Invalid NAS protocol supplied: %s.')
|
||||
@ -775,6 +795,7 @@ class V3StorageConnection(driver.HuaweiBase):
|
||||
pwd = root.findtext('Storage/UserPassword')
|
||||
product = root.findtext('Storage/Product')
|
||||
pool_node = root.findtext('Filesystem/StoragePool')
|
||||
logical_port_ip = root.findtext('Storage/LogicalPortIP')
|
||||
|
||||
if product != "V3":
|
||||
err_msg = (_(
|
||||
@ -797,6 +818,14 @@ class V3StorageConnection(driver.HuaweiBase):
|
||||
LOG.error(err_msg)
|
||||
raise exception.InvalidInput(err_msg)
|
||||
|
||||
if not (self.configuration.driver_handles_share_servers
|
||||
or logical_port_ip):
|
||||
err_msg = (_(
|
||||
'check_conf_file: Config file invalid. LogicalPortIP '
|
||||
'must be set when driver_handles_share_servers is False.'))
|
||||
LOG.error(err_msg)
|
||||
raise exception.InvalidInput(reason=err_msg)
|
||||
|
||||
def check_service(self):
|
||||
running_status = self.helper._get_cifs_service_status()
|
||||
if running_status != constants.STATUS_SERVICE_RUNNING:
|
||||
@ -807,3 +836,381 @@ class V3StorageConnection(driver.HuaweiBase):
|
||||
(service['SUPPORTV3'] == 'false') or
|
||||
(service['SUPPORTV4'] == 'false')):
|
||||
self.helper._start_nfs_service_status()
|
||||
|
||||
def setup_server(self, network_info, metadata=None):
|
||||
"""Set up share server with given network parameters."""
|
||||
self._check_network_type_validate(network_info['network_type'])
|
||||
|
||||
vlan_tag = network_info['segmentation_id'] or 0
|
||||
ip = network_info['network_allocations'][0]['ip_address']
|
||||
subnet = utils.cidr_to_netmask(network_info['cidr'])
|
||||
if not utils.is_valid_ip_address(ip, '4'):
|
||||
err_msg = (_(
|
||||
"IP (%s) is invalid. Only IPv4 addresses are supported.") % ip)
|
||||
LOG.error(err_msg)
|
||||
raise exception.InvalidInput(reason=err_msg)
|
||||
|
||||
ad_created = False
|
||||
ldap_created = False
|
||||
try:
|
||||
if network_info.get('security_services'):
|
||||
active_directory, ldap = self._get_valid_security_service(
|
||||
network_info.get('security_services'))
|
||||
|
||||
# Configure AD or LDAP Domain.
|
||||
if active_directory:
|
||||
self._configure_AD_domain(active_directory)
|
||||
ad_created = True
|
||||
if ldap:
|
||||
self._configure_LDAP_domain(ldap)
|
||||
ldap_created = True
|
||||
|
||||
# Create vlan and logical_port.
|
||||
vlan_id, logical_port_id = (
|
||||
self._create_vlan_and_logical_port(vlan_tag, ip, subnet))
|
||||
except exception.ManilaException:
|
||||
if ad_created:
|
||||
dns_ip_list = []
|
||||
user = active_directory['user']
|
||||
password = active_directory['password']
|
||||
self.helper.set_DNS_ip_address(dns_ip_list)
|
||||
self.helper.delete_AD_config(user, password)
|
||||
self._check_AD_expected_status(constants.STATUS_EXIT_DOMAIN)
|
||||
if ldap_created:
|
||||
self.helper.delete_LDAP_config()
|
||||
raise
|
||||
|
||||
return {
|
||||
'share_server_name': network_info['server_id'],
|
||||
'share_server_id': network_info['server_id'],
|
||||
'vlan_id': vlan_id,
|
||||
'logical_port_id': logical_port_id,
|
||||
'ip': ip,
|
||||
'subnet': subnet,
|
||||
'vlan_tag': vlan_tag,
|
||||
'ad_created': ad_created,
|
||||
'ldap_created': ldap_created,
|
||||
}
|
||||
|
||||
def _check_network_type_validate(self, network_type):
|
||||
if network_type not in ('flat', 'vlan'):
|
||||
err_msg = (_(
|
||||
'Invalid network type. Network type must be flat or vlan.'))
|
||||
raise exception.NetworkBadConfigurationException(reason=err_msg)
|
||||
|
||||
def _get_valid_security_service(self, security_services):
|
||||
"""Validate security services and return AD/LDAP config."""
|
||||
service_number = len(security_services)
|
||||
err_msg = _("Unsupported security services. "
|
||||
"Only AD and LDAP are supported.")
|
||||
if service_number > 2:
|
||||
LOG.error(err_msg)
|
||||
raise exception.InvalidInput(reason=err_msg)
|
||||
|
||||
active_directory = None
|
||||
ldap = None
|
||||
for ss in security_services:
|
||||
if ss['type'] == 'active_directory':
|
||||
active_directory = ss
|
||||
elif ss['type'] == 'ldap':
|
||||
ldap = ss
|
||||
else:
|
||||
LOG.error(err_msg)
|
||||
raise exception.InvalidInput(reason=err_msg)
|
||||
|
||||
return active_directory, ldap
|
||||
|
||||
def _configure_AD_domain(self, active_directory):
|
||||
dns_ip = active_directory['dns_ip']
|
||||
user = active_directory['user']
|
||||
password = active_directory['password']
|
||||
domain = active_directory['domain']
|
||||
if not (dns_ip and user and password and domain):
|
||||
raise exception.InvalidInput(
|
||||
reason=_("dns_ip or user or password or domain "
|
||||
"in security_services is None."))
|
||||
|
||||
# Check DNS server exists or not.
|
||||
ip_address = self.helper.get_DNS_ip_address()
|
||||
if ip_address and ip_address[0]:
|
||||
err_msg = (_("DNS server (%s) has already been configured.")
|
||||
% ip_address[0])
|
||||
LOG.error(err_msg)
|
||||
raise exception.InvalidInput(reason=err_msg)
|
||||
|
||||
# Check AD config exists or not.
|
||||
ad_exists, AD_domain = self.helper.get_AD_domain_name()
|
||||
if ad_exists:
|
||||
err_msg = (_("AD domain (%s) has already been configured.")
|
||||
% AD_domain)
|
||||
LOG.error(err_msg)
|
||||
raise exception.InvalidInput(reason=err_msg)
|
||||
|
||||
# Set DNS server ip.
|
||||
dns_ip_list = dns_ip.split(",")
|
||||
DNS_config = self.helper.set_DNS_ip_address(dns_ip_list)
|
||||
|
||||
# Set AD config.
|
||||
digits = string.digits
|
||||
random_id = ''.join([random.choice(digits) for i in range(9)])
|
||||
system_name = constants.SYSTEM_NAME_PREFIX + random_id
|
||||
|
||||
try:
|
||||
self.helper.add_AD_config(user, password, domain, system_name)
|
||||
self._check_AD_expected_status(constants.STATUS_JOIN_DOMAIN)
|
||||
except exception.ManilaException as err:
|
||||
if DNS_config:
|
||||
dns_ip_list = []
|
||||
self.helper.set_DNS_ip_address(dns_ip_list)
|
||||
raise exception.InvalidShare(
|
||||
reason=(_('Failed to add AD config. '
|
||||
'Reason: %s.') % err))
|
||||
|
||||
def _check_AD_expected_status(self, expected_status):
|
||||
wait_interval = self._get_wait_interval()
|
||||
timeout = self._get_timeout()
|
||||
retries = timeout / wait_interval
|
||||
interval = wait_interval
|
||||
backoff_rate = 1
|
||||
|
||||
@utils.retry(exception.InvalidShare,
|
||||
interval,
|
||||
retries,
|
||||
backoff_rate)
|
||||
def _check_AD_status():
|
||||
ad = self.helper.get_AD_config()
|
||||
if ad['DOMAINSTATUS'] != expected_status:
|
||||
raise exception.InvalidShare(
|
||||
reason=(_('AD domain (%s) status is not expected.')
|
||||
% ad['FULLDOMAINNAME']))
|
||||
|
||||
_check_AD_status()
|
||||
|
||||
def _configure_LDAP_domain(self, ldap):
|
||||
server = ldap['server']
|
||||
domain = ldap['domain']
|
||||
if not server or not domain:
|
||||
raise exception.InvalidInput(reason=_("Server or domain is None."))
|
||||
|
||||
# Check LDAP config exists or not.
|
||||
ldap_exists, LDAP_domain = self.helper.get_LDAP_domain_server()
|
||||
if ldap_exists:
|
||||
err_msg = (_("LDAP domain (%s) has already been configured.")
|
||||
% LDAP_domain)
|
||||
LOG.error(err_msg)
|
||||
raise exception.InvalidInput(reason=err_msg)
|
||||
|
||||
# Set LDAP config.
|
||||
server_number = len(server.split(','))
|
||||
if server_number == 1:
|
||||
server = server + ",,"
|
||||
elif server_number == 2:
|
||||
server = server + ","
|
||||
elif server_number > 3:
|
||||
raise exception.InvalidInput(
|
||||
reason=_("Cannot support more than three LDAP servers."))
|
||||
|
||||
self.helper.add_LDAP_config(server, domain)
|
||||
|
||||
def _create_vlan_and_logical_port(self, vlan_tag, ip, subnet):
|
||||
optimal_port, port_type = self._get_optimal_port()
|
||||
port_id = self.helper.get_port_id(optimal_port, port_type)
|
||||
home_port_id = port_id
|
||||
home_port_type = port_type
|
||||
vlan_id = 0
|
||||
vlan_exists = True
|
||||
|
||||
if port_type is None or port_id is None:
|
||||
err_msg = _("No appropriate port found to create logical port.")
|
||||
LOG.error(err_msg)
|
||||
raise exception.InvalidInput(reason=err_msg)
|
||||
if vlan_tag:
|
||||
vlan_exists, vlan_id = self.helper.get_vlan(port_id, vlan_tag)
|
||||
if not vlan_exists:
|
||||
# Create vlan.
|
||||
vlan_id = self.helper.create_vlan(
|
||||
port_id, port_type, vlan_tag)
|
||||
home_port_id = vlan_id
|
||||
home_port_type = constants.PORT_TYPE_VLAN
|
||||
|
||||
logical_port_exists, logical_port_id = (
|
||||
self.helper.get_logical_port(home_port_id, ip, subnet))
|
||||
if not logical_port_exists:
|
||||
try:
|
||||
# Create logical port.
|
||||
logical_port_id = (
|
||||
self.helper.create_logical_port(
|
||||
home_port_id, home_port_type, ip, subnet))
|
||||
except exception.ManilaException as err:
|
||||
if not vlan_exists:
|
||||
self.helper.delete_vlan(vlan_id)
|
||||
raise exception.InvalidShare(
|
||||
reason=(_('Failed to create logical port. '
|
||||
'Reason: %s.') % err))
|
||||
|
||||
return vlan_id, logical_port_id
|
||||
|
||||
def _get_optimal_port(self):
|
||||
"""Get an optimal physical port or bond port."""
|
||||
root = self.helper._read_xml()
|
||||
port_info = []
|
||||
port_list = root.findtext('Storage/Port')
|
||||
if port_list:
|
||||
port_list = port_list.split(";")
|
||||
for port in port_list:
|
||||
port = port.strip().strip('\n')
|
||||
if port:
|
||||
port_info.append(port)
|
||||
|
||||
eth_port, bond_port = self._get_online_port(port_info)
|
||||
optimal_port, port_type = (
|
||||
self._get_least_vlan_port(eth_port, bond_port))
|
||||
|
||||
if not optimal_port:
|
||||
err_msg = (_("Cannot find optimal port. port_info: %s.")
|
||||
% port_info)
|
||||
LOG.error(err_msg)
|
||||
raise exception.InvalidInput(reason=err_msg)
|
||||
|
||||
return optimal_port, port_type
|
||||
|
||||
def _get_online_port(self, all_port_list):
|
||||
eth_port = self.helper.get_all_eth_port()
|
||||
bond_port = self.helper.get_all_bond_port()
|
||||
|
||||
eth_status = constants.STATUS_ETH_RUNNING
|
||||
online_eth_port = []
|
||||
for eth in eth_port:
|
||||
if (eth_status == eth['RUNNINGSTATUS']
|
||||
and not eth['IPV4ADDR'] and not eth['BONDNAME']):
|
||||
online_eth_port.append(eth['LOCATION'])
|
||||
|
||||
online_bond_port = []
|
||||
for bond in bond_port:
|
||||
if eth_status == bond['RUNNINGSTATUS']:
|
||||
port_id = jsonutils.loads(bond['PORTIDLIST'])
|
||||
bond_eth_port = self.helper.get_eth_port_by_id(port_id[0])
|
||||
if bond_eth_port and not bond_eth_port['IPV4ADDR']:
|
||||
online_bond_port.append(bond['NAME'])
|
||||
|
||||
filtered_eth_port = []
|
||||
filtered_bond_port = []
|
||||
if len(all_port_list) == 0:
|
||||
filtered_eth_port = online_eth_port
|
||||
filtered_bond_port = online_bond_port
|
||||
else:
|
||||
all_port_list = list(set(all_port_list))
|
||||
for port in all_port_list:
|
||||
is_eth_port = False
|
||||
for eth in online_eth_port:
|
||||
if port == eth:
|
||||
filtered_eth_port.append(port)
|
||||
is_eth_port = True
|
||||
break
|
||||
if is_eth_port:
|
||||
continue
|
||||
for bond in online_bond_port:
|
||||
if port == bond:
|
||||
filtered_bond_port.append(port)
|
||||
break
|
||||
|
||||
return filtered_eth_port, filtered_bond_port
|
||||
|
||||
def _get_least_vlan_port(self, eth_port, bond_port):
|
||||
sorted_eth = []
|
||||
sorted_bond = []
|
||||
|
||||
if eth_port:
|
||||
sorted_eth = self._get_sorted_least_port(eth_port)
|
||||
if bond_port:
|
||||
sorted_bond = self._get_sorted_least_port(bond_port)
|
||||
|
||||
if sorted_eth and sorted_bond:
|
||||
if sorted_eth[1] >= sorted_bond[1]:
|
||||
return sorted_bond[0], constants.PORT_TYPE_BOND
|
||||
else:
|
||||
return sorted_eth[0], constants.PORT_TYPE_ETH
|
||||
elif sorted_eth and not sorted_bond:
|
||||
return sorted_eth[0], constants.PORT_TYPE_ETH
|
||||
elif not sorted_eth and sorted_bond:
|
||||
return sorted_bond[0], constants.PORT_TYPE_BOND
|
||||
else:
|
||||
return None, None
|
||||
|
||||
def _get_sorted_least_port(self, port_list):
|
||||
if not port_list:
|
||||
return None
|
||||
|
||||
vlan_list = self.helper.get_all_vlan()
|
||||
count = {}
|
||||
for item in port_list:
|
||||
count[item] = 0
|
||||
|
||||
for item in port_list:
|
||||
for vlan in vlan_list:
|
||||
pos = vlan['NAME'].rfind('.')
|
||||
if vlan['NAME'][:pos] == item:
|
||||
count[item] += 1
|
||||
|
||||
sort_port = sorted(count.items(), key=lambda count: count[1])
|
||||
|
||||
return sort_port[0]
|
||||
|
||||
def teardown_server(self, server_details, security_services=None):
|
||||
if not server_details:
|
||||
LOG.debug('Server details are empty.')
|
||||
return
|
||||
|
||||
logical_port_id = server_details.get('logical_port_id')
|
||||
vlan_id = server_details.get('vlan_id')
|
||||
ad_created = server_details.get('ad_created')
|
||||
ldap_created = server_details.get('ldap_created')
|
||||
|
||||
# Delete logical_port.
|
||||
if logical_port_id:
|
||||
logical_port_exists = (
|
||||
self.helper.check_logical_port_exists_by_id(logical_port_id))
|
||||
if logical_port_exists:
|
||||
self.helper.delete_logical_port(logical_port_id)
|
||||
|
||||
# Delete vlan.
|
||||
if vlan_id and vlan_id != '0':
|
||||
vlan_exists = self.helper.check_vlan_exists_by_id(vlan_id)
|
||||
if vlan_exists:
|
||||
self.helper.delete_vlan(vlan_id)
|
||||
|
||||
if security_services:
|
||||
active_directory, ldap = (
|
||||
self._get_valid_security_service(security_services))
|
||||
|
||||
if ad_created and ad_created == '1' and active_directory:
|
||||
dns_ip = active_directory['dns_ip']
|
||||
user = active_directory['user']
|
||||
password = active_directory['password']
|
||||
domain = active_directory['domain']
|
||||
|
||||
# Check DNS server exists or not.
|
||||
ip_address = self.helper.get_DNS_ip_address()
|
||||
if ip_address and ip_address[0] == dns_ip:
|
||||
dns_ip_list = []
|
||||
self.helper.set_DNS_ip_address(dns_ip_list)
|
||||
|
||||
# Check AD config exists or not.
|
||||
ad_exists, AD_domain = self.helper.get_AD_domain_name()
|
||||
if ad_exists and AD_domain == domain:
|
||||
self.helper.delete_AD_config(user, password)
|
||||
self._check_AD_expected_status(
|
||||
constants.STATUS_EXIT_DOMAIN)
|
||||
|
||||
if ldap_created and ldap_created == '1' and ldap:
|
||||
server = ldap['server']
|
||||
domain = ldap['domain']
|
||||
|
||||
# Check LDAP config exists or not.
|
||||
ldap_exists, LDAP_domain = (
|
||||
self.helper.get_LDAP_domain_server())
|
||||
if ldap_exists:
|
||||
LDAP_config = self.helper.get_LDAP_config()
|
||||
if (LDAP_config['LDAPSERVER'] == server
|
||||
and LDAP_config['BASEDN'] == domain):
|
||||
self.helper.delete_LDAP_config()
|
@ -25,6 +25,7 @@ from six.moves.urllib import request as urlreq # pylint: disable=E0611
|
||||
from manila import exception
|
||||
from manila.i18n import _
|
||||
from manila.i18n import _LE
|
||||
from manila.i18n import _LW
|
||||
from manila.share.drivers.huawei import constants
|
||||
from manila import utils
|
||||
|
||||
@ -419,39 +420,93 @@ class RestHelper(object):
|
||||
self._assert_rest_result(result, 'Get access id by share error!')
|
||||
|
||||
for item in result.get('data', []):
|
||||
if access_to == item['NAME']:
|
||||
if item['NAME'] in (access_to, '@' + access_to):
|
||||
return item['ID']
|
||||
|
||||
def _allow_access_rest(self, share_id, access_to,
|
||||
share_proto, access_level):
|
||||
"""Allow access to the share."""
|
||||
access_type = self._get_share_client_type(share_proto)
|
||||
url = "/" + access_type
|
||||
if share_proto == 'NFS':
|
||||
self._allow_nfs_access_rest(share_id, access_to, access_level)
|
||||
elif share_proto == 'CIFS':
|
||||
self._allow_cifs_access_rest(share_id, access_to, access_level)
|
||||
else:
|
||||
raise exception.InvalidInput(
|
||||
reason=(_('Invalid NAS protocol supplied: %s.')
|
||||
% share_proto))
|
||||
|
||||
access = {}
|
||||
if access_type == "NFS_SHARE_AUTH_CLIENT":
|
||||
access = {
|
||||
"TYPE": "16409",
|
||||
"NAME": access_to,
|
||||
"PARENTID": share_id,
|
||||
"ACCESSVAL": access_level,
|
||||
"SYNC": "0",
|
||||
"ALLSQUASH": "1",
|
||||
"ROOTSQUASH": "0",
|
||||
}
|
||||
elif access_type == "CIFS_SHARE_AUTH_CLIENT":
|
||||
access = {
|
||||
"NAME": access_to,
|
||||
"PARENTID": share_id,
|
||||
"PERMISSION": access_level,
|
||||
"DOMAINTYPE": "2",
|
||||
}
|
||||
def _allow_nfs_access_rest(self, share_id, access_to, access_level):
|
||||
url = "/NFS_SHARE_AUTH_CLIENT"
|
||||
access = {
|
||||
"TYPE": "16409",
|
||||
"NAME": access_to,
|
||||
"PARENTID": share_id,
|
||||
"ACCESSVAL": access_level,
|
||||
"SYNC": "0",
|
||||
"ALLSQUASH": "1",
|
||||
"ROOTSQUASH": "0",
|
||||
}
|
||||
data = jsonutils.dumps(access)
|
||||
result = self.call(url, data, "POST")
|
||||
|
||||
msg = 'Allow access error.'
|
||||
self._assert_rest_result(result, msg)
|
||||
|
||||
def _allow_cifs_access_rest(self, share_id, access_to, access_level):
|
||||
url = "/CIFS_SHARE_AUTH_CLIENT"
|
||||
domain_type = {
|
||||
'local': '2',
|
||||
'ad': '0'
|
||||
}
|
||||
error_msg = 'Allow access error.'
|
||||
access_info = ('Access info (access_to: %(access_to)s, '
|
||||
'access_level: %(access_level)s, share_id: %(id)s)'
|
||||
% {'access_to': access_to,
|
||||
'access_level': access_level,
|
||||
'id': share_id})
|
||||
|
||||
def send_rest(access_to, domain_type):
|
||||
access = {
|
||||
"NAME": access_to,
|
||||
"PARENTID": share_id,
|
||||
"PERMISSION": access_level,
|
||||
"DOMAINTYPE": domain_type,
|
||||
}
|
||||
data = jsonutils.dumps(access)
|
||||
result = self.call(url, data, "POST")
|
||||
error_code = result['error']['code']
|
||||
if error_code == 0:
|
||||
return True
|
||||
elif error_code != constants.ERROR_USER_OR_GROUP_NOT_EXIST:
|
||||
self._assert_rest_result(result, error_msg)
|
||||
return False
|
||||
|
||||
if '\\' not in access_to:
|
||||
# First, try to add user access.
|
||||
LOG.debug('Try to add user access. %s.', access_info)
|
||||
if send_rest(access_to, domain_type['local']):
|
||||
return
|
||||
# Second, if add user access failed,
|
||||
# try to add group access.
|
||||
LOG.debug('Failed with add user access, '
|
||||
'try to add group access. %s.', access_info)
|
||||
# Group name starts with @.
|
||||
if send_rest('@' + access_to, domain_type['local']):
|
||||
return
|
||||
else:
|
||||
LOG.debug('Try to add domain user access. %s.', access_info)
|
||||
if send_rest(access_to, domain_type['ad']):
|
||||
return
|
||||
# If add domain user access failed,
|
||||
# try to add domain group access.
|
||||
LOG.debug('Failed with add domain user access, '
|
||||
'try to add domain group access. %s.', access_info)
|
||||
# Group name starts with @.
|
||||
if send_rest('@' + access_to, domain_type['ad']):
|
||||
return
|
||||
|
||||
raise exception.InvalidShare(reason=error_msg)
|
||||
|
||||
def _get_share_client_type(self, share_proto):
|
||||
share_client_type = None
|
||||
if share_proto == 'NFS':
|
||||
@ -766,3 +821,276 @@ class RestHelper(object):
|
||||
|
||||
self._assert_rest_result(result,
|
||||
_('Remove filesystem from cache error.'))
|
||||
|
||||
def get_all_eth_port(self):
|
||||
url = "/ETH_PORT"
|
||||
result = self.call(url, None, 'GET')
|
||||
self._assert_rest_result(result, _('Get all eth port error.'))
|
||||
|
||||
all_eth = {}
|
||||
if "data" in result:
|
||||
all_eth = result['data']
|
||||
|
||||
return all_eth
|
||||
|
||||
def get_eth_port_by_id(self, port_id):
|
||||
url = "/ETH_PORT/" + port_id
|
||||
result = self.call(url, None, 'GET')
|
||||
self._assert_rest_result(result, _('Get eth port by id error.'))
|
||||
|
||||
if "data" in result:
|
||||
return result['data']
|
||||
|
||||
return None
|
||||
|
||||
def get_all_bond_port(self):
|
||||
url = "/BOND_PORT"
|
||||
result = self.call(url, None, 'GET')
|
||||
self._assert_rest_result(result, _('Get all bond port error.'))
|
||||
|
||||
all_bond = {}
|
||||
if "data" in result:
|
||||
all_bond = result['data']
|
||||
|
||||
return all_bond
|
||||
|
||||
def get_port_id(self, port_name, port_type):
|
||||
if port_type == constants.PORT_TYPE_ETH:
|
||||
all_eth = self.get_all_eth_port()
|
||||
for item in all_eth:
|
||||
if port_name == item['LOCATION']:
|
||||
return item['ID']
|
||||
elif port_type == constants.PORT_TYPE_BOND:
|
||||
all_bond = self.get_all_bond_port()
|
||||
for item in all_bond:
|
||||
if port_name == item['NAME']:
|
||||
return item['ID']
|
||||
|
||||
return None
|
||||
|
||||
def get_all_vlan(self):
|
||||
url = "/vlan"
|
||||
result = self.call(url, None, 'GET')
|
||||
self._assert_rest_result(result, _('Get all vlan error.'))
|
||||
|
||||
all_vlan = {}
|
||||
if "data" in result:
|
||||
all_vlan = result['data']
|
||||
|
||||
return all_vlan
|
||||
|
||||
def get_vlan(self, port_id, vlan_tag):
|
||||
url = "/vlan"
|
||||
result = self.call(url, None, 'GET')
|
||||
self._assert_rest_result(result, _('Get vlan error.'))
|
||||
|
||||
vlan_tag = six.text_type(vlan_tag)
|
||||
if "data" in result:
|
||||
for item in result['data']:
|
||||
if port_id == item['PORTID'] and vlan_tag == item['TAG']:
|
||||
return True, item['ID']
|
||||
|
||||
return False, None
|
||||
|
||||
def create_vlan(self, port_id, port_type, vlan_tag):
|
||||
url = "/vlan"
|
||||
data = jsonutils.dumps({"PORTID": port_id,
|
||||
"PORTTYPE": port_type,
|
||||
"TAG": six.text_type(vlan_tag),
|
||||
"TYPE": "280"})
|
||||
result = self.call(url, data, "POST")
|
||||
self._assert_rest_result(result, _('Create vlan error.'))
|
||||
|
||||
return result['data']['ID']
|
||||
|
||||
def check_vlan_exists_by_id(self, vlan_id):
|
||||
all_vlan = self.get_all_vlan()
|
||||
return any(vlan['ID'] == vlan_id for vlan in all_vlan)
|
||||
|
||||
def delete_vlan(self, vlan_id):
|
||||
url = "/vlan/" + vlan_id
|
||||
result = self.call(url, None, 'DELETE')
|
||||
if result['error']['code'] == constants.ERROR_LOGICAL_PORT_EXIST:
|
||||
LOG.warning(_LW('Cannot delete vlan because there is '
|
||||
'a logical port on vlan.'))
|
||||
return
|
||||
|
||||
self._assert_rest_result(result, _('Delete vlan error.'))
|
||||
|
||||
def get_logical_port(self, home_port_id, ip, subnet):
|
||||
url = "/LIF"
|
||||
result = self.call(url, None, 'GET')
|
||||
self._assert_rest_result(result, _('Get logical port error.'))
|
||||
|
||||
if "data" not in result:
|
||||
return False, None
|
||||
|
||||
for item in result['data']:
|
||||
if (home_port_id == item['HOMEPORTID']
|
||||
and ip == item['IPV4ADDR']
|
||||
and subnet == item['IPV4MASK']):
|
||||
if item['OPERATIONALSTATUS'] != 'true':
|
||||
self._activate_logical_port(item['ID'])
|
||||
return True, item['ID']
|
||||
|
||||
return False, None
|
||||
|
||||
def _activate_logical_port(self, logical_port_id):
|
||||
url = "/LIF/" + logical_port_id
|
||||
data = jsonutils.dumps({"OPERATIONALSTATUS": "true"})
|
||||
result = self.call(url, data, 'PUT')
|
||||
self._assert_rest_result(result, _('Activate logical port error.'))
|
||||
|
||||
def create_logical_port(self, home_port_id, home_port_type, ip, subnet):
|
||||
url = "/LIF"
|
||||
info = {
|
||||
"ADDRESSFAMILY": 0,
|
||||
"CANFAILOVER": "true",
|
||||
"HOMEPORTID": home_port_id,
|
||||
"HOMEPORTTYPE": home_port_type,
|
||||
"IPV4ADDR": ip,
|
||||
"IPV4GATEWAY": "",
|
||||
"IPV4MASK": subnet,
|
||||
"NAME": ip,
|
||||
"OPERATIONALSTATUS": "true",
|
||||
"ROLE": 2,
|
||||
"SUPPORTPROTOCOL": 3,
|
||||
"TYPE": "279",
|
||||
}
|
||||
|
||||
data = jsonutils.dumps(info)
|
||||
result = self.call(url, data, 'POST')
|
||||
self._assert_rest_result(result, _('Create logical port error.'))
|
||||
|
||||
return result['data']['ID']
|
||||
|
||||
def check_logical_port_exists_by_id(self, logical_port_id):
|
||||
all_logical_port = self.get_all_logical_port()
|
||||
return any(port['ID'] == logical_port_id for port in all_logical_port)
|
||||
|
||||
def get_all_logical_port(self):
|
||||
url = "/LIF"
|
||||
result = self.call(url, None, 'GET')
|
||||
self._assert_rest_result(result, _('Get all logical port error.'))
|
||||
|
||||
all_logical_port = {}
|
||||
if "data" in result:
|
||||
all_logical_port = result['data']
|
||||
|
||||
return all_logical_port
|
||||
|
||||
def delete_logical_port(self, logical_port_id):
|
||||
url = "/LIF/" + logical_port_id
|
||||
result = self.call(url, None, 'DELETE')
|
||||
self._assert_rest_result(result, _('Delete logical port error.'))
|
||||
|
||||
def set_DNS_ip_address(self, dns_ip_list):
|
||||
if len(dns_ip_list) > 3:
|
||||
message = _('Most three ips can be set to DNS.')
|
||||
LOG.error(message)
|
||||
raise exception.InvalidInput(reason=message)
|
||||
|
||||
url = "/DNS_Server"
|
||||
dns_info = {
|
||||
"ADDRESS": jsonutils.dumps(dns_ip_list),
|
||||
"TYPE": "260",
|
||||
}
|
||||
data = jsonutils.dumps(dns_info)
|
||||
result = self.call(url, data, 'PUT')
|
||||
self._assert_rest_result(result, _('Set DNS ip address error.'))
|
||||
|
||||
if "data" in result:
|
||||
return result['data']
|
||||
|
||||
return None
|
||||
|
||||
def get_DNS_ip_address(self):
|
||||
url = "/DNS_Server"
|
||||
result = self.call(url, None, 'GET')
|
||||
self._assert_rest_result(result, _('Get DNS ip address error.'))
|
||||
|
||||
ip_address = {}
|
||||
if "data" in result:
|
||||
ip_address = jsonutils.loads(result['data']['ADDRESS'])
|
||||
|
||||
return ip_address
|
||||
|
||||
def add_AD_config(self, user, password, domain, system_name):
|
||||
url = "/AD_CONFIG"
|
||||
info = {
|
||||
"ADMINNAME": user,
|
||||
"ADMINPWD": password,
|
||||
"DOMAINSTATUS": 1,
|
||||
"FULLDOMAINNAME": domain,
|
||||
"OU": "",
|
||||
"SYSTEMNAME": system_name,
|
||||
"TYPE": "16414",
|
||||
}
|
||||
data = jsonutils.dumps(info)
|
||||
result = self.call(url, data, 'PUT')
|
||||
self._assert_rest_result(result, _('Add AD config error.'))
|
||||
|
||||
def delete_AD_config(self, user, password):
|
||||
url = "/AD_CONFIG"
|
||||
info = {
|
||||
"ADMINNAME": user,
|
||||
"ADMINPWD": password,
|
||||
"DOMAINSTATUS": 0,
|
||||
"TYPE": "16414",
|
||||
}
|
||||
data = jsonutils.dumps(info)
|
||||
result = self.call(url, data, 'PUT')
|
||||
self._assert_rest_result(result, _('Delete AD config error.'))
|
||||
|
||||
def get_AD_config(self):
|
||||
url = "/AD_CONFIG"
|
||||
result = self.call(url, None, 'GET')
|
||||
self._assert_rest_result(result, _('Get AD config error.'))
|
||||
|
||||
if "data" in result:
|
||||
return result['data']
|
||||
|
||||
return None
|
||||
|
||||
def get_AD_domain_name(self):
|
||||
result = self.get_AD_config()
|
||||
if result and result['DOMAINSTATUS'] == '1':
|
||||
return True, result['FULLDOMAINNAME']
|
||||
|
||||
return False, None
|
||||
|
||||
def add_LDAP_config(self, server, domain):
|
||||
url = "/LDAP_CONFIG"
|
||||
info = {
|
||||
"BASEDN": domain,
|
||||
"LDAPSERVER": server,
|
||||
"PORTNUM": 389,
|
||||
"TRANSFERTYPE": "1",
|
||||
"TYPE": "16413",
|
||||
"USERNAME": "",
|
||||
}
|
||||
data = jsonutils.dumps(info)
|
||||
result = self.call(url, data, 'PUT')
|
||||
self._assert_rest_result(result, _('Add LDAP config error.'))
|
||||
|
||||
def delete_LDAP_config(self):
|
||||
url = "/LDAP_CONFIG"
|
||||
result = self.call(url, None, 'DELETE')
|
||||
self._assert_rest_result(result, _('Delete LDAP config error.'))
|
||||
|
||||
def get_LDAP_config(self):
|
||||
url = "/LDAP_CONFIG"
|
||||
result = self.call(url, None, 'GET')
|
||||
self._assert_rest_result(result, _('Get LDAP config error.'))
|
||||
|
||||
if "data" in result:
|
||||
return result['data']
|
||||
|
||||
return None
|
||||
|
||||
def get_LDAP_domain_server(self):
|
||||
result = self.get_LDAP_config()
|
||||
if result and result['LDAPSERVER']:
|
||||
return True, result['LDAPSERVER']
|
||||
|
||||
return False, None
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user