Parse and sanity check the route spec config.

    The config data is a blob of JSON that needs to be in this format:

    {
        "<CIDR-1>" : [ "host-1-ip", "host-2-ip", "host-3-ip" ],
        "<CIDR-2>" : [ "host-4-ip", "host-5-ip" ],
        "<CIDR-3>" : [ "host-6-ip", "host-7-ip", "host-8-ip", "host-9-ip" ]
    }

    Returns the validated route config. This validation is performed on any
    route-spec pushed out by the config watcher plugin.

    Duplicate hosts in the host lists are removed.

    Raises ValueError exception in case of problems.

def parse_route_spec_config(data):
    """
    Parse and sanity check the route spec config.

    The config data is a blob of JSON that needs to be in this format:

    {
        "<CIDR-1>" : [ "host-1-ip", "host-2-ip", "host-3-ip" ],
        "<CIDR-2>" : [ "host-4-ip", "host-5-ip" ],
        "<CIDR-3>" : [ "host-6-ip", "host-7-ip", "host-8-ip", "host-9-ip" ]
    }

    Returns the validated route config. This validation is performed on any
    route-spec pushed out by the config watcher plugin.

    Duplicate hosts in the host lists are removed.

    Raises ValueError exception in case of problems.

    """
    # Sanity checking on the data object
    if type(data) is not dict:
        raise ValueError("Expected dictionary at top level")
    try:
        for k, v in data.items():
            utils.ip_check(k, netmask_expected=True)
            if type(v) is not list:
                raise ValueError("Expect list of IPs as values in dict")
            hosts = set(v)   # remove duplicates
            for ip in hosts:
                utils.ip_check(ip)
            clean_host_list = sorted(list(hosts))
            data[k] = clean_host_list

    except ArgsError as e:
        raise ValueError(e.message)

    return data