-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #148 from SUSE-Enceladus/data-archive
Add metering archive
- Loading branch information
Showing
24 changed files
with
552 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
# | ||
# Copyright 2024 SUSE LLC | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
# | ||
|
||
"""Utility functions for handling a rolling dictionary archive.""" | ||
|
||
import functools | ||
import logging | ||
import json | ||
|
||
from csp_billing_adapter.config import Config | ||
from csp_billing_adapter.utils import retry_on_exception | ||
|
||
log = logging.getLogger('CSPBillingAdapter') | ||
|
||
|
||
def append_metering_records( | ||
archive: list, | ||
billing_record: dict, | ||
max_length: int, | ||
max_bytes: int | ||
) -> list: | ||
""" | ||
Append usage and metering records to the archive | ||
If archive is larger than max length drop the oldest | ||
record. If the archive is larger than the max bytes | ||
limit trim it until it satisfies the limit. | ||
:param archive: | ||
The list of meterings and usage records to append to. | ||
:param billing_record: | ||
The dictionary containing the most recent | ||
metering and usage records to be archived. | ||
:param max_length: | ||
The max size of the archive list. | ||
:param max_bytes: | ||
The max size in bytes of the archive. | ||
:return: | ||
The archive of meterings and usage records with the | ||
billing_record appended. If archive ends up greater | ||
than max lengh or max bytes the archive is trimmed | ||
as necessary to satisfy both max_length and | ||
max_bytes. | ||
""" | ||
archive.append(billing_record) | ||
|
||
if len(archive) > max_length: | ||
archive = archive[1:] | ||
|
||
if max_bytes > 1: | ||
# Treat 0 and 1 the same. Disable max bytes option. | ||
# This prevents infitite loop when value is 1 since | ||
# empty list is 2 bytes. | ||
while True: | ||
# Trim archive until it is smaller than max bytes | ||
archive_size = len(bytes(json.dumps(archive), 'utf-8')) | ||
|
||
if archive_size > max_bytes: | ||
archive = archive[1:] | ||
else: | ||
break | ||
|
||
return archive | ||
|
||
|
||
def archive_record( | ||
hook, | ||
config: Config, | ||
billing_record: dict | ||
) -> None: | ||
""" | ||
:param hook: | ||
The Pluggy plugin manager hook that will be | ||
used to call the meter_billing operation. | ||
:param config: | ||
The configuration specifying the metrics that | ||
need to be processed in the usage records list. | ||
:param billing_record: | ||
The dictionary containing the most recent | ||
metering and usage records to be archived. | ||
""" | ||
retention_period = config.archive_retention_period | ||
bytes_limit = config.archive_bytes_limit | ||
|
||
if retention_period < 1 or bytes_limit in (0, 1): | ||
# Archive feature has been disabled, do nothing | ||
return | ||
|
||
archive = retry_on_exception( | ||
functools.partial( | ||
hook.get_metering_archive, | ||
config=config, | ||
), | ||
logger=log, | ||
func_name="hook.get_metering_archive" | ||
) | ||
|
||
if archive is None: | ||
archive = [] | ||
|
||
archive = append_metering_records( | ||
archive, | ||
billing_record, | ||
retention_period, | ||
bytes_limit | ||
) | ||
|
||
retry_on_exception( | ||
functools.partial( | ||
hook.save_metering_archive, | ||
config=config, | ||
archive_data=archive | ||
), | ||
logger=log, | ||
func_name="hook.save_metering_archive" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
# | ||
# Copyright 2024 SUSE LLC | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
# | ||
|
||
""" | ||
Pluggy hook interface specifications for archival storage related functionality | ||
""" | ||
|
||
import pluggy | ||
|
||
from csp_billing_adapter.config import Config | ||
|
||
hookspec = pluggy.HookspecMarker('csp_billing_adapter') | ||
|
||
|
||
@hookspec(firstresult=True) | ||
def get_archive_location() -> str: | ||
""" | ||
Returns the location of the archive data storage | ||
""" | ||
|
||
|
||
@hookspec(firstresult=True) | ||
def get_metering_archive(config: Config) -> list: | ||
""" | ||
Retrieves the archive data from stateful storage | ||
:param config: The application configuration dictionary | ||
:return: Return a list of the archive data which contains a history | ||
of recent data usage and meterings. The length of this data | ||
is determined by application config. | ||
""" | ||
|
||
|
||
@hookspec(firstresult=True) | ||
def save_metering_archive(config: Config, archive_data: list) -> None: | ||
""" | ||
Saves the archive data to stateful storage | ||
:param config: The application configuration dictionary | ||
:param archive_data: A list of usage data and meterings | ||
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# | ||
# Copyright 2024 SUSE LLC | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
# | ||
|
||
"""In-memory archive plugin implementation.""" | ||
|
||
import logging | ||
|
||
import csp_billing_adapter | ||
|
||
from csp_billing_adapter.config import Config | ||
|
||
memory_archive = [] | ||
|
||
log = logging.getLogger('CSPBillingAdapter') | ||
|
||
|
||
@csp_billing_adapter.hookimpl(trylast=True) | ||
def get_archive_location(): | ||
"""Retrieve archive location.""" | ||
return '/tmp/fake_archive.json' | ||
|
||
|
||
@csp_billing_adapter.hookimpl(trylast=True) | ||
def get_metering_archive(config: Config): | ||
return memory_archive.copy() | ||
|
||
|
||
@csp_billing_adapter.hookimpl(trylast=True) | ||
def save_metering_archive(config: Config, archive_data: list): | ||
global memory_archive | ||
|
||
memory_archive = archive_data |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.