Coverage for manila/share/drivers/container/driver.py: 88%
309 statements
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:19 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:19 +0000
1# Copyright (c) 2016 Mirantis, Inc.
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.
16"""Container Driver for shares.
18This driver uses a container as a share server.
19Current implementation suggests that a container when started by Docker will
20be plugged into a Linux bridge. Also it is suggested that all interfaces
21willing to talk to each other reside in an OVS bridge."""
23import math
25from oslo_config import cfg
26from oslo_log import log
27from oslo_serialization import jsonutils
28from oslo_utils import importutils
29from oslo_utils import uuidutils
31from manila import exception
32from manila.i18n import _
33from manila.share import driver
34from manila import utils
37CONF = cfg.CONF
38LOG = log.getLogger(__name__)
41container_opts = [
42 cfg.StrOpt("container_linux_bridge_name",
43 default="docker0",
44 help="Linux bridge used by container hypervisor to plug "
45 "host-side veth to. It will be unplugged from here "
46 "by the driver."),
47 cfg.StrOpt("container_ovs_bridge_name",
48 default="br-int",
49 help="OVS bridge to use to plug a container to."),
50 cfg.BoolOpt("container_cifs_guest_ok",
51 default=True,
52 help="Determines whether to allow guest access to CIFS share "
53 "or not."),
54 cfg.StrOpt("container_image_name",
55 default="manila-docker-container",
56 help="Image to be used for a container-based share server."),
57 cfg.StrOpt("container_helper",
58 default="manila.share.drivers.container.container_helper."
59 "DockerExecHelper",
60 help="Container helper which provides container-related "
61 "operations to the driver."),
62 cfg.StrOpt("container_protocol_helper",
63 default="manila.share.drivers.container.protocol_helper."
64 "DockerCIFSHelper",
65 help="Helper which facilitates interaction with share server."),
66 cfg.StrOpt("container_security_service_helper",
67 default="manila.share.drivers.container.security_service_helper"
68 ".SecurityServiceHelper",
69 help="Helper which facilitates interaction with security "
70 "services."),
71 cfg.StrOpt("container_storage_helper",
72 default="manila.share.drivers.container.storage_helper."
73 "LVMHelper",
74 help="Helper which facilitates interaction with storage "
75 "solution used to actually store data. By default LVM "
76 "is used to provide storage for a share."),
77 cfg.StrOpt("container_volume_mount_path",
78 default="/tmp/shares",
79 help="Folder name in host to which logical volume will be "
80 "mounted prior to providing access to it from a "
81 "container."),
82]
85class ContainerShareDriver(driver.ShareDriver, driver.ExecuteMixin):
86 def __init__(self, *args, **kwargs):
87 super(ContainerShareDriver, self).__init__([True], *args, **kwargs)
88 self.configuration.append_config_values(container_opts)
89 self.backend_name = self.configuration.safe_get(
90 "share_backend_name") or "Docker"
91 self.container = importutils.import_class(
92 self.configuration.container_helper)(
93 configuration=self.configuration)
94 self.security_service_helper = importutils.import_class(
95 self.configuration.container_security_service_helper)(
96 configuration=self.configuration)
97 self.storage = importutils.import_class(
98 self.configuration.container_storage_helper)(
99 configuration=self.configuration)
100 self._helpers = {}
101 self.network_allocation_update_support = True
103 def _get_helper(self, share):
104 if share["share_proto"].upper() == "CIFS":
105 helper = self._helpers.get("CIFS")
106 if helper is not None:
107 return helper(self.container,
108 share=share,
109 config=self.configuration)
110 self._helpers["CIFS"] = importutils.import_class(
111 self.configuration.container_protocol_helper)
112 return self._helpers["CIFS"](self.container,
113 share=share,
114 config=self.configuration)
115 else:
116 raise exception.InvalidShare(
117 reason=_("Wrong, unsupported or disabled protocol."))
119 def _update_share_stats(self):
120 data = {
121 'share_backend_name': self.backend_name,
122 'storage_protocol': 'CIFS',
123 'reserved_percentage':
124 self.configuration.reserved_share_percentage,
125 'reserved_snapshot_percentage':
126 self.configuration.reserved_share_from_snapshot_percentage or
127 self.configuration.reserved_share_percentage,
128 'reserved_share_extend_percentage':
129 self.configuration.reserved_share_extend_percentage or
130 self.configuration.reserved_share_percentage,
131 'consistency_group_support': None,
132 'snapshot_support': False,
133 'create_share_from_snapshot_support': False,
134 'driver_name': 'ContainerShareDriver',
135 'pools': self.storage.get_share_server_pools(),
136 'security_service_update_support': True,
137 'share_server_multiple_subnet_support': True,
138 'mount_point_name_support': False,
139 }
140 super(ContainerShareDriver, self)._update_share_stats(data)
142 def create_share(self, context, share, share_server=None):
143 LOG.debug("Create share on server '%s'.", share_server["id"])
144 server_id = self._get_container_name(share_server["id"])
145 share_name = share.share_id
146 self.storage.provide_storage(share_name, share['size'])
148 location = self._create_export_and_mount_storage(
149 share, server_id, share_name)
151 return location
153 @utils.synchronized('container_driver_delete_share_lock', external=True)
154 def delete_share(self, context, share, share_server=None):
155 LOG.debug("Deleting share %(share)s on server '%(server)s'.",
156 {"server": share_server["id"],
157 "share": self._get_share_name(share)})
158 server_id = self._get_container_name(share_server["id"])
159 share_name = self._get_share_name(share)
161 self._delete_export_and_umount_storage(share, server_id, share_name,
162 ignore_errors=True)
164 self.storage.remove_storage(share_name)
165 LOG.debug("Deleted share %s successfully.", share_name)
167 def _get_share_name(self, share):
168 if share.get('export_location'):
169 return share['export_location'].split('/')[-1]
170 else:
171 return share.share_id
173 def extend_share(self, share, new_size, share_server=None):
174 server_id = self._get_container_name(share_server["id"])
175 share_name = self._get_share_name(share)
176 self.container.execute(
177 server_id,
178 ["umount", "/shares/%s" % share_name]
179 )
180 self.storage.extend_share(share_name, new_size, share_server)
181 lv_device = self.storage._get_lv_device(share_name)
182 self.container.execute(
183 server_id,
184 ["mount", lv_device, "/shares/%s" % share_name]
185 )
187 def ensure_share(self, context, share, share_server=None):
188 pass
190 def update_access(self, context, share, access_rules, add_rules,
191 delete_rules, update_rules, share_server=None):
192 server_id = self._get_container_name(share_server["id"])
193 share_name = self._get_share_name(share)
194 LOG.debug("Updating access to share %(share)s at "
195 "share server %(share_server)s.",
196 {"share_server": share_server["id"],
197 "share": share_name})
198 self._get_helper(share).update_access(server_id, share_name,
199 access_rules, add_rules,
200 delete_rules)
202 def get_network_allocations_number(self):
203 return 1
205 def _get_container_name(self, server_id):
206 return "manila_%s" % server_id.replace("-", "_")
208 def do_setup(self, *args, **kwargs):
209 pass
211 def check_for_setup_error(self, *args, **kwargs):
212 host_id = self.configuration.safe_get("neutron_host_id")
213 neutron_class = importutils.import_class(
214 'manila.network.neutron.neutron_network_plugin.'
215 'NeutronNetworkPlugin'
216 )
217 actual_class = importutils.import_class(
218 self.configuration.safe_get("network_api_class"))
219 if host_id is None and issubclass(actual_class, neutron_class):
220 msg = _("%s requires neutron_host_id to be "
221 "specified.") % neutron_class
222 raise exception.ManilaException(msg)
223 elif host_id is None: 223 ↛ exitline 223 didn't return from function 'check_for_setup_error' because the condition on line 223 was always true
224 LOG.warning("neutron_host_id is not specified. This driver "
225 "might not work as expected without it.")
227 def _connect_to_network(self, server_id, network_info, host_veth,
228 host_bridge, iface):
229 LOG.debug("Attempting to connect container to neutron network.")
230 network_allocation = network_info["network_allocations"][0]
231 port_address = network_allocation.ip_address
232 port_mac = network_allocation.mac_address
233 port_id = network_allocation.id
234 self.container.execute(
235 server_id,
236 ["ifconfig", iface, port_address, "up"]
237 )
238 self.container.execute(
239 server_id,
240 ["ip", "link", "set", "dev", iface, "address", port_mac]
241 )
242 msg_helper = {
243 'id': server_id,
244 'veth': host_veth,
245 'lb': host_bridge,
246 'ovsb': self.configuration.container_ovs_bridge_name,
247 'ip': port_address,
248 'network': network_info['neutron_net_id'],
249 'subnet': network_info['neutron_subnet_id'],
250 }
251 LOG.debug("Container %(id)s veth is %(veth)s.", msg_helper)
252 LOG.debug("Removing %(veth)s from %(lb)s.", msg_helper)
253 self._execute("ip", "link", "set", "dev", host_veth, "nomaster",
254 run_as_root=True)
256 LOG.debug("Plugging %(veth)s into %(ovsb)s.", msg_helper)
257 set_if = ['--', 'set', 'interface', host_veth]
258 e_mac = set_if + ['external-ids:attached-mac="%s"' % port_mac]
259 e_id = set_if + ['external-ids:iface-id="%s"' % port_id]
260 e_status = set_if + ['external-ids:iface-status=active']
261 e_mcid = set_if + ['external-ids:manila-container=%s' % server_id]
262 self._execute("ovs-vsctl", "--", "add-port",
263 self.configuration.container_ovs_bridge_name, host_veth,
264 *(e_mac + e_id + e_status + e_mcid), run_as_root=True)
265 LOG.debug("Now container %(id)s should be accessible from network "
266 "%(network)s and subnet %(subnet)s by address %(ip)s.",
267 msg_helper)
269 @utils.synchronized("container_driver_teardown_lock", external=True)
270 def _teardown_server(self, *args, **kwargs):
271 server_id = self._get_container_name(kwargs["server_details"]["id"])
272 veths = self.container.get_container_veths(server_id)
273 networks = self.container.get_container_networks(server_id)
275 for veth, network in zip(veths, networks):
276 LOG.debug("Deleting veth %s.", veth)
277 try:
278 self._execute("ovs-vsctl", "--", "del-port",
279 self.configuration.container_ovs_bridge_name,
280 veth, run_as_root=True)
281 except exception.ProcessExecutionError as e:
282 LOG.warning("Failed to delete port %s: port vanished.", veth)
283 LOG.error(e)
284 self.container.disconnect_network(network, server_id)
286 if network != "bridge": 286 ↛ 275line 286 didn't jump to line 275 because the condition on line 286 was always true
287 self.container.remove_network(network)
289 self.container.stop_container(server_id)
291 def _setup_server_network(self, server_id, network_info):
292 existing_interfaces = self.container.fetch_container_interfaces(
293 server_id)
294 new_interfaces = []
296 # If the share server network allocations are being updated, create
297 # interfaces starting with ethX + 1.
298 if existing_interfaces:
299 ifnum_offset = len(existing_interfaces)
300 for ifnum, subnet in enumerate(network_info):
301 # TODO(ecsantos): Newer Ubuntu images (systemd >= 197) use
302 # predictable network interface names (e.g., enp3s0) instead of
303 # the classical kernel naming scheme (e.g., eth0). The
304 # Container driver currently uses an Ubuntu Xenial Docker
305 # image, so if it's updated in the future, these "eth" strings
306 # should also be updated.
307 new_interfaces.append("eth" + str(ifnum + ifnum_offset))
308 # Otherwise (the share server was just created), create interfaces
309 # starting with eth0.
310 else:
311 for ifnum, subnet in enumerate(network_info):
312 new_interfaces.append("eth" + str(ifnum))
314 for new_interface, subnet in zip(new_interfaces, network_info):
315 network_name = "manila-docker-network-" + uuidutils.generate_uuid()
316 self.container.create_network(network_name)
317 self.container.connect_network(network_name, server_id)
319 bridge = self.container.get_network_bridge(network_name)
320 veth = self.container.get_veth_from_bridge(bridge)
321 self._connect_to_network(server_id, subnet, veth, bridge,
322 new_interface)
324 @utils.synchronized("veth-lock", external=True)
325 def _setup_server(self, network_info, metadata=None):
326 msg = "Creating share server '%s'."
327 common_net_info = network_info[0]
328 server_id = self._get_container_name(common_net_info["server_id"])
329 LOG.debug(msg, server_id)
331 try:
332 self.container.create_container(server_id)
333 self.container.start_container(server_id)
334 except Exception as e:
335 raise exception.ManilaException(_("Cannot create container: %s") %
336 e)
338 self._setup_server_network(server_id, network_info)
339 security_services = common_net_info.get('security_services')
341 if security_services:
342 self.setup_security_services(server_id, security_services)
344 LOG.info("Container %s was created.", server_id)
345 return {"id": common_net_info["server_id"]}
347 def _delete_export_and_umount_storage(
348 self, share, server_id, share_name, ignore_errors=False):
350 self._umount_storage(
351 share, server_id, share_name, ignore_errors=ignore_errors)
353 # (aovchinnikov): bug 1621784 manifests itself here as well as in
354 # storage helper. There is a chance that we won't be able to remove
355 # this directory, despite the fact that it is not shared anymore and
356 # already contains nothing. In such case the driver should not fail
357 # share deletion, but issue a warning.
358 self.container.execute(
359 server_id,
360 ["rm", "-fR", "/shares/%s" % share_name],
361 ignore_errors=True
362 )
364 def _umount_storage(
365 self, share, server_id, share_name, ignore_errors=False):
367 self._get_helper(share).delete_share(server_id, share_name,
368 ignore_errors=ignore_errors)
369 self.container.execute(
370 server_id,
371 ["umount", "/shares/%s" % share_name],
372 ignore_errors=ignore_errors
373 )
375 def _create_export_and_mount_storage(self, share, server_id, share_name):
376 self.container.execute(
377 server_id,
378 ["mkdir", "-m", "750", "/shares/%s" % share_name]
379 )
380 return self._mount_storage(share, server_id, share_name)
382 def _mount_storage(self, share, server_id, share_name):
383 lv_device = self.storage._get_lv_device(share_name)
384 self.container.execute(
385 server_id,
386 ["mount", lv_device, "/shares/%s" % share_name]
387 )
388 location = self._get_helper(share).create_share(server_id)
389 return location
391 def manage_existing_with_server(
392 self, share, driver_options, share_server=None):
393 if not share_server and self.driver_handles_share_servers:
394 raise exception.ShareBackendException(
395 "A share server object is needed to manage a share in this "
396 "driver mode of operation.")
397 server_id = self._get_container_name(share_server["id"])
398 share_name = self._get_share_name(share)
399 size = int(math.ceil(float(self.storage.get_size(share_name))))
401 self._delete_export_and_umount_storage(share, server_id, share_name)
403 new_share_name = share.share_id
404 self.storage.rename_storage(share_name, new_share_name)
406 location = self._create_export_and_mount_storage(
407 share, server_id, new_share_name)
409 result = {'size': size, 'export_locations': location}
410 LOG.info("Successfully managed share %(share)s, returning %(data)s",
411 {'share': share.id, 'data': result})
412 return result
414 def unmanage_with_server(self, share, share_server=None):
415 pass
417 def get_share_server_network_info(
418 self, context, share_server, identifier, driver_options):
419 name = self._get_correct_container_old_name(identifier)
420 return self.container.fetch_container_addresses(name, "inet")
422 def manage_server(self, context, share_server, identifier, driver_options):
423 new_name = self._get_container_name(share_server['id'])
424 old_name = self._get_correct_container_old_name(identifier)
425 self.container.rename_container(old_name, new_name)
426 return new_name, {'id': share_server['id']}
428 def unmanage_server(self, server_details, security_services=None):
429 pass
431 def _get_correct_container_old_name(self, name):
432 # Check if the container with the given name exists, else return
433 # the name based on the driver template
434 if not self.container.container_exists(name):
435 return self._get_container_name(name)
436 return name
438 def migration_check_compatibility(self, context, source_share,
439 destination_share, share_server=None,
440 destination_share_server=None):
441 return self.storage.migration_check_compatibility(
442 context, source_share, destination_share,
443 share_server=share_server,
444 destination_share_server=destination_share_server)
446 def migration_start(self, context, source_share, destination_share,
447 source_snapshots, snapshot_mappings,
448 share_server=None, destination_share_server=None):
449 self.storage.migration_start(
450 context, source_share, destination_share,
451 source_snapshots, snapshot_mappings,
452 share_server=share_server,
453 destination_share_server=destination_share_server)
455 def migration_continue(self, context, source_share, destination_share,
456 source_snapshots, snapshot_mappings,
457 share_server=None, destination_share_server=None):
458 return self.storage.migration_continue(
459 context, source_share, destination_share,
460 source_snapshots, snapshot_mappings, share_server=share_server,
461 destination_share_server=destination_share_server)
463 def migration_get_progress(self, context, source_share,
464 destination_share, source_snapshots,
465 snapshot_mappings, share_server=None,
466 destination_share_server=None):
467 return self.storage.migration_get_progress(
468 context, source_share, destination_share,
469 source_snapshots, snapshot_mappings, share_server=share_server,
470 destination_share_server=destination_share_server)
472 def migration_cancel(self, context, source_share, destination_share,
473 source_snapshots, snapshot_mappings,
474 share_server=None, destination_share_server=None):
475 self.storage.migration_cancel(
476 context, source_share, destination_share,
477 source_snapshots, snapshot_mappings, share_server=share_server,
478 destination_share_server=destination_share_server)
480 def migration_complete(self, context, source_share, destination_share,
481 source_snapshots, snapshot_mappings,
482 share_server=None, destination_share_server=None):
483 # Removes the source share reference from the source container
484 source_server_id = self._get_container_name(share_server["id"])
485 self._umount_storage(
486 source_share, source_server_id, source_share.share_id)
488 # storage removes source share
489 self.storage.migration_complete(
490 context, source_share, destination_share,
491 source_snapshots, snapshot_mappings, share_server=share_server,
492 destination_share_server=destination_share_server)
494 # Enables the access on the destination container
495 destination_server_id = self._get_container_name(
496 destination_share_server["id"])
497 new_export_locations = self._mount_storage(
498 destination_share, destination_server_id,
499 destination_share.share_id)
501 msg = ("Volume move operation for share %(shr)s was completed "
502 "successfully. Share has been moved from %(src)s to "
503 "%(dest)s.")
504 msg_args = {
505 'shr': source_share['id'],
506 'src': source_share['host'],
507 'dest': destination_share['host'],
508 }
509 LOG.info(msg, msg_args)
511 return {
512 'export_locations': new_export_locations,
513 }
515 def share_server_migration_check_compatibility(
516 self, context, share_server, dest_host, old_share_network,
517 new_share_network, shares_request_spec):
518 """Is called to check migration compatibility for a share server."""
519 return self.storage.share_server_migration_check_compatibility(
520 context, share_server, dest_host, old_share_network,
521 new_share_network, shares_request_spec)
523 def share_server_migration_start(self, context, src_share_server,
524 dest_share_server, shares, snapshots):
525 """Is called to perform 1st phase of migration of a share server."""
526 LOG.debug(
527 "Migration of share server with ID '%s' has been started.",
528 src_share_server["id"])
529 self.storage.share_server_migration_start(
530 context, src_share_server, dest_share_server, shares, snapshots)
532 def share_server_migration_continue(self, context, src_share_server,
533 dest_share_server, shares, snapshots):
535 return self.storage.share_server_migration_continue(
536 context, src_share_server, dest_share_server, shares, snapshots)
538 def share_server_migration_cancel(self, context, src_share_server,
539 dest_share_server, shares, snapshots):
540 """Is called to cancel a share server migration."""
541 self.storage.share_server_migration_cancel(
542 context, src_share_server, dest_share_server, shares, snapshots)
543 LOG.debug(
544 "Migration of share server with ID '%s' has been canceled.",
545 src_share_server["id"])
546 return
548 def share_server_migration_get_progress(self, context, src_share_server,
549 dest_share_server, shares,
550 snapshots):
551 """Is called to get share server migration progress."""
552 return self.storage.share_server_migration_get_progress(
553 context, src_share_server, dest_share_server, shares, snapshots)
555 def share_server_migration_complete(self, context, source_share_server,
556 dest_share_server, shares, snapshots,
557 new_network_allocations):
558 # Removes the source shares reference from the source container
559 source_server_id = self._get_container_name(source_share_server["id"])
560 for source_share in shares:
561 self._umount_storage(
562 source_share, source_server_id, source_share.share_id)
564 # storage removes source share
565 self.storage.share_server_migration_complete(
566 context, source_share_server, dest_share_server, shares, snapshots,
567 new_network_allocations)
569 destination_server_id = self._get_container_name(
570 dest_share_server["id"])
571 shares_updates = {}
572 for destination_share in shares:
573 share_id = destination_share.share_id
574 new_export_locations = self._mount_storage(
575 destination_share, destination_server_id, share_id)
577 shares_updates[destination_share['id']] = {
578 'export_locations': new_export_locations,
579 'pool_name': self.storage.get_share_pool_name(share_id),
580 }
582 msg = ("Volumes move operation from server %(server)s were completed "
583 "successfully. Share server has been moved from %(src)s to "
584 "%(dest)s.")
585 msg_args = {
586 'serv': source_share_server['id'],
587 'src': source_share_server['host'],
588 'dest': dest_share_server['host'],
589 }
590 LOG.info(msg, msg_args)
592 return {
593 'share_updates': shares_updates,
594 }
596 def setup_security_services(self, share_server_id, security_services):
597 """Is called to setup a security service in the share server."""
599 for security_service in security_services:
600 if security_service['type'].lower() != 'ldap':
601 raise exception.ShareBackendException(_(
602 "The container driver does not support security services "
603 "other than LDAP."))
605 self.security_service_helper.setup_security_service(
606 share_server_id, security_service)
608 def _get_different_security_service_keys(
609 self, current_security_service, new_security_service):
610 valid_keys = ['dns_ip', 'server', 'domain', 'user', 'password', 'ou']
611 different_keys = []
612 for key, value in current_security_service.items():
613 if (current_security_service[key] != new_security_service[key] 613 ↛ 612line 613 didn't jump to line 612 because the condition on line 613 was always true
614 and key in valid_keys):
615 different_keys.append(key)
616 return different_keys
618 def _check_if_all_fields_are_updatable(self, current_security_service,
619 new_security_service):
620 # NOTE(carloss): We only support updating user and password at
621 # the moment
622 updatable_fields = ['user', 'password']
623 different_keys = self._get_different_security_service_keys(
624 current_security_service, new_security_service)
625 for key in different_keys:
626 if key not in updatable_fields:
627 return False
628 return True
630 def update_share_server_security_service(self, context, share_server,
631 network_info,
632 share_instances,
633 share_instance_rules,
634 new_security_service,
635 current_security_service=None):
636 """Is called to update or add a sec service to a share server."""
638 if not self.check_update_share_server_security_service(
639 context, share_server, network_info, share_instances,
640 share_instance_rules, new_security_service,
641 current_security_service=current_security_service):
642 raise exception.ManilaException(_(
643 "The requested security service update is not supported by "
644 "the container driver."))
646 server_id = self._get_container_name(share_server['id'])
648 if not current_security_service:
649 self.setup_security_services(server_id, [new_security_service])
650 else:
651 self.security_service_helper.update_security_service(
652 server_id, current_security_service, new_security_service)
654 msg = (
655 "The security service was successfully added to the share "
656 "server %(server_id)s.")
657 msg_args = {
658 'server_id': share_server['id'],
659 }
660 LOG.info(msg, msg_args)
662 def check_update_share_server_security_service(
663 self, context, share_server, network_info, share_instances,
664 share_instance_rules, new_security_service,
665 current_security_service=None):
666 current_type = (
667 current_security_service['type'].lower()
668 if current_security_service else '')
669 new_type = new_security_service['type'].lower()
671 if new_type != 'ldap' or (current_type and current_type != 'ldap'):
672 LOG.error('Currently only LDAP security services are supported '
673 'by the container driver.')
674 return False
676 if not current_type:
677 return True
679 all_fields_are_updatable = self._check_if_all_fields_are_updatable(
680 current_security_service, new_security_service)
681 if not all_fields_are_updatable:
682 LOG.info(
683 "The Container driver does not support updating "
684 "security service parameters other than 'user' and "
685 "'password'.")
686 return False
687 return True
689 def _form_share_server_update_return(self, share_server,
690 current_network_allocations,
691 new_network_allocations,
692 share_instances):
693 server_id = self._get_container_name(share_server["id"])
694 addresses = self.container.fetch_container_addresses(server_id, "inet")
695 share_updates = {}
696 subnet_allocations = {}
698 for share_instance in share_instances:
699 export_locations = []
700 for address in addresses:
701 # TODO(ecsantos): The Container driver currently only
702 # supports CIFS. If NFS support is implemented in the
703 # future, the path should be adjusted accordingly.
704 export_location = {
705 "is_admin_only": False,
706 "path": "//%(ip_address)s/%(share_id)s" %
707 {
708 "ip_address": address,
709 "share_id": share_instance["share_id"]
710 },
711 "preferred": False
712 }
713 export_locations.append(export_location)
714 share_updates[share_instance["id"]] = export_locations
716 for subnet in current_network_allocations["subnets"]:
717 for network_allocation in subnet["network_allocations"]:
718 subnet_allocations[network_allocation["id"]] = (
719 network_allocation["ip_address"])
721 for network_allocation in (
722 new_network_allocations["network_allocations"]):
723 subnet_allocations[network_allocation["id"]] = (
724 network_allocation["ip_address"])
726 server_details = {
727 "subnet_allocations": jsonutils.dumps(subnet_allocations)
728 }
729 return {
730 "share_updates": share_updates,
731 "server_details": server_details
732 }
734 def check_update_share_server_network_allocations(
735 self, context, share_server, current_network_allocations,
736 new_share_network_subnet, security_services, share_instances,
737 share_instances_rules):
738 LOG.debug("Share server %(server)s can be updated with allocations "
739 "from new subnet.", {"server": share_server["id"]})
740 return True
742 def update_share_server_network_allocations(
743 self, context, share_server, current_network_allocations,
744 new_network_allocations, security_services, share_instances,
745 snapshots):
746 server_id = self._get_container_name(share_server["id"])
747 self._setup_server_network(server_id, [new_network_allocations])
748 return self._form_share_server_update_return(
749 share_server, current_network_allocations, new_network_allocations,
750 share_instances)