Added orm_client codebase

Initial commit for orm_client by adding the codebase.

Change-Id: I24f17561e6a426e6af9dbbe04e4a66599344d265
This commit is contained in:
raigax9 2017-08-25 15:47:33 -04:00 committed by Nishad Shah
parent 5de8154ed7
commit 2bc511c69f
62 changed files with 3973 additions and 0 deletions

View File

View File

@ -0,0 +1,49 @@
import config as conf
import json
import logging
import os
log = logging.getLogger(__name__)
def _get_regions(customer):
regions = []
try:
customer_json = json.loads(customer)
if 'regions' not in customer_json:
raise Exception("got bad response from orm cli ")
for region in customer_json['regions']:
regions.append(region['name'])
except Exception as exp:
raise Exception("got bad response from orm cli {}".format(exp.message))
message = "got no regions from orm cli"
if regions:
message = "got regions from orm cli --{}--".format(regions)
log.debug(message)
return regions
def _build_get_customer_cli_command(resource_id):
cli_command = """get_customer %s""" % resource_id
log.debug('cli command {}'.format(cli_command))
return cli_command
def _get_customer_regions(cli_command, service):
client_header = service.upper()
log.debug("get customer with cli")
os.chdir(conf.cli_dir)
cwd = os.getcwd()
customer = os.popen('./orm %s %s ' % (service.lower(), cli_command))
log.debug("got cusmer with cli ... check if got regions")
return _get_regions(customer.read())
def get_resource_regions(resource_id, service):
log.debug("---ORM CLI---")
regions = None
if service.upper() == 'CMS':
regions = _get_customer_regions(
_build_get_customer_cli_command(resource_id), service)
return regions

View File

@ -0,0 +1,102 @@
"""clean cms mpdule."""
import cli_comander as cli
import db_comander as db
import initializer
import logging
import sys
import utils
import yaml_handler as yh
log = logging.getLogger(__name__)
def _validate_service(service):
allowed_services = ['CMS', 'FMS']
if service.upper() not in allowed_services:
raise Exception("service should be one of {}".format(allowed_services))
return service.upper()
def _init():
initializer.init_log()
return
def read_csv_file(file):
log.debug("reading file {}".format(file))
return utils.read_csv_file(file)
def resource_db_clean(resource_id, service):
log.debug("cleaning {} db for resource {}".format(service, resource_id))
db.remove_resource_db(resource_id, service)
return
def check_yaml_file(resource_id):
log.debug('checking yml file if exist for resource {}'.format(resource_id))
files = yh.check_yaml_exist(resource_id)
message = 'no yaml files found for this resource'
if files:
message = "found files please remove manualy {}".format(files)
log.debug(message)
return
def get_resource_regions(resource_id, service_name):
db_regions = db.get_cms_db_resource_regions(resource_id)
orm_regions = cli.get_resource_regions(resource_id, service_name)
return orm_regions, db_regions
def clean_rds_resource_status(resource_id):
log.debug("clean rds status db for resource {}".format(resource_id))
db.remove_rds_resource_status(resource_id)
return
def _start_cleaning():
log.info('start cleaning')
file_path = sys.argv[1]
service = _validate_service(sys.argv[2])
resourses_to_clean = read_csv_file(file_path)
for resource_id in resourses_to_clean:
try:
log.debug(
'check if resource {} has any regions before clean'.format(
resource_id))
resource_regions, db_regions = get_resource_regions(resource_id,
service)
if resource_regions or db_regions:
log.error(
"got regions {} {} please clean regions from orm before"
" removing the resource {}".format(resource_regions,
db_regions,
resource_id))
raise Exception(
"got regions {} {} please clean regions from orm before"
" removing the resource {}".format(resource_regions,
db_regions,
resource_id))
log.debug('cleaning {}'.format(resource_id))
resource_db_clean(resource_id, service)
check_yaml_file(resource_id)
clean_rds_resource_status(resource_id)
except Exception as exp:
log.error("---------------{}---------------".format(exp.message))
if 'not found' not in exp.message:
log.exception(exp)
continue
return
if __name__ == '__main__':
warning_message = raw_input(
'IMPORTANT:- please note its your responsibility to backup the db'
' before runing this script... click enter before continue'
)
_init()
_start_cleaning()

View File

@ -0,0 +1,25 @@
"""config module."""
# db configs
sql_user = 'root'
sql_password = 'stack'
sql_server = '127.0.0.1'
sql_port = '3306'
# cms configs
customer_table_name = "customer"
customer_region_table_name = "customer_region"
cms_db_name = "orm_cms_db"
# cli configs
cli_dir = '../ormcli'
# rds configs
rds_db_name = 'orm_rds'
resource_status_table_name = 'resource_status'
# sot configs
local_repository_path = '/opt/app/orm/ORM'
file_name_format = 's_{}.yml'
relative_path_format = '/{}/hot/{}/{}'

View File

@ -0,0 +1,130 @@
import config as conf
import logging
import sqlalchemy
log = logging.getLogger(__name__)
db_engines = {}
def _db_create_engine(db_name):
global db_engines
if db_name not in db_engines:
db_address = 'mysql://{}:{}@{}:{}/{}'.format(conf.sql_user,
conf.sql_password,
conf.sql_server,
conf.sql_port,
db_name)
log.debug("DB:--- db address {}".format(db_address))
db_engines[db_name] = sqlalchemy.create_engine(db_address)
return db_engines
def _run_query(query, db_name):
db_engines = _db_create_engine(db_name)
connection = db_engines[db_name].connect()
try:
sqlres = connection.execute(query)
except Exception as exp:
sqlres = None
log.error("fail to delete resource {}".format(exp))
finally:
# close the connection
connection.close()
# db_engines[db_name].dispose()
return sqlres
def _build_delet_resource_status_query(resource_id, table_name):
query = '''
DELETE from %s
WHERE resource_id = '%s'
''' % (table_name, resource_id)
return query
def _build_delete_resource_query(resource_id, table_name):
query = '''
DELETE from %s
WHERE %s.uuid = '%s'
''' % (table_name, table_name, resource_id)
return query
def _build_get_resource_regions_query(resource_id, table_name):
query = '''
select region_id from %s
WHERE customer_id = '%s' and region_id != '-1'
''' % (table_name, resource_id)
return query
def _build_get_resource_id_query(resource_id, table_name):
query = '''
select * from %s
WHERE %s.uuid = '%s'
''' % (table_name, table_name, resource_id)
return query
def remove_cms_resource(resource_id):
query = _build_delete_resource_query(resource_id, conf.customer_table_name)
log.debug("DB---: deleting customer, query {}".format(query))
_run_query(query, conf.cms_db_name)
return
def remove_rds_resource_status(resource_id):
query = _build_delet_resource_status_query(resource_id,
conf.resource_status_table_name)
log.debug("DB---: deleting resource status, query {}".format(query))
_run_query(query, conf.rds_db_name)
return
def remove_ims_resource(resource_id):
return
def remove_fms_resource(resource_id):
return
def get_cms_db_resource_regions(resource_id):
regions = None
query = _build_get_resource_id_query(resource_id, conf.customer_table_name)
result = _run_query(query, conf.cms_db_name)
if not result.rowcount > 0:
raise Exception('resource {} not found'.format(resource_id))
resource_internal_id = result.first().__getitem__('id')
log.debug("got resource internal id {}".format(resource_internal_id))
# from resource id get regions
query = _build_get_resource_regions_query(resource_internal_id,
conf.customer_region_table_name)
log.debug(query)
result = _run_query(query, conf.cms_db_name)
if result.rowcount > 0:
regions = result.fetchall()
return regions
def get_ims_db_resource_regions(resource_id):
return
def get_fms_db_resource_regions(resource_id):
return
def get_rds_db_resource_status(resource_id):
return
def remove_resource_db(resource_id, service):
if service == 'CMS':
log.debug(
"cleaning {} db for resource {}".format(service, resource_id))
remove_cms_resource(resource_id)
return

View File

@ -0,0 +1,10 @@
import logging
log = logging.getLogger(__name__)
def init_log():
logging.basicConfig(format='%(asctime)s:%(levelname)s:%(message)s',
level=logging.DEBUG)
logging.info("logger set")

View File

@ -0,0 +1,23 @@
import csv
import logging
log = logging.getLogger(__name__)
def _validate_file(file):
if str(file).split('.')[-1] != 'csv':
log.error('please provide csv file')
raise TypeError('please provide csv file')
def read_csv_file(file):
_validate_file(file)
resources = []
with open(file, 'rb') as csvfile:
csv_dict = csv.DictReader(csvfile)
for resource in csv_dict:
resources.append(resource["uuids"])
log.debug(
'list of resources to clean ----{} -------'.format(resources))
return resources

View File

@ -0,0 +1,25 @@
import config as conf
import fnmatch
import os
def _get_resource_file_path():
file_path = conf.local_repository_path
return file_path
def _find_file(resource_id):
file_name = conf.file_name_format.format(resource_id)
folder_to_search = _get_resource_file_path(resource_id)
matches = []
for root, dirnames, filenames in os.walk(folder_to_search):
for filename in fnmatch.filter(filenames, file_name):
matches.append(os.path.join(root, filename))
return matches
def check_yaml_exist(resource_id):
files = _find_file(resource_id)
if files:
return files
return None

View File

@ -0,0 +1,41 @@
Flavorgen Usage
To install:
1. Download attached file flavorgen.tgz
2. Copy flavorgen.tgz to <orm_host>:/opt/app/orm/ormcli
3. cd /opt/app/orm/ormcli
4. tar xvzf flavorgen.tgz
All the predefined flavors are defined in flavor_dir. Edit if necessary.
To generate these flavors:
5. cd /opt/app/orm/ormcli/flavorgen
6. ./flavorator.py
You should normally create the list of flavors once.
Then when you want to add regions to all the flavors, use:
7. ./regionator.py --regions region1,region2
The argument is a comma-separated list of regions with no internal whitespace.
Use of -h will produce the following help:
./regionator.py -h
usage: regionator [-h] [--flavor_dir FLAVOR_DIR] [--host HOST]
[--cli_command CLI_COMMAND] [--regions REGIONS]
batch add region to flavor
optional arguments:
-h, --help show this help message and exit
--flavor_dir FLAVOR_DIR
<JSON flavor directory, default: ./flavor_dir>
--host HOST <orm host ip>
--cli_command CLI_COMMAND
<path to cli command>
--regions REGIONS <comma-separated regions to add, e.g. region1,region2>

View File

@ -0,0 +1,10 @@
{
"description":"A standard GV flavor with 12 VCPUs, 64 GB RAM, 400 GB Disk, 0 GB Swap and 0 GB Ephemeral",
"series": "gv",
"ram": "65536",
"ephemeral": "0",
"visibility": "public",
"vcpus": "12",
"swap": "0",
"disk": "400"
}

View File

@ -0,0 +1,10 @@
{
"description":"A standard GV flavor with 1 VCPU, 2 GB RAM, 12 GB Disk, 0 GB Swap and 20 GB Ephemeral",
"series": "gv",
"ram": "2048",
"ephemeral": "20",
"visibility": "public",
"vcpus": "1",
"swap": "0",
"disk": "12"
}

View File

@ -0,0 +1,10 @@
{
"description":"A standard GV flavor with 2 VCPUs, 2 GB RAM, 320 GB Disk, 0 GB Swap and 0 GB Ephemeral",
"series": "gv",
"ram": "2048",
"ephemeral": "0",
"visibility": "public",
"vcpus": "2",
"swap": "0",
"disk": "320"
}

View File

@ -0,0 +1,10 @@
{
"description":"A standard GV flavor with 2 VCPUs, 4 GB RAM, 12 GB Disk, 0 GB Swap and 40 GB Ephemeral",
"series": "gv",
"ram": "4096",
"ephemeral": "40",
"visibility": "public",
"vcpus": "2",
"swap": "0",
"disk": "12"
}

View File

@ -0,0 +1,10 @@
{
"description":"A standard GV flavor with 2 VCPUs, 4 GB RAM, 20 GB Disk, 0 GB Swap and 0 GB Ephemeral",
"series": "gv",
"ram": "4096",
"ephemeral": "0",
"visibility": "public",
"vcpus": "2",
"swap": "0",
"disk": "20"
}

View File

@ -0,0 +1,10 @@
{
"description":"A standard GV flavor with 4 VCPUs, 12 GB RAM, 320 GB Disk, 0 GB Swap and 0 GB Ephemeral",
"series": "gv",
"ram": "12288",
"ephemeral": "0",
"visibility": "public",
"vcpus": "4",
"swap": "0",
"disk": "320"
}

View File

@ -0,0 +1,10 @@
{
"description":"A standard GV flavor with 4 VCPUs, 16 GB RAM, 320 GB Disk, 0 GB Swap and 0 GB Ephemeral",
"series": "gv",
"ram": "16384",
"ephemeral": "0",
"visibility": "public",
"vcpus": "4",
"swap": "0",
"disk": "320"
}

View File

