"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)