Skip to content
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

Add third party plugins #26

Open
wants to merge 6 commits into
base: chameleoncloud/xena
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 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
69 changes: 45 additions & 24 deletions blazarclient/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
HEX_ELEM + '{4}', HEX_ELEM + '{4}',
HEX_ELEM + '{12}'])

import logging
LOG = logging.getLogger(__name__)

class OpenStackCommand(command.Command):
"""Base class for OpenStack commands."""
Expand Down Expand Up @@ -88,6 +90,26 @@ def get_client(self):
else:
return self.app.client

def get_manager_and_args(
self, parsed_args, args, add_to_body=False, blazar_client=None
):
if not blazar_client:
blazar_client = self.get_client()
if hasattr(parsed_args, "resource") and hasattr(blazar_client, parsed_args.resource):
resource_manager = getattr(blazar_client, parsed_args.resource)
if add_to_body:
args["resource_type"] = self.resource
elif hasattr(parsed_args, "resource"): # If third party resource
resource_manager = blazar_client.resource
if add_to_body:
args["resource_type"] = parsed_args.resource
else:
args.insert(0, parsed_args.resource)

Choose a reason for hiding this comment

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

This branching is a little difficult to parse at first glance. I prefer consolidating a little bit

if hasattr(parsed_args, "resource"):
    if hasattr(blazar_client, parsed_args.resource)
        resource_manager = getattr(blazar_client, parsed_args.resource)
        resource_type = self.resource
    else:
        resource_manager = blazar_client.resource
        resource_type = parsed_args.resource
    if add_to_body:
        args["resource_type"] = resource_type

else: # Else, no --resource, normal usage
resource_manager = getattr(blazar_client, self.resource)
return resource_manager, args


def get_parser(self, prog_name):
parser = super(BlazarCommand, self).get_parser(prog_name)
return parser
Expand Down Expand Up @@ -135,7 +157,7 @@ def get_data(self, parsed_args):
self.log.debug('get_data(%s)' % parsed_args)
blazar_client = self.get_client()
body = self.args2body(parsed_args)
resource_manager = getattr(blazar_client, self.resource)
resource_manager, body = self.get_manager_and_args(parsed_args, body, add_to_body=True, blazar_client=blazar_client)
data = resource_manager.create(**body)
self.format_output_data(data)

Expand Down Expand Up @@ -178,8 +200,8 @@ def run(self, parsed_args):
self.id_pattern)
else:
res_id = parsed_args.id
resource_manager = getattr(blazar_client, self.resource)
resource_manager.update(res_id, **body)
resource_manager, args = self.get_manager_and_args(parsed_args, [res_id], add_to_body=False, blazar_client=blazar_client)
data = resource_manager.update(*args, **body)

Choose a reason for hiding this comment

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

This variable appears unused

print('Updated %s: %s' % (self.resource, parsed_args.id),
file=self.app.stdout)
return
Expand All @@ -189,7 +211,7 @@ class DeleteCommand(BlazarCommand):
"""Delete a given resource."""

api = 'reservation'
resource = None
resource = "resource"
log = None

def get_parser(self, prog_name):
Expand All @@ -206,16 +228,15 @@ def get_parser(self, prog_name):
def run(self, parsed_args):
self.log.debug('run(%s)' % parsed_args)
blazar_client = self.get_client()
resource_manager = getattr(blazar_client, self.resource)
res_id = parsed_args.id

Choose a reason for hiding this comment

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

This gets overwritten immediately if self.allow_names is true. It should probably be in an else branch to be more clear.

if self.allow_names:
res_id = utils.find_resource_id_by_name_or_id(blazar_client,
self.resource,
parsed_args.id,
self.name_key,
self.id_pattern)
else:
res_id = parsed_args.id
resource_manager.delete(res_id)
resource_manager, args = self.get_manager_and_args(parsed_args, [res_id], add_to_body=False, blazar_client=blazar_client)
data = resource_manager.delete(*args)
print('Deleted %s: %s' % (self.resource, parsed_args.id),
file=self.app.stdout)
return
Expand All @@ -231,6 +252,8 @@ class ListCommand(BlazarCommand, lister.Lister):
list_columns = []
unknown_parts_flag = True

list_fn_name = "list"

def args2body(self, parsed_args):
params = {}
if parsed_args.sort_by:
Expand All @@ -249,8 +272,8 @@ def retrieve_list(self, parsed_args):
"""Retrieve a list of resources from Blazar server."""
blazar_client = self.get_client()
body = self.args2body(parsed_args)
resource_manager = getattr(blazar_client, self.resource)
data = resource_manager.list(**body)
resource_manager, body = self.get_manager_and_args(parsed_args, body, add_to_body=True, blazar_client=blazar_client)
data = getattr(resource_manager, self.list_fn_name)(**body)
return data