@ -0,0 +1,10 @@
{
"description":"A standard GV flavor with 4 VCPUs, 2 GB RAM, 80 GB Disk, 0 GB Swap and 160 GB Ephemeral",
"series": "gv",
"ram": "2048",
"ephemeral": "160",
"visibility": "public",
"vcpus": "4",
"swap": "0",
"disk": "80"
}

View File

@ -0,0 +1,10 @@
{
"description":"A standard GV flavor with 4 VCPUs, 32 GB RAM, 320 GB Disk, 0 GB Swap and 0 GB Ephemeral",
"series": "gv",
"ram": "32768",
"ephemeral": "0",
"visibility": "public",
"vcpus": "4",
"swap": "0",
"disk": "320"
}

View File

@ -0,0 +1,10 @@
{
"description":"A standard GV flavor with 8 VCPUs, 16 GB RAM, 40 GB Disk, 0 GB Swap and 0 GB Ephemeral",
"series": "gv",
"ram": "16384",
"ephemeral": "0",
"visibility": "public",
"vcpus": "8",
"swap": "0",
"disk": "40"
}

View File

@ -0,0 +1,10 @@
{
"description":"A standard GV flavor with 8 VCPUs, 16 GB RAM, 80 GB Disk, 0 GB Swap and 160 GB Ephemeral",
"series": "gv",
"ram": "16384",
"ephemeral": "160",
"visibility": "public",
"vcpus": "8",
"swap": "0",
"disk": "80"
}

View File

@ -0,0 +1,10 @@
{
"description":"A standard GV flavor with 8 VCPUs, 32 GB RAM, 80 GB Disk, 0 GB Swap and 320 GB Ephemeral",
"series": "gv",
"ram": "32768",
"ephemeral": "320",
"visibility": "public",
"vcpus": "8",
"swap": "0",
"disk": "80"
}

View File

@ -0,0 +1,10 @@
{
"description":"A standard GV flavor with 8 VCPUs, 64 GB RAM, 320 GB Disk, 0 GB Swap and 0 GB Ephemeral",
"series": "gv",
"ram": "65536",
"ephemeral": "0",
"visibility": "public",
"vcpus": "8",
"swap": "0",
"disk": "320"
}

View File

@ -0,0 +1,10 @@
{
"description":"A standard NV flavor with 12 VCPUs, 64 GB RAM, 400 GB Disk, 0 GB Swap and 0 GB Ephemeral",
"series": "nv",
"ram": "65536",
"ephemeral": "0",
"visibility": "public",
"vcpus": "12",
"swap": "0",
"disk": "400"
}

View File

@ -0,0 +1,11 @@
{
"description":"A standard NV flavor with 2 VCPUs, 2 GB RAM, 320 GB Disk, 0 GB Swap and 0 GB Ephemeral",
"series": "nv",
"ram": "2048",
"ephemeral": "0",
"visibility": "public",
"vcpus": "2",
"swap": "0",
"disk": "320"
}

View File

@ -0,0 +1,10 @@
{
"description":"A standard NV flavor with 2 VCPUs, 4 GB RAM, 20 GB Disk, 0 GB Swap and 0 GB Ephemeral",
"series": "nv",
"ram": "4096",
"ephemeral": "0",
"visibility": "public",
"vcpus": "2",
"swap": "0",
"disk": "20"
}

View File

@ -0,0 +1,10 @@
{
"description":"A standard NV flavor with 4 VCPUs, 12 GB RAM, 320 GB Disk, 0 GB Swap and 0 GB Ephemeral",
"series": "nv",
"ram": "12288",
"ephemeral": "0",
"visibility": "public",
"vcpus": "4",
"swap": "0",
"disk": "320"
}

View File

@ -0,0 +1,10 @@
{
"description":"A standard NV flavor with 4 VCPUs, 16 GB RAM, 320 GB Disk, 0 GB Swap and 0 GB Ephemeral",
"series": "nv",
"ram": "16384",
"ephemeral": "0",
"visibility": "public",
"vcpus": "4",
"swap": "0",
"disk": "320"
}

View File

@ -0,0 +1,10 @@
{
"description":"A standard NV flavor with 4 VCPUs, 32 GB RAM, 320 GB Disk, 0 GB Swap and 0 GB Ephemeral",
"series": "nv",
"ram": "32768",
"ephemeral": "0",
"visibility": "public",
"vcpus": "4",
"swap": "0",
"disk": "320"
}

View File

@ -0,0 +1,10 @@
{
"description":"A standard NV flavor with 8 VCPUs, 16 GB RAM, 40 GB Disk, 0 GB Swap and 0 GB Ephemeral",
"series": "nv",
"ram": "16384",
"ephemeral": "0",
"visibility": "public",
"vcpus": "8",
"swap": "0",
"disk": "40"
}

View File

@ -0,0 +1,10 @@
{
"description":"A standard NV flavor with 8 VCPUs, 64 GB RAM, 320 GB Disk, 0 GB Swap and 0 GB Ephemeral",
"series": "nv",
"ram": "65536",
"ephemeral": "0",
"visibility": "public",
"vcpus": "8",
"swap": "0",
"disk": "320"
}

View File

@ -0,0 +1,70 @@
#!/usr/bin/env python
import argparse
import json
import os
import subprocess
import tempfile
import time
# Default flavor json directory
FLAVOR_DIR = './flavor_dir'
def read_jsonfile(file):
return json.loads(open(file).read())
def calculate_name(flavor):
return "{0}.c{1}r{2}d{3}".format(flavor['series'], flavor['vcpus'],
int(flavor['ram']) / 1024, flavor['disk'])
def sh(harg, file_name):
# run a shell command, echoing output, painting error lines red,
# print runtime and status
# return status and output
cmd = create_command(harg, file_name)
print '>> Starting: ' + cmd
start = time.time()
output = ''
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
for line in iter(p.stdout.readline, b''):
out = line.rstrip()
print(">>> " + out)
output += out
end = time.time()
span = end - start
retcode = p.wait()
print '>> Ended: %s [%s, %d:%02d]' % (cmd, retcode, span / 60, span % 60)
return retcode, output
def create_command(harg, file_name):
cmd = 'python ../ormcli/orm fms %s create_flavor %s' % \
(harg, file_name)
if ';' in cmd or '&&' in cmd:
raise Exception("Violation of command injection, cmd is " + cmd)
return cmd
parser = argparse.ArgumentParser(prog='flavorator',
description='batch flavor creator')
args = parser.parse_args()
for file in [os.path.join(dp, f) for dp, dn, fn in
os.walk(os.path.expanduser(FLAVOR_DIR)) for f in fn]:
try:
f = read_jsonfile(file)
except ValueError:
continue
print f
flavor_name = calculate_name(f)
fh, file_name = tempfile.mkstemp()
os.write(fh, json.dumps({"flavor": f}))
os.close(fh)
res, output = sh('', file_name)
os.unlink(file_name)

View File

@ -0,0 +1,36 @@
#!/usr/bin/env python
import json
import sys
if sys.argv[1].isdigit():
vcpus = sys.argv[1]
else:
vcpus = "1"
if sys.argv[1].isdigit():
ram = sys.argv[1]
else:
ram = "1"
if sys.argv[1].isdigit():
disk = sys.argv[1]
else:
disk = "1"
def calculate_name(flavor):
return "{0}.c{1}r{2}d{3}".format(flavor['series'], flavor['vcpus'],
flavor['ram'], flavor['disk'])
data = {
"series": "gv",
"vcpus": "10",
"ram": "20",
"disk": "30",
"ephemeral": "0",
"swap": "0",
"visibility": "public"
}
flavor_name = calculate_name(data)
series = flavor_name.split('.')[0]
open(flavor_name, "w").write(json.dumps(data, indent=4) + '\n')

View File

@ -0,0 +1,37 @@
#!/usr/bin/env python
import json
import re
import sys
data = {
"swap": "0",
"visibility": "public"
}
flavor_name = sys.argv[1]
series, geometry = flavor_name.split('.')
try:
# Try with ephemeral
match = re.search('c(.+?)r(.+?)d(.+?)e(.*)', geometry)
vcpus = match.group(1)
ram = match.group(2)
disk = match.group(3)
ephemeral = match.group(4)
except AttributeError:
# Try without ephemeral. If this doesn't work, the input is invalid
match = re.search('c(.+?)r(.+?)d(.*)', geometry)
vcpus = match.group(1)
ram = match.group(2)
disk = match.group(3)
ephemeral = 0
# Fill the Flavor data
data['series'] = series
data['vcpus'] = vcpus
data['ram'] = str(int(ram) * 1024)
data['disk'] = disk
data['ephemeral'] = str(ephemeral)
# Write the Flavor JSON to the file
open(flavor_name, "w").write(json.dumps(data, indent=4) + '\n')

View File

@ -0,0 +1,161 @@
#!/usr/bin/env python
import argparse
import ast
import json
import os
import re
import subprocess
import tempfile
import time
# Default flavor json directory
FLAVOR_DIR = './flavor_dir'
CLI_PATH = '../ormcli/orm'
FID = None
FILE_NAME = None
FLAVOR_NAME = None
REGION_NAME = None
def get_flavor_type(path):
# The last directory name is the flavor type (e.g., 'medium')
return path.split('/')[-2]
def get_region_list(regions):
global REGION_NAME
result = []
for region in regions:
REGION_NAME = region
res, output = sh('get_region')
if not res:
result_region = ast.literal_eval(output)
result.append({'name': result_region['name'],
'designType': result_region['designType']})
else:
print 'Failed to get region %s, aborting...' % (region,)
exit(1)
return result
def create_command(cli_command):
if cli_command == 'add_region':
cmd = 'python %s fms add_region %s %s' % (CLI_PATH, FID, FILE_NAME,)
elif cli_command == 'get_flavor':
cmd = '%s fms get_flavor test %s' % (CLI_PATH, FLAVOR_NAME,)
elif cli_command == 'get_region':
cmd = '%s rms get_region %s' % (CLI_PATH, REGION_NAME,)
else:
raise ValueError('Received an unknown command: %s' % (cli_command,))
if ';' in cmd or '&&' in cmd:
raise Exception("Violation of command injection, cmd is " + cmd)
return cmd
def read_jsonfile(file):
return json.loads(open(file).read())
def calculate_name(flavor):
flavor_name = "{0}.c{1}r{2}d{3}".format(flavor['series'], flavor['vcpus'],
int(flavor['ram']) / 1024,
flavor['disk'])
if 'ephemeral' in flavor and int(flavor['ephemeral']) != 0:
flavor_name += 'e{0}'.format(flavor['ephemeral'])
return flavor_name
def sh(cli_command):
# run a shell command, echoing output, painting error lines red,
# print runtime and status
# return status and output
cmd = create_command(cli_command)
print '>> Starting: ' + cmd
start = time.time()
output = ''
errpat = re.compile('error', re.I)
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
for line in iter(p.stdout.readline, b''):
out = line.rstrip()
print(">>> " + out)
output += out
end = time.time()
span = end - start
retcode = p.wait()
print '>> Ended: %s [%s, %d:%02d]' % (cmd, retcode, span / 60, span % 60)
return retcode, output
parser = argparse.ArgumentParser(prog='regionator',
description='batch add region to flavor')
parser.add_argument('regions',
type=str,
default='',
help='<comma-separated regions to add, e.g. region1,'
'region2>')
parser.add_argument('series',
type=str,
default='',
nargs='?',
help='<comma-separated flavor series to add, e.g. nd,gv>')
args = parser.parse_args()
regions = args.regions.split(',')
series_list = args.series.split(',')
if not regions:
print "Must specify at least one region"
exit(1)
# Get all regions from RMS
region_list = get_region_list(regions)
any_update = False
for file in [os.path.join(dp, f) for dp, dn, fn in
os.walk(os.path.expanduser(FLAVOR_DIR)) for f in fn]:
try:
f = read_jsonfile(file)
except ValueError:
continue
updated = False
flavor_type = get_flavor_type(file)
if not series_list or series_list == [''] or f['series'] in series_list:
data = {'regions': []}
for region in region_list:
# Take only the regions whose design type matches the flavor's
if flavor_type.lower() == region['designType'].lower():
data['regions'].append({'name': region['name']})
updated = True
any_update = True
if updated:
# Create the json file
fh, file_name = tempfile.mkstemp()
FILE_NAME = file_name
os.write(fh, json.dumps(data))
os.close(fh)
FLAVOR_NAME = calculate_name(f)
res, output = sh('get_flavor')
if not res:
flavor = ast.literal_eval(output)
FID = flavor['flavor']['id']
print 'fid: ' + FID
res, output = sh('add_region')
os.unlink(FILE_NAME)
if not any_update:
if not args.series:
exp = 'design type of any of the regions:[{}]'.format(args.regions)
else:
exp = 'combination of regions:[{}] and series:[{}]'.format(
args.regions, args.series)
print('No flavor was updated, please make sure that the {} matches any '
'flavor under the flavor directory'.format(exp))

View File

