Skip to content

Add third party plugins #54

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 37 commits into
base: chameleoncloud/xena
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
276ab48
Add third party plugin support
Mark-Powers Nov 2, 2021
34f7da3
Merge branch 'chameleoncloud/train' of https://github.com/chameleoncl…
Mark-Powers Nov 15, 2021
7db8a66
Continue adding third party plugin support
Mark-Powers Nov 18, 2021
54ceb91
Add resources endpoint
Mark-Powers Nov 18, 2021
dfa7da4
Fix host plugin typos
Mark-Powers Nov 23, 2021
cc6016f
Fix third party host plugin
Mark-Powers Nov 24, 2021
aeb9e59
Fix formatting
Mark-Powers Nov 24, 2021
853574d
Update third party host plugin
Mark-Powers Nov 30, 2021
e754baa
Refactor host plugin
Mark-Powers Dec 1, 2021
35e2a2e
Clean up DummyPlugin
Mark-Powers Dec 2, 2021
97f91eb
Revert "[backport] Prevent failed lease update from setting lease sta…
Mark-Powers Jan 12, 2022
b98e8ef
Restore lease status after non fatal update exception
Mark-Powers Jan 13, 2022
29d3f4b
Merge branch 'chameleoncloud/train' into third_party_plugins
Mark-Powers Jan 18, 2022
3d1c210
Update third party plugins with docs, tests
Mark-Powers Jan 21, 2022
2e4b883
Add third party plugin support
Mark-Powers Nov 2, 2021
1c208f1
Continue adding third party plugin support
Mark-Powers Nov 18, 2021
cc43b3e
Add resources endpoint
Mark-Powers Nov 18, 2021
5d740ab
Fix host plugin typos
Mark-Powers Nov 23, 2021
b671d4a
Fix third party host plugin
Mark-Powers Nov 24, 2021
b11f78d
Fix formatting
Mark-Powers Nov 24, 2021
ef50627
Update third party host plugin
Mark-Powers Nov 30, 2021
844b0c6
Refactor host plugin
Mark-Powers Dec 1, 2021
cc52958
Clean up DummyPlugin
Mark-Powers Dec 2, 2021
19b897b
Update third party plugins with docs, tests
Mark-Powers Jan 21, 2022
f21a5ee
Fix migrations and resource queries
Mark-Powers Jan 25, 2022
972b2db
Fix broken checkouts
Mark-Powers Jan 25, 2022
1138230
Add project_id as stitchport tag
Mark-Powers Jan 27, 2022
d3480ab
Fix pep8
Mark-Powers Jan 27, 2022
bb34404
Update stitchport plugin to use binding:profile
Mark-Powers May 4, 2022
fd65787
Merge branch 'chameleoncloud/xena' into third_party_plugins
Mark-Powers Jun 1, 2023
9bdf28b
Merge branch 'chameleoncloud/xena' into third_party_plugins
Mark-Powers Jun 1, 2023
8e6acf7
Fix git issues
Mark-Powers Jun 6, 2023
9e5b632
Merge branch 'third_party_plugins' of https://github.com/chameleonclo…
Mark-Powers Jun 6, 2023
ea556b5
Fix git conflicts
Mark-Powers Jun 7, 2023
7503f18
Get third party plugins to work
Mark-Powers Jun 12, 2023
83d6bdd
Finish TODOs
Mark-Powers Jun 12, 2023
216b203
Update code based on review
Mark-Powers Jul 14, 2023
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
Prev Previous commit
Next Next commit
Update third party host plugin
  • Loading branch information
Mark-Powers committed Nov 30, 2021
commit 853574d58e16f8128d56806e17fbbaf9fb528dda
2 changes: 2 additions & 0 deletions blazar/api/v1/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from stevedore import enabled
from werkzeug import exceptions as werkzeug_exceptions

