"Fossies" - the Fresh Open Source Software Archive 
Member "manila-8.1.4/manila/share/drivers/netapp/dataontap/cluster_mode/lib_multi_svm.py" (19 Nov 2020, 18771 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.
For more information about "lib_multi_svm.py" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
8.1.3_vs_8.1.4.
1 # Copyright (c) 2015 Clinton Knight. All rights reserved.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License"); you may
4 # not use this file except in compliance with the License. You may obtain
5 # a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 # License for the specific language governing permissions and limitations
13 # under the License.
14 """
15 NetApp Data ONTAP cDOT multi-SVM storage driver library.
16
17 This library extends the abstract base library and completes the multi-SVM
18 functionality needed by the cDOT multi-SVM Manila driver. This library
19 variant creates Data ONTAP storage virtual machines (i.e. 'vservers')
20 as needed to provision shares.
21 """
22
23 import re
24
25 from oslo_log import log
26 from oslo_serialization import jsonutils
27 from oslo_utils import excutils
28
29 from manila import exception
30 from manila.i18n import _
31 from manila.share.drivers.netapp.dataontap.client import client_cmode
32 from manila.share.drivers.netapp.dataontap.cluster_mode import lib_base
33 from manila.share.drivers.netapp import utils as na_utils
34 from manila import utils
35
36
37 LOG = log.getLogger(__name__)
38 SUPPORTED_NETWORK_TYPES = (None, 'flat', 'vlan')
39 SEGMENTED_NETWORK_TYPES = ('vlan',)
40 DEFAULT_MTU = 1500
41 CLUSTER_IPSPACES = ('Cluster', 'Default')
42
43
44 class NetAppCmodeMultiSVMFileStorageLibrary(
45 lib_base.NetAppCmodeFileStorageLibrary):
46
47 @na_utils.trace
48 def check_for_setup_error(self):
49
50 if self._have_cluster_creds:
51 if self.configuration.netapp_vserver:
52 msg = ('Vserver is specified in the configuration. This is '
53 'ignored when the driver is managing share servers.')
54 LOG.warning(msg)
55
56 else: # only have vserver creds, which is an error in multi_svm mode
57 msg = _('Cluster credentials must be specified in the '
58 'configuration when the driver is managing share servers.')
59 raise exception.InvalidInput(reason=msg)
60
61 # Ensure one or more aggregates are available.
62 if not self._find_matching_aggregates():
63 msg = _('No aggregates are available for provisioning shares. '
64 'Ensure that the configuration option '
65 'netapp_aggregate_name_search_pattern is set correctly.')
66 raise exception.NetAppException(msg)
67
68 (super(NetAppCmodeMultiSVMFileStorageLibrary, self).
69 check_for_setup_error())
70
71 @na_utils.trace
72 def _get_vserver(self, share_server=None, vserver_name=None):
73
74 if share_server:
75 backend_details = share_server.get('backend_details')
76 vserver = backend_details.get(
77 'vserver_name') if backend_details else None
78
79 if not vserver:
80 msg = _('Vserver name is absent in backend details. Please '
81 'check whether Vserver was created properly.')
82 raise exception.VserverNotSpecified(msg)
83 elif vserver_name:
84 vserver = vserver_name
85 else:
86 msg = _('Share server not provided')
87 raise exception.InvalidInput(reason=msg)
88
89 if not self._client.vserver_exists(vserver):
90 raise exception.VserverNotFound(vserver=vserver)
91
92 vserver_client = self._get_api_client(vserver)
93 return vserver, vserver_client
94
95 def _get_ems_pool_info(self):
96 return {
97 'pools': {
98 'vserver': None,
99 'aggregates': self._find_matching_aggregates(),
100 },
101 }
102
103 @na_utils.trace
104 def _handle_housekeeping_tasks(self):
105 """Handle various cleanup activities."""
106 self._client.prune_deleted_nfs_export_policies()
107 self._client.prune_deleted_snapshots()
108 self._client.remove_unused_qos_policy_groups()
109
110 (super(NetAppCmodeMultiSVMFileStorageLibrary, self).
111 _handle_housekeeping_tasks())
112
113 @na_utils.trace
114 def _find_matching_aggregates(self):
115 """Find all aggregates match pattern."""
116 aggregate_names = self._client.list_non_root_aggregates()
117 pattern = self.configuration.netapp_aggregate_name_search_pattern
118 return [aggr_name for aggr_name in aggregate_names
119 if re.match(pattern, aggr_name)]
120
121 @na_utils.trace
122 def setup_server(self, network_info, metadata=None):
123 """Creates and configures new Vserver."""
124
125 vlan = network_info['segmentation_id']
126 ports = {}
127 for network_allocation in network_info['network_allocations']:
128 ports[network_allocation['id']] = network_allocation['ip_address']
129
130 @utils.synchronized('netapp-VLAN-%s' % vlan, external=True)
131 def setup_server_with_lock():
132 LOG.debug('Creating server %s', network_info['server_id'])
133 self._validate_network_type(network_info)
134
135 vserver_name = self._get_vserver_name(network_info['server_id'])
136 server_details = {
137 'vserver_name': vserver_name,
138 'ports': jsonutils.dumps(ports)
139 }
140
141 try:
142 self._create_vserver(vserver_name, network_info)
143 except Exception as e:
144 e.detail_data = {'server_details': server_details}
145 raise
146
147 return server_details
148
149 return setup_server_with_lock()
150
151 @na_utils.trace
152 def _validate_network_type(self, network_info):
153 """Raises exception if the segmentation type is incorrect."""
154 if network_info['network_type'] not in SUPPORTED_NETWORK_TYPES:
155 msg = _('The specified network type %s is unsupported by the '
156 'NetApp clustered Data ONTAP driver')
157 raise exception.NetworkBadConfigurationException(
158 reason=msg % network_info['network_type'])
159
160 @na_utils.trace
161 def _get_vserver_name(self, server_id):
162 return self.configuration.netapp_vserver_name_template % server_id
163
164 @na_utils.trace
165 def _create_vserver(self, vserver_name, network_info):
166 """Creates Vserver with given parameters if it doesn't exist."""
167
168 if self._client.vserver_exists(vserver_name):
169 msg = _('Vserver %s already exists.')
170 raise exception.NetAppException(msg % vserver_name)
171
172 # NOTE(lseki): If there's already an ipspace created for the same VLAN
173 # port, reuse it. It will be named after the previously created share
174 # server's neutron subnet id.
175 node_name = self._client.list_cluster_nodes()[0]
176 port = self._get_node_data_port(node_name)
177 vlan = network_info['segmentation_id']
178 ipspace_name = self._client.get_ipspace_name_for_vlan_port(
179 node_name, port, vlan) or self._create_ipspace(network_info)
180
181 LOG.debug('Vserver %s does not exist, creating.', vserver_name)
182 self._client.create_vserver(
183 vserver_name,
184 self.configuration.netapp_root_volume_aggregate,
185 self.configuration.netapp_root_volume,
186 self._find_matching_aggregates(),
187 ipspace_name)
188
189 vserver_client = self._get_api_client(vserver=vserver_name)
190 security_services = None
191 try:
192 self._create_vserver_lifs(vserver_name,
193 vserver_client,
194 network_info,
195 ipspace_name)
196
197 self._create_vserver_admin_lif(vserver_name,
198 vserver_client,
199 network_info,
200 ipspace_name)
201
202 self._create_vserver_routes(vserver_client,
203 network_info)
204
205 vserver_client.enable_nfs(
206 self.configuration.netapp_enabled_share_protocols)
207
208 security_services = network_info.get('security_services')
209 if security_services:
210 self._client.setup_security_services(security_services,
211 vserver_client,
212 vserver_name)
213 except Exception:
214 with excutils.save_and_reraise_exception():
215 LOG.error("Failed to configure Vserver.")
216 # NOTE(dviroel): At this point, the lock was already acquired
217 # by the caller of _create_vserver.
218 self._delete_vserver(vserver_name,
219 security_services=security_services,
220 needs_lock=False)
221
222 def _get_valid_ipspace_name(self, network_id):
223 """Get IPspace name according to network id."""
224 return 'ipspace_' + network_id.replace('-', '_')
225
226 @na_utils.trace
227 def _create_ipspace(self, network_info):
228 """If supported, create an IPspace for a new Vserver."""
229
230 if not self._client.features.IPSPACES:
231 return None
232
233 if (network_info['network_allocations'][0]['network_type']
234 not in SEGMENTED_NETWORK_TYPES):
235 return client_cmode.DEFAULT_IPSPACE
236
237 # NOTE(cknight): Neutron needs cDOT IP spaces because it can provide
238 # overlapping IP address ranges for different subnets. That is not
239 # believed to be an issue for any of Manila's other network plugins.
240 ipspace_id = network_info.get('neutron_subnet_id')
241 if not ipspace_id:
242 return client_cmode.DEFAULT_IPSPACE
243
244 ipspace_name = self._get_valid_ipspace_name(ipspace_id)
245 self._client.create_ipspace(ipspace_name)
246
247 return ipspace_name
248
249 @na_utils.trace
250 def _create_vserver_lifs(self, vserver_name, vserver_client, network_info,
251 ipspace_name):
252 """Create Vserver data logical interfaces (LIFs)."""
253
254 nodes = self._client.list_cluster_nodes()
255 node_network_info = zip(nodes, network_info['network_allocations'])
256
257 for node_name, network_allocation in node_network_info:
258 lif_name = self._get_lif_name(node_name, network_allocation)
259 self._create_lif(vserver_client, vserver_name, ipspace_name,
260 node_name, lif_name, network_allocation)
261
262 @na_utils.trace
263 def _create_vserver_admin_lif(self, vserver_name, vserver_client,
264 network_info, ipspace_name):
265 """Create Vserver admin LIF, if defined."""
266
267 network_allocations = network_info.get('admin_network_allocations')
268 if not network_allocations:
269 LOG.info('No admin network defined for Vserver %s.',
270 vserver_name)
271 return
272
273 node_name = self._client.list_cluster_nodes()[0]
274 network_allocation = network_allocations[0]
275 lif_name = self._get_lif_name(node_name, network_allocation)
276
277 self._create_lif(vserver_client, vserver_name, ipspace_name,
278 node_name, lif_name, network_allocation)
279
280 @na_utils.trace
281 def _create_vserver_routes(self, vserver_client, network_info):
282 """Create Vserver route and set gateways."""
283 route_gateways = []
284 # NOTE(gouthamr): Use the gateway from the tenant subnet/s
285 # for the static routes. Do not configure a route for the admin
286 # subnet because fast path routing will work for incoming
287 # connections and there are no requirements for outgoing
288 # connections on the admin network yet.
289 for net_allocation in (network_info['network_allocations']):
290 if net_allocation['gateway'] not in route_gateways:
291 vserver_client.create_route(net_allocation['gateway'])
292 route_gateways.append(net_allocation['gateway'])
293
294 @na_utils.trace
295 def _get_node_data_port(self, node):
296 port_names = self._client.list_node_data_ports(node)
297 pattern = self.configuration.netapp_port_name_search_pattern
298 matched_port_names = [port_name for port_name in port_names
299 if re.match(pattern, port_name)]
300 if not matched_port_names:
301 raise exception.NetAppException(
302 _('Could not find eligible network ports on node %s on which '
303 'to create Vserver LIFs.') % node)
304 return matched_port_names[0]
305
306 def _get_lif_name(self, node_name, network_allocation):
307 """Get LIF name based on template from manila.conf file."""
308 lif_name_args = {
309 'node': node_name,
310 'net_allocation_id': network_allocation['id'],
311 }
312 return self.configuration.netapp_lif_name_template % lif_name_args
313
314 @na_utils.trace
315 def _create_lif(self, vserver_client, vserver_name, ipspace_name,
316 node_name, lif_name, network_allocation):
317 """Creates LIF for Vserver."""
318
319 port = self._get_node_data_port(node_name)
320 ip_address = network_allocation['ip_address']
321 netmask = utils.cidr_to_netmask(network_allocation['cidr'])
322 vlan = network_allocation['segmentation_id']
323 network_mtu = network_allocation.get('mtu')
324 mtu = network_mtu or DEFAULT_MTU
325
326 if not vserver_client.network_interface_exists(
327 vserver_name, node_name, port, ip_address, netmask, vlan):
328
329 self._client.create_network_interface(
330 ip_address, netmask, vlan, node_name, port, vserver_name,
331 lif_name, ipspace_name, mtu)
332
333 @na_utils.trace
334 def get_network_allocations_number(self):
335 """Get number of network interfaces to be created."""
336 return len(self._client.list_cluster_nodes())
337
338 @na_utils.trace
339 def get_admin_network_allocations_number(self, admin_network_api):
340 """Get number of network allocations for creating admin LIFs."""
341 return 1 if admin_network_api else 0
342
343 @na_utils.trace
344 def teardown_server(self, server_details, security_services=None):
345 """Teardown share server."""
346 vserver = server_details.get(
347 'vserver_name') if server_details else None
348
349 if not vserver:
350 LOG.warning("Vserver not specified for share server being "
351 "deleted. Deletion of share server record will "
352 "proceed anyway.")
353 return
354
355 elif not self._client.vserver_exists(vserver):
356 LOG.warning("Could not find Vserver for share server being "
357 "deleted: %s. Deletion of share server "
358 "record will proceed anyway.", vserver)
359 return
360
361 self._delete_vserver(vserver, security_services=security_services)
362
363 @na_utils.trace
364 def _delete_vserver(self, vserver, security_services=None,
365 needs_lock=True):
366 """Delete a Vserver plus IPspace and security services as needed."""
367
368 ipspace_name = self._client.get_vserver_ipspace(vserver)
369
370 vserver_client = self._get_api_client(vserver=vserver)
371 network_interfaces = vserver_client.get_network_interfaces()
372
373 interfaces_on_vlans = []
374 vlans = []
375 for interface in network_interfaces:
376 if '-' in interface['home-port']:
377 interfaces_on_vlans.append(interface)
378 vlans.append(interface['home-port'])
379
380 if vlans:
381 vlans = '-'.join(sorted(set(vlans))) if vlans else None
382 vlan_id = vlans.split('-')[-1]
383 else:
384 vlan_id = None
385
386 def _delete_vserver_without_lock():
387 self._client.delete_vserver(vserver,
388 vserver_client,
389 security_services=security_services)
390
391 if (ipspace_name and ipspace_name not in CLUSTER_IPSPACES
392 and not self._client.ipspace_has_data_vservers(
393 ipspace_name)):
394 self._client.delete_ipspace(ipspace_name)
395
396 self._delete_vserver_vlans(interfaces_on_vlans)
397
398 @utils.synchronized('netapp-VLAN-%s' % vlan_id, external=True)
399 def _delete_vserver_with_lock():
400 _delete_vserver_without_lock()
401
402 if needs_lock:
403 return _delete_vserver_with_lock()
404 else:
405 return _delete_vserver_without_lock()
406
407 @na_utils.trace
408 def _delete_vserver_vlans(self, network_interfaces_on_vlans):
409 """Delete Vserver's VLAN configuration from ports"""
410 for interface in network_interfaces_on_vlans:
411 try:
412 home_port = interface['home-port']
413 port, vlan = home_port.split('-')
414 node = interface['home-node']
415 self._client.delete_vlan(node, port, vlan)
416 except exception.NetAppException:
417 LOG.exception("Deleting Vserver VLAN failed.")
418
419 def get_configured_ip_versions(self):
420 versions = [4]
421 options = self._client.get_net_options()
422 if options['ipv6-enabled']:
423 versions.append(6)
424 return versions
425
426 def manage_server(self, context, share_server, identifier, driver_options):
427 """Manages a vserver by renaming it and returning backend_details."""
428 new_vserver_name = self._get_vserver_name(share_server['id'])
429 old_vserver_name = self._get_correct_vserver_old_name(identifier)
430
431 if new_vserver_name != old_vserver_name:
432 self._client.rename_vserver(old_vserver_name, new_vserver_name)
433
434 backend_details = {'vserver_name': new_vserver_name}
435 return new_vserver_name, backend_details
436
437 def unmanage_server(self, server_details, security_services=None):
438 pass
439
440 def get_share_server_network_info(
441 self, context, share_server, identifier, driver_options):
442 """Returns a list of IPs for each vserver network interface."""
443 vserver_name = self._get_correct_vserver_old_name(identifier)
444
445 vserver, vserver_client = self._get_vserver(vserver_name=vserver_name)
446
447 interfaces = vserver_client.get_network_interfaces()
448 allocations = []
449 for lif in interfaces:
450 allocations.append(lif['address'])
451 return allocations
452
453 def _get_correct_vserver_old_name(self, identifier):
454
455 # In case vserver_name includes the template, we check and add it here
456 if not self._client.vserver_exists(identifier):
457 return self._get_vserver_name(identifier)
458 return identifier