diff --git a/chi/context.py b/chi/context.py index 045feb0..f8c1e42 100644 --- a/chi/context.py +++ b/chi/context.py @@ -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__) @@ -286,7 +290,7 @@ def list_sites(show: Optional[str] = None) -> List[str]: "user_support_contact": "help@chameleoncloud.org", } if not _sites: - raise ValueError("No sites returned.") + raise ResourceError("No sites returned.") if show == None: return _sites @@ -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: @@ -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()) @@ -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: """ @@ -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. diff --git a/chi/exception.py b/chi/exception.py new file mode 100644 index 0000000..69c2af1 --- /dev/null +++ b/chi/exception.py @@ -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) \ No newline at end of file diff --git a/chi/image.py b/chi/image.py index 16c414d..7dfcbaa 100644 --- a/chi/image.py +++ b/chi/image.py @@ -1,4 +1,5 @@ from .clients import glance +from .exception import CHIValueError, ResourceError from glanceclient.exc import NotFound @@ -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 diff --git a/chi/lease.py b/chi/lease.py index 59fdf73..35090a4 100644 --- a/chi/lease.py +++ b/chi/lease.py @@ -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 @@ -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): @@ -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) @@ -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 @@ -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""" @@ -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] @@ -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. @@ -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]) @@ -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"] @@ -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( @@ -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") diff --git a/chi/network.py b/chi/network.py index b162064..0bef5e1 100644 --- a/chi/network.py +++ b/chi/network.py @@ -1,4 +1,5 @@ from .clients import neutron +from .exception import CHIValueError, ResourceError from neutronclient.common.exceptions import NotFound @@ -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: @@ -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": { @@ -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]': diff --git a/chi/server.py b/chi/server.py index 44fc31a..eae470c 100644 --- a/chi/server.py +++ b/chi/server.py @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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: @@ -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: diff --git a/chi/share.py b/chi/share.py index acfafb7..5a302cd 100644 --- a/chi/share.py +++ b/chi/share.py @@ -1,4 +1,5 @@ from .clients import manila +from .exception import CHIValueError, ResourceError from manilaclient.exceptions import NotFound @@ -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 @@ -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