Coverage for plugins/modules/purefb_resource_access.py: 85%

59 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-10-30 17:06 -0400

1#!/usr/bin/python 

2# -*- coding: utf-8 -*- 

3 

4# Copyright: (c) 2026, Simon Dodsley (simon@everpuredata.com) 

5# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 

6 

7from __future__ import absolute_import, division, print_function 

8 

9__metaclass__ = type 

10 

11ANSIBLE_METADATA = { 

12 "metadata_version": "1.1", 

13 "status": ["preview"], 

14 "supported_by": "community", 

15} 

16 

17DOCUMENTATION = r""" 

18--- 

19module: purefb_resource_access 

20version_added: '1.28.0' 

21short_description: Manage Resource Access on Everpure FlashBlades 

22description: 

23 - Create or delete resource access on Everpure FlashBlades. 

24author: 

25 - Everpure Ansible Team (@sdodsley) <pure-ansible-team@everpuredata.com> 

26 - Leo Wahlandt (@valen98) <wahlandtleo@gmail.com> 

27options: 

28 resource_type: 

29 description: 

30 - The type of resource you want to add, given as the REST collection 

31 name of the resource. 

32 - The set of valid resource types is defined by the array and grows 

33 with the REST API version, so it is not validated by this module. 

34 - For a realm scope, Purity//FB 4.6 accepts the array-level resources 

35 that can be delegated to a realm, C(subnets) and C(dns). 

36 - Policies (for example NFS export policies) live at the array level 

37 and cannot be shared with a realm through a resource access; the 

38 array rejects them with C(Invalid resource type). 

39 type: str 

40 required: true 

41 resource_name: 

42 description: 

43 - The name of the resource you want to target. 

44 type: str 

45 required: true 

46 state: 

47 description: 

48 - Define whether the resource access should exist or not. 

49 type: str 

50 default: present 

51 choices: [ absent, present ] 

52 scope_type: 

53 description: 

54 - The type of scopes. 

55 - When using Realms, this value will be 'realms' 

56 - As with I(resource_type), the set of valid scope types is defined 

57 by the array and is not validated by this module. 

58 type: str 

59 required: true 

60 scope_name: 

61 description: 

62 - The name of the scope, that should be using the resource. 

63 - You can not rename a resource access. 

64 - Resource access are immutable. You have to delete it and then create a new one. 

65 type: str 

66 required: true 

67extends_documentation_fragment: 

68- everpure.flashblade.everpure.fb 

69""" 

70 

71EXAMPLES = r""" 

72- name: Delegate subnet subnet_foo to realm realm_foo 

73 everpure.flashblade.purefb_resource_access: 

74 resource_type: subnets 

75 resource_name: subnet_foo 

76 scope_type: realms 

77 scope_name: realm_foo 

78 fb_url: 10.10.10.2 

79 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 

80 

81- name: Share the management DNS configuration with realm realm_foo 

82 everpure.flashblade.purefb_resource_access: 

83 resource_type: dns 

84 resource_name: management 

85 scope_type: realms 

86 scope_name: realm_foo 

87 fb_url: 10.10.10.2 

88 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 

89 

90- name: Revoke the subnet delegation (the subnet itself is not deleted) 

91 everpure.flashblade.purefb_resource_access: 

92 resource_type: subnets 

93 resource_name: subnet_foo 

94 scope_type: realms 

95 scope_name: realm_foo 

96 state: absent 

97 fb_url: 10.10.10.2 

98 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 

99""" 

100 

101RETURN = r""" 

102""" 

103 

104HAS_PYPURECLIENT = True 

105try: 

106 from pypureclient.flashblade import ResourceAccessPost 

107except ImportError: 

108 HAS_PYPURECLIENT = False 

109 

110from ansible.module_utils.basic import AnsibleModule 

111from ansible_collections.everpure.flashblade.plugins.module_utils.purefb import ( 

112 get_system, 

113 purefb_argument_spec, 

114) 

115from ansible_collections.everpure.flashblade.plugins.module_utils.version import ( 

116 LooseVersion, 

117) 

118from ansible_collections.everpure.flashblade.plugins.module_utils.common import ( 

119 get_error_message, 

120 get_rest_api_version, 

121) 

122 

123MINIMUM_API_VERSION = "2.19" 

124 

125 

126def get_resource_access(module, blade): 

127 """Get resource access""" 

128 filter_string = ( 

129 "resource.resource_type='" 

130 + module.params["resource_type"] 

131 + "' and scope.name='" 

132 + module.params["scope_name"] 

133 + "' and scope.resource_type='" 

134 + module.params["scope_type"] 

135 + "'" 

136 ) 

137 res = blade.get_resource_accesses(filter=filter_string) 

138 if res.status_code == 200 and res.total_item_count: 

139 items = list(res.items) 

140 for item in items: 

141 if item.resource.name == module.params["resource_name"]: 

142 return True, item.id 

143 return False, False 

144 

145 

146def create_resource_access(module, blade): 

147 """Create a new resource_access""" 

148 changed = True 

149 if not module.check_mode: 

150 resource = ResourceAccessPost( 

151 resource={ 

152 "name": module.params["resource_name"], 

153 "resource_type": module.params["resource_type"], 

154 }, 

155 scope={ 

156 "name": module.params["scope_name"], 

157 "resource_type": module.params["scope_type"], 

158 }, 

159 ) 

160 res = blade.post_resource_accesses_batch(items=[resource]) 

161 if res.status_code != 200: 

162 module.fail_json( 

163 msg="Create resource access between {0} and {1} failed. Error: {2}".format( 

164 module.params["scope_name"], 

165 module.params["resource_type"], 

166 get_error_message(res), 

167 ) 

168 ) 

169 module.exit_json(changed=changed) 

170 

171 

172def delete_resource_access(module, blade, access_id): 

173 """Delete resource access""" 

174 changed = True 

175 if not module.check_mode: 

176 res = blade.delete_resource_accesses(ids=[access_id]) 

177 if res.status_code != 200: 

178 module.fail_json( 

179 msg="Delete resource access between {0} and {1} failed. Error: {2}".format( 

180 module.params["scope_name"], 

181 module.params["resource_type"], 

182 get_error_message(res), 

183 ) 

184 ) 

185 module.exit_json(changed=changed) 

186 

187 

188def main(): 

189 argument_spec = purefb_argument_spec() 

190 argument_spec.update( 

191 dict( 

192 resource_type=dict(type="str", required=True), 

193 resource_name=dict(type="str", required=True), 

194 state=dict(type="str", default="present", choices=["absent", "present"]), 

195 scope_type=dict(type="str", required=True), 

196 scope_name=dict(type="str", required=True), 

197 ) 

198 ) 

199 

200 module = AnsibleModule(argument_spec, supports_check_mode=True) 

201 if not HAS_PYPURECLIENT: 

202 module.fail_json(msg="py-pure-client sdk is required for this module") 

203 

204 state = module.params["state"] 

205 blade = get_system(module) 

206 api_version = get_rest_api_version(blade) 

207 if LooseVersion(MINIMUM_API_VERSION) > LooseVersion(api_version): 

208 module.fail_json( 

209 msg="Resource Access is not supported. Purity//FB 4.6.1, or higher, is required." 

210 ) 

211 resource, access_id = get_resource_access(module, blade) 

212 

213 if not resource and state == "present": 

214 create_resource_access(module, blade) 

215 elif resource and state == "absent": 

216 delete_resource_access(module, blade, access_id) 

217 

218 module.exit_json(changed=False) 

219 

220 

221if __name__ == "__main__": 

222 main()