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
 |