xiaodongwang 7f080e4e51 fix pep8 warning and move major util code from bin to compass dir.
Change-Id: I7baa536888db1ca46cadcdf07ef9cc0a1a3a12fb
2014-02-19 22:25:45 -08:00

389 lines
12 KiB
Python

"""Utils for API usage"""
from flask import make_response
from flask.ext.restful import Api
import re
from netaddr import IPAddress
import simplejson as json
from compass.api import app
API = Api(app)
def make_json_response(status_code, data):
"""Wrap json format to the reponse object"""
result = json.dumps(data, indent=4)
resp = make_response(result, status_code)
resp.headers['Content-type'] = 'application/json'
return resp
def make_csv_response(status_code, csv_data, fname):
"""Wrap CSV format to the reponse object"""
fname = '.'.join((fname, 'csv'))
resp = make_response(csv_data, status_code)
resp.mimetype = 'text/csv'
resp.headers['Content-Disposition'] = 'attachment; filename="%s"' % fname
return resp
def add_resource(*args, **kwargs):
"""Add resource"""
API.add_resource(*args, **kwargs)
def is_valid_ip(ip_address):
"""Valid the format of an Ip address"""
if not ip_address:
return False
regex = (r'^(([0-9]|[1-9][0-9]|1[0-9]{2}|[1-2][0-4][0-9]|25[0-5])\.)'
r'{3}'
r'([0-9]|[1-9][0-9]|1[0-9]{2}|[1-2][0-4][0-9]|25[0-5])')
if re.match(regex, ip_address):
return True
return False
def is_valid_ipnetowrk(ip_network):
"""Valid the format of an Ip network"""
if not ip_network:
return False
regex = (r'^(([0-9]|[1-9][0-9]|1[0-9]{2}|[1-2][0-4][0-9]|25[0-5])\.)'
r'{3}'
r'([0-9]|[1-9][0-9]|1[0-9]{2}|[1-2][0-4][0-9]|25[0-5])'
r'((\/[0-9]|\/[1-2][0-9]|\/[1-3][0-2]))$')
if re.match(regex, ip_network):
return True
return False
def is_valid_netmask(ip_addr):
"""Valid the format of a netmask"""
try:
ip_address = IPAddress(ip_addr)
return ip_address.is_netmask()
except Exception:
return False
def is_valid_gateway(ip_addr):
"""Valid the format of gateway"""
invalid_ip_prefix = ['0', '224', '169', '127']
try:
# Check if ip_addr is an IP address and not start with 0
ip_addr_prefix = ip_addr.split('.')[0]
if is_valid_ip(ip_addr) and ip_addr_prefix not in invalid_ip_prefix:
ip_address = IPAddress(ip_addr)
if not ip_address.is_multicast():
# Check if ip_addr is not multicast and reserved IP
return True
return False
except Exception:
return False
def _is_valid_nameservers(value):
"""Valid the format of nameservers."""
if value:
nameservers = value.strip(",").split(",")
for elem in nameservers:
if not is_valid_ip(elem):
return False
else:
return False
return True
def is_valid_security_config(config):
"""Valid the format of security section in config"""
outer_format = {
"server_credentials": {}, "service_credentials": {},
"console_credentials": {}
}
inner_format = {
"username": {}, "password": {}
}
valid_outter, err = is_valid_keys(outer_format, config, "Security")
if not valid_outter:
return (False, err)
for key in config:
content = config[key]
valid_inner, err = is_valid_keys(inner_format, content, key)
if not valid_inner:
return (False, err)
for sub_key in content:
if not content[sub_key]:
return (False, ("The value of %s in %s in security config "
"cannot be None!") % (sub_key, key))
return (True, '')
def is_valid_networking_config(config):
"""Valid the format of networking config"""
def _is_valid_interfaces_config(interfaces_config):
"""Valid the format of interfaces section in config"""
interfaces_section = {
"management": {}, "tenant": {}, "public": {}, "storage": {}
}
section = {
"ip_start": {"req": 1, "validator": is_valid_ip},
"ip_end": {"req": 1, "validator": is_valid_ip},
"netmask": {"req": 1, "validator": is_valid_netmask},
"gateway": {"req": 0, "validator": is_valid_gateway},
"nic": {},
"promisc": {}
}
# Check if interfaces outer layer keywords
is_valid_outer, err = is_valid_keys(interfaces_section,
interfaces_config, "interfaces")
if not is_valid_outer:
return (False, err)
promisc_nics = []
nonpromisc_nics = []
for key in interfaces_config:
content = interfaces_config[key]
is_valid_inner, err = is_valid_keys(section, content, key)
if not is_valid_inner:
return (False, err)
if content["promisc"] not in [0, 1]:
return (False, ("The value of Promisc in %s section of "
"interfaces can only be either 0 or 1!") % key)
if not content["nic"]:
return (False, ("The NIC in %s cannot be None!") % key)
if content["promisc"]:
if content["nic"] not in nonpromisc_nics:
promisc_nics.append(content["nic"])
continue
else:
return (False,
("The NIC in %s cannot be assigned in promisc "
"and nonpromisc mode at the same time!" % key))
else:
if content["nic"] not in promisc_nics:
nonpromisc_nics.append(content["nic"])
else:
return (False,
("The NIC in %s cannot be assigned in promisc "
"and nonpromisc mode at the same time!" % key))
# Validate other keywords in the section
for sub_key in content:
if sub_key == "promisc" or sub_key == "nic":
continue
value = content[sub_key]
is_required = section[sub_key]["req"]
validator = section[sub_key]["validator"]
if value:
if validator and not validator(value):
error_msg = "The format of %s in %s is invalid!" % \
(sub_key, key)
return (False, error_msg)
elif is_required:
return (False,
("%s in %s section in interfaces of networking "
"config cannot be None!") % (sub_key, key))
return (True, '')
def _is_valid_global_config(global_config):
"""Valid the format of 'global' section in config"""
global_section = {
"nameservers": {"req": 1, "validator": _is_valid_nameservers},
"search_path": {"req": 1, "validator": ""},
"gateway": {"req": 1, "validator": is_valid_gateway},
"proxy": {"req": 0, "validator": ""},
"ntp_server": {"req": 0, "validator": ""}
}
is_valid_format, err = is_valid_keys(global_section, global_config,
"global")
if not is_valid_format:
return (False, err)
for key in global_section:
value = global_config[key]
is_required = global_section[key]["req"]
validator = global_section[key]["validator"]
if value:
if validator and not validator(value):
return (False, ("The format of %s in global section of "
"networking config is invalid!") % key)
elif is_required:
return (False, ("The value of %s in global section of "
"netowrking config cannot be None!") % key)
return (True, '')
networking_config = {
"interfaces": _is_valid_interfaces_config,
"global": _is_valid_global_config
}
valid_format, err = is_valid_keys(networking_config, config, "networking")
if not valid_format:
return (False, err)
for key in networking_config:
validator = networking_config[key]
is_valid, err = validator(config[key])
if not is_valid:
return (False, err)
return (True, '')
def is_valid_partition_config(config):
"""Valid the configuration format"""
if not config:
return (False, '%s in partition cannot be null!' % config)
return (True, '')
def valid_host_config(config):
""" valid_format is used to check if the input config is qualified
the required fields and format.
The key is the required field and format of the input config
The value is the validator function name of the config value
"""
from api import errors
valid_format = {"/networking/interfaces/management/ip": "is_valid_ip",
"/networking/interfaces/tenant/ip": "is_valid_ip",
"/networking/global/gateway": "is_valid_gateway",
"/networking/global/nameserver": "",
"/networking/global/search_path": "",
"/roles": ""}
flat_config = {}
flatten_dict(config, flat_config)
config_keys = flat_config.keys()
for key in config_keys:
validator = None
try:
validator = valid_format[key]
except Exception:
continue
else:
value = flat_config[key]
if validator:
is_valid_format = globals()[validator](value)
if not is_valid_format:
error_msg = "The format '%s' is incorrect!" % value
raise errors.UserInvalidUsage(error_msg)
def flatten_dict(dictionary, output, flat_key=""):
"""This function will convert the dictionary into a list
For example:
dict = {'a':{'b': 'c'}, 'd': 'e'} ==>
list = ['a/b/c', 'd/e']
"""
keywords = dictionary.keys()
for key in keywords:
tmp = '/'.join((flat_key, key))
if isinstance(dictionary[key], dict):
flatten_dict(dictionary[key], output, tmp)
else:
output[tmp] = dictionary[key]
def update_dict_value(searchkey, dictionary):
"""Update dictionary value"""
keywords = dictionary.keys()
for key in keywords:
if key == searchkey:
if isinstance(dictionary[key], str):
dictionary[key] = ""
elif isinstance(dictionary[key], list):
dictionary[key] = []
elif isinstance(dictionary[key], dict):
update_dict_value(searchkey, dictionary[key])
else:
continue
def is_valid_keys(expected, input_dict, section=""):
"""Valid keys"""
excepted_keys = set(expected.keys())
input_keys = set(input_dict.keys())
if excepted_keys != input_keys:
invalid_keys = list(excepted_keys - input_keys) if \
len(excepted_keys) > len(input_keys) else\
list(input_keys - excepted_keys)
error_msg = ("Invalid or missing keywords in the %s "
"section of networking config. Please check these "
"keywords %s") % (section, invalid_keys)
return (False, error_msg)
return (True, "")
def get_col_val_from_dict(result, data):
"""
Convert a dict's values to a list.
:param result: a list of values for each column
:param data: input data
for example:
data = {"a": {"b": {"c": 1},
"d": 2}}
the result will be [1, 2]
"""
if not isinstance(data, dict):
data = str(data) if str(data) else 'None'
result.append(data)
return
for key in data:
get_col_val_from_dict(result, data[key])
def get_headers_from_dict(headers, colname, data):
"""Convert a column which value is dict to a list of column name and keys.
nested keys in dict will be joined by '.' as a column name in CSV.
:param headers: the result list to hold dict keys
:param colname: the column name
:param data: input data
for example:
the column name is 'config_data', and
the value is {"a": {"b": {"c": 1},
"d": 2}}
then headers will be ['config_data.a.b.c', 'config_data.a.d']
"""
if not colname:
raise "colname cannot be None!"
if not isinstance(data, dict):
headers.append(colname)
return
for key in data:
tmp_header = '.'.join((colname, key))
get_headers_from_dict(headers, tmp_header, data[key])