1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
|
"""Call arbitrary API endpoints."""
import json
import click
from SoftLayer.CLI.command import SLCommand as SLCommand
from SoftLayer.CLI import environment
from SoftLayer.CLI import exceptions
from SoftLayer.CLI import formatting
from SoftLayer.CLI import helpers
from SoftLayer import utils
SPLIT_TOKENS = [
('in', ' IN '),
('eq', '='),
]
def _build_filters(_filters):
"""Builds filters using the filter options passed into the CLI.
This only supports the equals keyword at the moment.
"""
root = utils.NestedDict({})
for _filter in _filters:
operation = None
for operation, token in SPLIT_TOKENS:
# split "some.key=value" into ["some.key", "value"]
top_parts = _filter.split(token, 1)
if len(top_parts) == 2:
break
else:
raise exceptions.CLIAbort('Failed to find valid operation for: %s' % _filter)
key, value = top_parts
current = root
# split "some.key" into ["some", "key"]
parts = [part.strip() for part in key.split('.')]
# Actually drill down and add the filter
for part in parts[:-1]:
current = current[part]
if operation == 'eq':
current[parts[-1]] = utils.query_filter(value.strip())
elif operation == 'in':
current[parts[-1]] = {
'operation': 'in',
'options': [{
'name': 'data',
'value': [p.strip() for p in value.split(',')],
}],
}
return root.to_dict()
def _build_python_example(args, kwargs):
sorted_kwargs = sorted(kwargs.items())
call_str = 'import SoftLayer\n\n'
call_str += 'client = SoftLayer.create_client_from_env()\n'
call_str += 'result = client.call('
arg_list = [repr(arg) for arg in args]
arg_list += [key + '=' + repr(value)
for key, value in sorted_kwargs if value]
call_str += ',\n '.join(arg_list)
call_str += ')'
return call_str
def _validate_filter(ctx, param, value): # pylint: disable=unused-argument
"""Validates a JSON style object filter"""
_filter = None
if value:
try:
_filter = json.loads(value)
if not isinstance(_filter, dict):
raise exceptions.CLIAbort("\"{}\" should be a JSON object, but is a {} instead.".
format(_filter, type(_filter)))
except json.JSONDecodeError as error:
raise exceptions.CLIAbort("\"{}\" is not valid JSON. {}".format(value, error))
return _filter
def _validate_parameters(ctx, param, value): # pylint: disable=unused-argument
"""Checks if value is a JSON string, and converts it to a datastructure if that is true"""
validated_values = []
for parameter in value:
if isinstance(parameter, str):
# looks like a JSON string...
if '{' in parameter or '[' in parameter:
try:
parameter = json.loads(parameter)
except json.JSONDecodeError as error:
click.secho("{} looked like json, but was invalid, passing to API as is. {}".
format(parameter, error), fg='red')
validated_values.append(parameter)
return validated_values
@click.command('call', short_help="Call arbitrary API endpoints.", cls=SLCommand)
@click.argument('service')
@click.argument('method')
@click.argument('parameters', nargs=-1, callback=_validate_parameters)
@click.option('--id', '_id', help="Init parameter")
@helpers.multi_option('--filter', '-f', '_filters',
help="Object filters. This should be of the form: 'property=value' or 'nested.property=value'."
"Complex filters should use --json-filter.")
@click.option('--mask', help="String-based object mask")
@click.option('--limit', type=click.INT, help="Result limit")
@click.option('--offset', type=click.INT, help="Result offset")
@click.option('--orderBy', type=click.STRING,
help="To set the sort direction, ASC or DESC can be provided."
"This should be of the form: '--orderBy nested.property' default DESC or "
"'--orderBy nested.property=ASC', e.g. "
" --orderBy subnets.id default DESC"
" --orderBy subnets.id=ASC")
@click.option('--output-python / --no-output-python',
help="Show python example code instead of executing the call")
@click.option('--json-filter', callback=_validate_filter,
help="A JSON string to be passed in as the object filter to the API call. "
"Remember to use double quotes (\") for variable names. Can NOT be used with --filter. "
"Dont use whitespace outside of strings, or the slcli might have trouble parsing it.")
@environment.pass_env
def cli(env, service, method, parameters, _id, _filters, mask, limit, offset, orderby=None,
output_python=False, json_filter=None):
"""Call arbitrary API endpoints with the given SERVICE and METHOD.
For parameters that require a datatype, use a JSON string for that parameter.
Example::
slcli call-api Account getObject
slcli call-api Account getVirtualGuests --limit=10 --mask=id,hostname
slcli call-api Virtual_Guest getObject --id=12345
slcli call-api Metric_Tracking_Object getBandwidthData --id=1234 \\
"2015-01-01 00:00:00" "2015-01-1 12:00:00" public
slcli call-api Account getVirtualGuests \\
-f 'virtualGuests.datacenter.name=dal05' \\
-f 'virtualGuests.maxCpu=4' \\
--mask=id,hostname,datacenter.name,maxCpu
slcli call-api Account getVirtualGuests \\
-f 'virtualGuests.datacenter.name IN dal05,sng01'
slcli call-api Account getVirtualGuests \\
--json-filter '{"virtualGuests":{"hostname":{"operation":"^= test"}}}' --limit=10
slcli -v call-api SoftLayer_User_Customer addBulkPortalPermission --id=1234567 \\
'[{"keyName": "NETWORK_MESSAGE_DELIVERY_MANAGE"}]'
slcli call-api Account getVirtualGuests \\
--orderBy virttualguests.id=ASC
slcli call-api SoftLayer_Notification_Occurrence_Event getAllObjects \\
--json-filter='{"endDate": {"operation": "greaterThanDate", \\
"options": [{"name":"date", "value": ["10/14/2022"]}]}}' --limit=50
"""
if _filters and json_filter:
raise exceptions.CLIAbort("--filter and --json-filter cannot be used together.")
object_filter = _build_filters(_filters)
if orderby:
orderby = utils.build_filter_orderby(orderby)
object_filter = utils.dict_merge(object_filter, orderby)
if json_filter:
object_filter = utils.dict_merge(json_filter, object_filter)
args = [service, method] + list(parameters)
kwargs = {
'id': _id,
'filter': object_filter,
'mask': mask,
'limit': limit,
'offset': offset,
}
if output_python:
env.python_output(_build_python_example(args, kwargs))
else:
result = env.client.call(*args, **kwargs)
env.fout(formatting.iter_to_table(result))
|