Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions bin/add_whitelist_rule
1 change: 1 addition & 0 deletions bin/create_backup
1 change: 1 addition & 0 deletions bin/create_insights_annotation
1 change: 1 addition & 0 deletions bin/delete_whitelist_rule
1 change: 1 addition & 0 deletions bin/get_insights_annotations
135 changes: 135 additions & 0 deletions hypernode_api_python/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
HYPERNODE_API_BRANCHER_APP_ENDPOINT = "/v2/brancher/app/{}/"
HYPERNODE_API_FPM_STATUS_APP_ENDPOINT = "/v2/nats/{}/hypernode.show-fpm-status"
HYPERNODE_API_BRANCHER_ENDPOINT = "/v2/brancher/{}/"
HYPERNODE_API_INSIGHTS_ANNOTATION_LIST_ENDPOINT = "/v2/insights-annotation/"
HYPERNODE_API_INSIGHTS_ANNOTATION_CREATE_ENDPOINT = "/v2/insights-annotation/create/"
HYPERNODE_API_PRODUCT_APP_DETAIL_ENDPOINT = "/v2/product/app/{}/current/"
HYPERNODE_API_PRODUCT_LIST_ENDPOINT = "/v2/product/"
HYPERNODE_API_PRODUCT_PRICE_DETAIL_ENDPOINT = "/v2/product/{}/with_price/"
Expand Down Expand Up @@ -315,6 +317,19 @@ def get_available_backups_for_app(self, app_name):
"""
return self.requests("GET", HYPERNODE_API_BACKUPS_ENDPOINT.format(app_name))

def create_backup(self, app_name):
"""
Create a new snapshot backup of the specified app. This requires
sla-standard to be enabled on the Hypernode.
Example:
> client.create_backup('yourhypernodeappname').json()
> 'A job to create a backup has been posted'

:param str app_name: The app to create the backup for
:return obj response: The request response object
"""
return self.requests("POST", HYPERNODE_API_BACKUPS_ENDPOINT.format(app_name))

def get_app_eav_description(self):
"""
List all the available EAV settings that are available. These are
Expand Down Expand Up @@ -581,6 +596,59 @@ def get_whitelist_rules(self, app_name, filter_data=None):
"GET", HYPERNODE_API_WHITELIST_ENDPOINT.format(app_name), filter_data
)

def add_whitelist_rule(self, app_name, data):
"""
Add a whitelist rule for the specified app. See get_whitelist_options
for the available options.
Example:
> client.add_whitelist_rule(
> 'yourhypernodeappname',
> data={'ip': '1.2.3.4', 'type': 'database', 'description': 'my description'}
> ).json()
> {
> 'id': 1234,
> 'created': '2024-05-25T13:39:48Z',
> 'domainname': 'yourhypernodeappname.hypernode.io',
> 'ip': '1.2.3.4',
> 'type': 'database',
> 'description': 'my description'
> }

:param str app_name: The name of the Hypernode to add the whitelist
rule for
:param dict data: Data describing the whitelist rule to add. An
example could be: {'ip': '1.2.3.4', 'type': 'database'}. The type can
be one of 'waf', 'database' or 'ftp', and optionally a 'description'
can be specified.
:return obj response: The request response object
"""
return self.requests(
"POST", HYPERNODE_API_WHITELIST_ENDPOINT.format(app_name), data=data
)

def delete_whitelist_rule(self, app_name, data):
"""
Remove a whitelist rule for the specified app. See get_whitelist_rules
for the currently configured whitelist rules. On success the response
will have status_code 204 and no content.
Example:
> client.delete_whitelist_rule(
> 'yourhypernodeappname',
> data={'ip': '1.2.3.4', 'type': 'database'}
> ).status_code
> 204

:param str app_name: The name of the Hypernode to remove the whitelist
rule for
:param dict data: Data describing the whitelist rule to remove. An
example could be: {'ip': '1.2.3.4', 'type': 'database'}. The type can
be one of 'waf', 'database' or 'ftp'.
:return obj response: The request response object
"""
return self.requests(
"DELETE", HYPERNODE_API_WHITELIST_ENDPOINT.format(app_name), data=data
)

