cliapi ( pronounced calliope ): A Python framework for creating Unix, "getopt()" style CLI scripts composed from an arbitrary set of APIs.
...at its core, cliapi is a data driven, state machine for dynamically generating CLIs.
-
entire CLI is generated by applying python decorators to API calls together with a dictionary which specifies a backing-API-store. This decorator is applied to each API that is supported by a "plugin provider". For each cloud provider (eg. Azure or GCE) a supporting "plugin" module is added to the
cliapi/providers
directory (seecliapi directory layout
below) Presumably the plugin developer will rarely need to change the cliapi framework itself which involves a little python meta-programming (1 decorator + 1 dictionary overriding class). -
consistent command line behavior is enforced by the cliapi framework across all plugin providers. Several common commands are automatically inherited by all plugin providers: (this allows a level of "discoverablility" to the CLI across all plugin providers)
- "--list-providers" Lists all the available plugin providers.
- "--provider=" specify a particular provider plugin for all CLI commands.
- "--all" Lists all data returned by all APIs supported by a single provider.
- "--list-apis" Lists all the APIs available by a particular plugin provider.
- "--query=" extracts specified API data using a python-dictionary-restricted syntax.
-
other behaviors enforced by the cliapi framework:
- error handling and help is also consistent across all plugin providers.
- multiple queries in same command will return a JSON list in "option order" by default
- single query will return a single JSON element.
-
multiple data queries within the same CLI will result in "at most" a single API call.
An example cliapi plugin provider for Azuremeta API + SUSE extensions can be found here: https://github.com/edlane/cliapi/blob/master/cliapi/providers/azure.py
-
provider: an associated set of APIs accessed from the CLI in a particular context or cloud environment e.g. "azure", "gce", "ec2", ... but can essentially be any mix of apis
-
api: an API for interfacing to remote or local services. If the interface can be implemented as a python function with an (*args, **kwargs) style calling convention AND it returns a JSON serializable object, THEN it can easily become a configurable CLI query. With the cliapi decorator, both required and optional parameters are expressible through the CLI. Help is also handled by the cliapi framework.
-
scoops: a dictionary which maps a CLI query name to a particular API data scoop. "scoops" are really just "sandboxed python eval()" statements. This allows predefined scoops to be expressed as CLI options. It also allows for restricted ad-hoc queries to be provided on the command line when a return value is not currently supported as an option in the CLI. The query uses Python's dictionary lookup syntax for slice slices (See examples using --query= below). Python's eval() function allows limiting access to a single data structure and "no builtins" through this facility:
-
fetchers: a dictionary which maps from a particular API name to the actual python function which provides the backing store for the contents of the top-level API dictionary.
├── cliapi root of cliapi package
│ ├── __init__.py
│ ├── cliapi_lib.py framework meta classes and decorators
│ ├── cliapi.py main() and the CLI generation code
│ └── what_cloud.py module (useful for detecting which cloud plugins are valid)
│ ├── providers directory for plugin providers
│ │ ├── __init__.py
│ │ ├── azure.py plugin for Azure/SUSE APIs
│ │ ├── <your plugin here> ...your plugin goes here
│ │ ├── ... ...additional plugins are automatically discovered by cliapi
│ │ └── test.py ..."because it's not a framework without at least 2 plugins"
├── design.md design considerations for cliapi
├── LICENSE provisional license (Apache2 is mutable into any other license)
├── README.md this document
└── setup.py Python installation script
example #1 - Common help AND plugin provider help (default provider = azure)
ed-sle12sp3byos:/home/lane/cliapi # cliapi --help
usage: /usr/bin/cliapi [display option#1]... [API option#1]... [CLI option]
***[ azure ]*** provider Display options:
--internal-ip
--location region location
--cloud-service what
--instance-name name of instance
--mac the MAC address for this interface
--external-ip
***[ azure ]*** provider API config options:
--api_version= default='2017-08-01', azure metadata api version
Common CLI options:
--help help for this CLI command
--provider= specify name of provider module
--list-providers list all available providers
--list-apis list all available APIs for specified provider
--query= specify a python dictionary style query command
--all output all API results for specified API options or defaults
example #2 - a predefined query option (default provider = azure)
ed-sle12sp3byos:/home/lane/cliapi # cliapi --internal-ip
"172.16.3.8"
example #3 - all values returned by all APIs (default provider = azure)
ed-sle12sp3byos:/home/lane/cliapi # cliapi --all
{
"meta_data": {
"compute": {
"location": "westus",
"vmSize": "Standard_B1ms",
"osType": "Linux",
"platformUpdateDomain": "0",
"sku": "12-SP3",
"name": "ed-sle12sp3byos",
"placementGroupId": "",
"resourceGroupName": "ed_lane",
"offer": "SLES-BYOS",
"vmId": "ce01dc32-6d0a-40bd-9534-a3509f768a53",
"tags": "",
"subscriptionId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"version": "2018.02.21",
"publisher": "SUSE",
"platformFaultDomain": "0"
},
"network": {
"interface": [
{
"ipv6": {
"ipAddress": []
},
"ipv4": {
"ipAddress": [
{
"publicIpAddress": "40.112.253.198",
"privateIpAddress": "172.16.3.8"
}
],
"subnet": [
{
"prefix": "24",
"address": "172.16.3.0"
}
]
},
"macAddress": "000D3A3AE8A5"
}
]
}
},
"cloud-service": "__ed-sle12sp3byosService.cloudapp.net",
"tag": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
example #4 - list of valid plugin providers
ed-sle12sp3byos:/home/lane/cliapi # cliapi --list-providers
[
"test",
"azure"
]
example #5 - list of APIs supported by plugin provider (default provider = azure)
ed-sle12sp3byos:/home/lane/cliapi # cliapi --list-apis
[
"tag",
"cloud-service",
"meta_data"
]
example #6 - an ad-hoc restricted python dictionary syntax query (provider='test')
lane@suse-laptop:~/develop/garage/cliapi> cliapi --query="['meta_data']['compute']['offer']" --provider=test
"SLES-BYOS"
example #7 - mixed multiple queries with a single CLI call (provider='test')
lane@suse-laptop:~/develop/garage/cliapi> cliapi --location --query="['meta_data']['compute']['offer']" --provider=test
[
"westus",
"SLES-BYOS"
]