ansible/roles/batcave/files/oshift_mod.py
Jiri Podivin f513e7cbcd Linting python scripts
Signed-off-by: Jiri Podivin <jpodivin@redhat.com>
2024-09-18 19:57:29 +00:00

741 lines
27 KiB
Python

#!/usr/bin/env python
from __future__ import print_function
"""
This is a python interface for using Openshift-2.0 REST
version = 2.0 changed the basic support to use the new requests module
(http://docs.python-requests.org/en/latest/index.html)
Source: https://github.com/openshift/python-interface/raw/master/oshift/__init__.py
"""
import os
import sys
import logging
from optparse import OptionParser
import time
import traceback
import json
import requests
class OpenShiftException(BaseException):
pass
class OpenShiftLoginException(OpenShiftException):
"""Authorization failed."""
pass
class OpenShiftAppException(OpenShiftException):
"""App not found."""
pass
class OpenShiftNullDomainException(OpenShiftException):
"""User's domain hasn't been initialized."""
pass
class OpenShift500Exception(OpenShiftException):
"""Internal Server Error"""
pass
#### set this to True if we want to enable performance analysis
DOING_PERFORMANCE_ANALYSIS = False
global log
def config_logger():
# create formatter
formatter = logging.Formatter("%(levelname)s [%(asctime)s] %(message)s",
"%H:%M:%S")
logger = logging.getLogger("dump_logs")
log_formatter = logging.Formatter(
"%(name)s: %(asctime)s - %(levelname)s: %(message)s")
stream_handler = logging.StreamHandler(sys.stdout)
stream_handler.setFormatter(formatter)
stream_handler.setLevel(logging.DEBUG)
logger.setLevel(logging.DEBUG)
logger.addHandler(stream_handler)
return logger
def config_parser():
# these are required options.
parser.set_defaults(VERBOSE=False)
parser.set_defaults(DEBUG=False)
parser.add_option("-d", action="store_true", dest="DEBUG", help="enable DEBUG (default true)")
parser.add_option("-i", "--ip", default="openshift.redhat.com", help="ip addaress of your devenv")
parser.add_option("-v", action="store_true", dest="VERBOSE", help="enable VERBOSE printing")
parser.add_option("-u", "--user", default=None, help="User name")
parser.add_option("-p", "--password", default=None, help="RHT password")
(options, args) = parser.parse_args()
if options.user is None:
options.user = os.getenv('OPENSHIFT_user_email')
if options.password is None:
options.password = os.getenv('OPENSHIFT_user_passwd')
return options, args
log = config_logger()
parser = OptionParser()
# helper function for to measure timedelta.
def timeit(method):
def timed(*args, **kw):
ts = time.time()
result = method(*args, **kw)
te = time.time()
log.info("%r (%r, %r) %2.2f sec" % (method.__name__, args, kw, te-ts))
return result, te-ts
return timed
class conditional_decorator(object):
def __init__(self, dec, condition):
self.decorator = dec
self.condition = condition
def __call__(self, func):
if not self.condition:
return func
else:
return self.decorator(func)
class RestApi(object):
"""
A base connection class to derive from.
"""
proto = 'https'
host = '127.0.0.1'
port = 443
username = None
password = None
headers = None
response = None
base_uri = None
verbose = False
debug = False
def __init__(self, host=None, port=443, username=username, password=password,
debug=False, verbose=False, proto=None, headers=None):
if proto is not None:
self.proto = proto
if host is not None:
self.host = host
if username:
self.username = username
if password:
self.password = password
if headers:
self.headers = headers
if verbose:
self.verbose = verbose
self.debug = debug
self.base_uri = self.proto + "://" + host + "/broker/rest"
def _get_auth_headers(self, username=None, password=None):
if username:
self.username = username
if password:
self.password = password
return (self.username, self.password)
def request(self, url, method, headers=None, params=None):
"""
wrapper method for Requests' methods
"""
if url.startswith("https://") or url.startswith("http://"):
self.url = url # self.base_uri + url
else:
self.url = self.base_uri + url
log.debug("URL: %s" % self.url)
auth = (self.username, self.password) # self._get_auth_headers()
#auth = self._get_auth_headers()
_headers = self.headers or {}
if headers:
_headers.update(headers)
if 'OPENSHIFT_REST_API' in os.environ:
user_specified_api_version = os.environ['OPENSHIFT_REST_API']
api_version = "application/json;version=%s" % user_specified_api_version
_headers['Accept'] = api_version
self.response = requests.request(
auth=None if None in auth else auth,
method=method, url=self.url, params=params,
headers=_headers, timeout=130, verify=False
)
try:
raw_response = self.response.raw
except Exception as e:
print("-"*80, file=sys.stderr)
traceback.print_exc(file=sys.stderr)
print("-"*80, file=sys.stderr)
raise e
self.data = self.response.json()
# see https://github.com/kennethreitz/requests/blob/master/requests/status_codes.py
if self.response.status_code == requests.codes.internal_server_error:
raise OpenShift500Exception('Internal Server Error: %s' % self.data)
if self.response.status_code == (200 or 201):
print("-"*80, file=sys.stderr)
log.debug("status: %s" % self.response.status_code)
#log.debug("msg: %s" % self.data()['messages'][0]['text'])
# the raw_response is not available
#log.debug("raw: %s"%raw_response)
print("-"*80, file=sys.stderr)
return (self.response.status_code, self.data)
class Openshift(object):
"""
wrappers class around REST API so use can use it with python
"""
rest = None
user = None
passwd = None
def __init__(self, host, user=None, passwd=None, debug=False, verbose=False, logger=None, proto=None, headers=None):
if user:
self.user = user
if passwd:
self.passwd = passwd
if logger:
global log
log = logger
self.rest = RestApi(host=host, username=self.user, password=self.passwd, debug=debug, verbose=verbose, proto=proto, headers=headers)
if 'OPENSHIFT_REST_API' in os.environ:
self.REST_API_VERSION = float(os.environ['OPENSHIFT_REST_API'])
else:
# just get the latest version returned from the Server
api_version, api_version_list = self.api_version()
self.REST_API_VERSION = api_version
def get_href(self, top_level_url, target_link, domain_name=None):
status, res = self.rest.request(method='GET', url=top_level_url)
index = target_link.upper()
if status == 'Authorization Required':
#log.error("Authorization failed. (Check your credentials)")
raise OpenShiftLoginException('Authorization Required')
if domain_name is None:
if self.rest.response.json()['data']:
res = self.rest.response.json()['data'][0]['links'][index]
return (res['href'], res['method'])
else:
raise OpenShiftNullDomainException("No domain has been initialized.")
#return ('Not Found', self.rest.response.json)
else: # domain name is specified, now find a match
json_data = self.rest.response.json()['data']
if json_data:
for jd in json_data:
if jd['name'] == domain_name:
res = jd['links'][index]
return (res['href'], res['method'])
### if here, then user has given a domain name that does not match what's registered with the system
return("Not Found", None)
else:
return(None, None)
##### /user (sshkey)
#@conditional_decorator(timeit, DOING_PERFORMANCE_ANALYSIS)
@conditional_decorator(timeit, DOING_PERFORMANCE_ANALYSIS)
def get_user(self):
log.debug("Getting user information...")
(status, raw_response) = self.rest.request(method='GET', url='/user')
if status == 'OK':
return (status, self.rest.response.json()['data']['login'])
else:
return (status, raw_response)
@conditional_decorator(timeit, DOING_PERFORMANCE_ANALYSIS)
def keys_list(self):
log.debug("Getting ssh key information...")
(status, raw_response) = self.rest.request(method='GET', url='/user/keys')
return (status, raw_response)
@conditional_decorator(timeit, DOING_PERFORMANCE_ANALYSIS)
def key_add(self, **kwargs):
"""
params: {name, type, key_path}
"""
ssh_key_str = None
if 'key_str' in kwargs:
ssh_key_str = kwargs['key_str']
else:
if 'key' not in kwargs:
# use a default path
sshkey = '~/.ssh/id_rsa.pub'
else:
sshkey = kwargs['key']
ssh_path = os.path.expanduser(sshkey)
ssh_key_str = open(ssh_path, 'r').read().split(' ')[1]
if 'name' not in kwargs:
kwargs['name'] = 'default'
if 'type' not in kwargs:
kwargs['type'] = 'ssh-rsa'
data_dict = {
'name': kwargs['name'],
'type': kwargs['type'],
'content': ssh_key_str
}
params = data_dict
status, raw_response = self.rest.request(method='POST', url='/user/keys', params=params)
return (status, raw_response)
##### /domains
#@conditional_decorator(timeit, DOING_PERFORMANCE_ANALYSIS)
# TODO: should the rhlogin really be hardcoded in this function?
def domain_create(self, name, rhlogin='nate@appsembler.com'):
log.debug("Creating domain '%s'" % name)
params = {
'id': name,
'rhlogin': rhlogin
}
status, res = self.rest.request(method='POST', url='/domains', params=params)
return (status, res)
@conditional_decorator(timeit, DOING_PERFORMANCE_ANALYSIS)
def domain_delete(self, domain_name=None, force=True):
""" destroy a user's domain, if no name is given, figure it out"""
if domain_name is None:
status, domain_name = self.domain_get()
url, method = self.get_href('/domains', 'delete', domain_name)
log.info("URL: %s" % url)
#res = self.rest.response.data[0]['links']['DELETE']
if force:
params = {'force': 'true'}
if url:
return self.rest.request(method=method, url=url, params=params)
else: # problem
return (url, self.rest.response.raw)
@conditional_decorator(timeit, DOING_PERFORMANCE_ANALYSIS)
def domain_get(self, name=None):
log.info("Getting domain information...")
url, method = self.get_href('/domains', 'get', name)
if url == 'Not Found':
return ('Not Found', None)
else:
(status, raw_response) = self.rest.request(method=method, url=url)
if status == 200:
if self.REST_API_VERSION < 1.6:
domain_index_name = 'id'
else:
domain_index_name = 'name'
return (status, self.rest.response.json()['data'][domain_index_name])
def domain_update(self, new_name):
params = {'id': new_name}
url, method = self.get_href("/domains", 'update')
(status, res) = self.rest.request(method=method, url=url, params=params)
return (status, res)
def app_list(self):
url, method = self.get_href('/domains', 'list_applications')
(status, res) = self.rest.request(method=method, url=url)
return (status, self.rest.response.json()['data'])
@conditional_decorator(timeit, DOING_PERFORMANCE_ANALYSIS)
def app_create(self, app_name, app_type, scale='false', init_git_url=None):
url, method = self.get_href('/domains', 'add_application')
valid_options = self.rest.response.json()['data'][0]['links']['ADD_APPLICATION']['optional_params'][0]['valid_options']
#if app_type not in valid_options:
# log.error("The app type you specified '%s' is not supported!" % app_type)
# log.debug("supported apps types are: %s" % valid_options)
try:
json_data = json.loads(json.dumps(app_type))
except:
json_data = None
if json_data:
# translate json data into list
is_dict = all(isinstance(i, dict) for i in json_data)
cart_info = []
if is_dict:
# need to construct a cart as a list from dictionary
for data in json_data:
cart_info.append(data['name'])
else:
cart_info = json_data
else:
cart_info.append(app_type)
data_dict = {
'name': app_name,
'cartridges[]': cart_info,
'scale': scale,
}
if init_git_url:
data_dict['initial_git_url'] = init_git_url
params = data_dict
#log.debug("URL: %s, METHOD: %s" % (url, method))
(status, res) = self.rest.request(method=method, url=url, params=params)
return (status, res)
##### /cartridges
def cartridges(self):
(status, raw_response) = self.rest.request(method='GET', url='/cartridges')
if status == 'OK':
# return a list of cartridges that are supported
return (status, self.rest.response.json()['data'])
else:
return (status, raw_response)
##### /api get a list of support operations
def api(self):
#log.debug("Getting supported APIs...")
(status, raw_response) = self.rest.request(method='GET', url='/api')
return (status, raw_response)
def api_version(self):
# return the current version being used and the list of supported versions
status, res = self.api()
return (float(res['version']), res['supported_api_versions'])
##### helper functions
def do_action(self, kwargs):
op = kwargs['op_type']
if op == 'cartridge':
status, res = self.cartridge_list(kwargs['app_name'])
elif op == 'keys':
status, res = self.keys_list()
json_data = self.rest.response.json()
action = kwargs['action']
name = kwargs['name']
raw_response = None
for data in json_data['data']:
if data['name'] == name:
params = data['links'][action]
log.debug("Action: %s" % action)
if len(params['required_params']) > 0:
# construct require parameter dictionary
data = {}
for rp in params['required_params']:
param_name = rp['name']
if kwargs['op_type'] == 'cartridge':
data[param_name] = action.lower()
else:
data[param_name] = kwargs[param_name]
data = data
else:
data = None
(status, raw_response) = self.rest.request(method=params['method'],
url=params['href'],
params=data)
return (status, self.rest.response.json())
return (status, raw_response)
#### application tempalte
@conditional_decorator(timeit, DOING_PERFORMANCE_ANALYSIS)
def app_templates(self):
(status, raw_response) = self.rest.request(method='GET', url='/application_template')
if status == 'OK':
return (status, self.rest.response.json())
else:
return (status, raw_response)
##### keys
@conditional_decorator(timeit, DOING_PERFORMANCE_ANALYSIS)
def key_delete(self, key_name):
"""
li.key_delete('ssh_key_name')
"""
params = {"action": 'DELETE', 'name': key_name, "op_type": 'keys'}
return self.do_action(params)
@conditional_decorator(timeit, DOING_PERFORMANCE_ANALYSIS)
def key_update(self, kwargs): # key_name, key_path, key_type='ssh-rsa'):
"""
li.key_update({'name': 'new_key_name', 'key': new_key_path})
"""
key_path = kwargs['key']
key_name = kwargs['name']
if 'key_type' in kwargs:
key_type = kwargs['key_type']
else:
key_type = 'ssh-rsa'
ssh_path = os.path.expanduser(key_path)
ssh_key_str = open(ssh_path, 'r').read().split(' ')[1]
params = {'op_type': 'keys', 'action': 'UPDATE', 'name': key_name, 'content': ssh_key_str, 'type': key_type}
return self.do_action(params)
@conditional_decorator(timeit, DOING_PERFORMANCE_ANALYSIS)
def key_get(self, name):
"""
li.key_get('target_key_name')
returns the actual key content :$
"""
params = {'action': 'GET', 'name': name, 'op_type': 'keys'}
url = "/user/keys/" + name
(status, raw_response) = self.rest.request(method='GET', url=url)
if status == 'OK':
return status, self.rest.response.json()['data']
else:
return (status, raw_response)
def key_action(self, kwargs):
status, res = self.keys_list()
json_data = self.rest.response.json()
action = kwargs['action']
name = kwargs['name']
for data in json_data['data']:
if data['name'] == name:
params = data['links'][action]
log.debug("Action: %s" % action)
if len(params['required_params']) > 0:
# construct require parameter dictionary
data = {}
for rp in params['required_params']:
param_name = rp['name']
data[param_name] = kwargs[param_name]
data = data
else:
data = None
break
(status, raw_response) = self.rest.request(method=params['method'],
url=params['href'],
params=data)
return (status, raw_response)
##### apps
@conditional_decorator(timeit, DOING_PERFORMANCE_ANALYSIS)
def app_create_scale(self, app_name, app_type, scale, init_git_url=None):
self.app_create(app_name=app_name, app_type=app_type, scale=scale, init_git_url=init_git_url)
@conditional_decorator(timeit, DOING_PERFORMANCE_ANALYSIS)
def app_delete(self, app_name):
params = {'action': 'DELETE', 'app_name': app_name}
return self.app_action(params)
@conditional_decorator(timeit, DOING_PERFORMANCE_ANALYSIS)
def app_start(self, app_name):
params = {"action": 'START', 'app_name': app_name}
return self.app_action(params)
@conditional_decorator(timeit, DOING_PERFORMANCE_ANALYSIS)
def app_stop(self, app_name):
params = {"action": 'STOP', 'app_name': app_name}
return self.app_action(params)
@conditional_decorator(timeit, DOING_PERFORMANCE_ANALYSIS)
def app_restart(self, app_name):
params = {"action": 'RESTART', 'app_name': app_name}
return self.app_action(params)
@conditional_decorator(timeit, DOING_PERFORMANCE_ANALYSIS)
def app_force_stop(self, app_name):
params = {"action": 'FORCE_STOP', 'app_name': app_name}
return self.app_action(params)
@conditional_decorator(timeit, DOING_PERFORMANCE_ANALYSIS)
def app_get_descriptor(self, app_name):
params = {'action': 'GET', 'app_name': app_name}
return self.app_action(params)
#############################################################
# event related functions
#############################################################
def app_scale_up(self, app_name):
params = {'action': 'SCALE_UP', 'app_name': app_name}
return self.app_action(params)
def app_scale_down(self, app_name):
params = {'action': 'SCALE_DOWN', 'app_name': app_name}
return self.app_action(params)
def app_add_alias(self, app_name, alias):
params = {'action': 'ADD_ALIAS', 'app_name': app_name, 'alias': alias}
return self.app_action(params)
def app_remove_alias(self, app_name, alias):
params = {'action': 'REMOVE_ALIAS', 'app_name': app_name, 'alias': alias}
return self.app_action(params)
def app_get_estimates(self):
url, method = self.get_href('/estimates', 'get_estimate')
(status, res) = self.rest.request(method=method, url=url)
return (status, self.rest.response.json()['data'])
#params = {'action': 'GET_ESTIMATE'}
#return self.app_action(params)
def app_action(self, params):
""" generic helper function that is capable of doing all the operations
for application
"""
# step1. find th url and method
status, res = self.app_list()
app_found = False
action = params['action']
if 'app_name' in params:
app_name = params['app_name']
if 'cartridge' in params:
cart_name = params['cartridge']
for app in res:
#for app in res['data']:
if app['name'] == app_name:
# found match, now do your stuff
params_dict = app['links'][action]
method = params_dict['method']
log.info("Action: %s" % action)
data = {}
if len(params_dict['required_params']) > 0:
param_name = params_dict['required_params'][0]['name']
rp = params_dict['required_params'][0]
#data[param_name] = cart_name #'name'] = rp['name']
for rp in params_dict['required_params']:
# construct the data
param_name = rp['name']
if param_name == 'event':
if isinstance(rp['valid_options'], list):
data[param_name] = rp['valid_options'][0]
else:
data[param_name] = rp['valid_options']
else:
data[param_name] = params[param_name] # cart_name #params['op_type']
#data[param_name] = params[param_name]
data = data
else:
data = None
req_url = params_dict['href']
(status, raw_response) = self.rest.request(method=method, url=req_url, params=data)
app_found = True
return (status, raw_response)
if not app_found:
raise OpenShiftAppException("Can not find the app matching your request")
#log.error("Can not find app matching your request '%s'" % app_name)
#return ("Error", None)
def get_gears(self, app_name, domain_name=None):
""" return gears information """
params = {"action": 'GET_GEAR_GROUPS', 'app_name': app_name}
return self.app_action(params)
################################
# cartridges
################################
def cartridge_list(self, app_name):
params = {"action": 'LIST_CARTRIDGES', 'app_name': app_name}
return self.app_action(params)
def cartridge_add(self, app_name, cartridge):
params = {"action": 'ADD_CARTRIDGE', 'app_name': app_name,
'cartridge': cartridge}
status, res = self.app_action(params)
return (status, self.rest.response.json()['messages'])
def cartridge_delete(self, app_name, name):
params = {"action": 'DELETE', 'name': name, "op_type": 'cartridge', 'app_name': app_name}
return self.do_action(params)
def cartridge_start(self, app_name, name):
params = {"action": 'START', 'name': name, "op_type": 'cartridge', 'app_name': app_name}
return self.do_action(params)
def cartridge_stop(self, app_name, name):
params = {"action": 'STOP', 'name': name, "op_type": 'cartridge', 'app_name': app_name}
return self.do_action(params)
def cartridge_restart(self, app_name, name):
params = {"action": 'RESTART', 'name': name, "op_type": 'cartridge', 'app_name': app_name}
return self.do_action(params)
def cartridge_reload(self, app_name, name):
params = {"action": 'RELOAD', 'name': name, "op_type": 'cartridge', 'app_name': app_name}
return self.do_action(params)
def cartridge_get(self, app_name, name):
params = {"action": 'GET', 'name': name, "op_type": 'cartridge', 'app_name': app_name}
return self.do_action(params)
def app_template_get(self):
""" return a list of application template from an app """
status, res = self.rest.request(method='GET', url='/application_template')
if status == 'OK':
return (status, self.rest.response.json()['data'])
else:
return (status, res)
def sortedDict(adict):
keys = list(adict.keys())
keys.sort()
return map(adict.get, keys)
def perf_test(li):
cart_types = ['php-5.3']
od = {
1: {'name': 'app_create', 'params': {'app_name': 'perftest'}},
#2: {'name': 'app_delete', 'params': {'app_name': 'perftest'}},
}
sod = sortedDict(od)
#li.domain_create('blahblah')
cart_types = ['php-5.3'] # 'php-5.3', 'ruby-1.8', 'jbossas-7']
for cart in cart_types:
for action in sod:
method_call = getattr(li, action['name'])
k, v = list(action['params'].items()[0])
if action['name'] == 'app_create':
method_call(v, cart)
else:
method_call(v)
if __name__ == '__main__':
(options, args) = config_parser()
li = Openshift(host=options.ip, user=options.user, passwd=options.password,
debug=options.DEBUG,verbose=options.VERBOSE)
status, res = li.domain_get()
self.info('xxx', 1)
#status, res = li.app_create(app_name="app1", app_type=["ruby-1.8", "mysql-5.1"], init_git_url="https://github.com/openshift/wordpress-example")
#status, res = li.app_create(app_name="app2", app_type="php-5.3", init_git_url="https://github.com/openshift/wordpress-example")
#status, res = li.app_create(app_name="app3", app_type=[{"name": "ruby-1.8"}, {"name": "mysql-5.1"}], init_git_url="https://github.com/openshift/wordpress-example")