gluon/gluon/particleGenerator/cli.py

385 lines
14 KiB
Python

# Copyright (c) 2016 Nokia, 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.
import os
import sys
import click
import json
import pkg_resources
from requests import delete
from requests import get
from requests import post
from requests import put
from gluon.common import exception as exc
from gluon.particleGenerator.generator import load_model
def print_basic_usage(argv, model_list):
print("Usage: %s --api <api_name> [OPTIONS] COMMAND[ARGS]..." %
os.path.basename(argv[0]))
print("\nOptions:")
print("--api TEXT Name of API, one of %s" % model_list)
print("--port INTEGER Port of endpoint (OS_PROTON_PORT)")
print("--host TEXT Host of endpoint (OS_PROTON_HOST)")
print("--help Show this message and exit.")
def get_api_model(argv, model_list):
try:
arg_idx = argv.index("--api")
val_idx = arg_idx + 1
except ValueError:
# If there is only one API model, --api is not needed
if len(model_list) == 1:
return model_list[0]
print("--api is not specified!\n")
print_basic_usage(argv, model_list)
sys.exit(-1)
try:
api_name = argv[val_idx]
except IndexError:
print("API name is not specified!\n")
print_basic_usage(argv, model_list)
sys.exit(-1)
if api_name not in model_list:
print("Invalid API name!\n")
print_basic_usage(argv, model_list)
sys.exit(-1)
del argv[arg_idx]
del argv[arg_idx]
return api_name
def get_token():
auth_url = os.environ.get('OS_AUTH_URL')
tenant = os.environ.get('OS_TENANT_NAME')
user = os.environ.get('OS_USERNAME')
password = os.environ.get('OS_PASSWORD')
if not auth_url or not tenant or not user or not password:
return None
auth_info = {'auth': {
'tenantName': tenant,
'passwordCredentials':
{'username': user, 'password': password}
}}
headers = {'Accept': 'application/json',
'Content-Type': 'application/json'}
resp = post(auth_url + '/v2.0/tokens', json=auth_info, headers=headers)
if resp.status_code != 200 and resp.status_code != 201:
raise exc.GluonClientException('Bad return status %d'
% resp.status_code,
status_code=resp.status_code)
json_resp = resp.json()
return json_resp.get('access').get('token').get('id')
def json_get(url):
headers = {'X-Auth-Token': get_token()}
resp = get(url, headers=headers)
if resp.status_code != 200:
raise exc.GluonClientException('Bad return status %d'
% resp.status_code,
status_code=resp.status_code)
try:
rv = json.loads(resp.content)
except Exception as e:
raise exc.MalformedResponseBody(reason="JSON unreadable: %s on %s"
% (e.args[0], resp.content))
return rv
def do_delete(url):
headers = {'X-Auth-Token': get_token()}
resp = delete(url, headers=headers)
if resp.status_code != 200 and resp.status_code != 204:
raise exc.GluonClientException('Bad return status %d'
% resp.status_code,
status_code=resp.status_code)
def do_post(url, values):
headers = {'X-Auth-Token': get_token()}
resp = post(url, json=values, headers=headers)
if resp.status_code != 200 and resp.status_code != 201:
raise exc.GluonClientException('Bad return status %d'
% resp.status_code,
status_code=resp.status_code)
try:
rv = json.loads(resp.content)
except Exception as e:
raise exc.MalformedResponseBody(reason="JSON unreadable: %s on %s"
% (e.args[0], resp.content))
return rv
def do_put(url, values):
headers = {'X-Auth-Token': get_token()}
resp = put(url, json=values, headers=headers)
if resp.status_code != 200:
raise exc.GluonClientException('Bad return status %d'
% resp.status_code,
status_code=resp.status_code)
try:
rv = json.loads(resp.content)
except Exception as e:
raise exc.MalformedResponseBody(reason="JSON unreadable: %s on %s"
% (e.args[0], resp.content))
return rv
def make_url(host, port, *args):
url = "http://%s:%d/proton" % (host, port)
for arg in args:
url = "%s/%s" % (url, arg)
return url
def make_list_func(api_model, version, tablename):
def list_func(**kwargs):
url = make_url(kwargs["host"], kwargs["port"], api_model,
version, tablename)
result = json_get(url)
print(json.dumps(result, indent=4))
return result
return list_func
def make_show_func(api_model, version, tablename, primary_key):
def show_func(**kwargs):
url = make_url(kwargs["host"], kwargs["port"], api_model,
version, tablename, kwargs[primary_key])
result = json_get(url)
print(json.dumps(result, indent=4))
return result
return show_func
def make_create_func(api_model, version, tablename):
def create_func(**kwargs):
url = make_url(kwargs["host"], kwargs["port"], api_model,
version, tablename)
del kwargs["host"]
del kwargs["port"]
data = {}
for key, val in kwargs.items():
if val is not None:
data[key] = val
result = do_post(url, data)
print(json.dumps(result, indent=4))
return create_func
def make_update_func(api_model, version, tablename, primary_key):
def update_func(**kwargs):
url = make_url(kwargs["host"], kwargs["port"], api_model,
version, tablename, kwargs[primary_key])
del kwargs["host"]
del kwargs["port"]
del kwargs[primary_key]
data = {}
for key, val in kwargs.items():
if val is not None:
data[key] = val
result = do_put(url, data)
print(json.dumps(result, indent=4))
return update_func
def make_delete_func(api_model, version, tablename, primary_key):
def delete_func(**kwargs):
url = make_url(kwargs["host"], kwargs["port"], api_model,
version, tablename, kwargs[primary_key])
do_delete(url)
return delete_func
def get_version(model):
version = str(model['info']['version'])
version = 'v' + version
return version
def get_primary_key(table_data):
primary = []
for k, v in table_data['attributes'].items():
if 'primary' in v:
primary = k
break
# If not specified, a UUID is used as the PK
if not primary:
table_data['attributes']['uuid'] = \
dict(type='string', length=36, primary=True, required=True)
primary = 'uuid'
table_data['primary'] = primary
return primary
def set_type(kwargs, col_desc):
if col_desc['type'] == 'string':
pass
elif col_desc['type'] == 'integer':
kwargs["type"] = int
elif col_desc['type'] == 'number':
kwargs["type"] = float
elif col_desc['type'] == 'boolean':
kwargs["type"] = bool
elif col_desc['type'] == 'enum':
kwargs["type"] = click.Choice(col_desc['values'])
else:
raise Exception('Unknown column type %s' % col_desc['type'])
def proc_model(cli, package_name="unknown",
model_dir="unknown",
api_model="unknown",
hostenv="unknown",
portenv="unknown",
hostdefault="unknown",
portdefault=0):
# print("loading model")
model = load_model(package_name, model_dir, api_model)
version = get_version(model)
for table_name, table_data in model['api_objects'].items():
get_primary_key(table_data)
for table_name, table_data in model['api_objects'].items():
try:
attrs = {}
for col_name, col_desc in table_data['attributes'].items():
try:
# Step 1: deal with object xrefs
if col_desc['type'] in model['api_objects']:
# If referencing another object,
# get the type of its primary key
tgt_name = col_desc['type']
tgt_data = model['api_objects'][tgt_name]
primary_col = tgt_data['primary']
table_data["attributes"][col_name]['type'] = \
tgt_data["attributes"][primary_col]["type"]
# Step 2: convert our special types to ones a CLI likes
if col_desc['type'] == 'uuid':
# UUIDs, from a CLI perspective, are a form of
# string
table_data["attributes"][col_name]['type'] = 'string'
table_data["attributes"][col_name]['length'] = 64
if col_desc.get('primary', False):
attrs['_primary_key'] = col_name
except Exception:
print('During processing of attribute ', col_name)
raise
if '_primary_key' not in attrs:
raise Exception("One and only one primary key has to "
"be given to each column")
attrs['__tablename__'] = table_data['api']['plural_name']
# chop off training 's'
attrs['__objname__'] = table_data['api']['name']
#
# Create CRUD commands for the table
#
hosthelp = "Host of endpoint (%s) " % hostenv
porthelp = "Port of endpoint (%s) " % portenv
list = make_list_func(api_model, version, attrs['__tablename__'])
list.func_name = "%s-list" % (attrs['__objname__'])
list = click.option("--host", envvar=hostenv,
default=hostdefault, help=hosthelp)(list)
list = click.option("--port", envvar=portenv,
default=portdefault, help=porthelp)(list)
cli.command()(list)
show = make_show_func(api_model, version, attrs['__tablename__'],
attrs['_primary_key'])
show.func_name = "%s-show" % (attrs['__objname__'])
show = click.option("--host", envvar=hostenv,
default=hostdefault, help=hosthelp)(show)
show = click.option("--port", envvar=portenv,
default=portdefault, help=porthelp)(show)
show = click.argument(attrs['_primary_key'])(show)
cli.command()(show)
create = make_create_func(api_model, version,
attrs['__tablename__'])
create.func_name = "%s-create" % (attrs['__objname__'])
create = click.option("--host", envvar=hostenv,
default=hostdefault, help=hosthelp)(create)
create = click.option("--port", envvar=portenv,
default=portdefault, help=porthelp)(create)
for col_name, col_desc in table_data['attributes'].items():
kwargs = {}
option_name = "--" + col_name
kwargs["default"] = None
required = col_desc.get('required', False)
kwargs["help"] = col_desc.get('description', "no description")
if required:
kwargs["required"] = True
set_type(kwargs, col_desc)
create = click.option(option_name, **kwargs)(create)
cli.command()(create)
update = make_update_func(api_model, version,
attrs['__tablename__'],
attrs['_primary_key'])
update.func_name = "%s-update" % (attrs['__objname__'])
update = click.option("--host", envvar=hostenv,
default=hostdefault, help=hosthelp)(update)
update = click.option("--port", envvar=portenv,
default=portdefault, help=porthelp)(update)
for col_name, col_desc in table_data['attributes'].items():
if col_name == attrs['_primary_key']:
continue
kwargs = {}
option_name = "--" + col_name
kwargs["default"] = None
kwargs["help"] = col_desc.get('description', "no description")
set_type(kwargs, col_desc)
update = click.option(option_name, **kwargs)(update)
update = click.argument(attrs['_primary_key'])(update)
cli.command()(update)
del_func = make_delete_func(api_model, version,
attrs['__tablename__'],
attrs['_primary_key'])
del_func.func_name = "%s-delete" % (attrs['__objname__'])
del_func = click.option("--host", envvar=hostenv,
default=hostdefault,
help=hosthelp)(del_func)
del_func = click.option("--port", envvar=portenv,
default=portdefault,
help=porthelp)(del_func)
del_func = click.argument(attrs['_primary_key'])(del_func)
cli.command()(del_func)
except Exception:
print('During processing of table ', table_name)
raise