diff --git a/openstackquery/__init__.py b/openstackquery/__init__.py index 55e7261..01d2af0 100644 --- a/openstackquery/__init__.py +++ b/openstackquery/__init__.py @@ -7,6 +7,7 @@ ProjectQuery, ImageQuery, HypervisorQuery, + AggregateQuery, ) # Create logger diff --git a/openstackquery/api/query_objects.py b/openstackquery/api/query_objects.py index d522b9c..be2b6d8 100644 --- a/openstackquery/api/query_objects.py +++ b/openstackquery/api/query_objects.py @@ -8,6 +8,7 @@ from openstackquery.mappings.project_mapping import ProjectMapping from openstackquery.mappings.server_mapping import ServerMapping from openstackquery.mappings.user_mapping import UserMapping +from openstackquery.mappings.aggregate_mapping import AggregateMapping if TYPE_CHECKING: from openstackquery.api.query_api import QueryAPI @@ -70,3 +71,10 @@ def HypervisorQuery() -> "QueryAPI": Simple helper function to setup a query using a factory """ return get_common(HypervisorMapping) + + +def AggregateQuery() -> QueryAPI: + """ + Simple helper function to setup a query using a factory + """ + return get_common(AggregateMapping) diff --git a/openstackquery/enums/props/aggregate_properties.py b/openstackquery/enums/props/aggregate_properties.py new file mode 100644 index 0000000..12760ce --- /dev/null +++ b/openstackquery/enums/props/aggregate_properties.py @@ -0,0 +1,66 @@ +from enum import auto +from typing import Dict, Optional + +from openstackquery.enums.props.prop_enum import PropEnum, PropFunc +from exceptions.query_property_mapping_error import QueryPropertyMappingError + + +class AggregateProperties(PropEnum): + """ + An enum class for Host Aggregate Properties + """ + + AGGREGATE_CREATED_AT = auto() + AGGREGATE_DELETED = auto() + AGGREGATE_DELETED_AT = auto() + AGGREGATE_HOST_IPS = auto() + AGGREGATE_HOSTTYPE = auto() + AGGREGATE_UPDATED_AT = auto() + AGGREGATE_ID = auto() + + @staticmethod + def _get_aliases() -> Dict: + """ + a method that returns all valid string alias mappings + """ + return { + AggregateProperties.AGGREGATE_CREATED_AT: ["created_at"], + AggregateProperties.AGGREGATE_DELETED: ["deleted"], + AggregateProperties.AGGREGATE_DELETED_AT: ["deleted_at"], + AggregateProperties.AGGREGATE_HOST_IPS: ["hosts"], + AggregateProperties.AGGREGATE_HOSTTYPE: ["metadata_hosttype", "hosttype"], + AggregateProperties.AGGREGATE_UPDATED_AT: ["updated_at"], + AggregateProperties.AGGREGATE_ID: ["id", "uuid"], + } + + @staticmethod + def get_prop_mapping(prop) -> Optional[PropFunc]: + """ + Method that returns the property function if function mapping exists for a given AggregateProperty Enum + how to get specified property from an openstacksdk Aggregate object is documented here: + https://docs.openstack.org/openstacksdk/latest/user/resources/compute/v2/aggregate.html + :param prop: A Aggregate Enum for which a function may exist for + """ + mapping = { + AggregateProperties.AGGREGATE_CREATED_AT: lambda a: a["created_at"], + AggregateProperties.AGGREGATE_DELETED: lambda a: a["deleted"], + AggregateProperties.AGGREGATE_DELETED_AT: lambda a: a["deleted_at"], + AggregateProperties.AGGREGATE_HOST_IPS: lambda a: a["hosts"], + AggregateProperties.AGGREGATE_HOSTTYPE: lambda a: a["metadata"].get( + "hosttype", None + ), + AggregateProperties.AGGREGATE_ID: lambda a: a["id"], + } + try: + return mapping[prop] + except KeyError as exp: + raise QueryPropertyMappingError( + f"Error: failed to get property mapping, property {prop.name} is not supported in AggregateProperties" + ) from exp + + @staticmethod + def get_marker_prop_func(): + """ + A getter method to return marker property function for pagination + """ + return AggregateProperties.get_prop_mapping(AggregateProperties.AGGREGATE_ID) diff --git a/openstackquery/mappings/aggregate_mapping.py b/openstackquery/mappings/aggregate_mapping.py new file mode 100644 index 0000000..b0947ab --- /dev/null +++ b/openstackquery/mappings/aggregate_mapping.py @@ -0,0 +1,106 @@ +from typing import Type + +from openstackquery.enums.props.aggregate_properties import AggregateProperties +from openstackquery.enums.query_presets import ( + QueryPresetsDateTime, + QueryPresetsString, + QueryPresetsGeneric, +) +from openstackquery.handlers.client_side_handler_datetime import ( + ClientSideHandlerDateTime, +) +from openstackquery.handlers.client_side_handler_generic import ( + ClientSideHandlerGeneric, +) +from openstackquery.handlers.client_side_handler_string import ClientSideHandlerString +from openstackquery.handlers.server_side_handler import ServerSideHandler +from openstackquery.mappings.mapping_interface import MappingInterface +from openstackquery.runners.aggregate_runner import AggregateRunner +from openstackquery.runners.runner_wrapper import RunnerWrapper +from openstackquery.structs.query_client_side_handlers import QueryClientSideHandlers + + +class AggregateMapping(MappingInterface): + """ + Mapping class for querying Openstack Aggregate objects + Define property mappings, kwarg mappings and filter function mappings, and runner mapping related to hypervisors here + """ + + @staticmethod + def get_chain_mappings(): + """ + Should return a dictionary containing property pairs mapped to query mappings. + This is used to define how to chain results from this query to other possible queries + """ + # TODO: find a way to map list of hostnames: + # AggregateProperties.HOST_IPS to HypervisorProperties.HYPERVISOR_NAME + return None + + @staticmethod + def get_runner_mapping() -> Type[RunnerWrapper]: + """ + Returns a mapping to associated Runner class for the Query (AggregateRunner) + """ + return AggregateRunner + + @staticmethod + def get_prop_mapping() -> Type[AggregateProperties]: + """ + Returns a mapping of valid presets for server side attributes (HypervisorProperties) + """ + return AggregateProperties + + @staticmethod + def get_server_side_handler() -> ServerSideHandler: + """ + method to configure a server handler which can be used to get 'filter' keyword arguments that + can be passed to openstack function conn.compute.aggregates() to filter results for a valid preset-property + pair + + valid filters documented here: + https://docs.openstack.org/openstacksdk/latest/user/proxies/compute.html + https://docs.openstack.org/api-ref/compute/?expanded=list-hypervisors-detail#list-aggregates + """ + # No server-side filters for AggregateQuery + return ServerSideHandler({}) + + @staticmethod + def get_client_side_handlers() -> QueryClientSideHandlers: + """ + method to configure a set of client-side handlers which can be used to get local filter functions + corresponding to valid preset-property pairs. These filter functions can be used to filter results after + listing all aggregates. + """ + datetime_props = [ + AggregateProperties.AGGREGATE_DELETED_AT, + AggregateProperties.AGGREGATE_UPDATED_AT, + AggregateProperties.AGGREGATE_CREATED_AT, + ] + + return QueryClientSideHandlers( + # set generic query preset mappings + generic_handler=ClientSideHandlerGeneric( + { + QueryPresetsGeneric.EQUAL_TO: ["*"], + QueryPresetsGeneric.NOT_EQUAL_TO: ["*"], + QueryPresetsGeneric.ANY_IN: ["*"], + QueryPresetsGeneric.NOT_ANY_IN: ["*"], + } + ), + string_handler=ClientSideHandlerString( + { + QueryPresetsString.MATCHES_REGEX: [ + AggregateProperties.AGGREGATE_HOSTTYPE + ] + } + ), + datetime_handler=ClientSideHandlerDateTime( + { + QueryPresetsDateTime.OLDER_THAN: datetime_props, + QueryPresetsDateTime.YOUNGER_THAN: datetime_props, + QueryPresetsDateTime.OLDER_THAN_OR_EQUAL_TO: datetime_props, + QueryPresetsDateTime.YOUNGER_THAN_OR_EQUAL_TO: datetime_props, + } + ), + integer_handler=None, + ) diff --git a/openstackquery/runners/aggregate_runner.py b/openstackquery/runners/aggregate_runner.py new file mode 100644 index 0000000..c64b5e9 --- /dev/null +++ b/openstackquery/runners/aggregate_runner.py @@ -0,0 +1,51 @@ +import logging +from typing import Optional, List + +from openstackquery.aliases import ServerSideFilters, OpenstackResourceObj +from openstackquery.openstack_connection import OpenstackConnection +from openstackquery.runners.runner_wrapper import RunnerWrapper +from openstack.compute.v2.aggregate import Aggregate + +logger = logging.getLogger(__name__) + + +class AggregateRunner(RunnerWrapper): + """ + Runner class for openstack Aggregate resource + AggregateRunner encapsulates running any openstacksdk Aggregate commands + """ + + RESOURCE_TYPE = Aggregate + + def parse_meta_params(self, conn: OpenstackConnection, **kwargs): + """ + This method is a helper function that will parse a set of meta params specific to the resource and + return a set of parsed meta-params to pass to _run_query + """ + logger.debug("AggregateQuery has no meta-params available") + return super().parse_meta_params(conn, **kwargs) + + def run_query( + self, + conn: OpenstackConnection, + filter_kwargs: Optional[ServerSideFilters] = None, + **kwargs, + ) -> List[OpenstackResourceObj]: + """ + This method runs the query by running openstacksdk commands + + For AggregateQuery, this command finds all aggregates that match a given set of filter_kwargs + :param conn: An OpenstackConnection object - used to connect to openstacksdk + :param filter_kwargs: An Optional list of filter kwargs to pass to conn.identity.aggregates() + to limit the hypervisors being returned. + - see https://docs.openstack.org/api-ref/compute/?expanded=list-hypervisors-detail#list-aggregates + """ + if not filter_kwargs: + # return server info + filter_kwargs = {} + logger.debug( + "running openstacksdk command conn.compute.aggregates(%s)", + ",".join(f"{key}={value}" for key, value in filter_kwargs.items()), + ) + + return conn.compute.aggregates(**filter_kwargs)