311 lines
9.9 KiB
Python
311 lines
9.9 KiB
Python
"""Utils for API usage"""
|
|
import logging
|
|
|
|
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_security_config(config):
|
|
"""Valid the format of security section in config"""
|
|
|
|
security_keys = ['server_credentials', 'service_credentials',
|
|
'console_credentials']
|
|
fields = ['username', 'password']
|
|
logging.debug('config: %s', config)
|
|
for key in security_keys:
|
|
try:
|
|
content = config[key]
|
|
except KeyError:
|
|
error_msg = "Missing '%s' in security config!" % key
|
|
logging.error(error_msg)
|
|
raise KeyError(error_msg)
|
|
|
|
for k in fields:
|
|
try:
|
|
value = content[k]
|
|
if not value:
|
|
return False, '%s in %s cannot be null!' % (k, key)
|
|
|
|
except KeyError:
|
|
error_msg = ("Missing '%s' in '%s' section of security config"
|
|
% (k, key))
|
|
logging.error(error_msg)
|
|
raise KeyError(error_msg)
|
|
|
|
return True, 'valid!'
|
|
|
|
|
|
def is_valid_networking_config(config):
|
|
"""Valid the format of networking config"""
|
|
|
|
networking = ['interfaces', 'global']
|
|
|
|
def _is_valid_interfaces_config(interfaces_config):
|
|
"""Valid the format of interfaces section in config"""
|
|
|
|
expected_keys = ['management', 'tenant', 'public', 'storage']
|
|
required_fields = ['nic', 'promisc']
|
|
normal_fields = ['ip_start', 'ip_end', 'netmask']
|
|
other_fields = ['gateway', 'vlan']
|
|
|
|
interfaces_keys = interfaces_config.keys()
|
|
for key in expected_keys:
|
|
if key not in interfaces_keys:
|
|
error_msg = "Missing '%s' in interfaces config!" % key
|
|
return False, error_msg
|
|
|
|
content = interfaces_config[key]
|
|
for field in required_fields:
|
|
if field not in content:
|
|
error_msg = "Keyword '%s' in interface %s cannot be None!"\
|
|
% (field, key)
|
|
return False, error_msg
|
|
|
|
value = content[field]
|
|
if value is None:
|
|
error_msg = ("The value of '%s' in '%s' "
|
|
'config cannot be None!' %
|
|
(field, key))
|
|
return False, error_msg
|
|
|
|
if field == 'promisc':
|
|
valid_values = [0, 1]
|
|
if int(value) not in valid_values:
|
|
return (
|
|
False,
|
|
('The value of Promisc for interface %s can '
|
|
'only be 0/1.bit_length' % key)
|
|
)
|
|
|
|
elif field == 'nic':
|
|
if not value.startswith('eth'):
|
|
return (
|
|
False,
|
|
('The value of nic for interface %s should start'
|
|
'with eth' % key)
|
|
)
|
|
|
|
if not content['promisc']:
|
|
for field in normal_fields:
|
|
value = content[field]
|
|
if field == 'netmask' and not is_valid_netmask(value):
|
|
return (False, "Invalid netmask format for interface "
|
|
" %s: '%s'!" % (key, value))
|
|
elif not is_valid_ip(value):
|
|
return (False,
|
|
"Invalid Ip format for interface %s: '%s'"
|
|
% (key, value))
|
|
|
|
for field in other_fields:
|
|
if field in content and field == 'gateway':
|
|
value = content[field]
|
|
if value and not is_valid_gateway(value):
|
|
return False, "Invalid gateway format '%s'" % value
|
|
|
|
return True, 'Valid!'
|
|
|
|
def _is_valid_global_config(global_config):
|
|
"""Valid the format of 'global' section in config"""
|
|
|
|
required_fields = ['nameservers', 'search_path', 'gateway']
|
|
global_keys = global_config.keys()
|
|
for key in required_fields:
|
|
if key not in global_keys:
|
|
error_msg = ("Missing %s in global config of networking config"
|
|
% key)
|
|
return False, error_msg
|
|
|
|
value = global_config[key]
|
|
if not value:
|
|
error_msg = ("Value of %s in global config cannot be None!" %
|
|
key)
|
|
return False, error_msg
|
|
|
|
if key == 'nameservers':
|
|
nameservers = [nameserver for nameserver in value.split(',')
|
|
if nameserver]
|
|
for nameserver in nameservers:
|
|
if not is_valid_ip(nameserver):
|
|
return (
|
|
False,
|
|
"The nameserver format is invalid! '%s'" % value
|
|
)
|
|
|
|
elif key == 'gateway' and not is_valid_gateway(value):
|
|
return False, "The gateway format is invalid! '%s'" % value
|
|
|
|
return True, 'Valid!'
|
|
|
|
#networking_keys = networking.keys()
|
|
is_valid = False
|
|
msg = None
|
|
for nkey in networking:
|
|
if nkey in config:
|
|
content = config[nkey]
|
|
|
|
if nkey == 'interfaces':
|
|
is_valid, msg = _is_valid_interfaces_config(content)
|
|
elif nkey == 'global':
|
|
is_valid, msg = _is_valid_global_config(content)
|
|
|
|
if not is_valid:
|
|
return is_valid, msg
|
|
|
|
else:
|
|
error_msg = "Missing '%s' in networking config!" % nkey
|
|
return False, error_msg
|
|
|
|
return True, 'valid!'
|
|
|
|
|
|
def is_valid_partition_config(config):
|
|
"""Valid the configuration format"""
|
|
|
|
if not config:
|
|
return False, '%s in partition cannot be null!' % config
|
|
|
|
return True, 'valid!'
|
|
|
|
|
|
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/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
|