grace.yu 9c54d45803 Update API
Change-Id: I9fa5660321cae7312f63ce359d67d9f9276e4d72
2014-01-31 16:00:13 -08:00

354 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 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 = ('^(([0-9]|[1-9][0-9]|1[0-9]{2}|[1-2][0-4][0-9]|25[0-5])\.)'
'{3}'
'([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 = ('^(([0-9]|[1-9][0-9]|1[0-9]{2}|[1-2][0-4][0-9]|25[0-5])\.)'
'{3}'
'([0-9]|[1-9][0-9]|1[0-9]{2}|[1-2][0-4][0-9]|25[0-5])'
'((\/[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):
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:
error_msg = ("Cannot find the path '%s'. Please check the keywords"
% key)
raise errors.UserInvalidUsage(error_msg)
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, newvalue, dictionary):
"""Update dictionary value"""
keywords = dictionary.keys()
for key in keywords:
if key == searchkey:
dictionary[key] = newvalue
elif isinstance(dictionary[key], dict):
update_dict_value(searchkey, newvalue, dictionary[key])
else:
continue
def is_valid_keys(expected, input_dict, section=""):
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 is_same_dict_keys(expected_dict, config_dict):
if not expected_dict or not config_dict:
return (False, "The Config cannot be None!")
if expected_dict.viewkeys() == config_dict.viewkeys():
for expected_key, config_key in zip(expected_dict, config_dict):
if isinstance(expected_dict[expected_key], str):
return (True, "")
is_same, err = is_same_dict_keys(expected_dict[expected_key],
config_dict[config_key])
if not is_same:
return (False, err)
return (True, "")
if len(expected_dict) >= len(config_dict):
invalid_list = list(expected_dict.viewkeys() - config_dict.viewkeys())
else:
invalid_list = list(config_dict.viewkeys() - expected_dict.viewkeys())
return (False, "Invalid key(s) %r in the config" % invalid_list)