CLOUDSTACK-8395: vmops plugin should work on both XS 6.5 and 6.2

This fixes the issue of Security Groups not working in case of XenServer 6.5;
- Uses nethash ipset data-structure to store CIDRs (efficient than iphash and
  avoids overflow errors in case users add /8 /4 ingress/egress cidrs)
- Support for ipset versions both on 6.2 and 6.5, both have different outputs. This
  fixes the issue of destroy_network_rules_for_vm failing
- Implements defensive filtering of list, instead of popping last item without
  checking if it's None or empty
- Greps using names that are 'quoted' to avoid bash errors
- Before setting up new network rule, tries to clean and remove old ipset entry
- Idents, whitespace and naming fixes

PS. This is my 1000th commit to the 🐵 project :)

This closes #186

(cherry picked from commit d91d161107e20ffc8ec088ee55f99d6743330946)
Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>

Conflicts:
	scripts/vm/hypervisor/xenserver/vmops
This commit is contained in:
Rohit Yadav 2015-04-21 17:35:36 +02:00
parent fd92f9d4d8
commit 496ffcad83

View File

@ -357,17 +357,17 @@ def allow_egress_traffic(session):
return 'true' return 'true'
def ipset(ipsetname, proto, start, end, ips): def ipset(ipsetname, proto, start, end, cidrs):
try: try:
util.pread2(['ipset', '-N', ipsetname, 'iphash']) util.pread2(['ipset', '-N', ipsetname, 'nethash'])
except: except:
logging.debug("ipset chain already exists" + ipsetname) logging.debug("ipset chain already exists: " + ipsetname)
result = True result = True
ipsettmp = ''.join(''.join(ipsetname.split('-')).split('_')) + str(int(time.time()) % 1000) ipsettmp = ''.join(''.join(ipsetname.split('-')).split('_')) + str(int(time.time()) % 1000)
try: try:
util.pread2(['ipset', '-N', ipsettmp, 'iphash']) util.pread2(['ipset', '-N', ipsettmp, 'nethash'])
except: except:
logging.debug("Failed to create temp ipset, reusing old name= " + ipsettmp) logging.debug("Failed to create temp ipset, reusing old name= " + ipsettmp)
try: try:
@ -375,12 +375,12 @@ def ipset(ipsetname, proto, start, end, ips):
except: except:
logging.debug("Failed to clear old temp ipset name=" + ipsettmp) logging.debug("Failed to clear old temp ipset name=" + ipsettmp)
return False return False
try: try:
for ip in ips: for cidr in cidrs:
try: try:
util.pread2(['ipset', '-A', ipsettmp, ip]) util.pread2(['ipset', '-A', ipsettmp, cidr])
except CommandException, cex: except CommandException, cex:
logging.debug("ipset cidr add failed due to: " + str(cex.reason))
if cex.reason.rfind('already in set') == -1: if cex.reason.rfind('already in set') == -1:
raise raise
except: except:
@ -392,8 +392,16 @@ def ipset(ipsetname, proto, start, end, ips):
try: try:
util.pread2(['ipset', '-W', ipsettmp, ipsetname]) util.pread2(['ipset', '-W', ipsettmp, ipsetname])
except: except:
logging.debug("Failed to swap ipset " + ipsetname) logging.debug("Failed to swap ipset, trying to delete and swap ipset: " + ipsetname)
result = False # the old ipset entry could be of iphash type, try to delete and recreate
try:
util.pread2(['ipset', '-X', ipsetname])
util.pread2(['ipset', '-N', ipsetname, 'nethash'])
util.pread2(['ipset', '-W', ipsettmp, ipsetname])
except:
logging.debug("Failed to swap ipset " + ipsetname)
result = False
logging.debug("Succeeded in re-initializing and swapping ipset")
try: try:
util.pread2(['ipset', '-F', ipsettmp]) util.pread2(['ipset', '-F', ipsettmp])
@ -417,7 +425,7 @@ def destroy_network_rules_for_vm(session, args):
util.pread2(['iptables', '-F', vmchain_default]) util.pread2(['iptables', '-F', vmchain_default])
util.pread2(['iptables', '-X', vmchain_default]) util.pread2(['iptables', '-X', vmchain_default])
except: except:
logging.debug("Ignoring failure to delete chain " + vmchain_default) logging.debug("Ignoring failure to delete chain " + vmchain_default)
destroy_ebtables_rules(vmchain) destroy_ebtables_rules(vmchain)
destroy_arptables_rules(vmchain) destroy_arptables_rules(vmchain)
@ -442,49 +450,42 @@ def destroy_network_rules_for_vm(session, args):
return 'true' return 'true'
try: try:
setscmd = "ipset --save | grep " + vmchain + " | grep '^-N' | awk '{print $2}'" setscmd = "ipset --save | grep '%s' | grep -e '^-N' -e '^create' | awk '{print $2}'" % vmchain
setsforvm = util.pread2(['/bin/bash', '-c', setscmd]).split('\n') ipset_names = filter(None, util.pread2(['/bin/bash', '-c', setscmd]).split('\n'))
for set in setsforvm: for ipset_name in ipset_names:
if set != '': if not ipset_name:
util.pread2(['ipset', '-F', set]) continue
util.pread2(['ipset', '-X', set]) util.pread2(['ipset', '-F', ipset_name])
util.pread2(['ipset', '-X', ipset_name])
except: except:
logging.debug("Failed to destroy ipsets for %" % vm_name) logging.debug("Failed to destroy ipsets for %" % vm_name)
return 'true' return 'true'
@echo @echo
def destroy_ebtables_rules(vm_chain): def destroy_ebtables_rules(vm_chain):
delcmd = "ebtables-save | grep '%s' | sed 's/-A/-D/'" % vm_chain
delcmd = "ebtables-save | grep " + vm_chain + " | sed 's/-A/-D/'"
delcmds = util.pread2(['/bin/bash', '-c', delcmd]).split('\n') delcmds = util.pread2(['/bin/bash', '-c', delcmd]).split('\n')
delcmds.pop() for cmd in filter(None, delcmds):
for cmd in delcmds:
try: try:
dc = cmd.split(' ') dc = 'ebtables ' + cmd
dc.insert(0, 'ebtables') util.pread2(filter(None, dc.split(' ')))
util.pread2(dc)
except: except:
logging.debug("Ignoring failure to delete ebtables rules for vm " + vm_chain) logging.debug("Ignoring failure to delete ebtables rules for vm " + vm_chain)
try: try:
util.pread2(['ebtables', '-F', vm_chain]) util.pread2(['ebtables', '-F', vm_chain])
util.pread2(['ebtables', '-X', vm_chain]) util.pread2(['ebtables', '-X', vm_chain])
except: except:
logging.debug("Ignoring failure to delete ebtables chain for vm " + vm_chain) logging.debug("Ignoring failure to delete ebtables chain for vm " + vm_chain)
@echo @echo
def destroy_arptables_rules(vm_chain): def destroy_arptables_rules(vm_chain):
delcmd = "arptables -vL FORWARD | grep " + vm_chain + " | sed 's/-i any//' | sed 's/-o any//' | awk '{print $1,$2,$3,$4}' " delcmd = "arptables -vL FORWARD | grep '%s' | sed 's/-i any//' | sed 's/-o any//' | awk '{print $1,$2,$3,$4}' " % vm_chain
delcmds = util.pread2(['/bin/bash', '-c', delcmd]).split('\n') delcmds = util.pread2(['/bin/bash', '-c', delcmd]).split('\n')
delcmds.pop() for cmd in filter(None, delcmds):
for cmd in delcmds:
try: try:
dc = cmd.split(' ') dc = 'arptables -D FORWARD ' + cmd
dc.insert(0, 'arptables') util.pread2(filter(None, dc.split(' ')))
dc.insert(1, '-D')
dc.insert(2, 'FORWARD')
util.pread2(dc)
except: except:
logging.debug("Ignoring failure to delete arptables rules for vm " + vm_chain) logging.debug("Ignoring failure to delete arptables rules for vm " + vm_chain)
@ -662,7 +663,6 @@ def default_network_rules_systemvm(session, args):
logging.debug("Failed to program default rules") logging.debug("Failed to program default rules")
return 'false' return 'false'
util.pread2(['iptables', '-A', vmchain, '-j', 'ACCEPT']) util.pread2(['iptables', '-A', vmchain, '-j', 'ACCEPT'])
if write_rule_log_for_vm(vm_name, '-1', '_ignore_', domid, '_initial_', '-1') == False: if write_rule_log_for_vm(vm_name, '-1', '_ignore_', domid, '_initial_', '-1') == False:
@ -760,7 +760,6 @@ def default_network_rules(session, args):
destroy_ebtables_rules(vmchain) destroy_ebtables_rules(vmchain)
try: try:
util.pread2(['iptables', '-N', vmchain]) util.pread2(['iptables', '-N', vmchain])
except: except:
@ -870,19 +869,15 @@ def delete_rules_for_vm_in_bridge_firewall_chain(vmName):
vm_name = vmName vm_name = vmName
vmchain = chain_name_def(vm_name) vmchain = chain_name_def(vm_name)
delcmd = "iptables-save | grep '\-A BRIDGE-FIREWALL' | grep " + vmchain + " | sed 's/-A/-D/'" delcmd = "iptables-save | grep '\-A BRIDGE-FIREWALL' | grep '%s' | sed 's/-A/-D/'" % vmchain
delcmds = util.pread2(['/bin/bash', '-c', delcmd]).split('\n') delcmds = util.pread2(['/bin/bash', '-c', delcmd]).split('\n')
delcmds.pop() for cmd in filter(None, delcmds):
for cmd in delcmds:
try: try:
dc = cmd.split(' ') dc = 'iptables ' + cmd
dc.insert(0, 'iptables') util.pread2(filter(None, dc.split(' ')))
dc.pop()
util.pread2(filter(None, dc))
except: except:
logging.debug("Ignoring failure to delete rules for vm " + vmName) logging.debug("Ignoring failure to delete rules for vm " + vmName)
@echo @echo
def network_rules_for_rebooted_vm(session, vmName): def network_rules_for_rebooted_vm(session, vmName):
vm_name = vmName vm_name = vmName
@ -1380,22 +1375,29 @@ def network_rules(session, args):
logging.debug("Programming network rules for vm %s seqno=%s numrules=%s signature=%s guestIp=%s,"\ logging.debug("Programming network rules for vm %s seqno=%s numrules=%s signature=%s guestIp=%s,"\
" update iptables, reason=%s" % (vm_name, seqno, len(lines), signature, vm_ip, reason)) " update iptables, reason=%s" % (vm_name, seqno, len(lines), signature, vm_ip, reason))
# Flush iptables rules to clear ipset references and before re-applying iptable rules
for chain in [chain_name(vm_name), egress_chain_name(vm_name)]:
try:
util.pread2(['iptables', '-F', chain])
except:
logging.debug("Ignoring failure to delete chain " + chain)
util.pread2(['iptables', '-N', chain])
cmds = [] cmds = []
egressrules = 0 egressrules = 0
for line in lines: for line in lines:
tokens = line.split(':') tokens = line.split(':')
if len(tokens) != 5: if len(tokens) != 5:
continue continue
type = tokens[0] token_type = tokens[0]
protocol = tokens[1] protocol = tokens[1]
start = tokens[2] start = tokens[2]
end = tokens[3] end = tokens[3]
cidrs = tokens.pop(); cidrs = tokens.pop().split(",")
ips = cidrs.split(",") cidrs.pop()
ips.pop()
allow_any = False allow_any = False
if type == 'E': if token_type == 'E':
vmchain = egress_chain_name(vm_name) vmchain = egress_chain_name(vm_name)
action = "RETURN" action = "RETURN"
direction = "dst" direction = "dst"
@ -1404,61 +1406,48 @@ def network_rules(session, args):
vmchain = chain_name(vm_name) vmchain = chain_name(vm_name)
action = "ACCEPT" action = "ACCEPT"
direction = "src" direction = "src"
if '0.0.0.0/0' in ips: if '0.0.0.0/0' in cidrs:
i = ips.index('0.0.0.0/0') i = cidrs.index('0.0.0.0/0')
del ips[i] del cidrs[i]
allow_any = True allow_any = True
range = start + ":" + end port_range = start + ":" + end
if ips: if cidrs:
ipsetname = vmchain + "_" + protocol + "_" + start + "_" + end ipsetname = vmchain + "_" + protocol + "_" + start + "_" + end
if start == "-1": if start == "-1":
ipsetname = vmchain + "_" + protocol + "_any" ipsetname = vmchain + "_" + protocol + "_any"
if ipset(ipsetname, protocol, start, end, ips) == False: if ipset(ipsetname, protocol, start, end, cidrs) == False:
logging.debug(" failed to create ipset for rule " + str(tokens)) logging.debug(" failed to create ipset for rule " + str(tokens))
if protocol == 'all': if protocol == 'all':
iptables = ['iptables', '-I', vmchain, '-m', 'state', '--state', 'NEW', '-m', 'set', keyword, ipsetname, direction, '-j', action] iptables = ['iptables', '-I', vmchain, '-m', 'state', '--state', 'NEW', '-m', 'set', keyword, ipsetname, direction, '-j', action]
elif protocol != 'icmp': elif protocol != 'icmp':
iptables = ['iptables', '-I', vmchain, '-p', protocol, '-m', protocol, '--dport', range, '-m', 'state', '--state', 'NEW', '-m', 'set', keyword, ipsetname, direction, '-j', action] iptables = ['iptables', '-I', vmchain, '-p', protocol, '-m', protocol, '--dport', port_range, '-m', 'state', '--state', 'NEW', '-m', 'set', keyword, ipsetname, direction, '-j', action]
else: else:
range = start + "/" + end port_range = start + "/" + end
if start == "-1": if start == "-1":
range = "any" port_range = "any"
iptables = ['iptables', '-I', vmchain, '-p', 'icmp', '--icmp-type', range, '-m', 'set', keyword, ipsetname, direction, '-j', action] iptables = ['iptables', '-I', vmchain, '-p', 'icmp', '--icmp-type', port_range, '-m', 'set', keyword, ipsetname, direction, '-j', action]
cmds.append(iptables) cmds.append(iptables)
logging.debug(iptables) logging.debug(iptables)
if allow_any and protocol != 'all': if allow_any and protocol != 'all':
if protocol != 'icmp': if protocol != 'icmp':
iptables = ['iptables', '-I', vmchain, '-p', protocol, '-m', protocol, '--dport', range, '-m', 'state', '--state', 'NEW', '-j', action] iptables = ['iptables', '-I', vmchain, '-p', protocol, '-m', protocol, '--dport', port_range, '-m', 'state', '--state', 'NEW', '-j', action]
else: else:
range = start + "/" + end port_range = start + "/" + end
if start == "-1": if start == "-1":
range = "any" port_range = "any"
iptables = ['iptables', '-I', vmchain, '-p', 'icmp', '--icmp-type', range, '-j', action] iptables = ['iptables', '-I', vmchain, '-p', 'icmp', '--icmp-type', port_range, '-j', action]
cmds.append(iptables) cmds.append(iptables)
logging.debug(iptables) logging.debug(iptables)
vmchain = chain_name(vm_name)
try:
util.pread2(['iptables', '-F', vmchain])
except:
logging.debug("Ignoring failure to delete chain " + vmchain)
util.pread2(['iptables', '-N', vmchain])
egress_vmchain = egress_chain_name(vm_name)
try:
util.pread2(['iptables', '-F', egress_vmchain])
except:
logging.debug("Ignoring failure to delete chain " + egress_vmchain)
util.pread2(['iptables', '-N', egress_vmchain])
for cmd in cmds: for cmd in cmds:
util.pread2(cmd) util.pread2(cmd)
vmchain = chain_name(vm_name)
egress_vmchain = egress_chain_name(vm_name)
if egressrules == 0 : if egressrules == 0 :
util.pread2(['iptables', '-A', egress_vmchain, '-j', 'RETURN']) util.pread2(['iptables', '-A', egress_vmchain, '-j', 'RETURN'])
else: else: