Shared filesystem management project for OpenStack.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cifs_cmode.py 6.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  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 cDOT CIFS protocol helper class.
  16. """
  17. import re
  18. from manila.common import constants
  19. from manila import exception
  20. from manila.i18n import _
  21. from manila.share.drivers.netapp.dataontap.protocols import base
  22. from manila.share.drivers.netapp import utils as na_utils
  23. class NetAppCmodeCIFSHelper(base.NetAppBaseHelper):
  24. """NetApp cDOT CIFS protocol helper class."""
  25. @na_utils.trace
  26. def create_share(self, share, share_name,
  27. clear_current_export_policy=True):
  28. """Creates CIFS share on Data ONTAP Vserver."""
  29. self._client.create_cifs_share(share_name)
  30. if clear_current_export_policy:
  31. self._client.remove_cifs_share_access(share_name, 'Everyone')
  32. # Return a callback that may be used for generating export paths
  33. # for this share.
  34. return (lambda export_address, share_name=share_name:
  35. r'\\%s\%s' % (export_address, share_name))
  36. @na_utils.trace
  37. def delete_share(self, share, share_name):
  38. """Deletes CIFS share on Data ONTAP Vserver."""
  39. host_ip, share_name = self._get_export_location(share)
  40. self._client.remove_cifs_share(share_name)
  41. @na_utils.trace
  42. @base.access_rules_synchronized
  43. def update_access(self, share, share_name, rules):
  44. """Replaces the list of access rules known to the backend storage."""
  45. # Ensure rules are valid
  46. for rule in rules:
  47. self._validate_access_rule(rule)
  48. new_rules = {rule['access_to']: rule['access_level'] for rule in rules}
  49. # Get rules from share
  50. existing_rules = self._get_access_rules(share, share_name)
  51. # Update rules in an order that will prevent transient disruptions
  52. self._handle_added_rules(share_name, existing_rules, new_rules)
  53. self._handle_ro_to_rw_rules(share_name, existing_rules, new_rules)
  54. self._handle_rw_to_ro_rules(share_name, existing_rules, new_rules)
  55. self._handle_deleted_rules(share_name, existing_rules, new_rules)
  56. @na_utils.trace
  57. def _validate_access_rule(self, rule):
  58. """Checks whether access rule type and level are valid."""
  59. if rule['access_type'] != 'user':
  60. msg = _("Clustered Data ONTAP supports only 'user' type for "
  61. "share access rules with CIFS protocol.")
  62. raise exception.InvalidShareAccess(reason=msg)
  63. if rule['access_level'] not in constants.ACCESS_LEVELS:
  64. raise exception.InvalidShareAccessLevel(level=rule['access_level'])
  65. @na_utils.trace
  66. def _handle_added_rules(self, share_name, existing_rules, new_rules):
  67. """Updates access rules added between two rule sets."""
  68. added_rules = {
  69. user_or_group: permission
  70. for user_or_group, permission in new_rules.items()
  71. if user_or_group not in existing_rules
  72. }
  73. for user_or_group, permission in added_rules.items():
  74. self._client.add_cifs_share_access(
  75. share_name, user_or_group, self._is_readonly(permission))
  76. @na_utils.trace
  77. def _handle_ro_to_rw_rules(self, share_name, existing_rules, new_rules):
  78. """Updates access rules modified (RO-->RW) between two rule sets."""
  79. modified_rules = {
  80. user_or_group: permission
  81. for user_or_group, permission in new_rules.items()
  82. if (user_or_group in existing_rules and
  83. permission == constants.ACCESS_LEVEL_RW and
  84. existing_rules[user_or_group] != 'full_control')
  85. }
  86. for user_or_group, permission in modified_rules.items():
  87. self._client.modify_cifs_share_access(
  88. share_name, user_or_group, self._is_readonly(permission))
  89. @na_utils.trace
  90. def _handle_rw_to_ro_rules(self, share_name, existing_rules, new_rules):
  91. """Returns access rules modified (RW-->RO) between two rule sets."""
  92. modified_rules = {
  93. user_or_group: permission
  94. for user_or_group, permission in new_rules.items()
  95. if (user_or_group in existing_rules and
  96. permission == constants.ACCESS_LEVEL_RO and
  97. existing_rules[user_or_group] != 'read')
  98. }
  99. for user_or_group, permission in modified_rules.items():
  100. self._client.modify_cifs_share_access(
  101. share_name, user_or_group, self._is_readonly(permission))
  102. @na_utils.trace
  103. def _handle_deleted_rules(self, share_name, existing_rules, new_rules):
  104. """Returns access rules deleted between two rule sets."""
  105. deleted_rules = {
  106. user_or_group: permission
  107. for user_or_group, permission in existing_rules.items()
  108. if user_or_group not in new_rules
  109. }
  110. for user_or_group, permission in deleted_rules.items():
  111. self._client.remove_cifs_share_access(share_name, user_or_group)
  112. @na_utils.trace
  113. def _get_access_rules(self, share, share_name):
  114. """Returns the list of access rules known to the backend storage."""
  115. return self._client.get_cifs_share_access(share_name)
  116. @na_utils.trace
  117. def get_target(self, share):
  118. """Returns OnTap target IP based on share export location."""
  119. return self._get_export_location(share)[0]
  120. @na_utils.trace
  121. def get_share_name_for_share(self, share):
  122. """Returns the flexvol name that hosts a share."""
  123. _, share_name = self._get_export_location(share)
  124. return share_name
  125. @staticmethod
  126. def _get_export_location(share):
  127. """Returns host ip and share name for a given CIFS share."""
  128. export_location = share['export_location'] or '\\\\\\'
  129. regex = r'^(?:\\\\|//)(?P<host_ip>.*)(?:\\|/)(?P<share_name>.*)$'
  130. match = re.match(regex, export_location)
  131. if match:
  132. return match.group('host_ip'), match.group('share_name')
  133. else:
  134. return '', ''