Coverage for watcher/common/placement_helper.py: 96%

77 statements  

« prev     ^ index     » next       coverage.py v7.8.2, created at 2025-06-17 12:22 +0000

1# Licensed under the Apache License, Version 2.0 (the "License"); 

2# you may not use this file except in compliance with the License. 

3# You may obtain a copy of the License at 

4# 

5# http://www.apache.org/licenses/LICENSE-2.0 

6# 

7# Unless required by applicable law or agreed to in writing, software 

8# distributed under the License is distributed on an "AS IS" BASIS, 

9# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 

10# implied. 

11# See the License for the specific language governing permissions and 

12# limitations under the License. 

13 

14from http import HTTPStatus 

15from oslo_config import cfg 

16from oslo_log import log as logging 

17 

18from watcher.common import clients 

19 

20CONF = cfg.CONF 

21LOG = logging.getLogger(__name__) 

22 

23 

24class PlacementHelper(object): 

25 

26 def __init__(self, osc=None): 

27 """:param osc: an OpenStackClients instance""" 

28 self.osc = osc if osc else clients.OpenStackClients() 

29 self._placement = self.osc.placement() 

30 

31 def get(self, url): 

32 return self._placement.get(url, raise_exc=False) 

33 

34 @staticmethod 

35 def get_error_msg(resp): 

36 json_resp = resp.json() 

37 # https://docs.openstack.org/api-ref/placement/#errors 

38 if 'errors' in json_resp: 38 ↛ 41line 38 didn't jump to line 41 because the condition on line 38 was always true

39 error_msg = json_resp['errors'][0].get('detail') 

40 else: 

41 error_msg = resp.text 

42 

43 return error_msg 

44 

45 def get_resource_providers(self, rp_name=None): 

46 """Calls the placement API for a resource provider record. 

47 

48 :param rp_name: Name of the resource provider, if None, 

49 list all resource providers. 

50 :return: A list of resource providers information 

51 or None if the resource provider doesn't exist. 

52 """ 

53 url = '/resource_providers' 

54 if rp_name: 

55 url += '?name=%s' % rp_name 

56 resp = self.get(url) 

57 if resp.status_code == HTTPStatus.OK: 

58 json_resp = resp.json() 

59 return json_resp['resource_providers'] 

60 

61 if rp_name: 61 ↛ 64line 61 didn't jump to line 64 because the condition on line 61 was always true

62 msg = "Failed to get resource provider %(name)s. " 

63 else: 

64 msg = "Failed to get all resource providers. " 

65 msg += "Got %(status_code)d: %(err_text)s." 

66 args = { 

67 'name': rp_name, 

68 'status_code': resp.status_code, 

69 'err_text': self.get_error_msg(resp), 

70 } 

71 LOG.error(msg, args) 

72 

73 def get_inventories(self, rp_uuid): 

74 """Calls the placement API to get resource inventory information. 

75 

76 :param rp_uuid: UUID of the resource provider to get. 

77 :return: A dictionary of inventories keyed by resource classes. 

78 """ 

79 url = '/resource_providers/%s/inventories' % rp_uuid 

80 resp = self.get(url) 

81 if resp.status_code == HTTPStatus.OK: 

82 json = resp.json() 

83 return json['inventories'] 

84 msg = ("Failed to get resource provider %(rp_uuid)s inventories. " 

85 "Got %(status_code)d: %(err_text)s.") 

86 args = { 

87 'rp_uuid': rp_uuid, 

88 'status_code': resp.status_code, 

89 'err_text': self.get_error_msg(resp), 

90 } 

91 LOG.error(msg, args) 

92 

93 def get_provider_traits(self, rp_uuid): 

94 """Queries the placement API for a resource provider's traits. 

95 

96 :param rp_uuid: UUID of the resource provider to grab traits for. 

97 :return: A list of traits. 

98 """ 

99 resp = self.get("/resource_providers/%s/traits" % rp_uuid) 

100 

101 if resp.status_code == HTTPStatus.OK: 

102 json = resp.json() 

103 return json['traits'] 

104 msg = ("Failed to get resource provider %(rp_uuid)s traits. " 

105 "Got %(status_code)d: %(err_text)s.") 

106 args = { 

107 'rp_uuid': rp_uuid, 

108 'status_code': resp.status_code, 

109 'err_text': self.get_error_msg(resp), 

110 } 

111 LOG.error(msg, args) 

112 

113 def get_allocations_for_consumer(self, consumer_uuid): 

114 """Retrieves the allocations for a specific consumer. 

115 

116 :param consumer_uuid: the UUID of the consumer resource. 

117 :return: A dictionary of allocation records keyed by resource 

118 provider uuid. 

119 """ 

120 url = '/allocations/%s' % consumer_uuid 

121 resp = self.get(url) 

122 if resp.status_code == HTTPStatus.OK: 

123 json = resp.json() 

124 return json['allocations'] 

125 msg = ("Failed to get allocations for consumer %(c_uuid)s. " 

126 "Got %(status_code)d: %(err_text)s.") 

127 args = { 

128 'c_uuid': consumer_uuid, 

129 'status_code': resp.status_code, 

130 'err_text': self.get_error_msg(resp), 

131 } 

132 LOG.error(msg, args) 

133 

134 def get_usages_for_resource_provider(self, rp_uuid): 

135 """Retrieves the usages for a specific provider. 

136 

137 :param rp_uuid: The UUID of the provider. 

138 :return: A dictionary that describes how much each class of 

139 resource is being consumed on this resource provider. 

140 """ 

141 url = '/resource_providers/%s/usages' % rp_uuid 

142 resp = self.get(url) 

143 if resp.status_code == HTTPStatus.OK: 

144 json = resp.json() 

145 return json['usages'] 

146 msg = ("Failed to get resource provider %(rp_uuid)s usages. " 

147 "Got %(status_code)d: %(err_text)s.") 

148 args = { 

149 'rp_uuid': rp_uuid, 

150 'status_code': resp.status_code, 

151 'err_text': self.get_error_msg(resp), 

152 } 

153 LOG.error(msg, args) 

154 

155 def get_candidate_providers(self, resources): 

156 """Returns a dictionary of resource provider summaries. 

157 

158 :param resources: A comma-separated list of strings indicating 

159 an amount of resource of a specified class that 

160 providers in each allocation request must collectively 

161 have the capacity and availability to serve: 

162 resources=VCPU:4,DISK_GB:64,MEMORY_MB:2048 

163 :returns: A dict, keyed by resource provider UUID, which can 

164 provide the required resources. 

165 """ 

166 url = "/allocation_candidates?%s" % resources 

167 resp = self.get(url) 

168 if resp.status_code == HTTPStatus.OK: 

169 data = resp.json() 

170 return data['provider_summaries'] 

171 

172 args = { 

173 'resource_request': resources, 

174 'status_code': resp.status_code, 

175 'err_text': self.get_error_msg(resp), 

176 } 

177 msg = ("Failed to get allocation candidates from placement " 

178 "API for resources: %(resource_request)s\n" 

179 "Got %(status_code)d: %(err_text)s.") 

180 LOG.error(msg, args)