def get_current_product_for_app(self, app_name):
"""
Retrieve information about the product the specified App is currently on.
Expand Down Expand Up @@ -859,6 +927,73 @@ def get_fpm_status(self, app_name):
"POST", HYPERNODE_API_FPM_STATUS_APP_ENDPOINT.format(app_name)
)

def get_insights_annotations(self, params=None):
"""
List all custom Insights annotations for the Hypernodes you have
access to. Only annotations created through the API are listed,
system annotations generated from platform events are not included.
Example:
> client.get_insights_annotations().json()
> {
> 'count': 1,
> 'next': None,
> 'previous': None,
> 'results': [
> {
> 'id': 123,
> 'name': 'Deployed release 1.2.3',
> 'x_axis': 1756384957,
> 'app': 'yourhypernodeappname',
> 'metrics': 'memory_usage',
> 'created': '2025-08-28T12:42:37Z',
> 'modified': '2025-08-28T12:42:37Z'
> }
> ]
> }

:param dict params: Optional query parameters to paginate the
results with. An example could be: {'limit': 10, 'offset': 20}
:return obj response: The request response object
"""
return self.requests(
"GET", HYPERNODE_API_INSIGHTS_ANNOTATION_LIST_ENDPOINT, params=params
)

def create_insights_annotation(self, data):
"""
Create a custom Insights annotation for a Hypernode. The annotation
is shown in the Insights graphs at the point in time given by
'x_axis' (a unix timestamp in seconds). Optionally restrict the
annotation to specific graphs with 'metrics', a comma-separated list
of metric names. When omitted, the annotation applies to all metrics.
Example:
> client.create_insights_annotation(
> data={
> 'name': 'Deployed release 1.2.3',
> 'x_axis': 1756384957,
> 'app': 'yourhypernodeappname',
> 'metrics': 'memory_usage'
> }
> ).json()
> {
> 'id': 123,
> 'name': 'Deployed release 1.2.3',
> 'x_axis': 1756384957,
> 'app': 'yourhypernodeappname',
> 'metrics': 'memory_usage',
> 'created': '2025-08-28T12:42:37Z',
> 'modified': '2025-08-28T12:42:37Z'
> }

:param dict data: Data describing the Insights annotation to create.
An example could be: {'name': 'Deployed release 1.2.3',
'x_axis': 1756384957, 'app': 'yourhypernodeappname'}
:return obj response: The request response object
"""
return self.requests(
"POST", HYPERNODE_API_INSIGHTS_ANNOTATION_CREATE_ENDPOINT, data=data
)

def create_brancher(self, app_name, data):
"""
Create a new branch (server replica) of your Hypernode.
Expand Down
195 changes: 195 additions & 0 deletions hypernode_api_python/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,24 @@ def get_available_backups_for_app(args=None):
print_response(client.get_available_backups_for_app(app_name))


def create_backup(args=None):
parser = ArgumentParser(
description="""
Create a new snapshot backup of the Hypernode. This requires
sla-standard to be enabled on the Hypernode.

Example:
$ ./bin/create_backup
"A job to create a backup has been posted"
""",
formatter_class=RawTextHelpFormatter,
)
parser.parse_args(args=args)
client = get_client()
app_name = get_app_name()
print_response(client.create_backup(app_name))