from blazar import policy
from blazar.api.v1 import api_version_request
from blazar.api.v1 import request_id
from blazar.api.v1 import request_log
Expand Down Expand Up @@ -101,6 +102,7 @@ def make_app():
app.register_blueprint(bp, url_prefix=bp.url_prefix)
resource_plugins.append(
{"name": plugin.resource_type(), "prefix": bp.url_prefix})
policy.register_plugin_opts(plugin.get_policy())

def resources_list():
return api_utils.render(resource_plugins)
Expand Down
2 changes: 1 addition & 1 deletion blazar/db/sqlalchemy/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2027,7 +2027,7 @@ def resource_properties_list(resource_type):
models.ExtraCapability.private,
resource_model.capability_value).join(resource_model).distinct()
# Add extra filter if using 3rd party resource type
ifresource_type not in EXTRA_CAPABILITY_MODELS:
if resource_type not in EXTRA_CAPABILITY_MODELS:
query = query.join(models.Resource).filter_by(
resource_type=resource_type)

Expand Down
7 changes: 4 additions & 3 deletions blazar/manager/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -499,10 +499,8 @@ def update_lease(self, lease_id, values):
'IDs are: %s' % ','.join([str(id) for id in invalid_ids]))

try:
[
for r in (reservations + existing_reservations):
self._get_plugin(r['resource_type'])
for r in (reservations + existing_reservations)
]
except KeyError:
raise exceptions.CantUpdateParameter(param='resource_type')

Expand All @@ -512,6 +510,9 @@ def update_lease(self, lease_id, values):
new_reservations = reservations
new_allocs = self._allocation_candidates(values,
existing_reservations)
LOG.info("LOOK HERE")
LOG.info(new_reservations)
LOG.info(new_allocs)
else:
# User is not updating reservation parameters, e.g., is only
# adjusting lease start/end dates.
Expand Down
3 changes: 1 addition & 2 deletions blazar/notification/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ def send_lease_notification(context, lease, notification):
try:
IMPL.send_lease_notification(context, lease, notification)
except Exception:
LOG.info("Lease notification")
LOG.info(lease)
pass


def format_lease_payload(lease):
Expand Down
167 changes: 156 additions & 11 deletions blazar/plugins/third_party_plugins/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@
from oslo_utils import strutils
from oslo_config import cfg
from oslo_serialization import jsonutils
from oslo_policy.policy import DocumentedRuleDefault

