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.6KB

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