# -*- coding: utf-8 -*-
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# this file is part of 'RAX-AutoScaler'
#
# Copyright 2014 Rackspace US, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import print_function
import os
import pyrax
import sys
import pyrax.exceptions as pexc
from termcolor import colored
import ConfigParser
import subprocess
import datetime
import json
import urllib2
import logging
[docs]def get_logger():
"""This function instantiate the logger.
:return: logger
"""
logger = logging.getLogger(__name__)
return logger
[docs]def check_file(fname):
"""This function checks if file exists and is readable.
:param fname: file name
:returns: file name with absolute path
"""
file_abspath = os.path.abspath(fname)
if os.path.isfile(file_abspath) and os.access(file_abspath, os.R_OK):
return file_abspath
else:
preDefinedPath = '/etc/rax-autoscaler/'+fname
# Check in /etc/rax-autoscaler/config path
if os.path.isfile(preDefinedPath):
if os.access(preDefinedPath, os.R_OK):
return preDefinedPath
# Either file is missing or is not readable
return
[docs]def get_config(config_file):
"""This function read and returns jsons configuration data
:param config_file: json configuration file name
:returns: json data
"""
logger = get_logger()
logger.info("Loading config file: '%s'" % config_file)
try:
json_data = open(config_file)
data = json.load(json_data)
return data
except Exception, e:
logger.error("Error: " + str(e))
return None
[docs]def get_machine_uuid():
"""This function uses subprocess to get node uuid and cached it for future use
:returns: server uuid
None
"""
logger = get_logger()
server_uptime = None
cache_uptime = None
cache_file = '.uuid.cache'
uuid = None
cache_content = [None] * 2
try:
uptime_file = open('/proc/uptime')
contents = uptime_file.read().split()
uptime_file.close()
server_uptime = str(int(float(contents[0])))
except Exception, e:
logger.warning("Unable to get uptime")
logger.debug('%s' % str(e))
pass
if server_uptime is None:
logger.debug("Failed to get server uptime")
else:
logger.debug("Checking if cache file '%s' already exists" % cache_file)
cache_file = check_file(cache_file)
if cache_file is not None:
logger.info("Getting uptime and node id from cache file")
cache_content = None
try:
rfh = open(cache_file, 'r').read()
cache_content = rfh.split('\n')
except:
logger.warning("Unable to read a file '%s' in '%s'"
% (cache_file, '/etc/rax-autoscaler'))
pass
if (not cache_content[0] or cache_content[0] is None
or not cache_content[1] or cache_content[1] is None):
logger.warning("Cache file is corrupted, failed to"
"read the content")
else:
try:
if int(cache_content[0]) < int(server_uptime):
uuid = cache_content[1]
else:
logger.warning("Invalid uptime found in cache file")
logger.debug("uptime: %s cache uptime: %s"
% (server_uptime, cache_content[0]))
except:
logger.warning("Invalid content found in cache file")
pass
if uuid is None:
logger.info('Launching xenstore query to get server uuid')
try:
name = subprocess.Popen(['xenstore-read name'], shell=True,
stdout=subprocess.PIPE
).communicate()[0]
id = name.strip()
uuid = id[9:]
except Exception, e:
logger.error("Error: " + str(e))
return None
if server_uptime is not None:
cache_file = '.uuid.cache'
# Check if file exists in cwd
if os.path.isfile(cache_file) is False:
if os.path.isdir('/etc/rax-autoscaler') is True:
cache_file = '/etc/rax-autoscaler/.uuid.cache'
logger.info("Creating cache file '%s'" % cache_file)
try:
wfh = open(cache_file, 'w')
wfh.write(server_uptime)
wfh.write('\n')
wfh.write(uuid)
wfh.close()
except Exception, e:
logger.warning("Unable to create a file '%s': '%s'"
% (cache_file, str(e)))
pass
return uuid
[docs]def get_user_value(args, config, key):
"""This function returns value associated with the key if its available in
user arguments else in json config file.
:param args: user arguments
:param config: json configuration data
:param key: key name
:returns: value associated with key
"""
logger = get_logger()
value = None
if args[key] is None:
try:
value = config['auth'][key.lower()]
except:
logger.error("Invalid config, '" + key +
"' key not found in authentication section")
else:
value = args[key]
return value
[docs]def get_group_value(config, group, key):
"""This function returns value in autoscale_groups section associated with
provided key.
:type config: object
:param group: group name
:param config: json configuration data
:param key: key name
:returns: value associated with key
"""
logger = get_logger()
try:
value = config['autoscale_groups'][group][key]
if value is not None:
return value
except:
logger.error("Error: unable to get value for key '" + key +
"' from group '" + group + "'")
return None
[docs]def get_webhook_value(config, group, key):
"""This function returns value in webhooks section of json file which is
associated with provided key.
:param group: group name
:param config: json configuration data
:param key: key name
:returns: value associated with key
"""
logger = get_logger()
try:
value = config['autoscale_groups'][group]['webhooks'][key]
if value is not None:
return value
except:
logger.warning("Unable to find value for key: '%s' in group '%s'"
% (key, group))
return None
[docs]def webhook_call(config_data, group, policy, key):
"""This function makes webhook calls.
:param config_data: json configuration data
:param group: group name
:param policy: policy type
:param key: key name
"""
logger = get_logger()
logger.info('Launching %s webhook call' % key)
url_list = get_webhook_value(config_data, group, policy)
if url_list is None:
return None
group_id = get_group_value(config_data, group, 'group_id')
if group_id is None:
return None
up_policy_id = get_group_value(config_data, group,
'scale_up_policy')
if up_policy_id is None:
return None
down_policy_id = get_group_value(config_data, group,
'scale_down_policy')
if down_policy_id is None:
return None
check_type = get_group_value(config_data, group, 'check_type')
if check_type is None:
return None
metric_name = get_group_value(config_data, group,
'metric_name')
if check_type is None:
return None
up_threshold = get_group_value(config_data, group,
'scale_up_threshold')
if up_threshold is None:
return None
down_threshold = get_group_value(config_data, group,
'scale_down_threshold')
if up_threshold is None:
return None
data = json.dumps({'group_id': group_id,
'scale_up_policy': up_policy_id,
'scale_down_policy': down_policy_id,
'check_type': check_type,
'metric_name': metric_name,
'scale_up_threshold': up_threshold,
'scale_down_threshold': down_threshold})
urls = url_list[key]
for url in urls:
logger.info("Sending POST request to url: '%s'" % url)
try:
req = urllib2.Request(url, data,
{'Content-Type': 'application/json'})
f = urllib2.urlopen(req)
response = f.read()
f.close()
except Exception, e:
logger.warning(str(e))
return None