from blazar import status
from blazar import policy
from blazar.api.v1 import validation
from blazar.api.v1 import utils as api_utils
from blazar.db import api as db_api
from blazar.db import exceptions as db_ex
from blazar.db import utils as db_utils
from blazar.manager import exceptions as manager_ex
from blazar.plugins import monitor
from blazar.policies import base
from blazar.utils import plugins as plugins_utils
from blazar.utils import trusts
from blazar.utils.openstack import keystone
Expand Down Expand Up @@ -140,7 +144,7 @@ def matching_resources(
shuffle(not_allocated_resource_ids)
return not_allocated_resource_ids[:int(min_resources)]
else:
raise NotEnoughResourcesAvailable()
raise manager_ex.NotEnoughResourcesAvailable()

def allocation_candidates(self, values):
return self.matching_resources(
Expand Down Expand Up @@ -271,23 +275,29 @@ def _update_allocations(self, dates_before, dates_after, reservation_id,
min_resources = min_resources - kept_resources \
if (min_resources - kept_resources) > 0 else 0
max_resources = max_resources - kept_resources
# TODO func does not exist
resource_ids = self._matching_resources(
resource_properties,
str(min_resources) + '-' + str(max_resources),
dates_after['start_date'], dates_after['end_date'],
lease['project_id'])
if len(resource_ids) >= min_resources:
for resource_id in resource_ids:
db_api.resource_allocation_create(
{'resource_id': resource_id,
'reservation_id': reservation_id})
# TODO call allocate() here?
self.update_new_resources()
else:
raise manager_ex.NotEnoughResourcesAvailable()

for allocation in allocs_to_remove:
db_api.resource_allocation_destroy(allocation['id'])

def update_new_resources(self, resource_reservation, resource_ids):
for resource_id in resource_ids:
db_api.resource_allocation_create(
{'resource_id': resource_id,
'reservation_id': reservation_id})
new_resource = db_api.resource_get(resource_id)
new_resourcess.append(new_resource['hypervisor_hostname'])
# TODO call allocate() here?

def _convert_int_param(self, param, name):
"""
Checks that the parameter is present and can be converted to int.
Expand Down Expand Up @@ -444,7 +454,11 @@ def notification_callback(self, event_type, payload):
def get_notification_event_types(self):
return []

def reallocate(self, allocation):
return True

def api_list(self):
policy.check_enforcement(self.resource_type(), "get")
raw_resource_list = db_api.resource_list(self.resource_type())
resource_list = []
for resource in raw_resource_list:
Expand All @@ -453,6 +467,7 @@ def api_list(self):

@trusts.use_trust_auth()
def api_create(self, data):
policy.check_enforcement(self.resource_type(), "post")
create_data = data["data"]
trust_id = data["trust_id"]
create_data["trust_id"] = trust_id
Expand All @@ -465,13 +480,15 @@ def api_create(self, data):
return resource

def api_get(self, resource_id):
policy.check_enforcement(self.resource_type(), "get")
resource = self.get(resource_id)
if resource is None:
raise manager_ex.ResourceNotFound(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This exception appears to come from plugin_ex, not manager_ex.

resource=resource_id, resource_type=self.resource_type())
return resource

def api_update(self, resource_id, data):
policy.check_enforcement(self.resource_type(), "put")
extras = data["extras"]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be data.get("extras")? I see we check for not extras in a couple lines

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's a good idea. I guess this would be the difference between not including it in the API request and it being empty. Either way, returning None is fine.

data = data["data"]
if not data and not extras:
Expand All @@ -487,6 +504,7 @@ def api_update(self, resource_id, data):
return db_api.resource_get(self.resource_type(), resource_id)

def api_delete(self, resource_id):
policy.check_enforcement(self.resource_type(), "delete")
resource = db_api.resource_get(self.resource_type(), resource_id)
if resource is None:
raise manager_ex.ResourceNotFound(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This exception appears to come from plugin_ex, not manager_ex.

Expand All @@ -509,10 +527,11 @@ def api_delete(self, resource_id):
)

def api_list_allocations(self, query):
policy.check_enforcement(self.resource_type(), "get_allocations")
resource_id_list = [
r['id'] for r in db_api.resource_list(self.resource_type())]
options = self.get_query_options(query, QUERY_TYPE_ALLOCATION)
options['detail'] = False # TODO use conf
options['detail'] = False
resource_allocations = self.query_resource_allocations(
resource_id_list, **options)

Expand All @@ -521,19 +540,42 @@ def api_list_allocations(self, query):
for resource, allocs in resource_allocations.items()
]

def api_get_allocations(self, resource_id, query):
def get_allocations(self, resource_id, query, detail=False):
options = self.get_query_options(query, QUERY_TYPE_ALLOCATION)
options['detail'] = False
resource_allocations = self.query_resource_allocations(
[resource_id], **options)
allocs = resource_allocations.get(resource_id, [])
return {"resource_id": resource_id, "reservations": allocs}

# TODO
def api_get_allocations(self, resource_id, query):
policy.check_enforcement(self.resource_type(), "get_allocations")
return self.get_allocations(resource_id, query)

def api_reallocate(self, resource_id, data):
raise Exception("unimplemented")
policy.check_enforcement(self.resource_type(), "reallocate")
allocations = self.get_allocations(resource_id, data, detail=True)

for alloc in allocations['reservations']:
reservation_flags = {}
resource_allocation = db_api.resource_allocation_get_all_by_values(
resource_id=resource_id,
reservation_id=alloc['id'])[0]

if self.reallocate(resource_allocation):
if alloc['status'] == status.reservation.ACTIVE:
reservation_flags.update(dict(resources_changed=True))
db_api.lease_update(alloc['lease_id'], dict(degraded=True))
else:
reservation_flags.update(dict(missing_resources=True))
db_api.lease_update(alloc['lease_id'], dict(degraded=True))

db_api.reservation_update(alloc['id'], reservation_flags)

return self.get_allocations(resource_id, data)

def api_list_resource_properties(self, query):
policy.check_enforcement(self.resource_type(), "get_resource_properties")
detail = False if not query else query.get('detail', False)
resource_properties = collections.defaultdict(list)

Expand All @@ -553,6 +595,7 @@ def api_list_resource_properties(self, query):
return resource_properties

def api_update_resource_property(self, property_name, data):
policy.check_enforcement(self.resource_type(), "patch_resource_properties")
return db_api.resource_property_update(
self.resource_type(), property_name, data)

Expand Down Expand Up @@ -639,7 +682,6 @@ def query_resource_allocations(self, resources, lease_id=None,
resource_allocations = {d: [] for d in resources}

for reservation in reservations:
# TODO detail config?
if detail:
del reservation['project_id']
del reservation['lease_name']
Expand All @@ -653,6 +695,109 @@ def query_resource_allocations(self, resources, lease_id=None,

return resource_allocations

def get_policy(self):
policy_root = f'blazar:{self.resource_type()}:%s'
resource_policy = [
DocumentedRuleDefault(
name=policy_root % 'get',
check_str=base.RULE_ADMIN,
description='Policy rule for List/Show Resource(s) API.',
operations=[
{
'path': '/{api_version}/'+self.resource_type(),
'method': 'GET'
},
{
'path': '/{api_version}/'+self.resource_type(),
'method': 'GET'
}
]
),
DocumentedRuleDefault(
name=policy_root % 'post',
check_str=base.RULE_ADMIN,
description='Policy rule for Create Resource API.',
operations=[
{
'path': '/{api_version}/'+self.resource_type(),
'method': 'POST'
}
]
),
DocumentedRuleDefault(
name=policy_root % 'put',
check_str=base.RULE_ADMIN,
description='Policy rule for Update Resource API.',
operations=[
{
'path': '/{api_version}/'+self.resource_type()+'/{resource_id}',
'method': 'PUT'
}
]
),
DocumentedRuleDefault(
name=policy_root % 'delete',
check_str=base.RULE_ADMIN,
description='Policy rule for Delete Resource API.',
operations=[
{
'path': '/{api_version}/'+self.resource_type()+'/{resource_id}',
'method': 'DELETE'
}
]
),
DocumentedRuleDefault(
name=policy_root % 'get_allocations',
check_str=base.RULE_ADMIN,
description='Policy rule for List/Get Resource(s) Allocations API.',
operations=[
{
'path': '/{api_version}/'+self.resource_type()+'/allocations',
'method': 'GET'
},
{
'path': '/{api_version}/'+self.resource_type()+'/{resource_id}/allocation',
'method': 'GET'
}
]
),
DocumentedRuleDefault(
name=policy_root % 'reallocate',
check_str=base.RULE_ADMIN,
description='Policy rule for Reallocate Resource API.',
operations=[
{
'path': '/{api_version}/'+self.resource_type()+'/{resource_id}/allocation',
'method': 'PUT'
}
]
),
DocumentedRuleDefault(
name=policy_root % 'get_resource_properties',
check_str=base.RULE_ADMIN,
description='Policy rule for Resource Properties API.',
operations=[
{
'path': '/{api_version}/'+self.resource_type()+'/resource_properties',
'method': 'GET'
}
]
),
DocumentedRuleDefault(
name=policy_root % 'patch_resource_properties',
check_str=base.RULE_ADMIN,
description='Policy rule for Resource Properties API.',
operations=[
{
'path': ('/{api_version}/'+self.resource_type()+'/resource_properties/'
'{property_name}'),
'method': 'PATCH'
}
]
),
]
return resource_policy


class ResourceMonitorPlugin(monitor.GeneralMonitorPlugin):
def __new__(cls, plugin, *args, **kwargs):
Expand Down
Loading