Skip to content

Commit f9d2ff3

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Add API to create scopes"
2 parents 788970e + 3a26234 commit f9d2ff3

7 files changed

Lines changed: 197 additions & 11 deletions

File tree

cloudkitty/api/v2/scope/state.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,10 @@ def put(self,
183183
voluptuous.Required('scope_key'): vutils.get_string_type(),
184184
voluptuous.Required('fetcher'): vutils.get_string_type(),
185185
voluptuous.Required('collector'): vutils.get_string_type(),
186+
# This "state" property should be removed in the next release.
186187
voluptuous.Required('state'): vutils.get_string_type(),
188+
voluptuous.Optional('last_processed_timestamp'):
189+
voluptuous.Coerce(tzutils.dt_from_iso),
187190
voluptuous.Required('active'): bool,
188191
voluptuous.Required('scope_activation_toggle_date'):
189192
vutils.get_string_type()
@@ -226,6 +229,79 @@ def patch(self, scope_id, scope_key=None, fetcher=None,
226229
'fetcher': update_storage_scope.fetcher,
227230
'collector': update_storage_scope.collector,
228231
'state': update_storage_scope.state.isoformat(),
232+
'last_processed_timestamp':
233+
update_storage_scope.last_processed_timestamp.isoformat(),
234+
'active': update_storage_scope.active,
235+
'scope_activation_toggle_date':
236+
update_storage_scope.scope_activation_toggle_date.isoformat()
237+
}
238+
239+
@api_utils.add_input_schema('body', {
240+
voluptuous.Required('scope_id'):
241+
api_utils.SingleQueryParam(str),
242+
voluptuous.Optional('scope_key'):
243+
api_utils.SingleQueryParam(str),
244+
voluptuous.Optional('fetcher'):
245+
api_utils.SingleQueryParam(str),
246+
voluptuous.Optional('collector'):
247+
api_utils.SingleQueryParam(str),
248+
voluptuous.Optional('active'):
249+
api_utils.SingleQueryParam(bool),
250+
})
251+
@api_utils.add_output_schema({
252+
voluptuous.Required('scope_id'): vutils.get_string_type(),
253+
voluptuous.Required('scope_key'): vutils.get_string_type(),
254+
voluptuous.Required('fetcher'): vutils.get_string_type(),
255+
voluptuous.Required('collector'): vutils.get_string_type(),
256+
# This "state" property should be removed in the next release.
257+
voluptuous.Required('state'): vutils.get_string_type(),
258+
voluptuous.Optional('last_processed_timestamp'):
259+
voluptuous.Coerce(tzutils.dt_from_iso),
260+
voluptuous.Required('active'): bool,
261+
voluptuous.Required('scope_activation_toggle_date'):
262+
vutils.get_string_type()
263+
})
264+
def post(self, scope_id, scope_key=None, fetcher=None, collector=None,
265+
active=None):
266+
267+
policy.authorize(
268+
flask.request.context,
269+
'scope:post_state',
270+
{'tenant_id': scope_id or flask.request.context.project_id}
271+
)
272+
results = self._storage_state.get_all(identifier=scope_id)
273+
274+
if len(results) >= 1:
275+
LOG.debug("There is already a scope with ID [%s], "
276+
"scopes found: [%s].", scope_id, results)
277+
raise http_exceptions.NotFound("Cannot create a scope with an "
278+
"already existing scope_id: %s."
279+
% scope_id)
280+
281+
LOG.debug("Creating storage scope with data: [scope_id=%s, "
282+
"scope_key=%s, fetcher=%s, collector=%s, active=%s].",
283+
scope_id, scope_key, fetcher, collector, active)
284+
285+
self._storage_state.create_scope(scope_id, None, fetcher=fetcher,
286+
collector=collector,
287+
scope_key=scope_key, active=active)
288+
289+
storage_scopes = self._storage_state.get_all(
290+
identifier=scope_id)
291+
292+
update_storage_scope = storage_scopes[0]
293+
last_processed_timestamp = None
294+
if update_storage_scope.last_processed_timestamp.isoformat():
295+
last_processed_timestamp =\
296+
update_storage_scope.last_processed_timestamp.isoformat()
297+
298+
return {
299+
'scope_id': update_storage_scope.identifier,
300+
'scope_key': update_storage_scope.scope_key,
301+
'fetcher': update_storage_scope.fetcher,
302+
'collector': update_storage_scope.collector,
303+
'state': last_processed_timestamp,
304+
'last_processed_timestamp': last_processed_timestamp,
229305
'active': update_storage_scope.active,
230306
'scope_activation_toggle_date':
231307
update_storage_scope.scope_activation_toggle_date.isoformat()

cloudkitty/common/policies/v2/scope.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,12 @@
3636
description='Enables operators to patch a storage scope',
3737
operations=[{'path': '/v2/scope',
3838
'method': 'PATCH'}]),
39-
39+
policy.DocumentedRuleDefault(
40+
name='scope:post_state',
41+
check_str=base.ROLE_ADMIN,
42+
description='Enables operators to create a storage scope',
43+
operations=[{'path': '/v2/scope',
44+
'method': 'POST'}]),
4045
]
4146

