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
« 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.
14from http import HTTPStatus
15from oslo_config import cfg
16from oslo_log import log as logging
18from watcher.common import clients
20CONF = cfg.CONF
21LOG = logging.getLogger(__name__)
24class PlacementHelper(object):
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()
31 def get(self, url):
32 return self._placement.get(url, raise_exc=False)
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
43 return error_msg
45 def get_resource_providers(self, rp_name=None):
46 """Calls the placement API for a resource provider record.
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']
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)
73 def get_inventories(self, rp_uuid):
74 """Calls the placement API to get resource inventory information.
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)
93 def get_provider_traits(self, rp_uuid):
94 """Queries the placement API for a resource provider's traits.
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)
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)
113 def get_allocations_for_consumer(self, consumer_uuid):
114 """Retrieves the allocations for a specific consumer.
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)
134 def get_usages_for_resource_provider(self, rp_uuid):
135 """Retrieves the usages for a specific provider.
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)
155 def get_candidate_providers(self, resources):
156 """Returns a dictionary of resource provider summaries.
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']
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)