Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @stackhpc/openstack
12 changes: 12 additions & 0 deletions .github/workflows/tag-and-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
name: Tag & Release
'on':
push:
branches:
- stackhpc/master
permissions:
actions: read
contents: write
jobs:
tag-and-release:
uses: stackhpc/.github/.github/workflows/tag-and-release.yml@main
7 changes: 7 additions & 0 deletions .github/workflows/tox.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
name: Tox Continuous Integration
'on':
pull_request:
jobs:
tox:
uses: stackhpc/.github/.github/workflows/tox.yml@main
4 changes: 4 additions & 0 deletions blazar/api/v1/leases/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# limitations under the License.

from oslo_log import log as logging
from oslo_utils import strutils

from blazar import context
from blazar.manager.leases import rpcapi as manager_rpcapi
Expand Down Expand Up @@ -54,6 +55,9 @@ def create_lease(self, data):
# two lines
ctx = context.current()
data['user_id'] = ctx.user_id
# TODO(johngarbutt) we need to agree a senible API!
data["dry_run"] = strutils.bool_from_string(
data.get('dry_run', "false"))
return self.manager_rpcapi.create_lease(data)

@policy.authorize('leases', 'get')
Expand Down
25 changes: 17 additions & 8 deletions blazar/enforcement/enforcement.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ def format_context(self, context, lease_values):
project_id=lease_values['project_id'],
auth_url=auth_url, region_name=region_name)

def format_lease(self, lease_values, reservations, allocations):
def format_lease(self, lease_values, reservations, allocations,
resource_requests=None):
lease = lease_values.copy()
lease['reservations'] = []

Expand All @@ -69,31 +70,39 @@ def format_lease(self, lease_values, reservations, allocations):
res['allocations'] = allocations[resource_type]
lease['reservations'].append(res)

if resource_requests:
lease['resource_requests'] = resource_requests

return lease

def check_create(self, context, lease_values, reservations, allocations):
def check_create(self, context, lease_values, reservations, allocations,
resource_requests=None):
context = self.format_context(context, lease_values)
lease = self.format_lease(lease_values, reservations, allocations)
lease = self.format_lease(
lease_values, reservations, allocations, resource_requests)

for _filter in self.enabled_filters:
_filter.check_create(context, lease)

def check_update(self, context, current_lease, new_lease,
current_allocations, new_allocations,
current_reservations, new_reservations):
current_reservations, new_reservations,
current_resource_requests=None,
new_resource_requests=None):
context = self.format_context(context, current_lease)
current_lease = self.format_lease(current_lease, current_reservations,
current_allocations)
current_allocations,
current_resource_requests)
new_lease = self.format_lease(new_lease, new_reservations,
new_allocations)
new_allocations, new_resource_requests)

for _filter in self.enabled_filters:
_filter.check_update(context, current_lease, new_lease)

def on_end(self, context, lease, allocations):
def on_end(self, context, lease, allocations, resource_requests=None):
context = self.format_context(context, lease)
lease_values = self.format_lease(lease, lease['reservations'],
allocations)
allocations, resource_requests)

for _filter in self.enabled_filters:
_filter.on_end(context, lease_values)
16 changes: 11 additions & 5 deletions blazar/enforcement/filters/external_service_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ class ExternalServiceFilter(base_filter.BaseFilter):
cfg.StrOpt(
'external_service_token',
default="",
help='Token used for authentication with the external service.')
help='Token used for authentication with the external service.'),
cfg.StrOpt(
'external_service_bearer_token',
default="",
help='Bearer token for authentication with the external service.')
]

def __init__(self, conf=None):
Expand Down Expand Up @@ -96,7 +100,8 @@ def __init__(self, conf=None):
raise ExternalServiceMisconfigured(
message=_("ExternalService has no endpoints set."))

self.token = conf.enforcement.external_service_token
self.x_auth_token = conf.enforcement.external_service_token
self.bearer_token = conf.enforcement.external_service_bearer_token

@staticmethod
def _validate_url(url):
Expand Down Expand Up @@ -124,9 +129,10 @@ def _construct_url(self, method, replacement_url):
def _get_headers(self):
headers = {'Content-Type': 'application/json'}

if self.token:
headers['X-Auth-Token'] = self.token

if self.x_auth_token:
headers['X-Auth-Token'] = self.x_auth_token
elif self.bearer_token:
headers['Authorization'] = "Bearer " + self.bearer_token
return headers

def _post(self, url, body):
Expand Down
163 changes: 120 additions & 43 deletions blazar/manager/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,12 +378,6 @@ def create_lease(self, lease_values):

allocations = self._allocation_candidates(
lease_values, reservations)
try:
self.enforcement.check_create(
context.current(), lease_values, reservations, allocations)
except common_ex.NotAuthorized as e:
LOG.error("Enforcement checks failed. %s", str(e))
raise common_ex.NotAuthorized(e)

events.append({'event_type': 'start_lease',
'time': start_date,
Expand Down Expand Up @@ -415,6 +409,20 @@ def create_lease(self, lease_values):
self._update_before_end_event_date(event, before_end_date,
lease_values)

# Exit early if this was just a dry run
if lease_values.get("dry_run", False):
resource_requests = self._get_enforcement_resources(
lease_values, reservations)
# Make sure enforcement is happy with the request
self.enforcement.check_create(
context.current(), lease_values, reservations,
allocations, resource_requests)
# Tell the exteral system we change our mind
self._call_enforcement_on_end(
context.current(), lease_values, reservations,
allocations)
return None

try:
if trust_id:
lease_values.update({'trust_id': trust_id})
Expand All @@ -427,39 +435,68 @@ def create_lease(self, lease_values):
except db_ex.BlazarDBException:
with save_and_reraise_exception():
LOG.exception('Cannot create a lease')
else:
try:
for reservation in reservations:
reservation['lease_id'] = lease['id']
reservation['start_date'] = lease['start_date']
reservation['end_date'] = lease['end_date']
self._create_reservation(reservation)
except Exception:
with save_and_reraise_exception():
LOG.exception("Failed to create reservation for a "
"lease. Rollback the lease and "
"associated reservations")
db_api.lease_destroy(lease_id)

try:
for event in events:
event['lease_id'] = lease['id']
db_api.event_create(event)
except (exceptions.UnsupportedResourceType,
common_ex.BlazarException):
with save_and_reraise_exception():
LOG.exception("Failed to create event for a lease. "
"Rollback the lease and associated "
"reservations")
db_api.lease_destroy(lease_id)

else:
db_api.lease_update(
lease_id,
{'status': status.lease.PENDING})
lease = db_api.lease_get(lease_id)
self._send_notification(lease, ctx, events=['create'])
return lease
# check enforcement only after the lease_id
# has been created
try:
lease_values['id'] = lease['id']
resource_requests = self._get_enforcement_resources(
lease_values, reservations)
self.enforcement.check_create(
context.current(), lease_values, reservations,
allocations, resource_requests)
except common_ex.NotAuthorized as e:
db_api.lease_destroy(lease_id)
LOG.error("Enforcement checks failed. %s", str(e))
raise common_ex.NotAuthorized(e)

try:
for reservation in reservations:
reservation['lease_id'] = lease['id']
reservation['start_date'] = lease['start_date']
reservation['end_date'] = lease['end_date']
self._create_reservation(reservation)
except Exception:
with save_and_reraise_exception():
LOG.exception("Failed to create reservation for a "
"lease. Rollback the lease and "
"associated reservations")
db_api.lease_destroy(lease_id)
self._call_enforcement_on_end(
context.current(), lease_values, reservations,
allocations)

try:
for event in events:
event['lease_id'] = lease['id']
db_api.event_create(event)
except (exceptions.UnsupportedResourceType,
common_ex.BlazarException):
with save_and_reraise_exception():
LOG.exception("Failed to create event for a lease. "
"Rollback the lease and associated "
"reservations")
db_api.lease_destroy(lease_id)
self._call_enforcement_on_end(
context.current(), lease_values, reservations,
allocations)

db_api.lease_update(
lease_id,
{'status': status.lease.PENDING})
lease = db_api.lease_get(lease_id)
self._send_notification(lease, ctx, events=['create'])
return lease

def _call_enforcement_on_end(self, ctx, lease, reservations, allocations):
# allow external enforcement know create reservation failed
try:
resource_requests = self._get_enforcement_resources(
lease, reservations)
self.enforcement.on_end(ctx, lease, allocations,
resource_requests)
except Exception as e:
LOG.error(e)

def _add_resource_type(self, reservations, existing_reservations):
rsvns_by_id = {}
Expand Down Expand Up @@ -573,10 +610,17 @@ def update_lease(self, lease_id, values):
new_allocs = existing_allocs

try:
self.enforcement.check_update(context.current(), lease, values,
existing_allocs, new_allocs,
current_resource_requests = self._get_enforcement_resources(
values, existing_reservations)
new_resource_requests = self._get_enforcement_resources(
values, new_reservations)
self.enforcement.check_update(context.current(), lease,
values, existing_allocs,
new_allocs,
existing_reservations,
new_reservations)
new_reservations,
current_resource_requests,
new_resource_requests)
except common_ex.NotAuthorized as e:
LOG.error("Enforcement checks failed. %s", str(e))
raise e
Expand Down Expand Up @@ -693,7 +737,10 @@ def delete_lease(self, lease_id):
# lease is no longer in play.
allocations = self._existing_allocations(reservations)
try:
self.enforcement.on_end(ctx, lease, allocations)
resource_requests = self._get_enforcement_resources(
lease, reservations)
self.enforcement.on_end(ctx, lease, allocations,
resource_requests)
except Exception as e:
LOG.error(e)

Expand Down Expand Up @@ -727,7 +774,10 @@ def end_lease(self, lease_id, event_id):
with trusts.create_ctx_from_trust(lease['trust_id']) as ctx:
allocations = self._existing_allocations(lease['reservations'])
try:
self.enforcement.on_end(ctx, lease, allocations)
resource_requests = self._get_enforcement_resources(
lease, lease['reservations'])
self.enforcement.on_end(ctx, lease, allocations,
resource_requests)
except Exception as e:
LOG.error(e)

Expand Down Expand Up @@ -818,6 +868,33 @@ def _allocation_candidates(self, lease, reservations):

return allocations

def _get_enforcement_resources(self, lease, reservations):
"""Returns dict by resource type of reservation candidates."""
resources = defaultdict(int)

for reservation in reservations:
res = reservation.copy()
resource_type = reservation['resource_type']
res['start_date'] = lease['start_date']
res['end_date'] = lease['end_date']

if resource_type not in self.plugins:
raise exceptions.UnsupportedResourceType(
resource_type=resource_type)

plugin = self.plugins.get(resource_type)

if not plugin:
raise common_ex.BlazarException(
'Invalid plugin names are specified: %s' % resource_type)

reservation_resources = plugin.get_enforcement_resources(res)

for resource, amount in reservation_resources.items():
resources[resource] += amount

return resources

def _existing_allocations(self, reservations):
allocations = {}

Expand Down
5 changes: 5 additions & 0 deletions blazar/plugins/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ def allocation_candidates(self, lease_values):
"""Get candidates for reservation allocation."""
pass

def get_enforcement_resources(self, reservation_values):
"""Get enforcement details for the lease."""
# default to the existing behavior of sending lease values
return {}

@abc.abstractmethod
def update_reservation(self, reservation_id, values):
"""Update reservation."""
Expand Down
Loading
Loading