@ -0,0 +1,41 @@
imagegen Usage
To install:
1. Download attached file imagegen.tgz
2. Copy imagegen.tgz to <orm_host>:/opt/app/orm/ormcli
3. cd /opt/app/orm/ormcli
4. tar xvzf imagegen.tgz
All the predefined imagess are defined in image_dir. Edit if necessary.
To generate these images:
5. cd /opt/app/orm/ormcli/imagegen
6. python imageator.py
You should normally create the list of images once.
Then when you want to add regions to all the images, use:
7. python regionator.py <region1>,<region2>
The argument is a comma-separated list of regions with no internal whitespace.
Use of -h will produce the following help:
./regionator.py -h
usage: regionator [-h] [--image_dir IMAGE_DIR] [--host HOST]
[--cli_command CLI_COMMAND] [--regions REGIONS]
batch add region to image
optional arguments:
-h, --help show this help message and exit
--image_dir IMAGE_DIR
<JSON image directory, default: ./image_dir>
--host HOST <orm host ip>
--cli_command CLI_COMMAND
<path to cli command>
--regions REGIONS <comma-separated regions to add, e.g. region1,region2>

View File

@ -0,0 +1,11 @@
{
"name": "test_image",
"url": "https://mirrors.it.att.com/images/image-name",
"visibility": "public",
"disk-format": "raw",
"container-format": "bare",
"min-disk": 0,
"owner": "ME",
"enabled": true,
"protected": false
}

View File

@ -0,0 +1,84 @@
#!/usr/bin/env python
from os.path import isfile, join
import argparse
import json
import os
import re
import subprocess
import tempfile
import time
# from colorama import init, Fore, Back, Style
# Default flavor json directory
IMAGE_DIR = './image_dir'
CLI_PATH = '../ormcli/orm'
def read_jsonfile(file):
return json.loads(open(file).read())
def sh(cmd):
# run a shell command, echoing output, painting error lines red,
# print runtime and status
# return status and output
print '>> Starting: ' + cmd
start = time.time()
output = ''
errpat = re.compile('error', re.I)
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
for line in iter(p.stdout.readline, b''):
out = line.rstrip()
print(">>> " + out)
output += out
end = time.time()
span = end - start
retcode = p.wait()
print '>> Ended: %s [%s, %d:%02d]' % (cmd, retcode, span / 60, span % 60)
return retcode, output
parser = argparse.ArgumentParser(prog='imageator',
description='batch image/region creator')
# parser.add_argument('--image_dir',
# type=str,
# default='./image_dir',
# help='<JSON image directory, default: ./image_dir>')
# parser.add_argument('--host',
# type=str,
# help='<orm host ip>')
# parser.add_argument('--cli_command',
# type=str,
# default='/opt/app/orm/ormcli/ormcli/orm',
# help='<path to cli command>')
args = parser.parse_args()
summary = []
for file in [f for f in os.listdir(IMAGE_DIR) if
isfile(join(IMAGE_DIR, f))]:
f = read_jsonfile(join(IMAGE_DIR, file))
print f
image_name = f['name']
fh, file_name = tempfile.mkstemp()
os.write(fh, json.dumps({"image": f}))
os.close(fh)
# harg = '--orm-base-url %s' % args.host if args.host else ''
res, output = sh('%s ims create_image test %s' % (
CLI_PATH, file_name))
os.unlink(file_name)
summary.append("File name: {}, Image name: {}, Create image status: {}\n".
format(file,
image_name,
'Success' if res == 0 else 'Failed'))
print "\nImage creation summary:"
print "-----------------------"
for s in summary:
print s

View File

@ -0,0 +1,98 @@
#!/usr/bin/env python
from os.path import isfile, join
import argparse
import ast
import json
import os
import re
import subprocess
import tempfile
import time
# from colorama import init, Fore, Back, Style
# Default flavor json directory
IMAGE_DIR = './image_dir'
CLI_PATH = '../ormcli/orm'
def read_jsonfile(file):
return json.loads(open(file).read())
def sh(cmd):
# run a shell command, echoing output, painting error lines red,
# print runtime and status
# return status and output
print '>> Starting: ' + cmd
start = time.time()
output = ''
errpat = re.compile('error', re.I)
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
for line in iter(p.stdout.readline, b''):
out = line.rstrip()
print(">>> " + out)
output += out
end = time.time()
span = end - start
retcode = p.wait()
print '>> Ended: %s [%s, %d:%02d]' % (cmd, retcode, span / 60, span % 60)
return retcode, output
parser = argparse.ArgumentParser(prog='regionator',
description='batch add region to image')
# parser.add_argument('--image_dir',
# type=str,
# default='./image_dir',
# help='<JSON image directory, default: ./image_dir>')
# parser.add_argument('--host',
# type=str,
# help='<orm host ip>')
# parser.add_argument('--cli_command',
# type=str,
# default='/opt/app/orm/ormcli/ormcli/orm',
# help='<path to cli command>')
parser.add_argument('regions',
type=str,
default='',
help='<comma-separated regions to add, e.g. region1,'
'region2>')
args = parser.parse_args()
regions = args.regions.split(',')
if not regions:
print "Must specify at least one region"
exit(0)
data = {'regions': [{'name': r} for r in regions]}
fh, file_name = tempfile.mkstemp()
os.write(fh, json.dumps(data))
os.close(fh)
# Prepare images dict with pairs {image_name:image_id}
img_dict = {}
# harg = '--orm-base-url %s' % args.host if args.host else ''
res, output = sh(
'%s ims %s list_images test ' % (CLI_PATH, ''))
if not res:
images = ast.literal_eval(output)
for img in images['images']:
img_dict[img['name']] = img['id']
print img_dict
for file in [f for f in os.listdir(IMAGE_DIR) if
isfile(join(IMAGE_DIR, f))]:
f = read_jsonfile(join(IMAGE_DIR, file))
print f
image_name = f['name']
if image_name in img_dict:
image_id = img_dict[image_name]
print 'image_id: ' + image_id
res, output = sh('%s ims add_regions test %s %s' % (
CLI_PATH, image_id, file_name))
else:
print 'image_name: {} does not exist. ignore.'.format(image_name)
os.unlink(file_name)

View File

@ -0,0 +1,69 @@
How to install orm cli
======================
Ensure you have python and pip installed
> tar xvzf ormcli.tgz
> cd ormcli
> pip install -r requirements.txt
How to run orm cli
==================
cms, fms, and rms are all services that should be installed somewhere.
To access these services, you'll need their IP addresses and port numbers,
which should be supplied with the --host and --port arguments
For general help
================
> orm -h
usage: orm [-h] <service> ...
ORM REST CLI
positional arguments:
<service>
rms Endpoint Discovery Service
cms Customer Management Service
fms Flavor Management Service
optional arguments:
-h, --help show this help message and exit
To get help on the cms subsystem
================================
> orm cms -h
usage: orm cms [-h] [--version] [--requester REQUESTER]
[--tracking_id TRACKING_ID] [--host HOST] [--port PORT]
[--timeout TIMEOUT]
auth_token auth_region client <subcommand> [-h] <args> ...
positional arguments:
auth_token keystone user authorization token
auth_region keystone region authorization id
client client (application_id)
<subcommand> [-h] <args>
create_customer datafile
update_customer custid datafile
add_region custid datafile
delete_region custid regionid
add_user custid regionid datafile
delete_default_user
custid userid
delete_user_from_region
custid regionid userid
get_customer custid
optional arguments:
-h, --help show this help message and exit
--version show program's version number and exit
--requester REQUESTER
requester (user_id)
--tracking_id TRACKING_ID
tracking id
--host HOST hostname or ip of CMS server
--port PORT port number of CMS server
--timeout TIMEOUT request timeout in ms (default: 10000)

View File

View File

@ -0,0 +1,53 @@
import config
import json
import requests
OK_CODE = 200
ORM_CLIENT_KWARGS = {'type': str, 'help': 'client name', 'default': None,
'nargs': '?'}
class MissingArgumentError(Exception):
"""Should be raised when an argument was found missing by CLI logic."""
pass
def get_keystone_ep(rms_url, region_name):
"""Get the Keystone EP from RMS.
:param rms_url: RMS server URL
:param region_name: The region name
:return: Keystone EP (string), None if it was not found
"""
try:
response = requests.get('%s/v2/orm/regions?regionname=%s' % (
rms_url, region_name, ), verify=config.verify)
except requests.exceptions.ConnectionError as e:
print('Could not connect to RMS, URL: {}'.format(rms_url))
return None
if response.status_code != OK_CODE:
print('RMS returned status: {}, content: {}'.format(
response.status_code, response.content))
return None
# RMS returned 200
lcp = response.json()
try:
for endpoint in lcp['regions'][0]['endpoints']:
if endpoint['type'] == 'identity':
return endpoint['publicURL']
except KeyError:
print('Response from RMS came in an unsupported format. '
'Make sure that you are using RMS 3.5')
return None
# Keystone EP not found in the response
print('No identity endpoint was found in the response from RMS')
return None
def pretty_print_json(json_to_print):
"""Print a json without the u' prefix."""
print(json.dumps(json_to_print))

View File

