Skip to content

Commit

Permalink
Add filter parameters to task list
Browse files Browse the repository at this point in the history
fixes #543

Co-authored-by: Grant Gainey <[email protected]>
  • Loading branch information
mdellweg and ggainey committed Oct 13, 2022
1 parent ab4fa6a commit 6f7cd36
Show file tree
Hide file tree
Showing 9 changed files with 239 additions and 51 deletions.
1 change: 1 addition & 0 deletions CHANGES/543.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added task filtering options.
31 changes: 26 additions & 5 deletions pulpcore/cli/common/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import re
import sys
import time
from typing import Any, ClassVar, Dict, List, NamedTuple, Optional, Set, Type, Union
from typing import Any, ClassVar, Dict, Iterable, List, NamedTuple, Optional, Set, Type, Union

import click
import yaml
Expand All @@ -28,6 +28,21 @@

DEFAULT_LIMIT = 25
BATCH_SIZE = 25
DATETIME_FORMATS = [
"%Y-%m-%dT%H:%M:%S.%fZ", # Pulp format
"%Y-%m-%d", # intl. format
"%Y-%m-%d %H:%M:%S", # ... with time
"%Y-%m-%dT%H:%M:%S", # ... with time and T as a separator
"%y/%m/%d", # US format
"%y/%m/%d %h:%M:%S %p", # ... with time
"%y/%m/%dT%h:%M:%S %p", # ... with time and T as a separator
"%y/%m/%d %H:%M:%S", # ... with time 24h
"%y/%m/%dT%H:%M:%S", # ... with time 24h and T as a separator
"%x", # local format
"%x %X", # ... with time
"%xT%X", # ... with time and T as a separator
]

href_regex = re.compile(r"\/([a-z0-9-_]+\/)+", flags=re.IGNORECASE)


Expand Down Expand Up @@ -66,9 +81,15 @@ def default(self, obj: Any) -> Any:
return super().default(obj)


def _preprocess_value(key: str, value: Any) -> Any:
def _preprocess_value(value: Any) -> Any:
if isinstance(value, str):
return value
if isinstance(value, PulpEntityContext):
return value.pulp_href
if isinstance(value, datetime.datetime):
return value.strftime(DATETIME_FORMATS[0])
if isinstance(value, Iterable):
return [_preprocess_value(item) for item in value]
return value


Expand All @@ -77,7 +98,7 @@ def preprocess_payload(payload: EntityDefinition) -> EntityDefinition:
return payload

return PreprocessedEntityDefinition(
{key: _preprocess_value(key, value) for key, value in payload.items() if value is not None}
{key: _preprocess_value(value) for key, value in payload.items() if value is not None}
)


Expand Down Expand Up @@ -514,8 +535,8 @@ def preprocess_entity(self, body: EntityDefinition, partial: bool = False) -> En
def list(self, limit: int, offset: int, parameters: Dict[str, Any]) -> List[Any]:
count: int = -1
entities: List[Any] = []
payload: Dict[str, Any] = self.scope
payload.update(parameters.copy())
payload: Dict[str, Any] = parameters.copy()
payload.update(self.scope)
payload["offset"] = offset
payload["limit"] = BATCH_SIZE
while limit != 0:
Expand Down
18 changes: 10 additions & 8 deletions pulpcore/cli/common/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from click.decorators import FC, F