def setup_columns(self, info, parsed_args):
Expand Down Expand Up @@ -292,7 +315,7 @@ def retrieve_list(self, parsed_args):
"""Retrieve a list of resources from Blazar server."""
blazar_client = self.get_client()
body = self.args2body(parsed_args)
resource_manager = getattr(blazar_client, self.resource)
resource_manager, body = self.get_manager_and_args(parsed_args, body, add_to_body=True, blazar_client=blazar_client)
data = resource_manager.list_allocations(**body)
return data

Expand All @@ -317,18 +340,15 @@ def get_parser(self, prog_name):
def get_data(self, parsed_args):
self.log.debug('get_data(%s)' % parsed_args)
blazar_client = self.get_client()

res_id = parsed_args.id
if self.allow_names:
res_id = utils.find_resource_id_by_name_or_id(blazar_client,
self.resource,
parsed_args.id,
self.name_key,
self.id_pattern)
else:
res_id = parsed_args.id

resource_manager = getattr(blazar_client, self.resource)
data = resource_manager.get(res_id)
resource_manager, args = self.get_manager_and_args(parsed_args, [res_id], add_to_body=False, blazar_client=blazar_client)
data = resource_manager.get(*args)
self.format_output_data(data)
return list(zip(*sorted(data.items())))

Expand All @@ -339,8 +359,8 @@ class ShowAllocationCommand(ShowCommand, show.ShowOne):
def get_data(self, parsed_args):
self.log.debug('get_data(%s)' % parsed_args)
blazar_client = self.get_client()
resource_manager = getattr(blazar_client, self.resource)
data = resource_manager.get_allocation(parsed_args.id)
resource_manager, args = self.get_manager_and_args(parsed_args, [parsed_args.id], add_to_body=False, blazar_client=blazar_client)
data = resource_manager.get_allocation(*args)
self.format_output_data(data)
return list(zip(*sorted(data.items())))

Expand Down Expand Up @@ -377,8 +397,8 @@ def run(self, parsed_args):
self.id_pattern)
else:
res_id = parsed_args.id
resource_manager = getattr(blazar_client, self.resource)
resource_manager.reallocate(res_id, body)
resource_manager, args = self.get_manager_and_args(parsed_args, [res_id, body], add_to_body=False, blazar_client=blazar_client)
resource_manager.reallocate(*args)
print('Reallocated %s: %s' % (self.resource, parsed_args.id),
file=self.app.stdout)
return
Expand All @@ -400,8 +420,9 @@ def get_parser(self, prog_name):
def get_data(self, parsed_args):
self.log.debug('get_data(%s)' % parsed_args)
blazar_client = self.get_client()
resource_manager = getattr(blazar_client, self.resource)
data = resource_manager.get_capability(parsed_args.capability_name)
resource_manager, args = self.get_manager_and_args(parsed_args, [parsed_args.capability_name], add_to_body=False, blazar_client=blazar_client)
LOG.info(args)
data = resource_manager.get_capability(*args)
self.format_output_data(data)
return list(zip(*sorted(data.items())))

