Coverage for manila/api/v2/share_manage.py: 94%

86 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2026-02-18 22:19 +0000

1# Copyright 2015 Mirantis inc. 

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 

15from webob import exc 

16 

17from manila.api import common 

18from manila.api.openstack import wsgi 

19from manila.api.views import shares as share_views 

20from manila import exception 

21from manila.i18n import _ 

22from manila import share 

23from manila.share import share_types 

24from manila.share import utils as share_utils 

25from manila import utils 

26 

27 

28class ShareManageMixin(object): 

29 

30 @wsgi.Controller.authorize('manage') 

31 def _manage(self, req, body, allow_dhss_true=False): 

32 context = req.environ['manila.context'] 

33 share_data = self._validate_manage_parameters(context, body) 

34 share_data = common.validate_public_share_policy(context, share_data) 

35 

36 # NOTE(vponomaryov): compatibility actions are required between API and 

37 # DB layers for 'name' and 'description' API params that are 

38 # represented in DB as 'display_name' and 'display_description' 

39 # appropriately. 

40 name = share_data.get('display_name', share_data.get('name')) 

41 description = share_data.get( 

42 'display_description', share_data.get('description')) 

43 

44 share = { 

45 'host': share_data['service_host'], 

46 'export_location_path': share_data['export_path'], 

47 'share_proto': share_data['protocol'].upper(), 

48 'share_type_id': share_data['share_type_id'], 

49 'display_name': name, 

50 'display_description': description, 

51 } 

52 

53 if share_data.get('is_public') is not None: 

54 share['is_public'] = share_data['is_public'] 

55 

56 driver_options = share_data.get('driver_options', {}) 

57 

58 if allow_dhss_true: 

59 share['share_server_id'] = share_data.get('share_server_id') 

60 

61 try: 

62 share_ref = self.share_api.manage(context, share, driver_options) 

63 except exception.PolicyNotAuthorized as e: 

64 raise exc.HTTPForbidden(explanation=e.msg) 

65 except (exception.InvalidShare, exception.InvalidShareServer) as e: 

66 raise exc.HTTPConflict(explanation=e.msg) 

67 except exception.InvalidInput as e: 

68 raise exc.HTTPBadRequest(explanation=e.msg) 

69 

70 return self._view_builder.detail(req, share_ref) 

71 

72 def _validate_manage_parameters(self, context, body): 

73 if not (body and self.is_valid_body(body, 'share')): 

74 msg = _("Share entity not found in request body") 

75 raise exc.HTTPUnprocessableEntity(explanation=msg) 

76 

77 required_parameters = ('export_path', 'service_host', 'protocol') 

78 

79 data = body['share'] 

80 

81 for parameter in required_parameters: 

82 if parameter not in data: 

83 msg = _("Required parameter %s not found") % parameter 

84 raise exc.HTTPUnprocessableEntity(explanation=msg) 

85 if not data.get(parameter): 85 ↛ 86line 85 didn't jump to line 86 because the condition on line 85 was never true

86 msg = _("Required parameter %s is empty") % parameter 

87 raise exc.HTTPUnprocessableEntity(explanation=msg) 

88 

89 if isinstance(data['export_path'], dict): 

90 # the path may be inside this dictionary 

91 try: 

92 data['export_path'] = data['export_path']['path'] 

93 except KeyError: 

94 msg = ("Export path must be a string, or a dictionary " 

95 "with a 'path' item") 

96 raise exc.HTTPUnprocessableEntity(explanation=msg) 

97 

98 if not share_utils.extract_host(data['service_host'], 'pool'): 

99 msg = _("service_host parameter should contain pool.") 

100 raise exc.HTTPBadRequest(explanation=msg) 

101 

102 try: 

103 utils.validate_service_host( 

104 context, share_utils.extract_host(data['service_host'])) 

105 except exception.ServiceNotFound as e: 

106 raise exc.HTTPNotFound(explanation=e.msg) 

107 except exception.PolicyNotAuthorized as e: 

108 raise exc.HTTPForbidden(explanation=e.msg) 

109 except exception.AdminRequired as e: 

110 raise exc.HTTPForbidden(explanation=e.msg) 

111 except exception.ServiceIsDown as e: 

112 raise exc.HTTPBadRequest(explanation=e.msg) 

113 

114 data['share_type_id'] = self._get_share_type_id( 

115 context, data.get('share_type')) 

116 

117 return data 

118 

119 @staticmethod 

120 def _get_share_type_id(context, share_type): 

121 try: 

122 stype = share_types.get_share_type_by_name_or_id(context, 

123 share_type) 

124 return stype['id'] 

125 except exception.ShareTypeNotFound as e: 

126 raise exc.HTTPNotFound(explanation=e.msg) 

127 

128 

129class ShareManageController(ShareManageMixin, wsgi.Controller): 

130 """Allows existing share to be 'managed' by Manila.""" 

131 

132 resource_name = "share" 

133 _view_builder_class = share_views.ViewBuilder 

134 

135 def __init__(self, *args, **kwargs): 

136 super(ShareManageController, self).__init__(*args, **kwargs) 

137 self.share_api = share.API() 

138 

139 @wsgi.Controller.api_version('1.0', '2.6') 

140 def create(self, req, body): 

141 """Legacy method for 'manage share' operation. 

142 

143 Should be removed when minimum API version becomes equal to or 

144 greater than v2.7 

145 """ 

146 body.get('share', {}).pop('is_public', None) 

147 return self._manage(req, body) 

148 

149 

150def create_resource(): 

151 return wsgi.Resource(ShareManageController())