from pulpcore.cli.common.context import (
DATETIME_FORMATS,
DEFAULT_LIMIT,
EntityDefinition,
EntityFieldDefinition,
Expand Down Expand Up @@ -526,6 +527,7 @@ def _type_callback(ctx: click.Context, param: click.Parameter, value: Optional[s
type=click.IntRange(1),
help=_("Limit the number of {entities} to show."),
)

offset_option = pulp_option(
"--offset",
default=0,
Expand Down Expand Up @@ -656,29 +658,29 @@ def _type_callback(ctx: click.Context, param: click.Parameter, value: Optional[s
pulp_created_gte_option = pulp_option(
"--created-after",
"pulp_created__gte",
help=_("Search for {entities} created at or after this ISO 8601 date"),
type=str,
help=_("Search for {entities} created at or after this date"),
type=click.DateTime(formats=DATETIME_FORMATS),
)

pulp_created_lte_option = pulp_option(
"--created-before",
"pulp_created__lte",
help=_("Search for {entities} created at or before this ISO 8601 date"),
type=str,
help=_("Search for {entities} created at or before this date"),
type=click.DateTime(formats=DATETIME_FORMATS),
)

pulp_last_updated_gte_option = pulp_option(
"--updated-after",
"pulp_last_updated__gte",
help=_("Search for {entities} last updated at or after this ISO 8601 date"),
type=str,
help=_("Search for {entities} last updated at or after this date"),
type=click.DateTime(formats=DATETIME_FORMATS),
)

pulp_last_updated_lte_option = pulp_option(
"--updated-before",
"pulp_last_updated__lte",
help=_("Search for {entities} last updated at or before this ISO 8601 date"),
type=str,
help=_("Search for {entities} last updated at or before this date"),
type=click.DateTime(formats=DATETIME_FORMATS),
)

retained_versions_option = pulp_option(
Expand Down
18 changes: 10 additions & 8 deletions pulpcore/cli/common/openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,31 +123,28 @@ def _download_api(self) -> bytes:

def extract_params(
self,
param_type: str,
param_in: str,
path_spec: Dict[str, Any],
method_spec: Dict[str, Any],
params: Dict[str, Any],
) -> Dict[str, Any]:
param_specs = {
entry["name"]: entry
for entry in path_spec.get("parameters", [])
if entry["in"] == param_type
if entry["in"] == param_in
}
param_specs.update(
{
entry["name"]: entry
for entry in method_spec.get("parameters", [])
if entry["in"] == param_type
if entry["in"] == param_in
}
)
result: Dict[str, Any] = {}
for name in list(params.keys()):
if name in param_specs:
param = params.pop(name)
param_spec = param_specs.pop(name)
style = param_spec.get(
"style", "form" if param_type in ("query", "cookie") else "simple"
)
param_schema = param_spec.get("schema")
if param_schema:
param_type = param_schema.get("type", "string")
Expand All @@ -157,7 +154,12 @@ def extract_params(
assert isinstance(param, Iterable) and not isinstance(
param, str
), f"Parameter {name} is expected to be a list."
if not param_spec.get("explode", style == "form"):
style = param_spec.get(
"style", "form" if param_in in ("query", "cookie") else "simple"
)
explode = param_spec.get("explode", style == "form")
if not explode:
# Not exploding means comma separated list
param = ",".join(param)
elif param_type == "integer":
assert isinstance(param, int)
Expand Down Expand Up @@ -338,7 +340,7 @@ def call(
validate_body=validate_body,
)

self.debug_callback(1, f"{method} {request.url}")
self.debug_callback(1, f"{operation_id} : {method} {request.url}")
for key, value in request.headers.items():
self.debug_callback(2, f" {key}: {value}")
if request.body is not None:
Expand Down
41 changes: 39 additions & 2 deletions pulpcore/cli/core/context.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import datetime
import hashlib
import os
import sys
Expand Down Expand Up @@ -327,6 +328,34 @@ class PulpTaskContext(PulpEntityContext):

resource_context: Optional[PulpEntityContext] = None

def list(self, limit: int, offset: int, parameters: Dict[str, Any]) -> List[Any]:
if not self.pulp_ctx.has_plugin(PluginRequirement("core", min="3.22.0dev")):
parameters = parameters.copy()
reserved_resources = parameters.pop("reserved_resources", None)
exclusive_resources = parameters.pop("exclusive_resources", None)
shared_resources = parameters.pop("shared_resources", None)
parameters.pop("reserved_resources__in", None)
parameters.pop("exclusive_resources__in", None)
parameters.pop("shared_resources__in", None)
reserved_resources_record = []
if reserved_resources:
reserved_resources_record.append(reserved_resources)
if exclusive_resources:
reserved_resources_record.append(exclusive_resources)
if shared_resources:
reserved_resources_record.append("shared:" + shared_resources)
if len(reserved_resources_record) > 1:
self.pulp_ctx.needs_plugin(
PluginRequirement(
"core",
min="3.22.0dev",
feature=_("specify multiple reserved resources"),
),
)
parameters["reserved_resources_record"] = reserved_resources_record

return super().list(limit=limit, offset=offset, parameters=parameters)

def cancel(self, task_href: str) -> Any:
return self.call(
"cancel",
Expand All @@ -337,11 +366,18 @@ def cancel(self, task_href: str) -> Any:
@property
def scope(self) -> Dict[str, Any]:
if self.resource_context:
return {"reserved_resources_record": [self.resource_context.pulp_href]}
if self.pulp_ctx.has_plugin(PluginRequirement("core", min="3.22.0dev")):
return {"reserved_resources": self.resource_context.pulp_href}
else:
return {"reserved_resources_record": [self.resource_context.pulp_href]}
else:
return {}

def purge(self, finished_before: Optional[str], states: Optional[List[str]]) -> Any:
def purge(
self,
finished_before: Optional[datetime.datetime],
states: Optional[List[str]],
) -> Any:
body: Dict[str, Any] = {}
if finished_before:
body["finished_before"] = finished_before
Expand Down Expand Up @@ -455,3 +491,4 @@ class PulpWorkerContext(PulpEntityContext):
ENTITIES = _("workers")
HREF = "worker_href"
ID_PREFIX = "workers"
HREF_PATTERN = r"workers/"
91 changes: 69 additions & 22 deletions pulpcore/cli/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@
import click

from pulpcore.cli.common.context import (
DATETIME_FORMATS,
PluginRequirement,
PulpContext,
PulpEntityContext,
pass_entity_context,
pass_pulp_context,
)
from pulpcore.cli.common.generic import list_command, pulp_group, pulp_option
from pulpcore.cli.common.generic import list_command, pulp_group, pulp_option, resource_option
from pulpcore.cli.common.i18n import get_translation
from pulpcore.cli.core.context import PulpTaskContext
from pulpcore.cli.core.context import PulpTaskContext, PulpWorkerContext

translation = get_translation(__name__)
_ = translation.gettext
Expand All @@ -22,27 +23,31 @@
# Generic reusable commands


def _task_group_filter_callback(
ctx: click.Context, param: click.Parameter, value: Optional[str]
) -> Optional[str]:
if value is not None:
pulp_ctx = ctx.find_object(PulpContext)
assert pulp_ctx is not None

# Example: "/pulp/api/v3/task-groups/a69230d2-506e-44c7-9c46-e64a905f85e7/"
match = re.match(
rf"({pulp_ctx.api_path}task-groups/)?"
r"([0-9a-f]{8})-?([0-9a-f]{4})-?([0-9a-f]{4})-?([0-9a-f]{4})-?([0-9a-f]{12})/?",
value,
)
if match:
value = "{}task-groups/{}-{}-{}-{}-{}/".format(
pulp_ctx.api_path, *match.group(2, 3, 4, 5, 6)
class HrefOrUuidCallback:
def __init__(self, base_href: str) -> None:
self.base_href = base_href

def __call__(
self, ctx: click.Context, param: click.Parameter, value: Optional[str]
) -> Optional[str]:
if value is not None:
pulp_ctx = ctx.find_object(PulpContext)
assert pulp_ctx is not None

# Example: "/pulp/api/v3/tasks/a69230d2-506e-44c7-9c46-e64a905f85e7/"
href_match = re.match(
rf"({pulp_ctx.api_path}{self.base_href}/)?"
r"([0-9a-f]{8})-?([0-9a-f]{4})-?([0-9a-f]{4})-?([0-9a-f]{4})-?([0-9a-f]{12})/?",
value,
)
else:
raise click.ClickException(_("Either an href or a UUID must be provided."))
if href_match:
value = "{}{}/{}-{}-{}-{}-{}/".format(
pulp_ctx.api_path, self.base_href, *href_match.group(2, 3, 4, 5, 6)
)
else:
raise click.ClickException(_("Either an href or a UUID must be provided."))

return value
return value


task_filter = [
Expand Down Expand Up @@ -79,7 +84,49 @@ def _task_group_filter_callback(
click.option(
"--task-group",
help=_("List only tasks in this task group. Provide pulp_href or UUID."),
callback=_task_group_filter_callback,
callback=HrefOrUuidCallback("task-groups"),
),
click.option(
"--parent-task",
help=_("Parent task by uuid or href."),
callback=HrefOrUuidCallback("tasks"),
),
resource_option(
"--worker",
default_plugin="core",
default_type="none",
context_table={"core:none": PulpWorkerContext},
href_pattern=PulpWorkerContext.HREF_PATTERN,
help=_("Worker used to execute the task by name or href."),
),
click.option(
"--created-resource",
"created_resources",
help=_("Href of a resource created in the task."),
),
click.option(
"--started-after",
"started_at__gte",
help=_("Filter for tasks started at or after this date"),
type=click.DateTime(formats=DATETIME_FORMATS),
),
click.option(
"--started-before",
"started_at__lte",
help=_("Filter for tasks started at or before this date"),
type=click.DateTime(formats=DATETIME_FORMATS),
),
click.option(
"--finished-after",
"finished_at__gte",
help=_("Filter for tasks finished at or after this date"),
type=click.DateTime(formats=DATETIME_FORMATS),
),
click.option(
"--finished-before",
"finished_at__lte",
help=_("Filter for tasks finished at or before this date"),
type=click.DateTime(formats=DATETIME_FORMATS),
),
]

Expand Down
Loading

0 comments on commit 6f7cd36

Please sign in to comment.