@ -0,0 +1,449 @@
#!/usr/bin/python
import argparse
import cli_common
import config
import os
import requests
class ResponseError(Exception):
pass
class ConnectionError(Exception):
pass
def add_to_parser(service_sub):
parser = \
service_sub.add_parser('cms',
help='Customer 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('--orm-base-url', type=str, help='ORM base URL',
default=get_environment_variable('orm-base-url'))
parser.add_argument('--tracking_id', type=str,
help='"X-AIC-ORM-Tracking-Id" header')
parser.add_argument('--port', type=int, help='port number of CMS 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='run without authentication',
default=False,
action="store_true")
subparsers = parser.add_subparsers(dest='subcmd',
metavar='<subcommand> [-h] <args>')
# customer
parser_create_customer = subparsers.add_parser('create_customer',
help='[<"X-AIC-ORM-Client" '
'header>] <data file '
'with new customer '
'JSON>')
parser_create_customer.add_argument('client',
**cli_common.ORM_CLIENT_KWARGS)
parser_create_customer.add_argument('datafile',
type=argparse.FileType('r'),
help='<data file with new customer '
'JSON>')
parser_delete_customer = subparsers.add_parser('delete_customer',
help='[<"X-AIC-ORM-Client" '
'header>] <customer '
'id>')
parser_delete_customer.add_argument('client',
**cli_common.ORM_CLIENT_KWARGS)
parser_delete_customer.add_argument('custid', type=str,
help='<customer id>')
parser_update_customer = subparsers.add_parser('update_customer',
help='[<"X-AIC-ORM-Client" '
'header>] <customer '
'id> <data file with '
'updated customer '
'JSON>')
parser_update_customer.add_argument('client',
**cli_common.ORM_CLIENT_KWARGS)
parser_update_customer.add_argument('custid', type=str,
help='<customer id>')
parser_update_customer.add_argument('datafile',
type=argparse.FileType('r'),
help='<data file with updated '
'customer JSON>')
# region
parser_add_region = subparsers.add_parser('add_region',
help='[<"X-AIC-ORM-Client" '
'header>] <customer id> '
'<data file with region('
's) JSON>')
parser_add_region.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
parser_add_region.add_argument('custid', type=str, help='<customer id>')
parser_add_region.add_argument('datafile', type=argparse.FileType('r'),
help='<data file with region(s) JSON>')
parser_replace_region = subparsers.add_parser('replace_region',
help='[<"X-AIC-ORM-Client" '
'header>] <customer '
'id> <data file with '
'region(s) JSON>')
parser_replace_region.add_argument('client',
**cli_common.ORM_CLIENT_KWARGS)
parser_replace_region.add_argument('custid', type=str,
help='<customer id>')
parser_replace_region.add_argument('datafile', type=argparse.FileType('r'),
help='<data file with region(s) JSON>')
parser_delete_region = subparsers.add_parser('delete_region',
help='[<"X-AIC-ORM-Client" '
'header>] <customer id> '
'<region id>')
parser_delete_region.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
parser_delete_region.add_argument('custid', type=str, help='<customer id>')
parser_delete_region.add_argument('regionid', type=str, help='<region id>')
# add user
parser_add_user = subparsers.add_parser('add_user',
help='[<"X-AIC-ORM-Client" '
'header>] <customer id> '
'<region id> <data file '
'with user(s) JSON>')
parser_add_user.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
parser_add_user.add_argument('custid', type=str, help='<customer id>')
parser_add_user.add_argument('regionid', type=str, help='<region id>')
parser_add_user.add_argument('datafile', type=argparse.FileType('r'),
help='<data file with user(s) JSON>')
# replace user
parser_replace_user = subparsers.add_parser('replace_user',
help='[<"X-AIC-ORM-Client" '
'header>] <customer id> '
'<region id> <data file '
'with user(s) JSON>')
parser_replace_user.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
parser_replace_user.add_argument('custid', type=str, help='<customer id>')
parser_replace_user.add_argument('regionid', type=str, help='<region id>')
parser_replace_user.add_argument('datafile', type=argparse.FileType('r'),
help='<data file with user(s) JSON>')
# delete user
parser_delete_user = subparsers.add_parser(
'delete_user',
help='[<"X-AIC-ORM-Client" header>] '
'<customer id> <region id> <user id>')
parser_delete_user.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
parser_delete_user.add_argument('custid', type=str,
help='<customer id>')
parser_delete_user.add_argument('regionid', type=str,
help='<region id>')
parser_delete_user.add_argument('userid', type=str,
help='<user id>')
# add default user
parser_add_default_user = \
subparsers.add_parser('add_default_user',
help='[<"X-AIC-ORM-Client" header>] '
'<customer id> '
'<data file with '
'region(s) JSON>')
parser_add_default_user.add_argument('client',
**cli_common.ORM_CLIENT_KWARGS)
parser_add_default_user.add_argument('custid', type=str,
help='<customer id>')
parser_add_default_user.add_argument('datafile',
type=argparse.FileType('r'),
help='<data file with user(s) JSON>')
# replace default user
parser_replace_default_user = \
subparsers.add_parser('replace_default_user',
help='[<"X-AIC-ORM-Client" header>] '
'<customer id> '
'<data file '
'with region(s) '
'JSON>')
parser_replace_default_user.add_argument('client',
**cli_common.ORM_CLIENT_KWARGS)
parser_replace_default_user.add_argument('custid', type=str,
help='<customer id>')
parser_replace_default_user.add_argument('datafile',
type=argparse.FileType('r'),
help='<data file with user(s) '
'JSON>')
# change enable
parser_enable_customer = subparsers.add_parser('enabled',
help='[<"X-AIC-ORM-Client" '
'header>] '
'<customer id> '
'<data file with '
'true/false JSON>')
parser_enable_customer.add_argument('client',
**cli_common.ORM_CLIENT_KWARGS)
parser_enable_customer.add_argument('custid', type=str,
help='<customer id>')
parser_enable_customer.add_argument('datafile',
type=argparse.FileType('r'),
help='<data file with true/false '
'JSON>')
# delete default user
parser_delete_default_user = \
subparsers.add_parser('delete_default_user',
help='[<"X-AIC-ORM-Client" header>] <customer '
'id> <user id>')
parser_delete_default_user.add_argument('client',
**cli_common.ORM_CLIENT_KWARGS)
parser_delete_default_user.add_argument('custid', type=str,
help='<customer id>')
parser_delete_default_user.add_argument('userid', type=str,
help='<user id>')
# add metadata
h1, h2, h3 = \
'[<"X-AIC-ORM-Client" header>]', '<customer id>', '<data file ' \
'with ' \
'metadata(' \
's) JSON>'
parser_add_metadata = subparsers.add_parser('add_metadata',
help='%s %s %s' % (h1, h2, h3))
parser_add_metadata.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
parser_add_metadata.add_argument('custid', type=str,
help=h2)
parser_add_metadata.add_argument('datafile', type=argparse.FileType('r'),
help=h3)
# replace metadata
h1, h2, h3 = \
'[<"X-AIC-ORM-Client" header>]', '<customer id>', '<data file ' \
'with ' \
'metadata(' \
's) JSON>'
parser_replace_metadata = subparsers.add_parser('replace_metadata',
help='%s %s %s' % (
h1, h2, h3))
parser_replace_metadata.add_argument('client',
**cli_common.ORM_CLIENT_KWARGS)
parser_replace_metadata.add_argument('custid', type=str,
help=h2)
parser_replace_metadata.add_argument('datafile',
type=argparse.FileType('r'),
help=h3)
# get customer
parser_get_customer = subparsers.add_parser('get_customer',
help='[<"X-AIC-ORM-Client" '
'header>] <customer id>')
parser_get_customer.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
parser_get_customer.add_argument('custid', type=str, help='<customer id>')
# list customers
h1 = '[<"X-AIC-ORM-Client" header>]'
h2 = '[--region <name>] [--user <name>] [--metadata <key:value>]' \
' [starts_with <name>] [contains <name>]'
parser_list_customer = subparsers.add_parser('list_customers',
help='%s %s' % (h1, h2))
parser_list_customer.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
parser_list_customer.add_argument('--region', type=str, help='region name')
parser_list_customer.add_argument('--user', type=str, help='user name')
parser_list_customer.add_argument('--starts_with', type=str,
help='customer name')
parser_list_customer.add_argument('--contains', type=str,
help='* contains in customer name')
parser_list_customer.add_argument('--metadata', action='append', nargs="+",
type=str, help='<key:value>')
return parser
def preparm(p):
return ('' if len(p) else '?') + ('&' if len(p) else '')
def cmd_details(args):
if args.subcmd == 'create_customer':
return requests.post, ''
elif args.subcmd == 'delete_customer':
return requests.delete, '/%s' % args.custid
elif args.subcmd == 'update_customer':
return requests.put, '/%s' % args.custid
elif args.subcmd == 'add_region':
return requests.post, '/%s/regions' % args.custid
elif args.subcmd == 'replace_region':
return requests.put, '/%s/regions' % args.custid
elif args.subcmd == 'delete_region':
return requests.delete, '/%s/regions/%s' % (args.custid, args.regionid)
elif args.subcmd == 'add_user':
return requests.post, '/%s/regions/%s/users' % (
args.custid, args.regionid)
elif args.subcmd == 'replace_user':
return requests.put, '/%s/regions/%s/users' % (
args.custid, args.regionid)
elif args.subcmd == 'delete_user':
return requests.delete, '/%s/regions/%s/users/%s' % (
args.custid, args.regionid, args.userid)
elif args.subcmd == 'add_default_user':
return requests.post, '/%s/users' % args.custid
elif args.subcmd == 'replace_default_user':
return requests.put, '/%s/users' % args.custid
elif args.subcmd == 'delete_default_user':
return requests.delete, '/%s/users/%s' % (args.custid, args.userid)
elif args.subcmd == 'add_metadata':
return requests.post, '/%s/metadata' % args.custid
elif args.subcmd == 'replace_metadata':
return requests.put, '/%s/metadata' % args.custid
elif args.subcmd == 'get_customer':
return requests.get, '/%s' % args.custid
elif args.subcmd == 'enabled':
return requests.put, '/%s/enabled' % args.custid
elif args.subcmd == 'list_customers':
param = ''
if args.region:
param += '%sregion=%s' % (preparm(param), args.region)
if args.user:
param += '%suser=%s' % (preparm(param), args.user)
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.metadata:
for meta in args.metadata:
param += '%smetadata=%s' % (preparm(param), meta[0])
return requests.get, '/%s' % param
def get_token(timeout, args, host):
headers = {
'Content-Type': 'application/json',
}
url = '%s/v2.0/tokens'
data = '''
{
"auth": {
"tenantName": "%s",
"passwordCredentials": {
"username": "%s",
"password": "%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('{}:8080'.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 % (tenant_name, username, password,)
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 != 200:
raise ResponseError(
'Failed to get token (Reason: {})'.format(
resp.status_code))
return resp.json()['access']['token']['id']
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 = 'AIC_ORM_{}'.format(
argument.replace('-', '_').upper())
return os.environ.get(environment_variable)
def run(args):
host = args.orm_base_url if args.orm_base_url else config.orm_base_url
port = args.port if args.port else 7080
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:%d/v1/orm/customers' % (host, port,) + cmd_url
if args.faceless:
auth_token = auth_region = requester = client = ''
else:
try:
auth_token = get_token(timeout, args, host)
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-AIC-ORM-Requester': requester,
'X-AIC-ORM-Client': client,
'X-AIC-ORM-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.json() if resp.status_code == 500 else ''
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)

View File

@ -0,0 +1,8 @@
# these values are used by ormcli to retrieve auth_token which is sent,
# along with region, with each cms and fms api request
tenant_name = ''
username = ''
password = False
auth_region = ''
orm_base_url = 'http://127.0.0.1'
verify = False

View File

@ -0,0 +1,395 @@
#!/usr/bin/python
import argparse
import cli_common
import 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('--orm-base-url', type=str, help='ORM base URL',
default=get_environment_variable('orm-base-url'))
parser.add_argument('--tracking_id', type=str, help='tracking id')
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('--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='run without authentication',
default=False,
action="store_true")
subparsers = parser.add_subparsers(dest='subcmd',
metavar='<subcommand> [-h] <args>')
# flavor
h1, h2 = ('[<"X-AIC-ORM-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-AIC-ORM-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-AIC-ORM-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-AIC-ORM-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-AIC-ORM-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-AIC-ORM-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-AIC-ORM-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-AIC-ORM-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-AIC-ORM-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-AIC-ORM-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-AIC-ORM-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-AIC-ORM-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-AIC-ORM-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-AIC-ORM-Client" header>]',
'[--visibility <public|private>] [--region <name>] [--tenant '
'<id>] [--series {gv,nv,ns,nd,ss}] [--alias <alias>]')
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')
# region for flavor
h1, h2, h3 = ('[<"X-AIC-ORM-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-AIC-ORM-Client" header>]', '<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)
# tenant for flavor
h1, h2, h3 = ('[<"X-AIC-ORM-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-AIC-ORM-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' % (
args.flavorid, args.regionid)
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.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/v2.0/tokens'
data = '''
{
"auth": {
"tenantName": "%s",
"passwordCredentials": {
"username": "%s",
"password": "%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('{}:8080'.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 % (tenant_name, username, password,)
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 != 200:
raise ResponseError(
'Failed to get token (Reason: {})'.format(
resp.status_code))
return resp.json()['access']['token']['id']
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 = 'AIC_ORM_{}'.format(
argument.replace('-', '_').upper())
return os.environ.get(environment_variable)
def run(args):
host = args.orm_base_url if args.orm_base_url else config.orm_base_url
port = args.port if args.port else 8082
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:%d/v1/orm/flavors' % (host, port,) + cmd_url
if args.faceless:
auth_token = auth_region = requester = client = ''
else:
try:
auth_token = get_token(timeout, args, host)
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-AIC-ORM-Requester': requester,
'X-AIC-ORM-Client': client,
'X-AIC-ORM-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)

View File

@ -0,0 +1,366 @@
#!/usr/bin/python
import argparse
import cli_common
import config
import os
import requests
class ResponseError(Exception):
pass
class ConnectionError(Exception):
pass
def add_to_parser(service_sub):
parser = service_sub.add_parser('ims',
help='Image 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('--orm-base-url', type=str, help='ORM base URL',
default=get_environment_variable('orm-base-url'))
parser.add_argument('--tracking_id', type=str, help='tracking id')
parser.add_argument('--port', type=int, help='port number of IMS 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='run without authentication',
default=False,
action="store_true")
subparsers = parser.add_subparsers(dest='subcmd',
metavar='<subcommand> [-h] <args>')
# image
h1, h2 = '[<"X-AIC-ORM-Client" header>]', '<data file with new image JSON>'
parser_create_image = subparsers.add_parser('create_image',
help='%s %s' % (h1, h2))
parser_create_image.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
parser_create_image.add_argument('datafile', type=argparse.FileType('r'),
help=h2)
h1, h2, h3 = '[<"X-AIC-ORM-Client" header>]', '<image id>', \
'<data file with updated image JSON>'
parser_update_image = subparsers.add_parser('update_image',
help='%s %s %s' % (h1,
h2,
h3))
parser_update_image.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
parser_update_image.add_argument('imageid', type=str, help=h2)
parser_update_image.add_argument('datafile', type=argparse.FileType('r'),
help=h3)
h1, h2 = '[<"X-AIC-ORM-Client" header>]', '<image id>'
parser_delete_image = subparsers.add_parser('delete_image',
help='%s %s' % (h1, h2))
parser_delete_image.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
parser_delete_image.add_argument('imageid', type=str, help=h2)
# get images
h1, h2 = '[<"X-AIC-ORM-Client" header>]', '<image id>'
parser_get_image = subparsers.add_parser('get_image',
help='%s %s' % (h1, h2))
parser_get_image.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
parser_get_image.add_argument('imageid', type=str, help=h2)
h1, h2 = '[<"X-AIC-ORM-Client" header>]', \
'[--visibility <public|private>] ' \
'[--region <name>] [--customer <id>]'
parser_list_images = subparsers.add_parser('list_images',
help='%s %s' % (h1, h2))
parser_list_images.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
parser_list_images.add_argument('--visibility', type=str,
choices=['public', 'private'])
parser_list_images.add_argument('--region', type=str, help='region name')
parser_list_images.add_argument('--customer', type=str, help='customer id')
# activate/deactivate image
h1, h2 = '[<"X-AIC-ORM-Client" header>]', '<image id>'
parser_enable = subparsers.add_parser('enable',
help='%s %s' % (h1, h2))
parser_enable.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
parser_enable.add_argument('imageid', type=str, help=h2)
parser_disable = subparsers.add_parser('disable',
help='%s %s' % (h1, h2))
parser_disable.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
parser_disable.add_argument('imageid', type=str, help=h2)
# region for image
h1, h2, h3 = '[<"X-AIC-ORM-Client" header>]', '<image id>', \
'<data file with region(s) JSON>'
parser_add_regions = subparsers.add_parser('add_regions',
help='%s %s %s' % (h1, h2, h3))
parser_add_regions.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
parser_add_regions.add_argument('imageid', type=str, help=h2)
parser_add_regions.add_argument('datafile',
type=argparse.FileType('r'),
help=h3)
h1, h2, h3 = '[<"X-AIC-ORM-Client" header>]', '<image id>', \
'<data file with region(s) JSON>'
parser_update_regions = subparsers.add_parser('update_regions',
help='%s %s %s' % (h1,
h2,
h3))
parser_update_regions.add_argument('client',
**cli_common.ORM_CLIENT_KWARGS)
parser_update_regions.add_argument('imageid', type=str, help=h2)
parser_update_regions.add_argument('datafile',
type=argparse.FileType('r'),
help=h3)
h1, h2, h3 = '[<"X-AIC-ORM-Client" header>]', '<image 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('imageid', type=str, help=h2)
parser_delete_region.add_argument('regionid', type=str, help=h3)
# customer for image
h1, h2, h3 = '[<"X-AIC-ORM-Client" header>]', '<image id>', \
'<data file with customer(s) JSON>'
parser_add_customers = subparsers.add_parser('add_customers',
help='%s %s %s' % (h1,
h2,
h3))
parser_add_customers.add_argument('client', **cli_common.ORM_CLIENT_KWARGS)
parser_add_customers.add_argument('imageid', type=str, help=h2)
parser_add_customers.add_argument('datafile',
type=argparse.FileType('r'),
help=h3)
h1, h2, h3 = '[<"X-AIC-ORM-Client" header>]', '<image id>', \
'<data file with customer(s) JSON>'
parser_update_customer = subparsers.add_parser('update_customers',
help='%s %s %s' % (h1,
h2,
h3))
parser_update_customer.add_argument('client',
**cli_common.ORM_CLIENT_KWARGS)
parser_update_customer.add_argument('imageid', type=str, help=h2)
parser_update_customer.add_argument('datafile',
type=argparse.FileType('r'),
help=h3)
h1, h2, h3 = '[<"X-AIC-ORM-Client" header>]', '<image id>', \
'<customer id>'
parser_delete_customer = subparsers.add_parser('delete_customer',
help='%s %s %s' % (h1,
h2,
h3))
parser_delete_customer.add_argument('client',
**cli_common.ORM_CLIENT_KWARGS)
parser_delete_customer.add_argument('imageid', type=str, help=h2)
parser_delete_customer.add_argument('customerid', type=str, help=h3)
def get_token(timeout, args, host):
headers = {
'Content-Type': 'application/json',
}
url = '%s/v2.0/tokens'
data = '''
{
"auth": {
"tenantName": "%s",
"passwordCredentials": {
"username": "%s",
"password": "%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('{}:8080'.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 % (tenant_name, username, password,)
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 != 200:
raise ResponseError(
'Failed to get token (Reason: {})'.format(
resp.status_code))
return resp.json()['access']['token']['id']
except Exception as e:
print e.message
raise ConnectionError(e.message)
def preparm(p):
return ('' if len(p) else '?') + ('&' if len(p) else '')
def cmd_details(args):
data = args.datafile.read() if 'datafile' in args else '{}'
if args.subcmd == 'create_image':
cmd, url = requests.post, ''
elif args.subcmd == 'update_image':
cmd, url = requests.put, '/%s' % args.imageid
elif args.subcmd == 'delete_image':
cmd, url = requests.delete, '/%s' % args.imageid
# activate/deactivate image
elif args.subcmd in ('enable', 'disable'):
cmd, url = requests.put, '/%s/enabled' % args.imageid
data = '{"enabled": %s}' % ('true' if args.subcmd == 'enable' else
'false')
# image regions
elif args.subcmd == 'add_regions':
cmd, url = requests.post, '/%s/regions' % args.imageid
elif args.subcmd == 'update_regions':
cmd, url = requests.put, '/%s/regions' % args.imageid
elif args.subcmd == 'delete_region':
cmd, url = requests.delete, '/%s/regions/%s' % (args.imageid,
args.regionid)
# image customers
elif args.subcmd == 'add_customers':
cmd, url = requests.post, '/%s/customers' % args.imageid
elif args.subcmd == 'update_customers':
cmd, url = requests.put, '/%s/customers' % args.imageid
elif args.subcmd == 'delete_customer':
cmd, url = requests.delete, '/%s/customers/%s' % (args.imageid,
args.customerid)
# list images
elif args.subcmd == 'get_image':
cmd, url = requests.get, '/%s' % args.imageid
elif args.subcmd == 'list_images':
param = ''
if args.visibility:
param += '%svisibility=%s' % (preparm(param),
args.visibility)
if args.region:
param += '%sregion=%s' % (preparm(param),
args.region)
if args.customer:
param += '%scustomer=%s' % (preparm(param),
args.customer)
cmd, url = requests.get, '/%s' % param
return cmd, url, data
def cmd_data(args):
# This is a special case where api has a payload needed but the CLI is
# seperated into 2 different commands. In this case we need to set the
# payload.
if args.subcmd == 'enable':
return "{\n \"enabled\": true\n}"
elif args.subcmd == 'disable':
return "{\n \"enabled\": false\n}"
else:
return args.datafile.read() if 'datafile' in args else '{}'
def get_environment_variable(argument):
# The rules are: all caps, underscores instead of dashes and prefixed
environment_variable = 'AIC_ORM_{}'.format(
argument.replace('-', '_').upper())
return os.environ.get(environment_variable)
def run(args):
host = args.orm_base_url if args.orm_base_url else config.orm_base_url
port = args.port if args.port else 8084
timeout = args.timeout if args.timeout else 10
rest_cmd, cmd_url, data = cmd_details(args)
url = '%s:%d/v1/orm/images' % (host, port,) + cmd_url
if args.faceless:
auth_token = auth_region = requester = client = ''
else:
try:
auth_token = get_token(timeout, args, host)
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-AIC-ORM-Requester': requester,
'X-AIC-ORM-Client': client,
'X-AIC-ORM-Tracking-Id': tracking_id
}
if args.verbose:
print("Sending API:\ntimeout: %d\ndata: %s\n"
"headers: %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)

View File

@ -0,0 +1,7 @@
#!/usr/bin/env python
import sys
import ormcli
if __name__ == "__main__":
ormcli.main(sys.argv)

View File

@ -0,0 +1,37 @@
#!/usr/bin/python
import argparse
import cmscli
import fmscli
import imscli
import rmscli
import sys
class Cli:
def create_parser(self):
self.parser = argparse.ArgumentParser(prog='orm',
description='ORM REST CLI')
service_sub = self.parser.add_subparsers(dest='service',
metavar='<service>')
self.submod = {'cms': cmscli, 'fms': fmscli, 'ims': imscli,
'rms': rmscli}
for s in self.submod.values():
s.add_to_parser(service_sub)
def parse(self, argv=sys.argv):
sys.argv = argv
self.args = self.parser.parse_args()
def logic(self):
self.submod[self.args.service].run(self.args)
def main(argv):
cli = Cli()
cli.create_parser()
cli.parse(argv)
cli.logic()
if __name__ == "__main__":
main(sys.argv)

View File

@ -0,0 +1,2 @@
requests
argparse

View File

@ -0,0 +1,385 @@
#!/usr/bin/python
import argparse
import cli_common
import config
import os
import requests
class ResponseError(Exception):
pass
class ConnectionError(Exception):
pass
def add_to_parser(service_sub):
parser = service_sub.add_parser('rms', help='Resource 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('--orm-base-url', type=str, help='ORM base URL',
default=get_environment_variable('orm-base-url'))
parser.add_argument('--tracking_id', type=str, help='tracking id')
parser.add_argument('--port', type=int, help='port number of IMS 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='run without authentication',
default=False,
action="store_true")
subparsers = parser.add_subparsers(dest='subcmd',
metavar='<subcommand> [-h] <args>')
# get group
h1 = '<group_id>'
parser_get_group = subparsers.add_parser('get_group', help=h1)
parser_get_group.add_argument('group_id', help=h1)
# get all groups
parser_list_groups = subparsers.add_parser('list_groups', help="")
# create group
h1 = '<group json file>'
parser_create_group = subparsers.add_parser('create_group', help=h1)
parser_create_group.add_argument('datafile', type=argparse.FileType('r'),
help=h1)
# update group
h1, h2 = '<group json file>', '<group_id>'
parser_update_group = subparsers.add_parser('update_group',
help="%s %s" % (h2, h1))
parser_update_group.add_argument('group_id', help=h2)
parser_update_group.add_argument('datafile', type=argparse.FileType('r'),
help=h1)
# delete group
h1 = '<group id>'
parser_delete_group = subparsers.add_parser('delete_group',
help='%s' % (h1))
parser_delete_group.add_argument('group_id', type=str, help=h1)
# get region
h1, h2 = '<region_name_or_id>', '[--use_version <api version>]'
parser_get_region = subparsers.add_parser('get_region',
help='%s %s' % (h1, h2))
parser_get_region.add_argument('--use_version', type=int,
help='<api version to use (1 or 2)>')
parser_get_region.add_argument('region_name_or_id', type=str, help=h1)
# update region
h1, h2 = '<region_id>', '<full region json file>'
parser_update_region = subparsers.add_parser('update_region',
help='%s %s' % (h1, h2))
parser_update_region.add_argument('region_id', type=str, help=h1)
parser_update_region.add_argument('datafile', type=argparse.FileType('r'),
help=h2)
# create region
h1 = '<full region json file>'
parser_create_region = subparsers.add_parser('create_region',
help='%s' % (h1))
parser_create_region.add_argument('datafile', type=argparse.FileType('r'),
help=h2)
# delete region
h1 = '<region id>'
parser_delete_region = subparsers.add_parser('delete_region',
help='%s' % (h1))
parser_delete_region.add_argument('region_id', type=str, help=h1)
# list regions
parser_list_region = subparsers.add_parser('list_regions',
help='\
[--use_version <api version>] [--type <type>] [--status <status>]\
[--metadata <metadata>] [--aicversion <aicversion>][--clli <clli>]\
[--regionname <regionname>] [--osversion <osversion>] [--valet <valet>]\
[--state <state>] [--country <country>] [--city <city>] [--street <street>]\
[--zip <zip>] [--vlcp_name <vlcp_name>]')
parser_list_region.add_argument('--use_version', type=int,
help='<api version to use>')
parser_list_region.add_argument('--type', type=str, help='<type>')
parser_list_region.add_argument('--status', type=str, help='<status>')
parser_list_region.add_argument('--metadata', action='append', nargs="+",
type=str, help='<metadata>')
parser_list_region.add_argument('--aicversion', type=str,
help='<aicversion>')
parser_list_region.add_argument('--clli', type=str, help='<clli>')
parser_list_region.add_argument('--regionname', type=str,
help='<regionname>')
parser_list_region.add_argument('--osversion', type=str,
help='<osversion>')
parser_list_region.add_argument('--valet', type=str, help='<valet>')
parser_list_region.add_argument('--state', type=str, help='<state>')
parser_list_region.add_argument('--country', type=str, help='<country>')
parser_list_region.add_argument('--city', type=str, help='<city>')
parser_list_region.add_argument('--street', type=str, help='<street>')
parser_list_region.add_argument('--zip', type=str, help='<zip>')
parser_list_region.add_argument('--vlcp_name', type=str,
help='<vlcp_name>')
# add metadata to region
h1, h2 = '<region_id>', '<metadata json file>'
parser_add_metadata = subparsers.add_parser('add_metadata',
help='%s %s' % (h1, h2))
parser_add_metadata.add_argument('region_id', type=str, help=h1)
parser_add_metadata.add_argument('datafile', type=argparse.FileType('r'),
help=h2)
# update region's metadata
h1, h2 = '<region_id>', '<metadata json file>'
parser_update_metadata = subparsers.add_parser('update_metadata',
help='%s %s' % (h1, h2))
parser_update_metadata.add_argument('region_id', type=str, help=h1)
parser_update_metadata.add_argument('datafile',
type=argparse.FileType('r'),
help=h2)
# delete metadata key from region
h1, h2 = '<region id>', '<metadata key>'
parser_delete_metadata = subparsers.add_parser('delete_metadata',
help='%s %s' % (h1, h2))
parser_delete_metadata.add_argument('region_id', type=str, help=h1)
parser_delete_metadata.add_argument('metadata_key', type=str, help=h2)
# get region's metadata
h1 = '<region id>'
parser_get_metadata = subparsers.add_parser('get_metadata',
help='%s' % (h1))
parser_get_metadata.add_argument('region_id', type=str, help=h1)
# update region's status
h1, h2 = '<region_id>', '<status>'
parser_update_status = subparsers.add_parser('update_status',
help='%s %s' % (h1, h2))
parser_update_status.add_argument('region_id', type=str, help=h1)
parser_update_status.add_argument('status', type=str, help=h2)
def get_token(timeout, args, host):
headers = {
'Content-Type': 'application/json',
}
url = '%s/v2.0/tokens'
data = '''
{
"auth": {
"tenantName": "%s",
"passwordCredentials": {
"username": "%s",
"password": "%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:
# If it does not exist in the configuration, we would like the
# exception to be raised
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('{}:8080'.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 % (tenant_name, username, password,)
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 != 200:
raise ResponseError(
'Failed to get token (Reason: {})'.format(
resp.status_code))
return resp.json()['access']['token']['id']
except Exception as e:
print e.message
raise ConnectionError(e.message)
def preparm(p):
return ('' if len(p) else '?') + ('&' if len(p) else '')
def cmd_details(args):
if args.subcmd == 'get_region':
return requests.get, '/%s' % args.region_name_or_id
elif args.subcmd == 'create_region':
return requests.post, ''
elif args.subcmd == 'update_region':
return requests.put, '/%s' % args.region_id
elif args.subcmd == 'delete_region':
return requests.delete, '/%s' % args.region_id
elif args.subcmd == 'list_regions':
param = ''
if args.type:
param += '%stype=%s' % (preparm(param), args.type)
if args.status:
param += '%sstatus=%s' % (preparm(param), args.status)
if args.metadata:
for meta in args.metadata:
param += '%smetadata=%s' % (preparm(param), meta[0])
if args.aicversion:
param += '%saicversion=%s' % (preparm(param), args.aicversion)
if args.clli:
param += '%sclli=%s' % (preparm(param), args.clli)
if args.regionname:
param += '%sregionname=%s' % (preparm(param), args.regionname)
if args.osversion:
param += '%sosversion=%s' % (preparm(param), args.osversion)
if args.valet:
param += '%svalet=%s' % (preparm(param), args.valet)
if args.state:
param += '%sstate=%s' % (preparm(param), args.state)
if args.country:
param += '%scountry=%s' % (preparm(param), args.country)
if args.city:
param += '%scity=%s' % (preparm(param), args.city)
if args.street:
param += '%sstreet=%s' % (preparm(param), args.street)
if args.zip:
param += '%szip=%s' % (preparm(param), args.zip)
if args.vlcp_name:
param += '%svlcp_name=%s' % (preparm(param), args.vlcp_name)
return requests.get, '/%s' % param
elif args.subcmd == 'add_metadata':
return requests.post, '/%s/metadata' % args.region_id
elif args.subcmd == 'update_metadata':
return requests.put, '/%s/metadata' % args.region_id
elif args.subcmd == 'delete_metadata':
return requests.delete, '/%s/metadata/%s' % (args.region_id,
args.metadata_key)
elif args.subcmd == 'get_metadata':
return requests.get, '/%s/metadata' % args.region_id
elif args.subcmd == 'update_status':
return requests.put, '/%s/status' % args.region_id
elif args.subcmd == 'get_group':
return requests.get, '/%s' % args.group_id
elif args.subcmd == 'list_groups':
return requests.get, '/'
elif args.subcmd == 'create_group':
return requests.post, '/'
elif args.subcmd == 'update_group':
return requests.put, '/%s' % args.group_id
elif args.subcmd == 'delete_group':
return requests.delete, '/%s' % args.group_id
def get_path(args):
path = 'v2/orm/regions'
if 'group' in args.subcmd:
path = 'v2/orm/groups'
return path
def get_environment_variable(argument):
# The rules are: all caps, underscores instead of dashes and prefixed
environment_variable = 'AIC_ORM_{}'.format(
argument.replace('-', '_').upper())
return os.environ.get(environment_variable)
def run(args):
url_path = get_path(args)
host = args.orm_base_url if args.orm_base_url else config.orm_base_url
port = args.port if args.port else 8080
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:%d/%s' % (host, port, url_path) + cmd_url
if args.faceless:
auth_token = auth_region = requester = client = ''
else:
try:
auth_token = get_token(timeout, args, host)
except Exception:
exit(1)
auth_region = globals()['auth_region']
requester = globals()['username']
client = requester
if (args.subcmd == 'get_region' or args.subcmd == 'list_regions') \
and "use_version" in args:
if args.use_version == 1:
url = '%s:%d/lcp' % (host, port) + cmd_url
elif args.use_version is not None and args.use_version != 2:
print 'API error: use_version argument - invalid value, ' \
'allowed values: 1 or 2'
exit(1)
if args.subcmd == "update_status":
data = '{"status": "%s"}' % args.status
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-AIC-ORM-Requester': requester,
'X-AIC-ORM-Client': client,
'X-AIC-ORM-Tracking-Id': tracking_id
}
if args.verbose:
print("Sending API:\ntimeout: %d\ndata: %s\n"
"headers: %s\ncmd: %s\nurl: %s\n" % (timeout,
data,
headers,
rest_cmd.__name__,
url))
try:
resp = rest_cmd(url, data=data, timeout=timeout, 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)

View File

View File

@ -0,0 +1,119 @@
{
"description": "Customer description",
"enabled": true,
"name": "myDomain",
"metadata": {
"my_server_name": "Apache1",
"ocx_cust": "123456889"
},
"regions": [
{
"name": "SAN1",
"type": "single",
"quotas": [
{
"compute": [
{
"instances": "10",
"injected-files": "10",
"keypairs": "10",
"ram": "10"
}
],
"storage": [
{
"gigabytes": "10",
"snapshots": "10",
"volumes": "10"
}
],
"network": [
{
"floatingip": "10",
"network": "10",
"port": "10",
"router": "10",
"subnet": "10"
}
]
}
]
},
{
"name": "AIC_MEDIUM",
"type": "group",
"quotas": [
{
"compute": [
{
"instances": "10",
"injected-files": "10",
"keypairs": "10",
"ram": "10"
}
],
"storage": [
{
"gigabytes": "10",
"snapshots": "10",
"volumes": "10"
}
],
"network": [
{
"floatingip": "10",
"network": "10",
"port": "10",
"router": "10",
"subnet": "10"
}
]
}
]
}
],
"users": [
{
"id": "userId1",
"role": [
"admin",
"other"
]
},
{
"id": "userId2",
"role": [
"storage"
]
}
],
"defaultQuotas": [
{
"compute": [
{
"instances": "10",
"injected-files": "10",
"keypairs": "10",
"ram": "10"
}
],
"storage": [
{
"gigabytes": "10",
"snapshots": "10",
"volumes": "10"
}
],
"network": [
{
"floatingip": "10",
"network": "10",
"port": "10",
"router": "10",
"subnet": "10"
}
]
}
]
}

View File

@ -0,0 +1,29 @@
{
"name": "abc1",
"url": "https://mirrors.it.att.com/images/image-name",
"visibility": "private",
"disk-format": "raw",
"container-format": "bare",
"min-disk": 1,
"min-ram": 1,
"tags": ["tag1", "tag2", "tag3"],
"properties": {
"property1": "value1",
"property2": "value2",
"property3": "value3"
},
"regions": [
{
"name": "rdm1",
"type": "single"
},
{
"name": "rdm3",
"type": "single"
}
],
"customers": [
"alef",
"bet"
]
}

View File

@ -0,0 +1,53 @@
import mock
from ormcli import cli_common
from unittest import TestCase
class CmsTests(TestCase):
@mock.patch.object(cli_common.requests, 'get')
def test_get_keystone_ep_sanity(self, mock_get):
my_response = mock.MagicMock()
my_response.status_code = cli_common.OK_CODE
my_response.json.return_value = {
'regions': [{'endpoints': [{
'type': 'identity', 'publicURL': 'test'}]}]}
mock_get.return_value = my_response
self.assertEqual(cli_common.get_keystone_ep('a', 'b'), 'test')
@mock.patch.object(cli_common.requests, 'get',
side_effect=cli_common.requests.exceptions
.ConnectionError())
def test_get_keystone_ep_connection_failed(self, mock_get):
self.assertIsNone(cli_common.get_keystone_ep('a', 'b'))
@mock.patch.object(cli_common.requests, 'get')
def test_get_keystone_ep_bad_status_code(self, mock_get):
my_response = mock.MagicMock()
my_response.status_code = cli_common.OK_CODE + 1
my_response.json.return_value = {
'regions': [{'endpoints': [{
'type': 'identity', 'publicURL': 'test'}]}]}
mock_get.return_value = my_response
self.assertIsNone(cli_common.get_keystone_ep('a', 'b'))
@mock.patch.object(cli_common.requests, 'get')
def test_get_keystone_ep_bad_response(self, mock_get):
my_response = mock.MagicMock()
my_response.status_code = cli_common.OK_CODE
my_response.json.return_value = {
'regions': [{'endpoinqs': [{
'type': 'identity', 'publicURL': 'test'}]}]}
mock_get.return_value = my_response
self.assertIsNone(cli_common.get_keystone_ep('a', 'b'))
my_response.json.return_value = {
'regions': [{'endpoints': [{
'type': 'identiqy', 'publicURL': 'test'}]}]}
mock_get.return_value = my_response
self.assertIsNone(cli_common.get_keystone_ep('a', 'b'))

View File

@ -0,0 +1,229 @@
from cStringIO import StringIO
import json
import mock
from ormcli import cmscli
from ormcli import ormcli
import requests
import sys
from unittest import TestCase
TJ = {'access': {'token': {'id': 'test'}}}
class CmsTests(TestCase):
def setUp(self):
out, sys.stdout, err, sys.stderr = sys.stdout, StringIO(), sys.stderr, StringIO()
self.mock_response = mock.Mock()
def respond(self, value, code, headers={}, oy=False):
# Set the response according to the parameter
if oy:
response = mock.Mock()
else:
response = self.mock_response
response.json.return_value = value
response.status_code = code
response.headers = headers
return response
def test_cmd_details(self):
# Set up the args parameter
args = mock.MagicMock()
args.custid = 'test_custid'
args.regionid = 'test_region'
args.userid = 'test_userid'
args.region = 'test_region'
args.user = 'test_user'
args.starts_with = 'test_startswith'
args.contains = 'test_contains'
subcmd_to_result = {
'create_customer': (requests.post, '',),
'delete_customer': (requests.delete, '/%s' % args.custid,),
'update_customer': (requests.put, '/%s' % args.custid,),
'add_region': (requests.post, '/%s/regions' % args.custid,),
'replace_region': (requests.put, '/%s/regions' % args.custid,),
'delete_region': (
requests.delete,
'/%s/regions/%s' % (args.custid, args.regionid),),
'add_user': (
requests.post,
'/%s/regions/%s/users' % (args.custid, args.regionid),),
'replace_user': (
requests.put,
'/%s/regions/%s/users' % (args.custid, args.regionid),),
'delete_user': (requests.delete, '/%s/regions/%s/users/%s' % (
args.custid, args.regionid, args.userid),),
'add_default_user': (requests.post, '/%s/users' % args.custid,),
'replace_default_user': (requests.put, '/%s/users' % args.custid,),
'delete_default_user': (
requests.delete, '/%s/users/%s' % (args.custid, args.userid),),
'add_metadata': (requests.post, '/%s/metadata' % args.custid,),
'replace_metadata': (requests.put, '/%s/metadata' % args.custid,),
'get_customer': (requests.get, '/%s' % args.custid,),
'list_customers': (requests.get,
'/?region=%s&user=%s&starts_with=%s'
'&contains=%s' % (args.region,
args.user, args.starts_with,
args.contains))
}
# Assert that each subcommand returns the expected details
for subcmd in subcmd_to_result:
args.subcmd = subcmd
self.assertEqual(subcmd_to_result[subcmd],
cmscli.cmd_details(args))
@mock.patch.object(cmscli.cli_common, 'get_keystone_ep',
return_value=None)
def test_get_token_keystone_ep_not_found(self, mock_get_keystone_ep):
args = mock.MagicMock()
args.username = 'test'
self.assertRaises(cmscli.ConnectionError, cmscli.get_token,
'a', args, 'c')
@mock.patch.object(cmscli.cli_common, 'get_keystone_ep')
@mock.patch.object(cmscli.requests, 'post')
def test_get_token_errors(self, mock_post, mock_get_keystone_ep):
# Bad status code
my_response = mock.MagicMock()
my_response.status_code = 201
mock_post.return_value = my_response
self.assertRaises(cmscli.ConnectionError, cmscli.get_token,
3, mock.MagicMock(), 'c')
# Post fails
mock_post.side_effect = ValueError('test')
self.assertRaises(cmscli.ConnectionError, cmscli.get_token,
3, mock.MagicMock(), 'c')
@mock.patch.object(cmscli.cli_common, 'get_keystone_ep')
@mock.patch.object(cmscli.requests, 'post')
@mock.patch.object(cmscli.requests, 'get')
def test_list_customers(self, mock_get, mock_post, mock_get_keystone_ep):
mock_post.return_value = self.respond(TJ, 200)
mock_get.return_value = self.mock_response
args = ormcli.main('orm cms list_customers t'.split())
sys.stdout.seek(0)
output = sys.stdout.read()
self.assertIn(json.dumps(TJ), output)
@mock.patch.object(cmscli.cli_common, 'get_keystone_ep')
@mock.patch.object(cmscli.requests, 'post')
@mock.patch.object(cmscli.requests, 'get')
@mock.patch.object(cmscli, 'get_token')
def test_list_customers_a(self, mock_get_token,
mock_get, mock_post, mock_get_keystone_ep):
mock_post.return_value = self.respond(TJ, 200)
mock_get.return_value = self.mock_response
mock_get.__name__ = 'a'
args = ormcli.main('orm cms --verbose list_customers t'.split())
sys.stdout.seek(0)
output = sys.stdout.read()
self.assertIn(json.dumps(TJ), output)
@mock.patch.object(cmscli.cli_common, 'get_keystone_ep')
@mock.patch.object(cmscli.requests, 'post')
@mock.patch.object(cmscli.requests, 'get')
def test_list_customers_e(self, mock_get, mock_post, mock_get_keystone_ep):
mock_post.return_value = self.respond(TJ, 200)
mock_get.side_effect = Exception('e')
with self.assertRaises(SystemExit) as cm:
args = ormcli.main('orm cms list_customers t'.split())
self.assertEqual(cm.exception.code, 1)
sys.stdout.seek(0)
output = sys.stdout.read()
self.assertIn('e', output)
@mock.patch.object(cmscli.cli_common, 'get_keystone_ep')
@mock.patch.object(cmscli.requests, 'post')
@mock.patch.object(cmscli.requests, 'get')
@mock.patch.object(cmscli, 'get_token')
@mock.patch.object(cmscli, 'globals')
def test_list_customers_errors(self, mock_globals, mock_get_token,
mock_get, mock_post,
mock_get_keystone_ep):
mock_post.return_value = self.respond(TJ, 200)
mock_get.return_value = self.respond(TJ, 204, oy=True)
with self.assertRaises(SystemExit) as cm:
args = ormcli.main('orm cms list_customers t'.split())
self.assertEqual(cm.exception.code, 0)
sys.stdout.seek(0)
output = sys.stdout.read()
self.assertEqual('', output)
def test_parsing(self):
cli = ormcli.Cli()
cli.create_parser()
cli.parse(
'orm cms --orm-base-url 12.11.10.9 --port 8832 --timeout 150 '
'add_user '
'client1 customer1 region1 '
'ormcli/tests/data/cms-add-cust.json'.split())
args = cli.args
self.assertEqual(args.orm_base_url, '12.11.10.9')
self.assertEqual(args.port, 8832)
self.assertEqual(args.timeout, 150)
@mock.patch('requests.post')
def test_timeout(self, mock_post):
mock_post.side_effect = Exception("timeout boom")
cli = ormcli.Cli()
cli.create_parser()
cli.parse(
'orm cms --faceless add_user client1 customer1 region1 '
'ormcli/tests/data/cms-add-cust.json'.split())
with self.assertRaises(SystemExit) as cm:
cli.logic()
self.assertEqual(cm.exception.code, 1)
sys.stdout.seek(0)
output = sys.stdout.read()
self.assertIn('timeout boom', output)
@mock.patch('requests.post')
@mock.patch.object(cmscli, 'get_token')
def test_no_keystone(self, mock_get_token, mock_post):
mock_post.side_effect = Exception("timeout boom")
cli = ormcli.Cli()
cli.create_parser()
cli.parse(
'orm cms add_user client1 customer1 region1 '
'ormcli/tests/data/cms-add-cust.json'.split())
with self.assertRaises(SystemExit) as cm:
cli.logic()
@mock.patch('requests.post')
@mock.patch.object(cmscli, 'get_token')
def test_response_code(self, mock_get_token, mock_post):
cli = ormcli.Cli()
cli.create_parser()
cli.parse(
'orm cms create_customer client1 '
'ormcli/tests/data/cms-add-cust.json'.split())
resp = self.respond({"access": {"token": {"id": 989}}}, 400)
mock_post.return_value = resp
with self.assertRaises(SystemExit) as cm:
cli.logic()
@mock.patch('requests.post')
def test_ok(self, mock_post):
cli = ormcli.Cli()
cli.create_parser()
cli.parse(
'orm cms create_customer client1 '
'ormcli/tests/data/cms-add-cust.json'.split())
mock_post.return_value = self.respond(
{"access": {"token": {"id": 989}}}, 200)
@mock.patch('requests.get')
@mock.patch('requests.post')
def test_list_customers(self, mock_post, mock_get):
cli = ormcli.Cli()
cli.create_parser()
cli.parse(
'orm cms list_customers --region 2 --user bob client1'.split())
resp = self.respond('{"Hi, mom"}', 200, {'X-Subject-Token': 989})
mock_post.return_value = self.respond(
{"access": {"token": {"id": 989}}}, 200)

View File

@ -0,0 +1,172 @@
from cStringIO import StringIO
import json
import mock
from ormcli import fmscli
from ormcli import ormcli
import requests
import sys
from unittest import TestCase
TJ = {'access': {'token': {'id': 'test'}}}
class FmsTests(TestCase):
def setUp(self):
out, sys.stdout, err, sys.stderr = sys.stdout, StringIO(), sys.stderr, StringIO()
self.mock_response = mock.Mock()
def respond(self, value, code, headers={}, oy=False):
# Set the response according to the parameter
if oy:
response = mock.Mock()
else:
response = self.mock_response
response.json.return_value = value
response.status_code = code
response.headers = headers
return response
def test_cmd_details(self):
# Set up the args parameter
args = mock.MagicMock()
args.flavorid = 'test_flavorid'
args.regionid = 'test_region'
args.region = 'test_region'
args.tagname = 'test_tagname'
args.eskeyname = 'test_eskeyname'
args.visibility = 'test_visibility'
args.tenant = 'test_tenant'
args.series = 'test_series'
args.starts_with = 'test_startswith'
args.contains = 'test_contains'
args.alias = 'test_alias'
list_flavors_url = '/?visibility=%s&region=%s&tenant=%s&series=%s' \
'&starts_with=%s&contains=%s&alias=%s'
subcmd_to_result = {
'create_flavor': (requests.post, '',),
'delete_flavor': (requests.delete, '/%s' % args.flavorid,),
'add_region': (requests.post, '/%s/regions' % args.flavorid,),
'add_tags': (requests.post, '/%s/tags' % args.flavorid,),
'replace_tags': (requests.put, '/%s/tags' % args.flavorid,),
'delete_tag': (
requests.delete,
'/%s/tags/%s' % (args.flavorid, args.tagname),),
'delete_all_tags': (requests.delete, '/%s/tags' % args.flavorid,),
'get_tags': (requests.get, '/%s/tags' % args.flavorid,),
'delete_region': (requests.delete, '/%s/regions/%s' % (
args.flavorid, args.regionid),),
'add_tenant': (requests.post, '/%s/tenants' % args.flavorid,),
'delete_tenant': (requests.delete, '/%s/tenants/%s' % (
args.flavorid, args.tenantid),),
'get_flavor': (requests.get, '/%s' % args.flavorid,),
'get_extra_specs': (
requests.get, '/%s/os_extra_specs' % args.flavorid,),
'delete_all_extra_specs': (
requests.delete, '/%s/os_extra_specs' % args.flavorid,),
'delete_extra_spec': (requests.delete, '/%s/os_extra_specs/%s' % (
args.flavorid, args.eskeyname),),
'add_extra_specs': (
requests.post, '/%s/os_extra_specs' % args.flavorid,),
'list_flavors': (requests.get,
list_flavors_url % (args.visibility, args.region,
args.tenant, args.series,
args.starts_with,
args.contains, args.alias))
}
# Assert that each subcommand returns the expected details
for subcmd in subcmd_to_result:
args.subcmd = subcmd
self.assertEqual(subcmd_to_result[subcmd],
fmscli.cmd_details(args))
@mock.patch.object(fmscli.cli_common, 'get_keystone_ep',
return_value=None)
def test_get_token_keystone_ep_not_found(self, mock_get_keystone_ep):
args = mock.MagicMock()
args.username = 'test'
self.assertRaises(fmscli.ConnectionError, fmscli.get_token,
'a', args, 'c')
@mock.patch.object(fmscli.cli_common, 'get_keystone_ep')
@mock.patch.object(fmscli.requests, 'post')
def test_get_token_errors(self, mock_post, mock_get_keystone_ep):
# Bad status code
my_response = mock.MagicMock()
my_response.status_code = 201
mock_post.return_value = my_response
self.assertRaises(fmscli.ConnectionError, fmscli.get_token,
3, mock.MagicMock(), 'c')
# Post fails
mock_post.side_effect = ValueError('test')
self.assertRaises(fmscli.ConnectionError, fmscli.get_token,
3, mock.MagicMock(), 'c')
@mock.patch.object(fmscli.cli_common, 'get_keystone_ep')
@mock.patch.object(fmscli.requests, 'post')
@mock.patch.object(fmscli.requests, 'get')
@mock.patch.object(fmscli, 'get_token')
@mock.patch.object(fmscli, 'globals')
def test_list_flavors(self, mock_globals, mock_get_token,
mock_get, mock_post, mock_get_keystone_ep):
mock_post.return_value = self.respond(TJ, 200)
mock_get.return_value = self.mock_response
args = ormcli.main('orm fms list_flavors t'.split())
sys.stdout.seek(0)
output = sys.stdout.read()
self.assertIn(json.dumps(TJ), output)
@mock.patch.object(fmscli.cli_common, 'get_keystone_ep')
@mock.patch.object(fmscli.requests, 'post')
@mock.patch.object(fmscli.requests, 'get')
@mock.patch.object(fmscli, 'get_token')
@mock.patch.object(fmscli, 'globals')
def test_list_flavors_a(self, mock_globals, mock_get_token,
mock_get, mock_post, mock_get_keystone_ep):
mock_post.return_value = self.respond(TJ, 200)
mock_get.return_value = self.mock_response
mock_get.__name__ = 'a'
args = ormcli.main('orm fms --verbose list_flavors t'.split())
sys.stdout.seek(0)
output = sys.stdout.read()
self.assertIn(json.dumps(TJ), output)
@mock.patch.object(fmscli.cli_common, 'get_keystone_ep')
@mock.patch.object(fmscli.requests, 'post')
@mock.patch.object(fmscli.requests, 'get')
def test_list_flavors_e(self, mock_get, mock_post, mock_get_keystone_ep):
mock_post.return_value = self.respond(TJ, 200)
mock_get.side_effect = Exception('e')
with self.assertRaises(SystemExit) as cm:
args = ormcli.main('orm fms list_flavors t'.split())
self.assertEqual(cm.exception.code, 1)
sys.stdout.seek(0)
output = sys.stdout.read()
self.assertIn('e', output)
@mock.patch.object(fmscli.cli_common, 'get_keystone_ep')
@mock.patch.object(fmscli.requests, 'post')
@mock.patch.object(fmscli.requests, 'get')
@mock.patch.object(fmscli, 'get_token')
@mock.patch.object(fmscli, 'globals')
def test_list_flavors_errors(self, mock_globals, mock_get_token,
mock_get, mock_post,
mock_get_keystone_ep):
mock_post.return_value = self.respond(TJ, 200)
mock_get.return_value = self.respond(TJ, 204, oy=True)
with self.assertRaises(SystemExit) as cm:
args = ormcli.main('orm fms list_flavors t'.split())
self.assertEqual(cm.exception.code, 0)
sys.stdout.seek(0)
output = sys.stdout.read()
self.assertEqual('', output)
mock_get.return_value = self.respond(TJ, 404, oy=True)
with self.assertRaises(SystemExit) as cm:
args = ormcli.main('orm fms --faceless list_flavors t'.split())
self.assertEqual(cm.exception.code, 1)
sys.stdout.seek(0)
output = sys.stdout.read()
self.assertIn('API error:', output)

View File

@ -0,0 +1,199 @@
from cStringIO import StringIO
import mock
from ormcli import imscli
from ormcli.imscli import cmd_data
from ormcli import ormcli
import sys
from unittest import TestCase
class ImsTests(TestCase):
def setUp(self):
out, sys.stdout, err, sys.stderr = sys.stdout, StringIO(), sys.stderr, StringIO()
self.mock_response = mock.Mock()
def respond(self, value, code, headers={}):
self.mock_response.json.return_value = value
self.mock_response.status_code = code
self.mock_response.headers = headers
return self.mock_response
def test_error_with_empty_args(self):
with self.assertRaises(SystemExit) as cm:
args = ormcli.main([])
self.assertEqual(cm.exception.code, 2)
sys.stderr.seek(0)
output = sys.stderr.read()
self.assertIn('too few arguments', output)
def test_help_command(self):
with self.assertRaises(SystemExit) as cm:
args = ormcli.main('orm --help'.split())
self.assertEqual(cm.exception.code, 0)
sys.stdout.seek(0)
output = sys.stdout.read()
self.assertIn('usage:', output)
self.assertIn('optional arguments:', output)
self.assertIn('<service>', output)
self.assertIn('ims', output)
self.assertIn('Image Management', output)
def test_ims_help_command(self):
with self.assertRaises(SystemExit) as cm:
args = ormcli.main('orm ims --help'.split())
self.assertEqual(cm.exception.code, 0)
sys.stdout.seek(0)
output = sys.stdout.read()
self.assertIn('usage:', output)
self.assertIn('timeout', output)
self.assertIn('optional arguments:', output)
self.assertIn('orm ims', output)
@mock.patch.object(imscli, 'cli_common')
@mock.patch('requests.put')
@mock.patch('requests.post')
@mock.patch.object(imscli, 'get_token')
@mock.patch.object(imscli, 'globals')
def test_timeout(self, mock_globals, mock_get_token,
mock_post, mock_put, mock_common):
mock_post.side_effect = Exception("timeout boom")
cli = ormcli.Cli()
cli.create_parser()
cli.parse(
'orm ims create_image client1 '
'ormcli/tests/data/ims-create-image.json'.split())
with self.assertRaises(SystemExit) as cm:
cli.logic()
self.assertEqual(cm.exception.code, 1)
sys.stdout.seek(0)
output = sys.stdout.read()
self.assertIn('timeout boom', output)
@mock.patch('requests.post')
@mock.patch.object(imscli, 'get_token')
@mock.patch.object(imscli, 'globals')
def test_no_keystone(self, mock_globals, mock_get_token, mock_post):
mock_post.side_effect = Exception("timeout boom")
cli = ormcli.Cli()
cli.create_parser()
globals()['auth_region'] = 'test'
cli.parse(
'orm ims create_image client1 '
'ormcli/tests/data/ims-create-image.json'.split())
with self.assertRaises(SystemExit) as cm:
cli.logic()
# @mock.patch.object(imscli, 'cli_common')
# @mock.patch('requests.post')
# def test_response_code(self, mock_post, mock_common):
# cli = ormcli.Cli()
# cli.create_parser()
# cli.parse(
# 'orm ims create_image client1 '
# 'ormcli/tests/data/ims-create-image.json'.split())
# resp = self.respond({"access": {"token": {"id": 989}}}, 200)
# mock_post.return_value = resp
# cli.logic()
# sys.stdout.seek(0)
# output = sys.stdout.read()
# # The response json should be printed, but since we mock post() only
# # once, the response would be the same response received
# # from Keystone
# self.assertIn('{\'access\': {\'token\': {\'id\': 989}}}', output)
# @mock.patch.object(imscli, 'cli_common')
# @mock.patch('requests.get')
# @mock.patch('requests.post')
# def test_get_image_sanity(self, mock_post, mock_get, mock_common):
# mock_post.return_value = self.respond(
# {"access": {"token": {"id": 989}}}, 201)
# cli = ormcli.Cli()
# cli.create_parser()
# cli.parse('orm ims get_image client1 test'.split())
# mock_get.return_value = self.respond(
# {"access": {"token": {"id": 989}}}, 200)
# cli.logic()
# sys.stdout.seek(0)
# output = sys.stdout.read()
# self.assertIn('{\'access\': {\'token\': {\'id\': 989}}}', output)
# @mock.patch.object(imscli, 'cli_common')
# @mock.patch('requests.get')
# @mock.patch('requests.post')
# def test_list_images(self, mock_post, mock_get, mock_common):
# mock_post.return_value = self.respond(
# {"access": {"token": {"id": 989}}}, 201)
# cli = ormcli.Cli()
# cli.create_parser()
# cli.parse(
# 'orm ims list_images client1 --visibility public --region a '
# '--tenant b'.split())
# resp = self.respond({"access": {"token": {"id": 989}}}, 200)
# mock_get.return_value = self.respond(
# {"access": {"token": {"id": 989}}}, 200)
# cli.logic()
# sys.stdout.seek(0)
# output = sys.stdout.read()
# self.assertIn('{\'access\': {\'token\': {\'id\': 989}}}', output)
@mock.patch.object(imscli, 'cli_common')
@mock.patch('requests.get')
@mock.patch('requests.post')
@mock.patch.object(imscli, 'get_token')
@mock.patch.object(imscli, 'globals')
def test_list_images_bad_request(self, mock_get_token, mock_globals,
mock_post, mock_get, mock_common):
mock_post.return_value = self.respond(
{"access": {"token": {"id": 989}}}, 201)
cli = ormcli.Cli()
cli.create_parser()
cli.parse(
'orm ims list_images client1 --visibility public --region a '
'--customer b'.split())
resp = self.respond({"access": {"token": {"id": 989}}}, 200)
with self.assertRaises(SystemExit) as cm:
cli.logic()
self.assertEqual(cm.exception.code, 1)
sys.stdout.seek(0)
output = sys.stdout.read()
self.assertIn('API error', output)
def test_cmd_data_enable(self):
my_args = mock.MagicMock()
my_args.subcmd = 'enable'
cm_data = cmd_data(my_args)
self.assertEqual("{\n \"enabled\": true\n}", cm_data)
def test_cmd_data_disable(self):
my_args = mock.MagicMock()
my_args.subcmd = 'disable'
cm_data = cmd_data(my_args)
self.assertEqual("{\n \"enabled\": false\n}", cm_data)
def test_cmd_data_no_data_file(self):
my_args = mock.MagicMock()
my_args.subcmd = 'xyz'
my_args.datafile.read.return_value = "123"
cm_data = cmd_data(my_args)
self.assertEqual("{}", cm_data)
def test_cmd_data_from_data_file(self):
my_args = MyDataFile()
cm_data = cmd_data(my_args)
self.assertEqual("123", cm_data)
class MyDataFile(object):
def __init__(self):
self.subcmd = '1'
self.datafile = FakeDataFIle()
def __iter__(self):
return iter(['datafile'])
class FakeDataFIle(object):
def read(self):
return '123'

View File

@ -0,0 +1,188 @@
from cStringIO import StringIO
import mock
from ormcli import ormcli
from ormcli import rmscli
import requests
import sys
from unittest import TestCase
class RmsTests(TestCase):
def setUp(self):
out, sys.stdout, err, sys.stderr = sys.stdout, StringIO(), sys.stderr, StringIO()
self.mock_response = mock.Mock()
def respond(self, value, code, headers={}):
self.mock_response.json.return_value = value
self.mock_response.status_code = code
self.mock_response.headers = headers
return self.mock_response
def test_cmd_details(self):
args = mock.MagicMock()
args.get_group = 'test_get_group'
args.list_groups = 'test_list_groups'
args.create_group = 'test_create_group'
args.update_group = 'test_update_group'
args.region_name_or_id = 'test_region_name_or_id'
args.type = '1'
args.status = '2'
args.metadata = '3'
args.aicversion = '4'
args.clli = '5'
args.regionname = '6'
args.osversion = '7'
args.valet = '8'
args.state = '9'
args.country = '10'
args.city = '11'
args.street = '12'
args.zip = '13'
args.vlcp_name = '14'
AAAAAAA = '/?type=%s&status=%s&metadata=%s&aicversion=%s&clli=%s'\
'&regionname=%s&osversion=%s&valet=%s&state=%s&country=%s'\
'&city=%s&street=%s&zip=%s&vlcp_name=%s' % (args.type,
args.status,
args.metadata,
args.aicversion,
args.clli,
args.regionname,
args.osversion,
args.valet,
args.state,
args.country,
args.city,
args.street,
args.zip,
args.vlcp_name,)
subcmd_to_result = {'get_region': (requests.get,
'/%s' % args.region_name_or_id),
'get_group': (requests.get, '/%s' % args.group_id),
'list_groups': (requests.get, '/'),
'create_group': (requests.post, '/'),
'update_group': (
requests.put, '/%s' % args.group_id),
'list_regions': (requests.get, AAAAAAA)
}
for subcmd in subcmd_to_result:
args.subcmd = subcmd
self.assertEqual(subcmd_to_result[subcmd],
rmscli.cmd_details(args))
def test_parsing(self):
cli = ormcli.Cli()
cli.create_parser()
cli.parse(
'orm rms --orm-base-url 12.11.10.9 --port 8832 --timeout 150 '
'list_regions --type big '.split())
args = cli.args
self.assertEqual(args.orm_base_url, '12.11.10.9')
self.assertEqual(args.port, 8832)
self.assertEqual(args.type, 'big')
self.assertEqual(args.timeout, 150)
@mock.patch('requests.get')
def test_timeout(self, mock_get):
cli = ormcli.Cli()
cli.create_parser()
cli.parse(
'orm rms --faceless --orm-base-url 12.11.10.9 --port 8832'
' --timeout 1 get_region x'.split())
mock_get.side_effect = Exception("timeout boom")
with self.assertRaises(SystemExit) as cm:
cli.logic()
self.assertEqual(cm.exception.code, 1)
sys.stdout.seek(0)
output = sys.stdout.read()
self.assertIn('timeout boom', output)
@mock.patch('requests.get')
def test_one_zone(self, mock_get):
cli = ormcli.Cli()
cli.create_parser()
cli.parse(
'orm rms --faceless --orm-base-url 12.11.10.9 --port 8832'
' --timeout 150 get_region zoneone'.split())
resp = self.respond(
{
"clli": "n/a",
"name": "SNA 1",
"enabled": 1,
"state": "functional",
"aic_version": "aic3.0",
"endpoints": [
{
"type": "horizon",
"publicurl": "http://horizon1.com"
},
{
"type": "identity",
"publicurl": "http://identity1.com"
},
{
"type": "ord",
"publicurl": "http://ord1.com"
}
],
"id": "SNA1",
"metadata": []
}, 200,
{'X-Subject-Token': 989})
mock_get.return_value = resp
cli.logic()
sys.stdout.seek(0)
output = sys.stdout.read()
self.assertIn('"aic_version": "aic3.0"', output)
# def test_error_with_wrong_port(self):
# args = self.parser.parse_args('--port 1111'.split())
# with self.assertRaises(SystemExit) as cm:
# rmscli.rmscli_logic(args)
# self.assertEqual(cm.exception.code, 1)
# sys.stdout.seek(0)
# output = sys.stdout.read()
# self.assertIn('Connection refused', output)
# def test_help_command(self):
# with self.assertRaises(SystemExit) as cm:
# args = self.parser.parse_args(['--help'])
# self.assertEqual(cm.exception.code, 0)
# sys.stdout.seek(0)
# output = sys.stdout.read()
# self.assertIn('usage:', output)
# self.assertIn('timeout', output)
# self.assertIn('optional arguments:', output)
# self.assertIn('--host', output)
# @mock.patch('requests.get')
# def test_timeout(self, mock_get):
# args = self.parser.parse_args('--host 1.1.1.1 --timeout
# 1000'.split())
# mock_get.side_effect = Exception("HTTPConnectionPool(
# host='1.1.1.1', port=8080): Max retries exceeded with url: /lcp (
# Caused by ConnectTimeoutError(
# <requests.packages.urllib3.connection.HTTPConnection object at
# 0x7f9469c1a310>, 'Connection to 1.1.1.1 timed out. (connect
# timeout=1.0)'))")
# with self.assertRaises(SystemExit) as cm:
# rmscli.rmscli_logic(args)
# self.assertEqual(cm.exception.code, 1)
# sys.stdout.seek(0)
# output = sys.stdout.read()
# self.assertIn('ConnectTimeoutError', output)
# learn how to mock 'real' request.get
# @mock.patch('rmscli.rmscli.rmscli.requests.get', autospec=True)
# def test_bad_status(self, mock_get):
# args = self.parser.parse_args([])
# mock_get.return_value = Response({},500)
# with self.assertRaises(SystemExit) as cm:
# rmscli.rmscli_logic(args)
# self.assertEqual(cm.exception.code, 1)
# sys.stdout.seek(0)
# output = sys.stdout.read()
# self.assertIn('GET', output)

View File

@ -0,0 +1,6 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
pecan==1.0.2
requests==2.2.1

6
orm/orm_client/setup.cfg Normal file
View File

@ -0,0 +1,6 @@
[nosetests]
match=^test
where=ormcli
nocapture=1
cover-package=ormcli
cover-erase=1

19
orm/orm_client/setup.py Normal file
View File

@ -0,0 +1,19 @@
try:
from setuptools import setup, find_packages
except ImportError:
from ez_setup import use_setuptools
use_setuptools()
from setuptools import setup, find_packages
setup(
name='ormcli',
version='0.1',
description='',
author='',
author_email='',
install_requires=[],
test_suite='ormcli',
zip_safe=False,
include_package_data=True,
packages=find_packages(exclude=['ez_setup'])
)

View File

@ -0,0 +1,9 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
# Hacking already pins down pep8, pyflakes and flake8
mock
coverage
testfixtures
pytest-pep8

19
orm/orm_client/tox.ini Normal file
View File

@ -0,0 +1,19 @@
[tox]
envlist=py27,cover
[testenv]
deps= -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands=
py.test --pep8 -m pep8
[testenv:cover]
commands=
coverage run setup.py test
coverage report --omit=.tox/*,ormcli/tests/*,setup.py
coverage html --omit=.tox/*,ormcli/tests/*,setup.py
#commands={envpython} setup.py test -v {posargs}
[testenv:pep8]
commands =
py.test --pep8 -m pep8