#!/usr/bin/env python
# $Id$
#
# Copyright (C) 2010  SPARTA, Inc. dba Cobham Analytic Solutions
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND SPARTA DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS.  IN NO EVENT SHALL SPARTA BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
#
#
# Helper script to load existing data from csv into the Django DB.
# Primarly useful for the initial load, as the GUI does not sync changes
# made directly to the csv files back into the database.
#
# This script should be run from the directory containing the myrpki.conf
# for the handle you are loading data
#

import os
import csv
import socket # for socket.error

import rpki
import rpki.resource_set
import rpki.ipaddrs
from rpki.myrpki import csv_reader

from rpkigui.myrpki import models
from rpkigui.myrpki.views import add_roa_requests

cfg_file = os.getenv("MYRPKI_CONF", "myrpki.conf")
cfg = rpki.config.parser(cfg_file, "myrpki")
handle = cfg.get('handle')
asn_csv = cfg.get('asn_csv')
prefix_csv = cfg.get('prefix_csv')
roa_csv = cfg.get('roa_csv')

print 'processing csv files for resource handle', handle

conf = models.Conf.objects.get(handle=handle)

# every parent has a favorite
def best_child(parent, parent_range):
    '''Return the child address range that is the closest match, or
    returns the arguments if no children.'''
    best = None
    best_range = None
    for q in parent.children.all():
        if best is None:
            best = q
            best_range = q.as_resource_range()
        else:
            t = q.as_resource_range()
            if t.min >= best_range.min and t.max <= best_range.max:
                best = q
                best_range = t
    if best:
        if best.children.all():
            best, best_range = best_child(best, best_range)
        return (best, best_range)

    return parent, parent_range

def get_or_create_prefix(address_range):
    '''Returns a AddressRange object for the resource_range_ip specified
    as an argument.  If no match is found, a new AddressRange object is
    created as a child of the best matching received resource.'''

    # get all resources from our parents
    prefix_set = models.AddressRange.objects.filter(
            from_cert__parent__in=conf.parents.all())

    # gross, since we store the address ranges as strings in the django
    # db, we can't use the normal __lte and __gte filters, so we get to
    # do it in python instead.
    for prefix in prefix_set:
        prefix_range = prefix.as_resource_range()
        if (prefix_range.min <= address_range.min and
                prefix_range.max >= address_range.max):
            # there should only ever be a single matching prefix
            break
    else:
        raise RuntimeError, '%s does not match any received address range.' % (
                address_range,)

    # find the best match among the children + grandchildren
    prefix, prefix_range = best_child(prefix, prefix_range)

    print 'best match for %s is %s' % (address_range, prefix)
    if prefix_range.min != address_range.min or prefix_range.max != address_range.max:
        # create suballocation
        print 'creating new range' 
        prefix = models.AddressRange.objects.create(lo=str(address_range.min),
                hi=str(address_range.max), parent=prefix)

    return prefix

def get_or_create_asn(asn):
    asn_set = models.Asn.objects.filter(lo__lte=asn.min, hi__gte=asn.max,
            from_cert__parent__in=conf.parents.all())
    if not asn_set:
        raise RuntimeError, '%s does not match any received AS range' % (asn,)
    best = None
    for a in asn_set:
        if best is None:
            best = a
        elif a.lo >= best.lo and a.hi <= best.hi:
            best = a
    print 'best match for %s is %s' % (asn, best)
    if best.lo != asn.min or best.hi != asn.max:
        best = models.Asn.objects.create(lo=asn.min, hi=asn.max, parent=best)
    return best

def do_asns():
    for child_handle, asn in csv_reader(asn_csv, columns=2):
        asn_range = rpki.resource_set.resource_range_as.parse_str(asn)
        child = conf.children.get(handle=child_handle)
        asn = get_or_create_asn(asn_range)
        child.asn.add(asn)

def do_prefixes():
    for child_handle, prefix in csv_reader(prefix_csv, columns=2):
        child = conf.children.get(handle=child_handle)
        try:
            rs = rpki.resource_set.resource_range_ipv4.parse_str(prefix)
        except ValueError, err:
            rs = rpki.resource_set.resource_range_ipv6.parse_str(prefix)
        obj = get_or_create_prefix(rs)
        obj.allocated = child
        obj.save()

def do_roas():
    for prefix, asn, group in csv_reader(roa_csv, columns=3):
        try:
            rs = rpki.resource_set.roa_prefix_ipv4.parse_str(prefix)
        except ValueError, err:
            rs = rpki.resource_set.roa_prefix_ipv6.parse_str(prefix)

        print str(rs.min()), str(rs.max()), rs.max_prefixlen
        obj = get_or_create_prefix(rs.to_resource_range())
        add_roa_requests(conf, obj, [int(asn)], rs.max_prefixlen)

do_asns()
do_prefixes()
do_roas()