Skip to content

Commit 3a26234

Browse files
Add API to create scopes
This commit adds an API enabling the POST operation to create scopes in an ad hoc fashion. This is useful for operators to register scopes before they are created as resources in the collected backend so their processing can be discarded right away, for example for trial projects/accounts. Otherwise, we need to wait for them to create resources, then for example Ceilometer has to monitor these resources, persist measures in Gnocchi, then CloudKitty has to discover the scopes and finally we can disable their processing. Change-Id: I3e947d36c9d5d5da07115d35dde578ae300cbe5c
1 parent 65a4768 commit 3a26234

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
@@ -101,6 +101,10 @@
101101
# PATCH /v2/scope
102102
#"scope:patch_state": "role:admin"
103103

104+
# Enables operators to create a storage scope
105+
# POST /v2/scope
106+
#"scope:post_state": "role:admin"
107+
104108
# Get a rating summary
105109
# GET /v2/summary
106110
#"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)