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

Reduce load time for the CLI client #2170

Draft
wants to merge 16 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 7 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
73 changes: 53 additions & 20 deletions cli_client/python/timesketch_cli_client/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,28 @@

from requests.exceptions import ConnectionError as RequestConnectionError


# pylint: disable=import-error
from timesketch_api_client import config as timesketch_config

# pylint: enable=import-error


from timesketch_cli_client.commands import analyze
from timesketch_cli_client.commands import config
from timesketch_cli_client.commands import importer
from timesketch_cli_client.commands import upload
from timesketch_cli_client.commands import search
from timesketch_cli_client.commands import sketch as sketch_command
from timesketch_cli_client.commands import timelines
from timesketch_cli_client.commands import version
from timesketch_cli_client.commands import events
from timesketch_cli_client.commands import sigma

from .definitions import DEFAULT_OUTPUT_FORMAT
from .version import get_version


class TimesketchCli(object):
"""Timesketch CLI state object.
"""CLI state object.

Attributes:
sketch_from_flag: Sketch ID if provided by flag
Expand All @@ -50,21 +52,49 @@ def __init__(self, api_client=None, sketch_from_flag=None, conf_file=""):
Args:
sketch_from_flag: Sketch ID if provided by flag.
"""
self.api = api_client
self.cached_api_client = api_client
self.cached_config_assistant = None
self.conf_file = conf_file
self.sketch_from_flag = sketch_from_flag

if not api_client:
try:
# TODO: Consider other config sections here as well.
self.api = timesketch_config.get_client(load_cli_config=True)
if not self.api:
raise RequestConnectionError
except RequestConnectionError:
click.echo("ERROR: Cannot connect to the Timesketch server.")
sys.exit(1)
@property
def config_assistant(self):
"""Config assistant from the API client.

Returns:
Config Assistant object.
"""
if self.cached_config_assistant:
return self.cached_config_assistant

from timesketch_api_client import config as timesketch_config

_config_assistant = timesketch_config.ConfigAssistant()
_config_assistant.load_config_file(self.conf_file, load_cli_config=True)
self.cached_config_assistant = _config_assistant
return self.cached_config_assistant

@property
def api_client(self):
"""Timesketch API client.

self.config_assistant = timesketch_config.ConfigAssistant()
self.config_assistant.load_config_file(conf_file, load_cli_config=True)
Returns:
API client object.
"""
if self.cached_api_client:
return self.cached_api_client

from timesketch_api_client import config as timesketch_config

try:
_api_client = timesketch_config.get_client(load_cli_config=True)
if not _api_client:
raise RequestConnectionError
self.cached_api_client = _api_client
except RequestConnectionError:
click.echo("ERROR: Cannot connect to the Timesketch server.")
sys.exit(1)
return self.cached_api_client

@property
def sketch(self):
Expand All @@ -77,9 +107,13 @@ def sketch(self):
sketch_from_config = self.config_assistant.get_config("sketch")

if self.sketch_from_flag:
active_sketch = self.api.get_sketch(sketch_id=int(self.sketch_from_flag))
active_sketch = self.api_client.get_sketch(
sketch_id=int(self.sketch_from_flag)
)
elif sketch_from_config:
active_sketch = self.api.get_sketch(sketch_id=int(sketch_from_config))
active_sketch = self.api_client.get_sketch(
sketch_id=int(sketch_from_config)
)

if not active_sketch:
click.echo(
Expand All @@ -88,7 +122,6 @@ def sketch(self):
)
sys.exit(1)

# Make sure we have access to the sketch.
try:
active_sketch.name
except KeyError:
Expand All @@ -113,7 +146,6 @@ def output_format(self):


@click.group(context_settings={"help_option_names": ["-h", "--help"]})
@click.version_option(version=get_version(), prog_name="Timesketch CLI")
@click.option("--sketch", type=int, default=None, help="Sketch to work in.")
@click.pass_context
def cli(ctx, sketch):
Expand All @@ -140,7 +172,8 @@ def cli(ctx, sketch):
cli.add_command(search.saved_searches_group)
cli.add_command(analyze.analysis_group)
cli.add_command(sketch_command.sketch_group)
cli.add_command(importer.importer)
cli.add_command(upload.upload)
cli.add_command(version.version)
cli.add_command(events.events_group)
cli.add_command(sigma.sigma_group)

Expand Down
4 changes: 2 additions & 2 deletions cli_client/python/timesketch_cli_client/commands/analyze.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@

import click

from timesketch_api_client import error


@click.group("analyze")
def analysis_group():
Expand Down Expand Up @@ -49,6 +47,8 @@ def run_analyzer(ctx, analyzer_name, timeline_id):
analyzer_name: Name of the analyzer to run.
timeline_id: Timeline ID of the timeline to analyze.
"""
from timesketch_api_client import error

sketch = ctx.obj.sketch
timelines = []
if timeline_id == "all":
Expand Down
16 changes: 3 additions & 13 deletions cli_client/python/timesketch_cli_client/commands/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@

import click

