Skip to content

Commit

Permalink
Soufianej exception 1.0 (#59)
Browse files Browse the repository at this point in the history
* Python-chi 1.0 context module

Added the following new methods:

- list_sites(show: [None, “widget”, “text”] = None) -> [str]
- use_site(site_name: str = DEFAULT_SITE) -> None
- use_project(project_id: str = None) -> None
- choose_site() -> None, displays a dropdown widget to choose the site
- choose_project() -> None, displays a dropdown widget to choose the project
- check_credentials() -> None, prints authentication metadata
- set_log_level(debug: Bool), sets openstack debug logging to true,
  including HTTP request logs
- _is_ipynb() -> Bool, checks if the code is running within an ipy
  notebook. Used to determine whether to execute widgets

* Added custom exceptions

Raised when argument is not valid. These errors might be fixed by checking hardware catalog or documentation. Examples where this might be seen are:
- Site name is not valid
- Node type is not valid
- e.g. Resource does not exist

Raised when a request has valid arguments, but the resources are being used incorrectly, or can’t be used as requested. This type of error might depend on the time the notebook is run, due to the shared nature of the testbed.
Examples:
- Nodes matching filters (e.g. node_type) are unavailable
- Cannot allocate FIP
- Allocation expires soon
- Allocation has insufficient SUs for request

Raised when an error occurs with some Chameleon resource.
For example, if your node is having hardware issues, and so fails to provision, this will be raised.

Replaced thrown exceptions with their appropriate custom exception
accross all modules.

* Python-chi 1.0 context module

Added the following new methods:

- list_sites(show: [None, “widget”, “text”] = None) -> [str]
- list_projects(show: [None, “widget”, “text”] = None) -> [str]
- use_site(site_name: str = DEFAULT_SITE) -> None
- use_project(project_id: str = None) -> None
- choose_site() -> None, displays a dropdown widget to choose the site
- choose_project() -> None, displays a dropdown widget to choose the project
- check_credentials() -> None, prints authentication metadata
- set_log_level(debug: str), changes logging level to either ERROR or
  DEBUG, including HTTP request logs if the latter is chosen

* Changed one more exception

* Python-chi 1.0 context module

Added the following new methods:

- list_sites(show: [None, “widget”, “text”] = None) -> [str]
- list_projects(show: [None, “widget”, “text”] = None) -> [str]
- use_site(site_name: str = DEFAULT_SITE) -> None
- use_project(project_id: str = None) -> None
- choose_site() -> None, displays a dropdown widget to choose the site
- choose_project() -> None, displays a dropdown widget to choose the project
- check_credentials() -> None, prints authentication metadata
- set_log_level(debug: str), changes logging level to either ERROR or
  DEBUG, including HTTP request logs if the latter is chosen

* typo

---------

Co-authored-by: Soufiane Jounaid <[email protected]>
Co-authored-by: Soufiane Jounaid <[email protected]>
Co-authored-by: Mark Powers <[email protected]>
  • Loading branch information
4 people committed Sep 26, 2024
1 parent 81ba5fe commit 00a5005
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 38 deletions.
14 changes: 9 additions & 5 deletions chi/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@
import requests

from . import jupyterhub
from .exception import CHIValueError, ResourceError

import openstack
import ipywidgets as widgets
import requests
import logging

LOG = logging.getLogger(__name__)
Expand Down Expand Up @@ -286,7 +290,7 @@ def list_sites(show: Optional[str] = None) -> List[str]:
"user_support_contact": "[email protected]",
}
if not _sites:
raise ValueError("No sites returned.")
raise ResourceError("No sites returned.")

if show == None:
return _sites
Expand Down Expand Up @@ -323,7 +327,7 @@ def list_sites(show: Optional[str] = None) -> List[str]:
print(f" Location: {site['location']}")
print(f" User Support Contact: {site['user_support_contact']}")
else:
raise ValueError("Invalid value for 'show' parameter.")
raise CHIValueError("Invalid value for 'show' parameter.")


def use_site(site_name: str) -> None:
Expand Down Expand Up @@ -368,7 +372,7 @@ def use_site(site_name: str) -> None:

