patternpythonMinor
REST API Communication script | Argparsing
Viewed 0 times
scriptrestargparsingcommunicationapi
Problem
I have been making a script to interact with a library I created before. I have ended up with a working script, but feel that the argparsing could be done a lot cleaner, and better.
First of all, some details about the script. These are the different processes that I try to support, using * to show it's required:
-
Login - target, username, password*
-
Query - target, username, password, query, starttime, endtime, discoverfields, summaryfields, fieldssummary, localsearch, searchtype, timeout, wait
-
Status - target, username, password, status, searchid*, wait
-
Histogram - target, username, password, histogram, searchid*
-
Drilldown - target, username, password, drilldown, searchid*, starttime, endtime
-
Event - target, username, password, event, searchid*, dir, fields, length, offset
-
Raw event - target, username, password, rawevent, searchid, rowid
-
Chart data - target, username, password, chartdata, searchid*, fields, length, offset
So my questions are these:
Here is my code, currently print is used for output, but will support CSV and JSON in the future:
```
#!/usr/bin/env python3
"""Script to generate searches on the ArcSight Logger"""
import arcsightrest
import argparse
parser
First of all, some details about the script. These are the different processes that I try to support, using * to show it's required:
-
Login - target, username, password*
-
Query - target, username, password, query, starttime, endtime, discoverfields, summaryfields, fieldssummary, localsearch, searchtype, timeout, wait
-
Status - target, username, password, status, searchid*, wait
-
Histogram - target, username, password, histogram, searchid*
-
Drilldown - target, username, password, drilldown, searchid*, starttime, endtime
-
Event - target, username, password, event, searchid*, dir, fields, length, offset
-
Raw event - target, username, password, rawevent, searchid, rowid
-
Chart data - target, username, password, chartdata, searchid*, fields, length, offset
So my questions are these:
- Would using sub parsers be better, if so should I define the same sub parsers for each "parent" parser? So if two or more requires
searchidthen should I define it on all the parents?
- Is there a better way to require to use one, and only one of the parents when running the script? So they cannot be combined, and they can't be missing.
- Is there a better way to write help for sub parsers? For example
help query starttime.
- Is using
.formatstill useful in Python 3?
- Currently if a parent argument is called, it will use all optional arguments in the calls, even if empty, is this okay? Or you would end up with unlimited amounts of
ifstatements.
- Any coding standards around parsers like this that can be followed?
Here is my code, currently print is used for output, but will support CSV and JSON in the future:
```
#!/usr/bin/env python3
"""Script to generate searches on the ArcSight Logger"""
import arcsightrest
import argparse
parser
Solution
Yes, subparsers can improve a lot of things. You could also use
The important thing being that, using subparsers, you can limit the amount of available options to the action you are performing.
Moreover, you should consider using positional arguments rather than optional ones +
Looking at your commands, you have two possibilities:
Usage would be:
The disadvantage being that you need to specify the search id for each search parser. Or
where you can factorize the definition of
I’ll show the second approach, though, as it is easy to get back to the first one from there:
```
#!/usr/bin/env python3
"""Script to generate searches on the ArcSight Logger"""
import arcsightrest
import argparse
def parse_command_line():
parser = argparse.ArgumentParser(
description='Script used to send search queries '
'to ArcSight Logger API')
# General informations
parser.add_argument('target', help='IP Address of the Loggger')
parser.add_argument('username', help='Username to access the logger')
parser.add_argument('password', help='Password to access the logger')
parser.add_argument('-s', '--unsecuressl', action='store_true',
help='Disable ssl warnings')
# Commands
command_subparser = parser.add_subparsers(dest='action', metavar='action')
# Query
query = command_subparser.add_parser('query', help='search informations')
query.add_argument('query', help='Query to be used in the search')
query.add_argument(
'--starttime', help='From which time the query should look')
query.add_argument('--endtime', help='To which time the query should look')
query.add_argument(
'--discoverfields', help='Try to discover fields in the events found')
query.add_argument('--summaryfields', help='The list of fields')
query.add_argument('--fieldssummary', help='Use fields summary')
query.add_argument(
'--localsearch', help='Indicates the search is local only')
query.add_argument('--searchtype', help='Interactive search or not')
query.add_argument(
'--timeout', help='The number of milliseconds to keep the '
'search after it has finished running')
query.add_argument(
'--wait', action='store_true', help='Wait for search to finish')
# Past search
search = command_subparser.add_parser(
'search', help='actions on previous search')
search.add_argument(
'search_id', help='Search id of a currently running search')
search_subparser = search.add_subparsers(
dest='search_kind', metavar='kind')
# Status settings
status = search_subparser.add_parser(
'status', help='Status of running search')
status.add_argument(
'--wait', action='store_true', help='Wait for search to finish')
# Histogram Settings
histogram = search_subparser.add_parser(
'histogram', help='Get histogram overview of specific earch')
# Drilldown Settings
drilldown = search_subparser.add_parser(
'drilldown', help='Gets drilldown of specific search')
drilldown.add_argument(
'--starttime', help='From which time the search should look')
drilldown.add_argument(
'--endtime', help='To which time the search should look')
# Event Settings
event = search_subparser.add_parser(
'event', help='Get all information from a finished search')
event.add_argument(
'--dir', help='Sort direction based on event time')
event.add_argument('--field')
event.add_argument('--length')
event.add_argument('--offset')
# Raw Event Settings
raw_event = search_subparser.add_parser(
'rawevent', help='Get the raw event results from a search')
raw_event.add_argument(
'row_id', help='Specific row id for the raw event')
# Chart Data Settings
chart_data = search_subparser.add_parser(
'chartdata', help='Returns data in a chart format')
chart_data.add_argument('--field')
chart_data.add_argument('--length')
chart_data.add_argument
if __name__ == '__main__': even though it is not necessary for this kind of "executable only" script.The important thing being that, using subparsers, you can limit the amount of available options to the action you are performing.
Moreover, you should consider using positional arguments rather than optional ones +
required=True. It feels cleaner on the help message.Looking at your commands, you have two possibilities:
- use only one subparser that will provide a parser for each of your commands (
login,query,status,histogram, …);
- or use two subparsers: one providing the
login,queryandsearchcommands and a second one undersearchto provide the various kind of searches.
Usage would be:
$ python script.py [-s] # login
$ python script.py [-s] query [options]
$ python script.py [-s] status [-w]
$ python script.py [-s] histogram
etc…The disadvantage being that you need to specify the search id for each search parser. Or
$ python script.py [-s] # login
$ python script.py [-s] query [options]
$ python script.py [-s] search status [-w]
$ python script.py [-s] search histogram
etc…where you can factorize the definition of
search_id to a common search parser but you will put the burden on the user by having they to provide a longer command line.I’ll show the second approach, though, as it is easy to get back to the first one from there:
```
#!/usr/bin/env python3
"""Script to generate searches on the ArcSight Logger"""
import arcsightrest
import argparse
def parse_command_line():
parser = argparse.ArgumentParser(
description='Script used to send search queries '
'to ArcSight Logger API')
# General informations
parser.add_argument('target', help='IP Address of the Loggger')
parser.add_argument('username', help='Username to access the logger')
parser.add_argument('password', help='Password to access the logger')
parser.add_argument('-s', '--unsecuressl', action='store_true',
help='Disable ssl warnings')
# Commands
command_subparser = parser.add_subparsers(dest='action', metavar='action')
# Query
query = command_subparser.add_parser('query', help='search informations')
query.add_argument('query', help='Query to be used in the search')
query.add_argument(
'--starttime', help='From which time the query should look')
query.add_argument('--endtime', help='To which time the query should look')
query.add_argument(
'--discoverfields', help='Try to discover fields in the events found')
query.add_argument('--summaryfields', help='The list of fields')
query.add_argument('--fieldssummary', help='Use fields summary')
query.add_argument(
'--localsearch', help='Indicates the search is local only')
query.add_argument('--searchtype', help='Interactive search or not')
query.add_argument(
'--timeout', help='The number of milliseconds to keep the '
'search after it has finished running')
query.add_argument(
'--wait', action='store_true', help='Wait for search to finish')
# Past search
search = command_subparser.add_parser(
'search', help='actions on previous search')
search.add_argument(
'search_id', help='Search id of a currently running search')
search_subparser = search.add_subparsers(
dest='search_kind', metavar='kind')
# Status settings
status = search_subparser.add_parser(
'status', help='Status of running search')
status.add_argument(
'--wait', action='store_true', help='Wait for search to finish')
# Histogram Settings
histogram = search_subparser.add_parser(
'histogram', help='Get histogram overview of specific earch')
# Drilldown Settings
drilldown = search_subparser.add_parser(
'drilldown', help='Gets drilldown of specific search')
drilldown.add_argument(
'--starttime', help='From which time the search should look')
drilldown.add_argument(
'--endtime', help='To which time the search should look')
# Event Settings
event = search_subparser.add_parser(
'event', help='Get all information from a finished search')
event.add_argument(
'--dir', help='Sort direction based on event time')
event.add_argument('--field')
event.add_argument('--length')
event.add_argument('--offset')
# Raw Event Settings
raw_event = search_subparser.add_parser(
'rawevent', help='Get the raw event results from a search')
raw_event.add_argument(
'row_id', help='Specific row id for the raw event')
# Chart Data Settings
chart_data = search_subparser.add_parser(
'chartdata', help='Returns data in a chart format')
chart_data.add_argument('--field')
chart_data.add_argument('--length')
chart_data.add_argument
Code Snippets
$ python script.py <target> <username> <password> [-s] # login
$ python script.py <target> <username> <password> [-s] query <query> [options]
$ python script.py <target> <username> <password> [-s] status <search_id> [-w]
$ python script.py <target> <username> <password> [-s] histogram <search_id>
etc…$ python script.py <target> <username> <password> [-s] # login
$ python script.py <target> <username> <password> [-s] query <query> [options]
$ python script.py <target> <username> <password> [-s] search <search_id> status [-w]
$ python script.py <target> <username> <password> [-s] search <search_id> histogram
etc…#!/usr/bin/env python3
"""Script to generate searches on the ArcSight Logger"""
import arcsightrest
import argparse
def parse_command_line():
parser = argparse.ArgumentParser(
description='Script used to send search queries '
'to ArcSight Logger API')
# General informations
parser.add_argument('target', help='IP Address of the Loggger')
parser.add_argument('username', help='Username to access the logger')
parser.add_argument('password', help='Password to access the logger')
parser.add_argument('-s', '--unsecuressl', action='store_true',
help='Disable ssl warnings')
# Commands
command_subparser = parser.add_subparsers(dest='action', metavar='action')
# Query
query = command_subparser.add_parser('query', help='search informations')
query.add_argument('query', help='Query to be used in the search')
query.add_argument(
'--starttime', help='From which time the query should look')
query.add_argument('--endtime', help='To which time the query should look')
query.add_argument(
'--discoverfields', help='Try to discover fields in the events found')
query.add_argument('--summaryfields', help='The list of fields')
query.add_argument('--fieldssummary', help='Use fields summary')
query.add_argument(
'--localsearch', help='Indicates the search is local only')
query.add_argument('--searchtype', help='Interactive search or not')
query.add_argument(
'--timeout', help='The number of milliseconds to keep the '
'search after it has finished running')
query.add_argument(
'--wait', action='store_true', help='Wait for search to finish')
# Past search
search = command_subparser.add_parser(
'search', help='actions on previous search')
search.add_argument(
'search_id', help='Search id of a currently running search')
search_subparser = search.add_subparsers(
dest='search_kind', metavar='kind')
# Status settings
status = search_subparser.add_parser(
'status', help='Status of running search')
status.add_argument(
'--wait', action='store_true', help='Wait for search to finish')
# Histogram Settings
histogram = search_subparser.add_parser(
'histogram', help='Get histogram overview of specific earch')
# Drilldown Settings
drilldown = search_subparser.add_parser(
'drilldown', help='Gets drilldown of specific search')
drilldown.add_argument(
'--starttime', help='From which time the search should look')
drilldown.add_argument(
'--endtime', help='To which time the search should look')
# Event Settings
event = search_subparser.add_parser(
'event', help='Get all information from a finished search')
event.add_argument(
'--dir', help='Sort direction based on event time')
event.add_argument('--field')
event.add_argument('--length')
event.add$ python script.py <target> <username> <password> query ... --wait
$ python script.py <target> <username> <password> search 12345 rawevent 12Context
StackExchange Code Review Q#148729, answer score: 5
Revisions (0)
No revisions yet.