2caafdc8aa
Create Dockerfile and Makefile for Ranger CLI. Changes made to CLI code to account for separate URLs for each ranger service. Minor update to flavor code to fix error detected during bandit scan. Change-Id: If787e3dda4039d8755abd5dad05cacf685113565
428 lines
19 KiB
Python
428 lines
19 KiB
Python
#!/usr/bin/python
|
|
import argparse
|
|
import cli_common
|
|
import config
|
|
import orm.base_config as base_config
|
|
import os
|
|
import requests
|
|
|
|
|
|
class ResponseError(Exception):
|
|
pass
|
|
|
|
|
|
class ConnectionError(Exception):
|
|
pass
|
|
|
|
|
|
def add_to_parser(service_sub):
|
|
parser = \
|
|
service_sub.add_parser('fms', help='Flavor Management Service',
|
|
formatter_class=lambda prog:
|
|
argparse.HelpFormatter(prog,
|
|
max_help_position=30,
|
|
width=120))
|
|
parser.add_argument('--version', action='version', version='%(prog)s 1.0')
|
|
parser.add_argument('--auth-region', type=str,
|
|
help='Region used for authentication',
|
|
default=get_environment_variable('auth-region'))
|
|
parser.add_argument('--tenant-name', type=str,
|
|
help='Keystone user tenant name',
|
|
default=get_environment_variable('tenant-name'))
|
|
parser.add_argument('--username', type=str, help='Keystone user name',
|
|
default=get_environment_variable('username'))
|
|
parser.add_argument('--password', type=str, help='Keystone user password',
|
|
default=get_environment_variable('password'))
|
|
parser.add_argument('--rms-base-url', type=str, help='RMS base URL',
|
|
default=get_environment_variable('rms-base-url'))
|
|
parser.add_argument('--fms-base-url', type=str, help='FMS base URL',
|
|
default=get_environment_variable('fms-base-url'))
|
|
parser.add_argument('--tracking_id', type=str, help='tracking id')
|
|
parser.add_argument('--port', type=int, help='port number of FMS server')
|
|
parser.add_argument('--timeout', type=int,
|
|
help='request timeout in seconds (default: 10)')
|
|
parser.add_argument('-v', '--verbose', help='show details',
|
|
action="store_true")
|
|
parser.add_argument('-f', '--faceless',
|
|
help=argparse.SUPPRESS,
|
|
default=False,
|
|
action="store_true")
|
|
subparsers = parser.add_subparsers(dest='subcmd',
|
|
metavar='<subcommand> [-h] <args>')
|
|
|
|
# flavor
|
|
h1, h2 = ('[<"X-RANGER-Client" header>]',
|
|
'<data file with new flavor JSON>')
|
|
parser_create_flavor = subparsers.add_parser('create_flavor',
|
|
help='%s %s' % (h1, h2))
|
|
parser_create_flavor.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
|
|
parser_create_flavor.add_argument('datafile', type=argparse.FileType('r'),
|
|
help=h2)
|
|
|
|
h1, h2, h3 = ('[<"X-RANGER-Client" header>]', '<flavor id>',
|
|
'<data file with tag(s) JSON>',)
|
|
parser_add_tags = subparsers.add_parser('add_tags',
|
|
help='%s %s %s' % (h1, h2, h3))
|
|
parser_add_tags.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
|
|
parser_add_tags.add_argument('flavorid', type=str, help=h2)
|
|
parser_add_tags.add_argument('datafile', type=argparse.FileType('r'),
|
|
help=h3)
|
|
|
|
h1, h2, h3 = ('[<"X-RANGER-Client" header>]', '<flavor id>',
|
|
'<data file with tag(s) JSON>',)
|
|
parser_replace_tags = subparsers.add_parser('replace_tags',
|
|
help='%s %s %s' % (h1, h2, h3))
|
|
parser_replace_tags.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
|
|
parser_replace_tags.add_argument('flavorid', type=str, help=h2)
|
|
parser_replace_tags.add_argument('datafile', type=argparse.FileType('r'),
|
|
help=h3)
|
|
|
|
h1, h2, h3 = '[<"X-RANGER-Client" header>]', '<flavor id>', '<tag name>'
|
|
parser_delete_tag = subparsers.add_parser('delete_tag',
|
|
help='%s %s %s' % (h1, h2, h3))
|
|
parser_delete_tag.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
|
|
parser_delete_tag.add_argument('flavorid', type=str, help=h2)
|
|
parser_delete_tag.add_argument('tagname', type=str, help=h3)
|
|
|
|
h1, h2 = '[<"X-RANGER-Client" header>]', '<flavor id>'
|
|
parser_delete_all_tags = subparsers.add_parser('delete_all_tags',
|
|
help='%s %s' % (h1, h2))
|
|
parser_delete_all_tags.add_argument('client',
|
|
**cli_common.ORM_CLIENT_KWARGS)
|
|
parser_delete_all_tags.add_argument('flavorid', type=str, help=h2)
|
|
|
|
h1, h2 = '[<"X-RANGER-Client" header>]', '<flavor id>'
|
|
parser_get_tags = subparsers.add_parser('get_tags',
|
|
help='%s %s' % (h1, h2))
|
|
parser_get_tags.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
|
|
parser_get_tags.add_argument('flavorid', type=str, help=h2)
|
|
|
|
# extra specs
|
|
h1, h2 = '[<"X-RANGER-Client" header>]', '<flavor id>'
|
|
parser_get_extra_specs = subparsers.add_parser('get_extra_specs',
|
|
help='%s %s' % (h1, h2))
|
|
parser_get_extra_specs.add_argument('client',
|
|
**cli_common.ORM_CLIENT_KWARGS)
|
|
parser_get_extra_specs.add_argument('flavorid', type=str, help=h2)
|
|
|
|
h1, h2, h3 = ('[<"X-RANGER-Client" header>]', '<flavor id>',
|
|
'<datafile with extra_specs json>',)
|
|
parser_replace_extra_specs = subparsers.add_parser('replace_extra_specs',
|
|
help='%s %s %s' % (h1,
|
|
h2,
|
|
h3))
|
|
parser_replace_extra_specs.add_argument('client',
|
|
**cli_common.ORM_CLIENT_KWARGS)
|
|
parser_replace_extra_specs.add_argument('flavorid', type=str, help=h2)
|
|
parser_replace_extra_specs.add_argument('datafile',
|
|
type=argparse.FileType('r'),
|
|
help=h3)
|
|
|
|
h1, h2 = '[<"X-RANGER-Client" header>]', '<flavor id>'
|
|
parser_delete_all_extra_specs = subparsers.add_parser(
|
|
'delete_all_extra_specs', help='%s %s' % (h1, h2))
|
|
parser_delete_all_extra_specs.add_argument('client',
|
|
**cli_common.ORM_CLIENT_KWARGS)
|
|
parser_delete_all_extra_specs.add_argument('flavorid', type=str, help=h2)
|
|
|
|
h1, h2, h3 = ('[<"X-RANGER-Client" header>]', '<flavor id>',
|
|
'<extra_spec key name>',)
|
|
parser_delete_extra_spec = subparsers.add_parser('delete_extra_spec',
|
|
help='%s %s %s' % (
|
|
h1, h2, h3))
|
|
parser_delete_extra_spec.add_argument('client',
|
|
**cli_common.ORM_CLIENT_KWARGS)
|
|
parser_delete_extra_spec.add_argument('flavorid', type=str, help=h2)
|
|
parser_delete_extra_spec.add_argument('eskeyname', type=str, help=h3)
|
|
|
|
h1, h2, h3 = ('[<"X-RANGER-Client" header>]', '<flavor id>',
|
|
'<datafile with extra_specs json>',)
|
|
parser_add_extra_specs = subparsers.add_parser('add_extra_specs',
|
|
help='%s %s %s' % (
|
|
h1, h2, h3))
|
|
parser_add_extra_specs.add_argument('client',
|
|
**cli_common.ORM_CLIENT_KWARGS)
|
|
parser_add_extra_specs.add_argument('flavorid', type=str, help=h2)
|
|
parser_add_extra_specs.add_argument('datafile',
|
|
type=argparse.FileType('r'),
|
|
help=h3)
|
|
|
|
h1, h2 = '[<"X-RANGER-Client" header>]', '<flavor id>'
|
|
parser_delete_flavor = subparsers.add_parser('delete_flavor',
|
|
help='%s %s' % (h1, h2))
|
|
parser_delete_flavor.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
|
|
parser_delete_flavor.add_argument('flavorid', type=str, help=h2)
|
|
|
|
h1, h2 = '[<"X-RANGER-Client" header>]', '<flavor id or flavor name>'
|
|
parser_get_flavor = subparsers.add_parser('get_flavor',
|
|
help='%s %s' % (h1, h2))
|
|
parser_get_flavor.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
|
|
parser_get_flavor.add_argument('flavorid', type=str, help=h2)
|
|
|
|
h1, h2 = ('[<"X-RANGER-Client" header>]',
|
|
'[--visibility <public|private>] [--region <name>] [--tenant '
|
|
'<id>] [--series {gv,nv,ns,nd,ss}] [--alias <alias>] '
|
|
'[--starts_with <name>] [--contains <name>] '
|
|
'[--vm_type <vm_type>] [--vnf_name <vnf_name>]')
|
|
parser_list_flavor = subparsers.add_parser('list_flavors',
|
|
help='%s %s' % (h1, h2))
|
|
parser_list_flavor.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
|
|
parser_list_flavor.add_argument('--visibility', type=str,
|
|
choices=['public', 'private'])
|
|
parser_list_flavor.add_argument('--region', type=str, help='region name')
|
|
parser_list_flavor.add_argument('--tenant', type=str, help='tenant id')
|
|
parser_list_flavor.add_argument('--starts_with', type=str,
|
|
help='flavor name starts with *')
|
|
parser_list_flavor.add_argument('--contains', type=str,
|
|
help='* contains in flavor name')
|
|
parser_list_flavor.add_argument('--series', type=str,
|
|
choices=['gv', 'nv', 'ns', 'nd', 'ss'])
|
|
parser_list_flavor.add_argument('--alias', type=str, help='flavor alias')
|
|
parser_list_flavor.add_argument('--vm_type', type=str, help='vm type')
|
|
parser_list_flavor.add_argument('--vnf_name', type=str, help='vnf name')
|
|
|
|
# region for flavor
|
|
h1, h2, h3 = ('[<"X-RANGER-Client" header>]', '<flavor id>',
|
|
'<data file with region(s) JSON>',)
|
|
parser_add_region = subparsers.add_parser('add_region',
|
|
help='%s %s %s' % (h1, h2, h3))
|
|
parser_add_region.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
|
|
parser_add_region.add_argument('flavorid', type=str, help=h2)
|
|
parser_add_region.add_argument('datafile', type=argparse.FileType('r'),
|
|
help=h3)
|
|
|
|
h1, h2, h3 = '[<"X-RANGER-Client" header>] [--force_delete]', \
|
|
'<flavor id>', '<region id>'
|
|
parser_delete_region = subparsers.add_parser('delete_region',
|
|
help='%s %s %s' % (
|
|
h1, h2, h3))
|
|
parser_delete_region.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
|
|
parser_delete_region.add_argument('flavorid', type=str, help=h2)
|
|
parser_delete_region.add_argument('regionid', type=str, help=h3)
|
|
parser_delete_region.add_argument('--force_delete',
|
|
help='force delete region',
|
|
action="store_true")
|
|
|
|
# tenant for flavor
|
|
h1, h2, h3 = ('[<"X-RANGER-Client" header>]', '<flavor id>',
|
|
'<data file with tenant(s) JSON>',)
|
|
parser_add_tenant = subparsers.add_parser('add_tenant',
|
|
help='%s %s %s' % (h1, h2, h3))
|
|
parser_add_tenant.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
|
|
parser_add_tenant.add_argument('flavorid', type=str, help=h2)
|
|
parser_add_tenant.add_argument('datafile', type=argparse.FileType('r'),
|
|
help=h3)
|
|
|
|
h1, h2, h3 = '[<"X-RANGER-Client" header>]', '<flavor id>', '<tenant id>'
|
|
parser_delete_tenant = subparsers.add_parser('delete_tenant',
|
|
help='%s %s %s' % (
|
|
h1, h2, h3))
|
|
parser_delete_tenant.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
|
|
parser_delete_tenant.add_argument('flavorid', type=str, help=h2)
|
|
parser_delete_tenant.add_argument('tenantid', type=str, help=h3)
|
|
|
|
|
|
def preparm(p):
|
|
return ('' if len(p) else '?') + ('&' if len(p) else '')
|
|
|
|
|
|
def cmd_details(args):
|
|
if args.subcmd == 'create_flavor':
|
|
return requests.post, ''
|
|
elif args.subcmd == 'update_flavor':
|
|
return requests.put, '/%s' % args.flavorid
|
|
elif args.subcmd == 'delete_flavor':
|
|
return requests.delete, '/%s' % args.flavorid
|
|
elif args.subcmd == 'add_region':
|
|
return requests.post, '/%s/regions' % args.flavorid
|
|
elif args.subcmd == 'add_tags':
|
|
return requests.post, '/%s/tags' % args.flavorid
|
|
elif args.subcmd == 'replace_tags':
|
|
return requests.put, '/%s/tags' % args.flavorid
|
|
elif args.subcmd == 'delete_tag':
|
|
return requests.delete, '/%s/tags/%s' % (args.flavorid, args.tagname)
|
|
elif args.subcmd == 'delete_all_tags':
|
|
return requests.delete, '/%s/tags' % args.flavorid
|
|
elif args.subcmd == 'get_tags':
|
|
return requests.get, '/%s/tags' % args.flavorid
|
|
elif args.subcmd == 'delete_region':
|
|
return requests.delete, '/%s/regions/%s/%s' % (
|
|
args.flavorid, args.regionid, args.force_delete)
|
|
elif args.subcmd == 'add_tenant':
|
|
return requests.post, '/%s/tenants' % args.flavorid
|
|
elif args.subcmd == 'delete_tenant':
|
|
return requests.delete, '/%s/tenants/%s' % (
|
|
args.flavorid, args.tenantid)
|
|
elif args.subcmd == 'get_flavor':
|
|
return requests.get, '/%s' % args.flavorid
|
|
elif args.subcmd == 'get_extra_specs':
|
|
return requests.get, '/%s/os_extra_specs' % args.flavorid
|
|
elif args.subcmd == 'delete_all_extra_specs':
|
|
return requests.delete, '/%s/os_extra_specs' % args.flavorid
|
|
elif args.subcmd == 'delete_extra_spec':
|
|
return requests.delete, '/%s/os_extra_specs/%s' % (
|
|
args.flavorid, args.eskeyname)
|
|
elif args.subcmd == 'add_extra_specs':
|
|
return requests.post, '/%s/os_extra_specs' % args.flavorid
|
|
elif args.subcmd == 'replace_extra_specs':
|
|
return requests.put, '/%s/os_extra_specs' % args.flavorid
|
|
elif args.subcmd == 'list_flavors':
|
|
param = ''
|
|
if args.visibility:
|
|
param += '%svisibility=%s' % (preparm(param), args.visibility)
|
|
if args.region:
|
|
param += '%sregion=%s' % (preparm(param), args.region)
|
|
if args.tenant:
|
|
param += '%stenant=%s' % (preparm(param), args.tenant)
|
|
if args.series:
|
|
param += '%sseries=%s' % (preparm(param), args.series)
|
|
if args.vm_type:
|
|
param += '%svm_type=%s' % (preparm(param), args.vm_type)
|
|
if args.vnf_name:
|
|
param += '%svnf_name=%s' % (preparm(param), args.vnf_name)
|
|
if args.starts_with:
|
|
param += '%sstarts_with=%s' % (preparm(param), args.starts_with)
|
|
if args.contains:
|
|
param += '%scontains=%s' % (preparm(param), args.contains)
|
|
if args.alias:
|
|
param += '%salias=%s' % (preparm(param), args.alias)
|
|
return requests.get, '/%s' % param
|
|
|
|
|
|
def get_token(timeout, args, host):
|
|
headers = {
|
|
'Content-Type': 'application/json',
|
|
}
|
|
url = '%s/v3/auth/tokens'
|
|
data = '''
|
|
{
|
|
"auth":{
|
|
"identity":{
|
|
"methods":[
|
|
"password"
|
|
],
|
|
"password":{
|
|
"user":{
|
|
"domain":{
|
|
"name":"%s"
|
|
},
|
|
"name":"%s",
|
|
"password":"%s"
|
|
}
|
|
}
|
|
},
|
|
"scope":{
|
|
"project":{
|
|
"name":"%s",
|
|
"domain":{
|
|
"id":"%s"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}'''
|
|
for argument in ('tenant_name', 'username', 'password', 'auth_region'):
|
|
argument_value = getattr(args, argument, None)
|
|
if argument_value is not None:
|
|
globals()[argument] = argument_value
|
|
else:
|
|
configuration_value = getattr(config, argument)
|
|
if configuration_value:
|
|
globals()[argument] = configuration_value
|
|
else:
|
|
message = ('ERROR: {} for token generation was not supplied. '
|
|
'Please use its command-line argument or '
|
|
'environment variable.'.format(argument))
|
|
print message
|
|
raise cli_common.MissingArgumentError(message)
|
|
|
|
keystone_ep = cli_common.get_keystone_ep('{}'.format(host), auth_region)
|
|
if keystone_ep is None:
|
|
raise ConnectionError(
|
|
'Failed in get_token, host: {}, region: {}'.format(host,
|
|
auth_region))
|
|
url = url % (keystone_ep,)
|
|
data = data % (base_config.user_domain_name, username, password, tenant_name, base_config.project_domain_name,)
|
|
|
|
if args.verbose:
|
|
print(
|
|
"Getting token:\ntimeout: %d\nheaders: %s\nurl: %s\n" % (
|
|
timeout, headers, url))
|
|
try:
|
|
resp = requests.post(url, timeout=timeout, data=data, headers=headers)
|
|
if resp.status_code != 201:
|
|
raise ResponseError(
|
|
'Failed to get token (Reason: {})'.format(
|
|
resp.status_code))
|
|
return resp.headers['x-subject-token']
|
|
|
|
except Exception as e:
|
|
print e.message
|
|
raise ConnectionError(e.message)
|
|
|
|
|
|
def get_environment_variable(argument):
|
|
# The rules are: all caps, underscores instead of dashes and prefixed
|
|
environment_variable = 'RANGER_{}'.format(
|
|
argument.replace('-', '_').upper())
|
|
|
|
return os.environ.get(environment_variable)
|
|
|
|
|
|
def run(args):
|
|
rms_url = args.rms_base_url if args.rms_base_url else base_config.rms['base_url']
|
|
host = args.fms_base_url if args.fms_base_url else base_config.fms['base_url']
|
|
port = args.port if args.port else base_config.fms['port']
|
|
data = args.datafile.read() if 'datafile' in args else '{}'
|
|
timeout = args.timeout if args.timeout else 10
|
|
|
|
rest_cmd, cmd_url = cmd_details(args)
|
|
url = '%s/v1/orm/flavors' % (host) + cmd_url
|
|
if args.faceless:
|
|
auth_token = auth_region = requester = client = ''
|
|
else:
|
|
try:
|
|
auth_token = get_token(timeout, args, rms_url)
|
|
except Exception:
|
|
exit(1)
|
|
auth_region = globals()['auth_region']
|
|
requester = globals()['username']
|
|
client = requester
|
|
|
|
tracking_id = args.tracking_id if args.tracking_id else None
|
|
headers = {
|
|
'content-type': 'application/json',
|
|
'X-Auth-Token': auth_token,
|
|
'X-Auth-Region': auth_region,
|
|
'X-RANGER-Requester': requester,
|
|
'X-RANGER-Client': client,
|
|
'X-RANGER-Tracking-Id': tracking_id
|
|
}
|
|
|
|
if args.verbose:
|
|
print(
|
|
"Sending API:\ntimeout: %d\ndata: %s\nheaders: %s\ncmd: %s\nurl:"
|
|
" %s\n" % (
|
|
timeout, data, headers, rest_cmd.__name__, url))
|
|
try:
|
|
resp = rest_cmd(url, timeout=timeout, data=data, headers=headers,
|
|
verify=config.verify)
|
|
except Exception as e:
|
|
print e
|
|
exit(1)
|
|
|
|
if not 200 <= resp.status_code < 300:
|
|
content = resp.content
|
|
print 'API error: %s %s (Reason: %d)\n%s' % (
|
|
rest_cmd.func_name.upper(), url, resp.status_code, content)
|
|
exit(1)
|
|
|
|
if resp.status_code == 204: # no content
|
|
exit(0)
|
|
|
|
rj = resp.json()
|
|
if rj == 'Not found':
|
|
print 'No output was found'
|
|
else:
|
|
cli_common.pretty_print_json(rj)
|