def get_eav_description(args=None):
parser = ArgumentParser(
description="""
Expand Down Expand Up @@ -483,6 +501,84 @@ def get_whitelist_rules(args=None):
print_response(client.get_whitelist_rules(app_name))


def add_whitelist_rule(args=None):
parser = ArgumentParser(
description="""
Add a WAF whitelist rule for the Hypernode.

Example:
$ ./bin/add_whitelist_rule 1.2.3.4 --type database --description "my description"
{
"id": 1234,
"created": "2024-05-25T13:39:48Z",
"domainname": "yourhypernodeappname.hypernode.io",
"ip": "1.2.3.4",
"type": "database",
"description": "my description"
}
""",
formatter_class=RawTextHelpFormatter,
)
parser.add_argument(
"ip",
help="The IP address to whitelist",
)
parser.add_argument(
"--type",
help="The type of whitelist rule to add",
choices=["waf", "database", "ftp"],
default="database",
)
parser.add_argument(
"--description",
help="An optional description for the whitelist rule",
default="",
)
args = parser.parse_args(args=args)
client = get_client()
app_name = get_app_name()
data = {"ip": args.ip, "type": args.type, "description": args.description}
print_response(client.add_whitelist_rule(app_name, data=data))


def delete_whitelist_rule(args=None):
parser = ArgumentParser(
description="""
Remove a WAF whitelist rule for the Hypernode.

Example:
$ ./bin/delete_whitelist_rule 1.2.3.4 --type database
Whitelist rule '1.2.3.4' (database) has been removed.
""",
formatter_class=RawTextHelpFormatter,
)
parser.add_argument(
"ip",
help="The whitelisted IP address to remove. See ./bin/get_whitelist_rules",
)
parser.add_argument(
"--type",
help="The type of the whitelist rule to remove",
choices=["waf", "database", "ftp"],
default="database",
)
args = parser.parse_args(args=args)
client = get_client()
app_name = get_app_name()
data = {"ip": args.ip, "type": args.type}
try:
client.delete_whitelist_rule(app_name, data=data)
print("Whitelist rule '{}' ({}) has been removed.".format(args.ip, args.type))
exit(EX_OK)
except Exception as e:
print(
"Whitelist rule '{}' ({}) failed to be removed: {}".format(
args.ip, args.type, e
)
)
exit(EX_UNAVAILABLE)


def get_current_product_for_app(args=None):
parser = ArgumentParser(
description="""
Expand Down Expand Up @@ -703,6 +799,105 @@ def destroy_brancher(args=None):
exit(EX_UNAVAILABLE)


def get_insights_annotations(args=None):
parser = ArgumentParser(
description="""
List all custom Insights annotations for the Hypernodes you have access to.
Only annotations created through the API are listed, system annotations
generated from platform events are not included.

Example:
$ ./bin/get_insights_annotations
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 123,
"name": "Deployed release 1.2.3",
"x_axis": 1756384957,
"app": "yourhypernodeappname",
"metrics": "memory_usage",
"created": "2025-08-28T12:42:37Z",
"modified": "2025-08-28T12:42:37Z"
}
]
}
""",
formatter_class=RawTextHelpFormatter,
)
parser.add_argument(
"--limit",
help="Number of results to return per page",
type=int,
)
parser.add_argument(
"--offset",
help="The initial index from which to return the results",
type=int,
)
args = parser.parse_args(args=args)
client = get_client()
params = {}
if args.limit is not None:
params["limit"] = args.limit
if args.offset is not None:
params["offset"] = args.offset
print_response(client.get_insights_annotations(params=params))


def create_insights_annotation(args=None):
parser = ArgumentParser(
description="""
Create a custom Insights annotation for the Hypernode. The annotation is
shown in the Insights graphs at the specified point in time. Optionally
restrict the annotation to specific graphs with --metrics. When omitted,
the annotation applies to all metrics.

Example:
$ ./bin/create_insights_annotation "Deployed release 1.2.3" 1756384957 --metrics memory_usage
{
"id": 123,
"name": "Deployed release 1.2.3",
"x_axis": 1756384957,
"app": "yourhypernodeappname",
"metrics": "memory_usage",
"created": "2025-08-28T12:42:37Z",
"modified": "2025-08-28T12:42:37Z"
}
""",
formatter_class=RawTextHelpFormatter,
)
parser.add_argument(
"name",
help="The name of the annotation to create",
)
parser.add_argument(
"x_axis",
help="The point in time of the annotation as a unix timestamp in seconds",
type=int,
)
parser.add_argument(
"--metrics",
help="An optional comma-separated list of metric names to restrict "
"the annotation to",
)
parser.add_argument(
"--metadata",
help="Optional metadata to store with the annotation",
)
args = parser.parse_args(args=args)
client = get_client()
app_name = get_app_name()
data = {"name": args.name, "x_axis": args.x_axis, "app": app_name}
if args.metrics is not None:
data["metrics"] = args.metrics
if args.metadata is not None:
data["metadata"] = args.metadata
print_response(client.create_insights_annotation(data=data))


def get_fpm_status(args=None):
parser = ArgumentParser(
description="""
Expand Down
Loading
Loading