site = _sites.get(site_name)
if not site:
raise ValueError(
raise CHIValueError(
(
f'No site named "{site_name}" exists! Possible values: '
", ".join(_sites.keys())
Expand Down Expand Up @@ -445,7 +449,7 @@ def list_projects(show: str = None) -> List[str]:
elif show == None:
return list(project_names)
else:
raise ValueError("Invalid value for 'show' parameter.")
raise CHIValueError("Invalid value for 'show' parameter.")

def use_project(project: str) -> None:
"""
Expand Down Expand Up @@ -503,7 +507,7 @@ def set_log_level(level: str = "ERROR") -> None:
openstack.enable_logging(debug=False, http_debug=False)
LOG.setLevel(logging.ERROR)
else:
raise ValueError("Invalid log level value, please choose between 'ERROR' and 'DEBUG'")
raise CHIValueError("Invalid log level value, please choose between 'ERROR' and 'DEBUG'")

def session():
"""Get a Keystone Session object suitable for authenticating a client.
Expand Down
27 changes: 27 additions & 0 deletions chi/exception.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class CHIValueError(Exception):
"""Raised when argument is not valid. These errors might be fixed by
checking hardware catalog or documentation. Examples where this might
be seen are:
- Site name is not valid
- Node type is not valid
- Resource does not exist
"""
def __init__(self, message):
super().__init__(message)


class ResourceError(Exception):
"""Raised when a request has valid arguments, but the resources are
being used incorrectly, or can not be used as requested. This type
of error might depend on the time the request is run, due to the
shared nature of the testbed."""
def __init__(self, message):
super().__init__(message)


class ServiceError(Exception):
"""Raised when an error occurs with some Chameleon resource.
For example, if your node is having hardware issues, and so
fails to provision, this will be raised."""
def __init__(self, message):
super().__init__(message)
5 changes: 3 additions & 2 deletions chi/image.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .clients import glance
from .exception import CHIValueError, ResourceError

from glanceclient.exc import NotFound

Expand Down Expand Up @@ -42,9 +43,9 @@ def get_image_id(name):
"""
images = list(glance().images.list(filters={'name': name}))
if not images:
raise ValueError(f'No images found matching name "{name}"')
raise CHIValueError(f'No images found matching name "{name}"')
elif len(images) > 1:
raise ValueError(f'Multiple images found matching name "{name}"')
raise ResourceError(f'Multiple images found matching name "{name}"')
return images[0].id


Expand Down
29 changes: 15 additions & 14 deletions chi/lease.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from blazarclient.exception import BlazarClientException

from .clients import blazar, neutron
from .exception import CHIValueError, ResourceError, ServiceError
from .context import get as get_from_context, session
from .network import get_network_id, PUBLIC_NETWORK, list_floating_ips
from .server import Server, ServerError
Expand Down Expand Up @@ -85,7 +86,7 @@ def lease_create_args(
if length is None and end is None:
length = DEFAULT_LEASE_LENGTH
elif length is not None and end is not None:
raise ValueError("provide either 'length' or 'end', not both")
raise CHIValueError("provide either 'length' or 'end', not both")

if end is None:
if isinstance(length, numbers.Number):
Expand Down Expand Up @@ -150,7 +151,7 @@ def lease_create_nodetype(*args, **kwargs):
try:
node_type = kwargs.pop("node_type")
except KeyError:
raise ValueError("no node_type specified")
raise CHIValueError("no node_type specified")
kwargs["node_resource_properties"] = ["==", "$node_type", node_type]
return lease_create_args(*args, **kwargs)

Expand Down Expand Up @@ -234,7 +235,7 @@ def __repr__(self):
def __enter__(self):
if self.lease is None:
# don't support reuse in multiple with's.
raise RuntimeError("Lease context manager not reentrant")
raise ResourceError("Lease context manager not reentrant")
self.wait()
return self

Expand Down Expand Up @@ -323,7 +324,7 @@ def wait(self):
if self.ready:
break
else:
raise RuntimeError("timeout, lease failed to start")
raise ServiceError("timeout, lease failed to start")

def delete(self):
"""Deletes the lease"""
Expand Down Expand Up @@ -538,13 +539,13 @@ def _reservation_matching(lease_ref, match_fn, multiple=False):
matches = [r for r in reservations if match_fn(r)]

if not matches:
raise ValueError("No matching reservation found")
raise ResourceError("No matching reservation found")

if multiple:
return matches
else:
if len(matches) > 1:
raise ValueError("Multiple matching reservations found")
raise ResourceError("Multiple matching reservations found")
return matches[0]


Expand Down Expand Up @@ -622,7 +623,7 @@ def add_fip_reservation(reservation_list, count=1):


def add_device_reservation(
reservation_list, count=1, machine_name=None, device_model=None, device_name=None
reservation_list, count=1, machine_name=None, device_model=None, device_name=None
):
"""Add an IoT/edge device reservation to a reservation list.
Expand Down Expand Up @@ -652,12 +653,12 @@ def add_device_reservation(
resource_properties = []
if device_name:
if count > 1:
raise ValueError(
raise ResourceError(
"Cannot reserve multiple devices if device_name is a constraint."
)
resource_properties.append(["==", "$name", device_name])
if machine_name:
resource_properties.append(["==", "$machine_name", machine_name])
resource_properties.append(["==", "$machine_name", machine_name])
if device_model:
resource_properties.append(["==", "$model", device_model])

Expand Down Expand Up @@ -734,9 +735,9 @@ def get_lease_id(lease_name) -> str:
"""
matching = [l for l in blazar().lease.list() if l["name"] == lease_name]
if not matching:
raise ValueError(f"No leases found for name {lease_name}")
raise CHIValueError(f"No leases found for name {lease_name}")
elif len(matching) > 1:
raise ValueError(f"Multiple leases found for name {lease_name}")
raise ResourceError(f"Multiple leases found for name {lease_name}")
return matching[0]["id"]


Expand All @@ -761,7 +762,7 @@ def create_lease(lease_name, reservations=[], start_date=None, end_date=None):
start_date = utcnow()

if not reservations:
raise ValueError("No reservations provided.")
raise CHIValueError("No reservations provided.")

try:
return blazar().lease.create(
Expand Down Expand Up @@ -818,6 +819,6 @@ def wait_for_active(ref):
if status == "ACTIVE":
return lease
elif status == "ERROR":
raise RuntimeError("Lease went into ERROR state")
raise ServiceError("Lease went into ERROR state")
time.sleep(10)
raise TimeoutError("Lease failed to start")
raise ServiceError("Lease failed to start")
13 changes: 7 additions & 6 deletions chi/network.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .clients import neutron
from .exception import CHIValueError, ResourceError

from neutronclient.common.exceptions import NotFound

Expand Down Expand Up @@ -59,22 +60,22 @@
def _resolve_id(resource, name) -> str:
list_fn = getattr(neutron(), f'list_{resource}', None)
if not callable(list_fn):
raise ValueError(f'Invalid resource type "{resource}"')
raise CHIValueError(f'Invalid resource type "{resource}"')
resources = [
x for x in list_fn()[resource]
if x['name'] == name
]
if not resources:
raise RuntimeError(f'No {resource} found with name {name}')
raise CHIValueError(f'No {resource} found with name {name}')
elif len(resources) > 1:
raise RuntimeError(f'Found multiple {resource} with name {name}')
raise ResourceError(f'Found multiple {resource} with name {name}')
return resources[0]['id']


def _resolve_resource(resource, name_or_id) -> dict:
get_fn = getattr(neutron(), f'show_{resource}', None)
if not callable(get_fn):
raise ValueError(f'Invalid resource type "{resource}"')
raise CHIValueError(f'Invalid resource type "{resource}"')
try:
res = get_fn(name_or_id)
except NotFound:
Expand Down Expand Up @@ -651,7 +652,7 @@ def get_free_floating_ip(allocate=True) -> dict:
return fip
except StopIteration:
if not allocate:
raise RuntimeError(
raise ResourceError(
"No free floating IPs in project and not allocating a new one")
return _neutron.create_floatingip({
"floatingip": {
Expand Down Expand Up @@ -701,7 +702,7 @@ def get_floating_ip(ip_address) -> dict:
for fip in ips:
if fip['floating_ip_address'] == ip_address:
return fip
raise Exception(f"Floating IP {ip_address} not found")
raise CHIValueError(f"Floating IP {ip_address} not found")


def list_floating_ips() -> 'list[dict]':
Expand Down
15 changes: 8 additions & 7 deletions chi/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from openstack.compute.v2.server import Server as OpenStackServer

from .clients import connection, glance, nova, neutron
from .exception import CHIValueError, ResourceError, ServiceError
from .context import get as get_from_context, session
from .image import get_image, get_image_id
from .keypair import Keypair
Expand Down Expand Up @@ -185,7 +186,7 @@ def __init__(self, id=None, lease=None, key=None, image=DEFAULT_IMAGE,
)
self.server = self.conn.compute.create_server(**server_kwargs)
else:
raise ValueError(
raise CHIValueError(
"Missing required argument: 'id' or 'lease' required.")

self.id = self.server.id
Expand Down Expand Up @@ -321,7 +322,7 @@ def get_flavor_id(name) -> str:
"""
flavor = next((f for f in nova().flavors.list() if f.name == name), None)
if not flavor:
raise NotFound(f'No flavors found matching name {name}')
raise CHIValueError(f'No flavors found matching name {name}')
return flavor


Expand Down Expand Up @@ -398,9 +399,9 @@ def get_server_id(name) -> str:
"""
servers = [s for s in nova().servers.list() if s.name == name]
if not servers:
raise ValueError(f'No matching servers found for name "{name}"')
raise CHIValueError(f'No matching servers found for name "{name}"')
elif len(servers) > 1:
raise ValueError(f'Multiple matching servers found for name "{name}"')
raise ResourceError(f'Multiple matching servers found for name "{name}"')
return servers[0].id


Expand Down Expand Up @@ -542,7 +543,7 @@ def wait_for_tcp(host, port, timeout=(60 * 20), sleep_time=5):
except OSError as ex:
time.sleep(sleep_time)
if time.perf_counter() - start_time >= timeout:
raise TimeoutError((
raise ServiceError((
f'Waited too long for the port {port} on host {host} to '
'start accepting connections.')) from ex

Expand Down Expand Up @@ -637,7 +638,7 @@ def create_server(server_name, reservation_id=None, key_name=None, network_id=No
ValueError: if an invalid count is provided.
"""
if count < 1:
raise ValueError('Must launch at least one server.')
raise CHIValueError('Must launch at least one server.')
if not key_name:
key_name = update_keypair().id
if not network_id:
Expand All @@ -652,7 +653,7 @@ def create_server(server_name, reservation_id=None, key_name=None, network_id=No
else:
flavor_id = next((f.id for f in list_flavors()), None)
if not flavor_id:
raise NotFound('Could not auto-select flavor to use')
raise ResourceError('Could not auto-select flavor to use')

scheduler_hints = {}
if reservation_id:
Expand Down
9 changes: 5 additions & 4 deletions chi/share.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .clients import manila
from .exception import CHIValueError, ResourceError

from manilaclient.exceptions import NotFound

Expand All @@ -18,9 +19,9 @@ def _get_default_share_type_id():
# we only support one share type - cephfsnfstype
share_types = manila().share_types.list()
if not share_types:
raise ValueError("No share types found")
raise CHIValueError("No share types found")
elif len(share_types) > 1:
raise ValueError("Multiple share types found")
raise ResourceError("Multiple share types found")
return share_types[0].id


Expand Down Expand Up @@ -113,9 +114,9 @@ def get_share_id(name):
"""
shares = list(manila().shares.list(search_opts={'name': name}))
if not shares:
raise ValueError(f'No shares found matching name "{name}"')
raise CHIValueError(f'No shares found matching name "{name}"')
elif len(shares) > 1:
raise ValueError(f'Multiple shares found matching name "{name}"')
raise ResourceError(f'Multiple shares found matching name "{name}"')
return shares[0].id


Expand Down

0 comments on commit 00a5005

Please sign in to comment.