4247

cloudkitty/storage_state/__init__.py

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,8 @@ def set_last_processed_timestamp(
168168
collector=None, scope_key=None):
169169
"""Set the last processed timestamp of a scope.
170170
171+
If the scope does not exist yet in the database, it will create it.
172+
171173
:param identifier: Identifier of the scope
172174
:type identifier: str
173175
:param last_processed_timestamp: last processed timestamp of the scope
@@ -191,18 +193,52 @@ def set_last_processed_timestamp(
191193
r.last_processed_timestamp = last_processed_timestamp
192194
session.commit()
193195
else:
194-
state_object = self.model(
195-
identifier=identifier,
196-
last_processed_timestamp=last_processed_timestamp,
197-
fetcher=fetcher,
198-
collector=collector,
199-
scope_key=scope_key,
200-
)
201-
session.add(state_object)
202-
session.commit()
203-
196+
self.create_scope(identifier, last_processed_timestamp,
197+
fetcher=fetcher, collector=collector,
198+
scope_key=scope_key)
204199
session.close()
205200

201+
def create_scope(self, identifier, last_processed_timestamp, fetcher=None,
202+
collector=None, scope_key=None, active=True,
203+
session=None):
204+
"""Creates a scope in the database.
205+
206+
:param identifier: Identifier of the scope
207+
:type identifier: str
208+
:param last_processed_timestamp: last processed timestamp of the scope
209+
:type last_processed_timestamp: datetime.datetime
210+
:param fetcher: Fetcher associated to the scope
211+
:type fetcher: str
212+
:param collector: Collector associated to the scope
213+
:type collector: str
214+
:param scope_key: scope_key associated to the scope
215+
:type scope_key: str
216+
:param active: indicates if the scope is active
217+
:type active: bool
218+
:param session: the current database session to be reused
219+
:type session: object
220+
"""
221+
222+
is_session_reused = True
223+
if not session:
224+
session = db.get_session()
225+
session.begin()
226+
is_session_reused = False
227+
228+
state_object = self.model(
229+
identifier=identifier,
230+
last_processed_timestamp=last_processed_timestamp,
231+
fetcher=fetcher,
232+
collector=collector,
233+
scope_key=scope_key,
234+
active=active
235+
)
236+
session.add(state_object)
237+
session.commit()
238+
239+
if not is_session_reused:
240+
session.close()
241+
206242
def get_state(self, identifier,
207243
fetcher=None, collector=None, scope_key=None):
208244
LOG.warning("The method 'get_state' is deprecated."

doc/source/_static/cloudkitty.policy.yaml.sample

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@
109109
# PATCH /v2/scope
110110
#"scope:patch_state": "role:admin"
111111

112+
# Enables operators to create a storage scope
113+
# POST /v2/scope
114+
#"scope:post_state": "role:admin"
115+
112116
# Get a rating summary
113117
# GET /v2/summary
114118
#"summary:get_summary": "rule:admin_or_owner"

doc/source/api-reference/v2/scope/scope.inc

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,56 @@ Response
127127
- active: active_key_resp
128128

129129

130+
Response Example
131+
----------------
132+
133+
.. literalinclude:: ./api_samples/scope/scope_get.json
134+
:language: javascript
135+
136+
Create a scope
137+
================================
138+
139+
Create a scope.
140+
141+
.. rest_method:: POST /v2/scope
142+
143+
.. rest_parameters:: scope/scope_parameters.yml
144+
145+
- collector: collector
146+
- fetcher: fetcher
147+
- scope_id: scope_id
148+
- scope_key: scope_key
149+
- active: active_body
150+
151+
Status codes
152+
------------
153+
154+
.. rest_status_code:: success http_status.yml
155+
156+
- 200
157+
158+
.. rest_status_code:: error http_status.yml
159+
160+
- 400
161+
- 403
162+
- 404
163+
- 405
164+
165+
Response
166+
--------
167+
168+
.. rest_parameters:: scope/scope_parameters.yml
169+
170+
- scope_id: scope_id_resp
171+
- scope_key: scope_key_resp
172+
- fetcher: fetcher_resp
173+
- collector: collector_resp
174+
- state: state
175+
- last_processed_timestamp: last_processed_timestamp
176+
- active: active_key_resp
177+
- scope_activation_toggle_date: scope_activation_toggle_date
178+
179+
130180
Response Example
131181
----------------
132182

doc/source/api-reference/v2/scope/scope_parameters.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,14 @@ last_processed_timestamp:
8888
type: iso8601 timestamp
8989
required: true
9090

91+
scope_activation_toggle_date:
92+
in: body
93+
description: |
94+
It represents the last time the scope was activated/deactivated via the
95+
PATCH API.
96+
type: iso8601 timestamp
97+
required: true
98+
9199
scope_id_body:
92100
<<: *scope_id
93101
in: body
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
features:
3+
- |
4+
Introduce an API to create scopes with a POST request. This is useful for
5+
operators to register scopes before they are created as resources in the
6+
collected backend and disable their processing without waiting for the
7+
scopes to be discovered by CloudKitty.

0 commit comments

Comments
 (0)