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