compass-core/compass/api/util.py
grace.yu 4c69d120cd Add initial code for user authentication
Change-Id: Ie0c439244f1ae3af707b73ef64b1a411c2aede20
2014-04-14 18:13:04 -07:00

412 lines
13 KiB
Python

# Copyright 2014 Huawei Technologies Co. Ltd
#
# 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.
"""Utils for API usage."""
import netaddr
import re
from flask.ext.restful import Api
from flask import make_response
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) + '\r\n'
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 = netaddr.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 = netaddr.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": ""},
"ha_vip": {"req": 0, "validator": is_valid_ip}
}
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 the host configuration format.
.. note::
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 flatten dict.
.. note::
For example:
dict = {'a':{'b': 'c'}, 'd': 'e'} ==>
flatten dict = {'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=""):
"""Validate 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
.. note::
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.
.. note::
nested keys in dict will be joined by '.' as a column name in CSV.
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']
:param headers: the result list to hold dict keys
:param colname: the column name
:param data: input data
"""
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])