from timesketch_cli_client.definitions import SUPPORTED_OUTPUT_FORMATS


@click.group("config")
def config_group():
Expand All @@ -34,12 +32,7 @@ def set_group():
@click.argument("sketch_id")
@click.pass_context
def set_sketch(ctx, sketch_id):
"""Set the active sketch.

Args:
ctx: Click CLI context object.
Copy link
Collaborator

Choose a reason for hiding this comment

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

just stumbled across this PR, any reason you removed the docstring here?

sketch_id: ID of the sketch to save to config.
"""
"""Set the active sketch."""
ctx.obj.config_assistant.set_config("sketch", sketch_id)
ctx.obj.config_assistant.save_config()

Expand All @@ -48,12 +41,9 @@ def set_sketch(ctx, sketch_id):
@click.argument("output_format")
@click.pass_context
def set_output_format(ctx, output_format):
"""Set the output format.
"""Set the output format."""
from timesketch_cli_client.definitions import SUPPORTED_OUTPUT_FORMATS

Args:
ctx: Click CLI context object.
output_format: Format to use for output text.
"""
if output_format not in SUPPORTED_OUTPUT_FORMATS:
supported_formats = " ".join(SUPPORTED_OUTPUT_FORMATS)
click.echo(f"Unsupported format. Choose between {supported_formats}")
Expand Down
12 changes: 7 additions & 5 deletions cli_client/python/timesketch_cli_client/commands/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@
import sys

import click
from tabulate import tabulate

from timesketch_api_client import search


def format_output(search_obj, output_format, show_headers, show_internal_columns):
Expand All @@ -35,6 +32,8 @@ def format_output(search_obj, output_format, show_headers, show_internal_columns
Returns:
Search results in the requested output format.
"""
from tabulate import tabulate

dataframe = search_obj.to_pandas()

# Label is being set regardless of return_fields. Remove if it is not in
Expand Down Expand Up @@ -81,8 +80,9 @@ def describe_query(search_obj):
@click.option(
"--query",
"-q",
default="*",
help="Search query in OpenSearch query string format",
required=True,
default="*",
)
@click.option(
"--time",
Expand Down Expand Up @@ -156,6 +156,8 @@ def search_group(
show_internal_columns,
):
"""Search and explore."""
from timesketch_api_client import search

sketch = ctx.obj.sketch
output_format = ctx.obj.output_format
search_obj = search.Search(sketch=sketch)
Expand Down Expand Up @@ -244,7 +246,7 @@ def search_group(

@click.group("saved-searches")
def saved_searches_group():
"""Managed saved searches."""
"""Manage saved searches."""


@saved_searches_group.command("list")
Expand Down
4 changes: 2 additions & 2 deletions cli_client/python/timesketch_cli_client/commands/sketch.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def sketch_group():
@click.pass_context
def list_sketches(ctx):
"""List all sketches."""
api_client = ctx.obj.api
api_client = ctx.obj.api_client
for sketch in api_client.list_sketches():
click.echo(f"{sketch.id} {sketch.name}")

Expand All @@ -50,7 +50,7 @@ def describe_sketch(ctx):
@click.pass_context
def create_sketch(ctx, name, description):
"""Create a new sketch."""
api_client = ctx.obj.api
api_client = ctx.obj.api_client
if not description:
description = name
sketch = api_client.create_sketch(name=name, description=description)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,17 @@
import time

import click
from timesketch_import_client import importer as import_client


@click.command("import")
@click.command("upload")
jaegeral marked this conversation as resolved.
Show resolved Hide resolved
@click.option("--name", help="Name of the timeline.")
@click.option("--timeout", type=int, default=600, help="Seconds to wait for indexing.")
@click.argument("file_path", type=click.Path(exists=True))
@click.pass_context
def importer(ctx, name, timeout, file_path):
"""Import timeline.
def upload(ctx, name, timeout, file_path):
"""Upload a timeline to the server."""
from timesketch_import_client import importer as import_client

Args:
ctx: Click CLI context object.
name: Name of the timeline to create.
timeout: Seconds to wait for indexing.
file_path: File path to the file to import.
"""
sketch = ctx.obj.sketch
if not name:
name = click.format_filename(file_path, shorten=True)
Expand Down Expand Up @@ -78,4 +72,4 @@ def importer(ctx, name, timeout, file_path):
retry_count += 1
time.sleep(sleep_time_seconds)

click.echo(f"Timeline imported: {timeline.name}")
click.echo(f"Timeline uploaded: {timeline.name}")
24 changes: 24 additions & 0 deletions cli_client/python/timesketch_cli_client/commands/version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 2022 Google Inc. All rights reserved.
#
# 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.
"""Commands for importing timelines."""

import click

from ..version import get_version


@click.command("version")
def version():
"""Get the version of this tool."""
click.echo(f"Timesketch CLI version {get_version()}")
2 changes: 1 addition & 1 deletion cli_client/python/timesketch_cli_client/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# limitations under the License.
"""Version information for the Timesketch CLI client."""

__version__ = "20220411"
__version__ = "20220414"


def get_version():
Expand Down