Expand All @@ -415,7 +436,7 @@ def run(self, parsed_args):
self.log.debug('run(%s)' % parsed_args)
blazar_client = self.get_client()
body = self.args2body(parsed_args)
resource_manager = getattr(blazar_client, self.resource)
resource_manager, body = self.get_manager_and_args(parsed_args, body, add_to_body=True, blazar_client=blazar_client)
resource_manager.set_capability(**body)
print(
'Updated %s extra capability: %s' % (
Expand Down
8 changes: 6 additions & 2 deletions blazarclient/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,12 @@ def find_resource_id_by_name_or_id(client, resource_type, name_or_id,


def _find_resource_id_by_name(client, resource_type, name, name_key):
resource_manager = getattr(client, resource_type)
resources = resource_manager.list()
if hasattr(client, resource_type):
resource_manager = getattr(client, resource_type)
resources = resource_manager.list()
else: # If third party resource
resource_manager = client.resource
resources = resource_manager.list(resource_type)

named_resources = []
key = name_key if name_key else 'name'
Expand Down
7 changes: 7 additions & 0 deletions blazarclient/v1/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from blazarclient.v1 import hosts
from blazarclient.v1 import leases
from blazarclient.v1 import networks
from blazarclient.v1 import resources


class Client(object):
Expand Down Expand Up @@ -76,3 +77,9 @@ def __init__(self, blazar_url=None, auth_token=None, session=None,
session=self.session,
version=self.version,
**kwargs)
self.resource = resources.ResourceClientManager(
blazar_url=self.blazar_url,
auth_token=self.auth_token,
session=self.session,
version=self.version,
**kwargs)
112 changes: 112 additions & 0 deletions blazarclient/v1/resources.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Copyright (c) 2019 StackHPC Ltd.
#
# 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.

from blazarclient import base
from blazarclient.i18n import _

import logging
LOG = logging.getLogger(__name__)

class ResourceClientManager(base.BaseClientManager):

Choose a reason for hiding this comment

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

Most of the dict lookups in this class assume that the server will also have plugins installed. Is this assumption safe to make?

Copy link
Author

Choose a reason for hiding this comment

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

I think this is fine, as blazar already assumes you have oshosts/network/fip running. There is a /resources endpoint in 3rd party plugins patch which shows enabled resources we could check, but that would require an extra API call for each command.

def list_resources(self, sort_by=None):
resp, body = self.request_manager.get('/resources')
if sort_by:
resources = sorted(body, key=lambda l: l[sort_by])

Choose a reason for hiding this comment

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

This variable is unused, and sorted produces a copy, so nothing will actually be sorted to the user.

Copy link
Author

Choose a reason for hiding this comment

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

fixed, should be body =

return body

def create(self, resource_type, data, **kwargs):
values = {'data': data}
values.update(**kwargs)
resp, body = self.request_manager.post(f'/{resource_type}', body=values)
return body['resource']

def get(self, resource_type, resource_id):
resp, body = self.request_manager.get(
f'/{resource_type}/{resource_id}')
return body['resource']

def update(self, resource_type, res_id, data, extras):
LOG.info("RESOURCE CLIENT UPDATE")

Choose a reason for hiding this comment

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

Is this leftover debugging?

Copy link
Author

Choose a reason for hiding this comment

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

oops

if not data and not extras:
return _('No information to update passed.')
LOG.info(data)
LOG.info(extras)
body = {"data": data, "extras": extras}
resp, body = self.request_manager.put(
f'/{resource_type}/{res_id}', body=body
)
return body['resource']

def delete(self, resource_type, resource_id):
resp, body = self.request_manager.delete(

Choose a reason for hiding this comment

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

These variables are unused

f'/{resource_type}/{resource_id}')

def list(self, resource_type, sort_by=None):
resp, body = self.request_manager.get(f'/{resource_type}')
resources = body['resources']
if sort_by:
resources = sorted(resources, key=lambda l: l[sort_by])
return resources

def get_allocation(self, resource_type, resource_id):
resp, body = self.request_manager.get(
f'/{resource_type}/{resource_id}/allocation')
return body['allocation']

def list_allocations(self, resource_type, sort_by=None):
resp, body = self.request_manager.get(f'/{resource_type}/allocations')
allocations = body['allocations']
if sort_by:
allocations = sorted(allocations, key=lambda l: l[sort_by])
return allocations

def reallocate(self, resource_type, resource_id, values):
resp, body = self.request_manager.put(
f'/{resource_type}/{resource_id}/allocation', body=values)
return body['allocation']

def list_capabilities(self, resource_type, detail=False, sort_by=None):
url = f'/{resource_type}/properties'

if detail:
url += '?detail=True'

resp, body = self.request_manager.get(url)
resource_properties = body['resource_properties']

# Values is a reserved word in cliff so need to rename values column.
if detail:
for p in resource_properties:
p['capability_values'] = p['values']
del p['values']

if sort_by:
resource_properties = sorted(resource_properties,
key=lambda l: l[sort_by])
return resource_properties

def get_capability(self, resource_type, capability_name):
resource_property = [
x for x in self.list_capabilities(resource_type, detail=True)
if x['property'] == capability_name]

return {} if not resource_property else resource_property[0]

def set_capability(self, resource_type, capability_name, private):
data = {'private': private}
resp, body = self.request_manager.patch(
f'/{resource_type}/properties/{capability_name}', body=data)

return body['resource_property']
7 changes: 6 additions & 1 deletion blazarclient/v1/shell_commands/leases.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,10 @@
"resource_type": 'device'
},
"others": {
".*": None
"min": "",
"max": "",
"resource_properties": {},
"resource_type": "",
}
}

Expand Down Expand Up @@ -280,6 +283,8 @@ def _parse_params(self, str_params, default, err_msg):
prog = re.compile('^(?:(.*),)?(%s)=(.*)$'
% "|".join(default.keys()))

self.log.info("%s", str_params)
self.log.info("%s", default)
while str_params != "":
match = prog.search(str_params)

Expand Down
Loading