#
# CIRCLE is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# CIRCLE is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with CIRCLE.  If not, see <http://www.gnu.org/licenses/>.

from __future__ import unicode_literals, absolute_import

from django.core.management.base import BaseCommand, CommandError
import logging

from firewall.models import Firewall, VlanGroup, Rule
from django.contrib.auth.models import User

logger = logging.getLogger(__name__)


class Command(BaseCommand):

    def add_arguments(self, parser):

        group = parser.add_mutually_exclusive_group(required=True)

        group.add_argument('--port',
                           action='store',
                           dest='port',
                           type=int,
                           help='port which will open (0-65535)')

        group.add_argument('--port-range',
                           action='store',
                           dest='range',
                           type=int,
                           nargs=2,
                           help='closed port range which will open (0-65535)',
                           metavar=('LOWER', 'HIGHER'))

        parser.add_argument('--protocol',
                            action='store',
                            dest='proto',
                            required=True,
                            choices=('tcp', 'udp', 'icmp'),
                            help='protocol name')

        parser.add_argument('--action',
                            action='store',
                            dest='action',
                            default='accept',
                            choices=('accept', 'drop', 'ignore'),
                            help='action of the rule')

        parser.add_argument('--dir',
                            action='store',
                            dest='dir',
                            default='in',
                            choices=('in', 'out'),
                            help='direction of the rule')

        parser.add_argument('--firewall',
                            action='store',
                            dest='firewall',
                            required=True,
                            help='firewall name which open the given port')

        parser.add_argument('--vlan-group',
                            action='store',
                            dest='vlan_group',
                            required=True,
                            help='vlan group name where the port will open')

        parser.add_argument('--owner',
                            action='store',
                            dest='owner',
                            required=True,
                            help='name of user who owns the rule')

    def handle(self, *args, **options):

        port = options['port']
        range = options['range']
        proto = options['proto']
        action = options['action']
        dir = options['dir']
        owner = options['owner']
        firewall = options['firewall']
        fnet = options['vlan_group']

        try:
            owner = User.objects.get(username=owner)
            firewall = Firewall.objects.get(name=firewall)
            fnet = VlanGroup.objects.get(name=fnet)
        except User.DoesNotExist:
            raise CommandError("User '%s' does not exist" % owner)
        except Firewall.DoesNotExist:
            raise CommandError("Firewall '%s' does not exist" % firewall)
        except VlanGroup.DoesNotExist:
            raise CommandError("VlanGroup '%s' does not exist" % fnet)

        if port:
            self.validate_port(port)
            try:
                rule = self.make_rule(dport=port, proto=proto, action=action,
                                      direction=dir, owner=owner,
                                      firewall=firewall, foreign_network=fnet)
                rule.save()
            except Warning as e:
                logger.warning(e)
        else:
            lower = min(range)
            higher = max(range)
            self.validate_port(lower)
            self.validate_port(higher)

            rules = []

            for port in xrange(lower, higher+1):
                try:
                    rule = self.make_rule(port, proto, action, dir,
                                          owner, firewall, fnet)
                    rules.append(rule)
                except Warning as e:
                    logger.warning(e)

            Rule.objects.bulk_create(rules)

    def make_rule(self, **kwargs):

        rule, created = Rule.objects.get_or_create(**kwargs)

        if not created:
            raise Warning(('Rule does exist: %s' %
                          unicode(rule)).encode('utf-8'))

        rule.full_clean()

        return rule

    def validate_port(self, port):
        if port < 0 or port > 65535:
            raise CommandError("Port '%i' not in range [0-65535]" % port)