#!/usr/bin/env python
# vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright 2010-2011 OpenStack Foundation
# All Rights Reserved.
#
#    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.

"""
This script is used to configure iptables, ebtables, and arptables rules on
XenServer hosts.
"""

import os
import sys

# This is written to Python 2.4, since that is what is available on XenServer
import simplejson as json

import novalib  # noqa


def main(dom_id, command, only_this_vif=None):
    xsls = novalib.execute_get_output('/usr/bin/xenstore-ls',
                              '/local/domain/%s/vm-data/networking' % dom_id)
    macs = [line.split("=")[0].strip() for line in xsls.splitlines()]

    for mac in macs:
        xsread = novalib.execute_get_output('/usr/bin/xenstore-read',
                                    '/local/domain/%s/vm-data/networking/%s' %
                                    (dom_id, mac))
        data = json.loads(xsread)
        for ip in data['ips']:
            if data["label"] == "public":
                vif = "vif%s.0" % dom_id
            else:
                vif = "vif%s.1" % dom_id

            if (only_this_vif is None) or (vif == only_this_vif):
                params = dict(IP=ip['ip'], VIF=vif, MAC=data['mac'])
                apply_ebtables_rules(command, params)
                apply_arptables_rules(command, params)
                apply_iptables_rules(command, params)


# A note about adding rules:
#   Whenever we add any rule to iptables, arptables or ebtables we first
#   delete the same rule to ensure the rule only exists once.


def apply_iptables_rules(command, params):
    iptables = lambda *rule: novalib.execute('/sbin/iptables', *rule)

    iptables('-D', 'FORWARD', '-m', 'physdev',
             '--physdev-in', params['VIF'],
             '-s', params['IP'],
             '-j', 'ACCEPT')
    if command == 'online':
        iptables('-A', 'FORWARD', '-m', 'physdev',
                 '--physdev-in', params['VIF'],
                 '-s', params['IP'],
                 '-j', 'ACCEPT')


def apply_arptables_rules(command, params):
    arptables = lambda *rule: novalib.execute('/sbin/arptables', *rule)

    arptables('-D', 'FORWARD', '--opcode', 'Request',
              '--in-interface', params['VIF'],
              '--source-ip', params['IP'],
              '--source-mac', params['MAC'],
              '-j', 'ACCEPT')
    arptables('-D', 'FORWARD', '--opcode', 'Reply',
              '--in-interface', params['VIF'],
              '--source-ip', params['IP'],
              '--source-mac', params['MAC'],
              '-j', 'ACCEPT')
    if command == 'online':
        arptables('-A', 'FORWARD', '--opcode', 'Request',
                  '--in-interface', params['VIF'],
                  '--source-mac', params['MAC'],
                  '-j', 'ACCEPT')
        arptables('-A', 'FORWARD', '--opcode', 'Reply',
                  '--in-interface', params['VIF'],
                  '--source-ip', params['IP'],
                  '--source-mac', params['MAC'],
                  '-j', 'ACCEPT')


def apply_ebtables_rules(command, params):
    ebtables = lambda *rule: novalib.execute("/sbin/ebtables", *rule)

    ebtables('-D', 'FORWARD', '-p', '0806', '-o', params['VIF'],
             '--arp-ip-dst', params['IP'],
             '-j', 'ACCEPT')
    ebtables('-D', 'FORWARD', '-p', '0800', '-o', params['VIF'],
             '--ip-dst', params['IP'],
             '-j', 'ACCEPT')
    if command == 'online':
        ebtables('-A', 'FORWARD', '-p', '0806',
                 '-o', params['VIF'],
                 '--arp-ip-dst', params['IP'],
                 '-j', 'ACCEPT')
        ebtables('-A', 'FORWARD', '-p', '0800',
                 '-o', params['VIF'],
                 '--ip-dst', params['IP'],
                 '-j', 'ACCEPT')

    ebtables('-D', 'FORWARD', '-s', '!', params['MAC'],
             '-i', params['VIF'], '-j', 'DROP')
    if command == 'online':
        ebtables('-I', 'FORWARD', '1', '-s', '!', params['MAC'],
                 '-i', params['VIF'], '-j', 'DROP')


if __name__ == "__main__":
    if len(sys.argv) < 3:
        print ("usage: %s dom_id online|offline [vif]" %
               os.path.basename(sys.argv[0]))
        sys.exit(1)
    else:
        dom_id, command = sys.argv[1:3]
        vif = len(sys.argv) == 4 and sys.argv[3] or None
        main(dom_id, command, vif)
