mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
tested by creating domr and user vm and migrating them 1. if you add host for kvm , you need specify kvm://ip_address, otherwiset there is no to differ kvm and xenserver hosts, edison cloud you please test this, may need some minor fixes. 2. if you want to session inside your plugin call, you can not use the one which is passed in, due to it is a slavesession, you need to call get_xapi_session() to get a new local session and use it. I have modified some of these, I may ignore some. Please let me know if you see XENAPI_PLUGIN_EXCEPTION.
1137 lines
36 KiB
Python
Executable File
1137 lines
36 KiB
Python
Executable File
#!/usr/bin/python
|
|
#
|
|
# A plugin for executing script needed by vmops cloud
|
|
|
|
import os, sys, time
|
|
import XenAPIPlugin
|
|
import XenAPI
|
|
sys.path.append("/opt/xensource/sm/")
|
|
import SR, VDI, SRCommand, util, lvutil
|
|
from util import CommandException
|
|
import shutil
|
|
import vhdutil
|
|
import lvhdutil
|
|
import subprocess
|
|
from lvmcache import LVMCache
|
|
from journaler import Journaler
|
|
from lock import Lock
|
|
import errno
|
|
import subprocess
|
|
import xs_errors
|
|
import cleanup
|
|
import hostvmstats
|
|
import socket
|
|
import stat
|
|
import random
|
|
import base64
|
|
import tempfile
|
|
|
|
|
|
def echo(fn):
|
|
def wrapped(*v, **k):
|
|
name = fn.__name__
|
|
util.SMlog("#### VMOPS enter %s ####" % name )
|
|
res = fn(*v, **k)
|
|
util.SMlog("#### VMOPS exit %s ####" % name )
|
|
return res
|
|
return wrapped
|
|
|
|
|
|
def get_xapi_session():
|
|
xapi = XenAPI.xapi_local();
|
|
xapi.login_with_password("","")
|
|
return xapi._session
|
|
|
|
@echo
|
|
def gethostvmstats(session, args):
|
|
collect_host_stats = args['collectHostStats']
|
|
consolidation_function = args['consolidationFunction']
|
|
interval = args['interval']
|
|
start_time = args['startTime']
|
|
session = get_xapi_session()
|
|
result = hostvmstats.get_stats(session, collect_host_stats, consolidation_function, interval, start_time)
|
|
return result
|
|
|
|
@echo
|
|
def find_bond(session, args):
|
|
nic = args['arg1']
|
|
try:
|
|
cmd = ["bash", "/opt/xensource/bin/find_bond.sh", nic]
|
|
txt = util.pread2(cmd)
|
|
except:
|
|
txt = ''
|
|
|
|
return txt
|
|
|
|
@echo
|
|
def setup_iscsi(session, args):
|
|
uuid=args['uuid']
|
|
try:
|
|
cmd = ["bash", "/opt/xensource/bin/setup_iscsi.sh", uuid]
|
|
txt = util.pread2(cmd)
|
|
except:
|
|
txt = ''
|
|
return txt
|
|
|
|
@echo
|
|
def execute_script(session, args):
|
|
return ""
|
|
|
|
@echo
|
|
def getvncport(session, args):
|
|
domid = args['domID']
|
|
hvm = args['hvm']
|
|
if hvm == 'true':
|
|
path = "/local/domain/" + domid + "/console/vnc-port"
|
|
else:
|
|
path = "/local/domain/" + domid + "/serial/0/vnc-port"
|
|
try:
|
|
cmd = ["xenstore-read", path]
|
|
txt = util.pread2(cmd)
|
|
except:
|
|
txt = ''
|
|
|
|
return txt
|
|
|
|
@echo
|
|
def getgateway(session, args):
|
|
mgmt_ip = args['mgmtIP']
|
|
try:
|
|
cmd = ["bash", "/opt/xensource/bin/network_info.sh", "-g", mgmt_ip]
|
|
txt = util.pread2(cmd)
|
|
except:
|
|
txt = ''
|
|
|
|
return txt
|
|
|
|
@echo
|
|
def getnetwork(session, args):
|
|
mgmt_ip = args['mgmtIP']
|
|
try:
|
|
cmd = ["bash", "/opt/xensource/bin/network_info.sh", "-l", mgmt_ip]
|
|
txt = util.pread2(cmd)
|
|
except:
|
|
txt = ''
|
|
|
|
return txt
|
|
|
|
@echo
|
|
def preparemigration(session, args):
|
|
uuid = args['uuid']
|
|
try:
|
|
cmd = ["/opt/xensource/bin/make_migratable.sh", uuid]
|
|
util.pread2(cmd)
|
|
txt = 'success'
|
|
except:
|
|
util.SMlog("Catch prepare migration exception" )
|
|
txt = ''
|
|
|
|
return txt
|
|
|
|
@echo
|
|
def setIptables(session, args):
|
|
try:
|
|
cmd = ["/bin/bash", "/opt/xensource/bin/setupxenserver.sh"]
|
|
txt = util.pread2(cmd)
|
|
txt = 'success'
|
|
except:
|
|
util.SMlog(" setIptables execution failed " )
|
|
txt = ''
|
|
|
|
return txt
|
|
|
|
@echo
|
|
def patchdomr(session, args):
|
|
vmname = args['vmname']
|
|
vmtype = args['vmtype']
|
|
device = args['device']
|
|
try:
|
|
cmd = ["/bin/bash", "/opt/xensource/bin/prepsystemvm.sh", "-l", vmname, "-t", vmtype, "-d", device]
|
|
txt = util.pread2(cmd)
|
|
txt = 'success'
|
|
except:
|
|
util.SMlog(" patch domr failed " )
|
|
txt = ''
|
|
|
|
return txt
|
|
|
|
@echo
|
|
def pingdomr(session, args):
|
|
host = args['host']
|
|
port = args['port']
|
|
socket.setdefaulttimeout(3)
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
try:
|
|
s.connect((host,int(port)))
|
|
txt = 'success'
|
|
except:
|
|
txt = ''
|
|
|
|
s.close()
|
|
|
|
return txt
|
|
|
|
@echo
|
|
def pingxenserver(session, args):
|
|
txt = 'success'
|
|
return txt
|
|
|
|
@echo
|
|
def ipassoc(session, args):
|
|
sargs = args['args']
|
|
cmd = sargs.split(' ')
|
|
cmd.insert(0, "/opt/xensource/bin/ipassoc.sh")
|
|
cmd.insert(0, "/bin/bash")
|
|
try:
|
|
txt = util.pread2(cmd)
|
|
txt = 'success'
|
|
except:
|
|
util.SMlog(" ip associate failed " )
|
|
txt = ''
|
|
|
|
return txt
|
|
|
|
@echo
|
|
def vm_data(session, args):
|
|
router_ip = args.pop('routerIP')
|
|
vm_ip = args.pop('vmIP')
|
|
|
|
util.SMlog(" adding vmdata for VM with IP: " + vm_ip + " to router with IP: " + router_ip)
|
|
|
|
for pair in args:
|
|
pairList = pair.split(',')
|
|
vmDataFolder = pairList[0]
|
|
vmDataFile = pairList[1]
|
|
vmDataValue = args[pair]
|
|
cmd = ["/bin/bash", "/opt/xensource/bin/vm_data.sh", "-r", router_ip, "-v", vm_ip, "-F", vmDataFolder, "-f", vmDataFile]
|
|
|
|
fd = None
|
|
tmp_path = None
|
|
if (vmDataValue != "none"):
|
|
try:
|
|
fd,tmp_path = tempfile.mkstemp()
|
|
tmpfile = open(tmp_path, 'w')
|
|
|
|
if (vmDataFolder == "userdata"):
|
|
vmDataValue = base64.urlsafe_b64decode(vmDataValue)
|
|
|
|
tmpfile.write(vmDataValue)
|
|
tmpfile.close()
|
|
cmd.append("-d")
|
|
cmd.append(tmp_path)
|
|
except:
|
|
util.SMlog(" vmdata failed to write tempfile " )
|
|
os.close(fd)
|
|
os.remove(tmp_path)
|
|
return ''
|
|
|
|
try:
|
|
txt = util.pread2(cmd)
|
|
txt = 'success'
|
|
except:
|
|
util.SMlog(" vmdata failed with folder: " + vmDataFolder + " and file: " + vmDataFile)
|
|
txt = ''
|
|
|
|
if (fd != None):
|
|
os.close(fd)
|
|
os.remove(tmp_path)
|
|
|
|
return txt
|
|
|
|
def pingtest(session, args):
|
|
sargs = args['args']
|
|
cmd = sargs.split(' ')
|
|
cmd.insert(0, "/opt/xensource/bin/pingtest.sh")
|
|
cmd.insert(0, "/bin/bash")
|
|
try:
|
|
txt = util.pread2(cmd)
|
|
txt = 'success'
|
|
except:
|
|
util.SMlog(" pingtest failed " )
|
|
txt = ''
|
|
|
|
return txt
|
|
|
|
@echo
|
|
def savePassword(session, args):
|
|
sargs = args['args']
|
|
cmd = sargs.split(' ')
|
|
cmd.insert(0, "/opt/xensource/bin/save_password_to_domr.sh")
|
|
cmd.insert(0, "/bin/bash")
|
|
try:
|
|
txt = util.pread2(cmd)
|
|
txt = 'success'
|
|
except:
|
|
util.SMlog(" save password to domr failed " )
|
|
txt = ''
|
|
|
|
return txt
|
|
|
|
@echo
|
|
def saveDhcpEntry(session, args):
|
|
sargs = args['args']
|
|
cmd = sargs.split(' ')
|
|
cmd.insert(0, "/opt/xensource/bin/dhcp_entry.sh")
|
|
cmd.insert(0, "/bin/bash")
|
|
try:
|
|
txt = util.pread2(cmd)
|
|
txt = 'success'
|
|
except:
|
|
util.SMlog(" save dhcp entry failed " )
|
|
txt = ''
|
|
|
|
return txt
|
|
|
|
@echo
|
|
def setLinkLocalIP(session, args):
|
|
brName = args['brName']
|
|
try:
|
|
cmd = ["ip", "route", "del", "169.254.0.0/16"]
|
|
txt = util.pread2(cmd)
|
|
except:
|
|
txt = ''
|
|
try:
|
|
cmd = ["ifconfig", brName, "169.254.0.1", "netmask", "255.255.0.0"]
|
|
txt = util.pread2(cmd)
|
|
except:
|
|
txt = ''
|
|
try:
|
|
cmd = ["ip", "route", "add", "169.254.0.0/16", "dev", brName, "src", "169.254.0.1"]
|
|
txt = util.pread2(cmd)
|
|
except:
|
|
txt = ''
|
|
txt = 'success'
|
|
return txt
|
|
|
|
@echo
|
|
def setFirewallRule(session, args):
|
|
sargs = args['args']
|
|
cmd = sargs.split(' ')
|
|
cmd.insert(0, "/opt/xensource/bin/call_firewall.sh")
|
|
cmd.insert(0, "/bin/bash")
|
|
try:
|
|
txt = util.pread2(cmd)
|
|
txt = 'success'
|
|
except:
|
|
util.SMlog(" set firewall rule failed " )
|
|
txt = ''
|
|
|
|
return txt
|
|
|
|
@echo
|
|
def setLoadBalancerRule(session, args):
|
|
sargs = args['args']
|
|
cmd = sargs.split(' ')
|
|
cmd.insert(0, "/opt/xensource/bin/call_loadbalancer.sh")
|
|
cmd.insert(0, "/bin/bash")
|
|
try:
|
|
txt = util.pread2(cmd)
|
|
txt = 'success'
|
|
except:
|
|
util.SMlog(" set loadbalancer rule failed " )
|
|
txt = ''
|
|
|
|
return txt
|
|
|
|
@echo
|
|
def createFile(session, args):
|
|
file_path = args['filepath']
|
|
file_contents = args['filecontents']
|
|
|
|
try:
|
|
f = open(file_path, "w")
|
|
f.write(file_contents)
|
|
f.close()
|
|
txt = 'success'
|
|
except:
|
|
util.SMlog(" failed to create HA proxy cfg file ")
|
|
txt = ''
|
|
|
|
return txt
|
|
|
|
@echo
|
|
def deleteFile(session, args):
|
|
file_path = args["filepath"]
|
|
|
|
try:
|
|
if os.path.isfile(file_path):
|
|
os.remove(file_path)
|
|
txt = 'success'
|
|
except:
|
|
util.SMlog(" failed to remove HA proxy cfg file ")
|
|
txt = ''
|
|
|
|
return txt
|
|
|
|
@echo
|
|
def checkMount(session, args):
|
|
mountPath = args['mount']
|
|
mountPath = os.path.join(SR.MOUNT_BASE, mountPath)
|
|
status = "0"
|
|
try:
|
|
p = subprocess.Popen(["/bin/bash", "-c", "if [ -d " + mountPath + " ]; then echo 1; else echo 0;fi"], stdout=subprocess.PIPE)
|
|
cnt = 10
|
|
while cnt > 0:
|
|
if p.poll() == None:
|
|
time.sleep(1)
|
|
cnt = cnt -1
|
|
else:
|
|
cnt = -1
|
|
if cnt < 0:
|
|
status = p.communicate()[0].strip("\n")
|
|
else:
|
|
subprocess.Popen(["/bin/bash", "-c", "kill -9 " + str(p.pid)])
|
|
status = "0"
|
|
|
|
if status == "0":
|
|
try:
|
|
cmd = ["umount", "-f", "-l", mountPath]
|
|
txt = util.pread2(cmd)
|
|
except:
|
|
util.SMlog(" umount failed ")
|
|
except:
|
|
util.SMlog("failed to check")
|
|
return status
|
|
|
|
@echo
|
|
def checkIscsi(session, args):
|
|
scsiid = args['scsiid']
|
|
devpath = "/dev/disk/by-id/scsi-" + scsiid
|
|
status = "0"
|
|
try:
|
|
if util.pathexists(devpath) :
|
|
rdevpath = os.readlink(devpath)
|
|
rdevpath = rdevpath.replace(".", "")
|
|
rdevpath = rdevpath.replace("/", "")
|
|
rdevpath = "/block/" + rdevpath
|
|
cmd = ["scsi_id", "-g", "-s", rdevpath ]
|
|
txt = util.pread2(cmd)
|
|
txt = txt.replace("\n", "")
|
|
if scsiid == txt:
|
|
status = "1"
|
|
except:
|
|
util.SMlog("failed to check Iscsi")
|
|
|
|
return status
|
|
|
|
@echo
|
|
def networkUsage(session, args):
|
|
sargs = args['args']
|
|
cmd = sargs.split(' ')
|
|
cmd.insert(0, "/opt/xensource/bin/networkUsage.sh")
|
|
cmd.insert(0, "/bin/bash")
|
|
try:
|
|
txt = util.pread2(cmd)
|
|
except:
|
|
util.SMlog(" network usage error " )
|
|
txt = ''
|
|
|
|
return txt
|
|
|
|
@echo
|
|
def setup_heartbeat_sr(session, args):
|
|
host = args['host']
|
|
sr = args['sr']
|
|
try:
|
|
cmd = ["bash", "/opt/xensource/bin/setup_heartbeat_sr.sh", host, sr]
|
|
txt = util.pread2(cmd)
|
|
except:
|
|
txt = ''
|
|
return txt
|
|
|
|
@echo
|
|
def check_heartbeat(session, args):
|
|
host = args['host']
|
|
interval = args['interval']
|
|
try:
|
|
cmd = ["bash", "/opt/xensource/bin/check_heartbeat.sh", host, interval]
|
|
txt = util.pread2(cmd)
|
|
except:
|
|
txt=''
|
|
return txt
|
|
|
|
|
|
@echo
|
|
def heartbeat(session, args):
|
|
host = args['host']
|
|
interval = args['interval']
|
|
try:
|
|
cmd = ["/bin/bash", "/opt/xensource/bin/launch_hb.sh", host, interval]
|
|
txt = util.pread2(cmd)
|
|
except:
|
|
txt='fail'
|
|
return txt
|
|
|
|
def get_private_nic( args):
|
|
session = get_xapi_session()
|
|
vms = session.xenapi.VM.get_all()
|
|
host_uuid = args.get('host_uuid')
|
|
host = session.xenapi.host.get_by_uuid(host_uuid)
|
|
piflist = session.xenapi.host.get_PIFs(host)
|
|
mgmtnic = 'eth0'
|
|
for pif in piflist:
|
|
pifrec = session.xenapi.PIF.get_record(pif)
|
|
network = pifrec.get('network')
|
|
nwrec = session.xenapi.network.get_record(network)
|
|
if nwrec.get('name_label') == 'cloud-private':
|
|
return pifrec.get('device')
|
|
if pifrec.get('management'):
|
|
mgmtnic = pifrec.get('device')
|
|
|
|
return mgmtnic
|
|
|
|
|
|
|
|
@echo
|
|
def can_bridge_firewall(session, args):
|
|
host_uuid = args.get('host_uuid')
|
|
try:
|
|
util.pread2(['iptables', '-N', 'BRIDGE-FIREWALL'])
|
|
util.pread2(['iptables', '-I', 'BRIDGE-FIREWALL', '-m', 'state', '--state', 'RELATED,ESTABLISHED', '-j', 'ACCEPT'])
|
|
util.pread2(['iptables', '-D', 'FORWARD', '-j', 'RH-Firewall-1-INPUT'])
|
|
except:
|
|
util.SMlog('Chain BRIDGE-FIREWALL already exists')
|
|
privnic = get_private_nic(args)
|
|
result = 'true'
|
|
try:
|
|
util.pread2(['/bin/bash', '-c', 'iptables -n -L FORWARD | grep BRIDGE-FIREWALL'])
|
|
except:
|
|
try:
|
|
util.pread2(['iptables', '-I', 'FORWARD', '-m', 'physdev', '--physdev-is-bridged', '-j', 'BRIDGE-FIREWALL'])
|
|
util.pread2(['iptables', '-A', 'FORWARD', '-m', 'physdev', '--physdev-is-bridged', '--physdev-out', privnic, '-j', 'ACCEPT'])
|
|
util.pread2(['iptables', '-A', 'FORWARD', '-j', 'DROP'])
|
|
except:
|
|
result = 'false'
|
|
|
|
if not os.path.exists('/var/run/cloud'):
|
|
os.makedirs('/var/run/cloud')
|
|
|
|
cleanup_rules_for_dead_vms()
|
|
cleanup_rules()
|
|
|
|
return result
|
|
|
|
|
|
def ipset(ipsetname, proto, start, end, ips):
|
|
try:
|
|
util.pread2(['ipset', '-N', ipsetname, 'iptreemap'])
|
|
except:
|
|
util.SMlog("ipset chain already exists" + ipsetname)
|
|
|
|
result = True
|
|
ipsettmp = ipsetname + str(int(time.time())%3600)
|
|
|
|
try:
|
|
util.pread2(['ipset', '-N', ipsettmp, 'iptreemap'])
|
|
for ip in ips:
|
|
try:
|
|
util.pread2(['ipset', '-A', ipsettmp, ip])
|
|
except CommandException, cex:
|
|
if cex.reason.rfind('already in set') == -1:
|
|
raise
|
|
util.pread2(['ipset', '-W', ipsettmp, ipsetname])
|
|
util.pread2(['ipset', '-X', ipsettmp])
|
|
except:
|
|
util.SMlog("Failed to program ipset " + ipsetname)
|
|
result = False
|
|
|
|
return result
|
|
|
|
@echo
|
|
def destroy_network_rules_for_vm(session, args):
|
|
vm_name = args.pop('vmName')
|
|
vmchain = vm_name
|
|
|
|
delete_rules_for_vm_in_bridge_firewall_chain(vm_name)
|
|
if vm_name.startswith('i-') or vm_name.startswith('r-'):
|
|
vmchain = '-'.join(vm_name.split('-')[:-1])
|
|
|
|
destroy_ebtables_rules(vmchain)
|
|
|
|
try:
|
|
util.pread2(['iptables', '-F', vmchain])
|
|
util.pread2(['iptables', '-X', vmchain])
|
|
except:
|
|
util.SMlog("Ignoring failure to delete chain " + vmchain)
|
|
|
|
remove_rule_log_for_vm(vm_name)
|
|
|
|
if 1 in [ vm_name.startswith(c) for c in ['r-', 's-', 'v-'] ]:
|
|
return 'true'
|
|
|
|
try:
|
|
setscmd = "ipset --save | grep " + vmchain + " | grep '^-N' | awk '{print $2}'"
|
|
setsforvm = util.pread2(['/bin/bash', '-c', setscmd]).split('\n')
|
|
for set in setsforvm:
|
|
if set != '':
|
|
util.pread2(['ipset', '-F', set])
|
|
util.pread2(['ipset', '-X', set])
|
|
except:
|
|
util.SMlog("Failed to destroy ipsets for %" % vm_name)
|
|
|
|
|
|
return 'true'
|
|
|
|
@echo
|
|
def destroy_ebtables_rules(vm_name):
|
|
if not os.path.exists('/usr/local/sbin/ebtables'):
|
|
return
|
|
|
|
delcmd = "/usr/local/sbin/ebtables-save | grep ROUTING | grep " + vm_name + " | sed 's/-A/-D/'"
|
|
delcmds = util.pread2(['/bin/bash', '-c', delcmd]).split('\n')
|
|
delcmds.pop()
|
|
for cmd in delcmds:
|
|
try:
|
|
dc = cmd.split(' ')
|
|
dc.insert(0, '/usr/local/sbin/ebtables')
|
|
dc.insert(1, '-t')
|
|
dc.insert(2, 'nat')
|
|
util.pread2(dc)
|
|
except:
|
|
util.SMlog("Ignoring failure to delete ebtables rules for vm " + vm_name)
|
|
chains = [vm_name+"-in", vm_name+"-out"]
|
|
for chain in chains:
|
|
try:
|
|
util.pread2(['/usr/local/sbin/ebtables', '-t', 'nat', '-F', chain])
|
|
util.pread2(['/usr/local/sbin/ebtables', '-t', 'nat', '-X', chain])
|
|
except:
|
|
util.SMlog("Ignoring failure to delete ebtables chain for vm " + vm_name)
|
|
|
|
|
|
|
|
@echo
|
|
def default_ebtables_rules(vm_name, vif, vm_ip, vm_mac):
|
|
if not os.path.exists('/usr/local/sbin/ebtables'):
|
|
return
|
|
|
|
vmchain_in = vm_name + "-in"
|
|
vmchain_out = vm_name + "-out"
|
|
|
|
for chain in [vmchain_in, vmchain_out]:
|
|
try:
|
|
util.pread2(['/usr/local/sbin/ebtables', '-t', 'nat', '-N', chain])
|
|
except:
|
|
util.pread2(['/usr/local/sbin/ebtables', '-t', 'nat', '-F', chain])
|
|
|
|
try:
|
|
# -s ! 52:54:0:56:44:32 -j DROP
|
|
util.pread2(['/usr/local/sbin/ebtables', '-t', 'nat', '-A', 'PREROUTING', '-i', vif, '-j', vmchain_in])
|
|
util.pread2(['/usr/local/sbin/ebtables', '-t', 'nat', '-A', 'POSTROUTING', '-o', vif, '-j', vmchain_out])
|
|
except:
|
|
util.SMlog("Failed to program default rules")
|
|
return 'false'
|
|
|
|
try:
|
|
util.pread2(['/usr/local/sbin/ebtables', '-t', 'nat', '-A', vmchain_in, '-i', vif, '-s', '!', vm_mac, '-j', 'DROP'])
|
|
util.pread2(['/usr/local/sbin/ebtables', '-t', 'nat', '-A', vmchain_in, '-p', 'ARP', '-s', '!', vm_mac, '-j', 'DROP'])
|
|
util.pread2(['/usr/local/sbin/ebtables', '-t', 'nat', '-A', vmchain_in, '-p', 'ARP', '--arp-mac-src', '!', vm_mac, '-j', 'DROP'])
|
|
util.pread2(['/usr/local/sbin/ebtables', '-t', 'nat', '-A', vmchain_in, '-p', 'ARP', '--arp-ip-src', '!', vm_ip, '-j', 'DROP'])
|
|
util.pread2(['/usr/local/sbin/ebtables', '-t', 'nat', '-A', vmchain_in, '-p', 'ARP', '--arp-op', 'Request', '-j', 'ACCEPT'])
|
|
util.pread2(['/usr/local/sbin/ebtables', '-t', 'nat', '-A', vmchain_in, '-p', 'ARP', '--arp-op', 'Reply', '-j', 'ACCEPT'])
|
|
util.pread2(['/usr/local/sbin/ebtables', '-t', 'nat', '-A', vmchain_in, '-p', 'ARP', '-j', 'DROP'])
|
|
except:
|
|
util.SMlog("Failed to program default ebtables IN rules")
|
|
return 'false'
|
|
|
|
try:
|
|
util.pread2(['/usr/local/sbin/ebtables', '-t', 'nat', '-A', vmchain_out, '-p', 'ARP', '--arp-op', 'Reply', '--arp-mac-dst', '!', vm_mac, '-j', 'DROP'])
|
|
util.pread2(['/usr/local/sbin/ebtables', '-t', 'nat', '-A', vmchain_out, '-p', 'ARP', '--arp-ip-dst', '!', vm_ip, '-j', 'DROP'])
|
|
util.pread2(['/usr/local/sbin/ebtables', '-t', 'nat', '-A', vmchain_out, '-p', 'ARP', '--arp-op', 'Request', '-j', 'ACCEPT'])
|
|
util.pread2(['/usr/local/sbin/ebtables', '-t', 'nat', '-A', vmchain_out, '-p', 'ARP', '--arp-op', 'Reply', '-j', 'ACCEPT'])
|
|
util.pread2(['/usr/local/sbin/ebtables', '-t', 'nat', '-A', vmchain_out, '-p', 'ARP', '-j', 'DROP'])
|
|
except:
|
|
util.SMlog("Failed to program default ebtables OUT rules")
|
|
return 'false'
|
|
|
|
|
|
@echo
|
|
def default_network_rules_systemvm(session, args):
|
|
vm_name = args.pop('vmName')
|
|
try:
|
|
vm = session.xenapi.VM.get_by_name_label(vm_name)
|
|
if len(vm) != 1:
|
|
return 'false'
|
|
vm_rec = session.xenapi.VM.get_record(vm[0])
|
|
vm_vifs = vm_rec.get('VIFs')
|
|
vifnums = [session.xenapi.VIF.get_record(vif).get('device') for vif in vm_vifs]
|
|
domid = vm_rec.get('domid')
|
|
except:
|
|
util.SMlog("### Failed to get domid or vif list for vm ##" + vm_name)
|
|
return 'false'
|
|
|
|
if domid == '-1':
|
|
util.SMlog("### Failed to get domid for vm (-1): " + vm_name)
|
|
return 'false'
|
|
|
|
vifs = ["vif" + domid + "." + v for v in vifnums]
|
|
#vm_name = '-'.join(vm_name.split('-')[:-1])
|
|
vmchain = vm_name
|
|
if vm_name.startswith('r-'):
|
|
vmchain = '-'.join(vm_name.split('-')[:-1])
|
|
|
|
delete_rules_for_vm_in_bridge_firewall_chain(vm_name)
|
|
|
|
try:
|
|
util.pread2(['iptables', '-N', vmchain])
|
|
except:
|
|
util.pread2(['iptables', '-F', vmchain])
|
|
|
|
|
|
for vif in vifs:
|
|
try:
|
|
util.pread2(['iptables', '-A', 'BRIDGE-FIREWALL', '-m', 'physdev', '--physdev-is-bridged', '--physdev-out', vif, '-j', vmchain])
|
|
util.pread2(['iptables', '-A', 'BRIDGE-FIREWALL', '-m', 'physdev', '--physdev-is-bridged', '--physdev-in', vif, '-j', vmchain])
|
|
except:
|
|
util.SMlog("Failed to program default rules")
|
|
return 'false'
|
|
|
|
util.pread2(['iptables', '-A', vmchain, '-j', 'ACCEPT'])
|
|
|
|
if write_rule_log_for_vm(vm_name, '-1', '_ignore_', domid, '_initial_', '-1') == False:
|
|
util.SMlog("Failed to log default network rules for systemvm, ignoring")
|
|
return 'true'
|
|
|
|
|
|
@echo
|
|
def default_network_rules(session, args):
|
|
vmName = args.pop('vmName')
|
|
vm_name = vmName
|
|
vm_ip = args.pop('vmIP')
|
|
vm_id = args.pop('vmID')
|
|
vm_mac = args.pop('vmMAC')
|
|
|
|
try:
|
|
vm = session.xenapi.VM.get_by_name_label(vm_name)
|
|
if len(vm) != 1:
|
|
util.SMlog("### Failed to get record for vm " + vm_name)
|
|
return 'false'
|
|
vm_rec = session.xenapi.VM.get_record(vm[0])
|
|
domid = vm_rec.get('domid')
|
|
except:
|
|
util.SMlog("### Failed to get domid for vm " + vm_name)
|
|
return 'false'
|
|
if domid == '-1':
|
|
util.SMlog("### Failed to get domid for vm (-1): " + vm_name)
|
|
return 'false'
|
|
|
|
vif = "vif" + domid + ".0"
|
|
|
|
delete_rules_for_vm_in_bridge_firewall_chain(vm_name)
|
|
|
|
|
|
vm_name = '-'.join(vm_name.split('-')[:-1])
|
|
vmchain = vm_name
|
|
|
|
destroy_ebtables_rules(vm_name)
|
|
|
|
try:
|
|
util.pread2(['iptables', '-F', vmchain])
|
|
util.pread2(['iptables', '-X', vmchain])
|
|
except:
|
|
util.SMlog('Ignoring failure to delete old rules')
|
|
|
|
try:
|
|
util.pread2(['iptables', '-N', vmchain])
|
|
except:
|
|
util.pread2(['iptables', '-F', vmchain])
|
|
|
|
try:
|
|
util.pread2(['iptables', '-A', 'BRIDGE-FIREWALL', '-m', 'physdev', '--physdev-is-bridged', '--physdev-out', vif, '-j', vmchain])
|
|
util.pread2(['iptables', '-A', 'BRIDGE-FIREWALL', '-m', 'physdev', '--physdev-is-bridged', '--physdev-in', vif, '-j', vmchain])
|
|
util.pread2(['iptables', '-A', vmchain, '-m', 'state', '--state', 'RELATED,ESTABLISHED', '-j', 'ACCEPT'])
|
|
#allow dhcp
|
|
util.pread2(['iptables', '-A', vmchain, '-p', 'udp', '--dport', '67:68', '--sport', '67:68', '-j', 'ACCEPT'])
|
|
#don't let vm spoof its ip address
|
|
util.pread2(['iptables', '-A', vmchain, '-m', 'physdev', '--physdev-is-bridged', '--physdev-in', vif, '--source', vm_ip, '-j', 'RETURN'])
|
|
util.pread2(['iptables', '-A', vmchain, '-j', 'DROP'])
|
|
except:
|
|
util.SMlog("Failed to program default rules for vm " + vm_name)
|
|
return 'false'
|
|
|
|
default_ebtables_rules(vm_name, vif, vm_ip, vm_mac)
|
|
|
|
if write_rule_log_for_vm(vmName, vm_id, vm_ip, domid, '_initial_', '-1') == False:
|
|
util.SMlog("Failed to log default network rules, ignoring")
|
|
|
|
util.SMlog("Programmed default rules for vm " + vm_name)
|
|
return 'true'
|
|
|
|
def check_domid_changed(session, vmName):
|
|
curr_domid = '-1'
|
|
try:
|
|
vm = session.xenapi.VM.get_by_name_label(vmName)
|
|
if len(vm) != 1:
|
|
util.SMlog("### Could not get record for vm ## " + vmName)
|
|
else:
|
|
vm_rec = session.xenapi.VM.get_record(vm[0])
|
|
curr_domid = vm_rec.get('domid')
|
|
except:
|
|
util.SMlog("### Failed to get domid for vm ## " + vmName)
|
|
|
|
|
|
logfilename = "/var/run/cloud/" + vmName +".log"
|
|
if not os.path.exists(logfilename):
|
|
return ['-1', curr_domid]
|
|
|
|
lines = (line.rstrip() for line in open(logfilename))
|
|
|
|
[_vmName,_vmID,_vmIP,old_domid,_signature,_seqno] = ['_', '-1', '_', '-1', '_', '-1']
|
|
for line in lines:
|
|
[_vmName,_vmID,_vmIP,old_domid,_signature,_seqno] = line.split(',')
|
|
break
|
|
|
|
return [curr_domid, old_domid]
|
|
|
|
def delete_rules_for_vm_in_bridge_firewall_chain(vmName):
|
|
vm_name = vmName
|
|
if vm_name.startswith('i-') or vm_name.startswith('r-'):
|
|
vm_name = '-'.join(vm_name.split('-')[:-1])
|
|
|
|
vmchain = vm_name
|
|
|
|
delcmd = "iptables -S BRIDGE-FIREWALL | grep " + vmchain + " | sed 's/-A/-D/'"
|
|
delcmds = util.pread2(['/bin/bash', '-c', delcmd]).split('\n')
|
|
delcmds.pop()
|
|
for cmd in delcmds:
|
|
try:
|
|
dc = cmd.split(' ')
|
|
dc.insert(0, 'iptables')
|
|
dc.pop()
|
|
util.pread2(dc)
|
|
except:
|
|
util.SMlog("Ignoring failure to delete rules for vm " + vmName)
|
|
|
|
|
|
def network_rules_for_rebooted_vm(session, vmName):
|
|
vm_name = vmName
|
|
[curr_domid, old_domid] = check_domid_changed(session, vmName)
|
|
|
|
if curr_domid == old_domid:
|
|
return True
|
|
|
|
if old_domid == '-1':
|
|
return True
|
|
|
|
if curr_domid == '-1':
|
|
return True
|
|
|
|
util.SMlog("Found a rebooted VM -- reprogramming rules for " + vmName)
|
|
|
|
delete_rules_for_vm_in_bridge_firewall_chain(vmName)
|
|
if 1 in [ vm_name.startswith(c) for c in ['r-', 's-', 'v-'] ]:
|
|
default_network_rules_systemvm(session, {"vmName":vmName})
|
|
return True
|
|
|
|
vif = "vif" + curr_domid + ".0"
|
|
vmchain = '-'.join(vm_name.split('-')[:-1])
|
|
|
|
util.pread2(['iptables', '-A', 'BRIDGE-FIREWALL', '-m', 'physdev', '--physdev-is-bridged', '--physdev-out', vif, '-j', vmchain])
|
|
util.pread2(['iptables', '-A', 'BRIDGE-FIREWALL', '-m', 'physdev', '--physdev-is-bridged', '--physdev-in', vif, '-j', vmchain])
|
|
#change antispoof rule in vmchain
|
|
try:
|
|
delcmd = "iptables -S " + vmchain + " | grep physdev-in | sed 's/-A/-D/'"
|
|
inscmd = "iptables -S " + vmchain + " | grep physdev-in | sed -r 's/vif[0-9]+.0/" + vif + "/' | sed 's/-A/-I/'"
|
|
ipts = []
|
|
for cmd in [delcmd, inscmd]:
|
|
cmds = util.pread2(['/bin/bash', '-c', cmd]).split('\n')
|
|
cmds.pop()
|
|
for c in cmds:
|
|
ipt = c.split(' ')
|
|
ipt.insert(0, 'iptables')
|
|
ipt.pop()
|
|
ipts.append(ipt)
|
|
|
|
for ipt in ipts:
|
|
try:
|
|
util.pread2(ipt)
|
|
except:
|
|
util.SMlog("Failed to rewrite antispoofing rules for vm " + vmName)
|
|
except:
|
|
util.SMlog("No rules found for vm " + vmchain)
|
|
|
|
|
|
rewrite_rule_log_for_vm(vmName, curr_domid)
|
|
return True
|
|
|
|
def rewrite_rule_log_for_vm(vm_name, new_domid):
|
|
logfilename = "/var/run/cloud/" + vm_name +".log"
|
|
if not os.path.exists(logfilename):
|
|
return
|
|
lines = (line.rstrip() for line in open(logfilename))
|
|
|
|
[_vmName,_vmID,_vmIP,_domID,_signature,_seqno] = ['_', '-1', '_', '-1', '_', '-1']
|
|
for line in lines:
|
|
[_vmName,_vmID,_vmIP,_domID,_signature,_seqno] = line.split(',')
|
|
break
|
|
|
|
write_rule_log_for_vm(_vmName, _vmID, '0.0.0.0', new_domid, _signature, '-1')
|
|
|
|
def get_rule_log_for_vm(session, vmName):
|
|
vm_name = vmName;
|
|
logfilename = "/var/run/cloud/" + vm_name +".log"
|
|
if not os.path.exists(logfilename):
|
|
return ''
|
|
|
|
lines = (line.rstrip() for line in open(logfilename))
|
|
|
|
[_vmName,_vmID,_vmIP,_domID,_signature,_seqno] = ['_', '-1', '_', '-1', '_', '-1']
|
|
for line in lines:
|
|
[_vmName,_vmID,_vmIP,_domID,_signature,_seqno] = line.split(',')
|
|
break
|
|
|
|
return ','.join([_vmName, _vmID, _vmIP, _domID, _signature, _seqno])
|
|
|
|
@echo
|
|
def get_rule_logs_for_vms(session, args):
|
|
host_uuid = args.pop('host_uuid')
|
|
try:
|
|
session = get_xapi_session()
|
|
|
|
thishost = session.xenapi.host.get_by_uuid(host_uuid)
|
|
hostrec = session.xenapi.host.get_record(thishost)
|
|
vms = hostrec.get('resident_VMs')
|
|
except:
|
|
util.SMlog("Failed to get host from uuid " + host_uuid)
|
|
return ' '
|
|
|
|
result = []
|
|
try:
|
|
for name in [session.xenapi.VM.get_name_label(x) for x in vms]:
|
|
if 1 not in [ name.startswith(c) for c in ['r-', 's-', 'v-', 'i-'] ]:
|
|
continue
|
|
network_rules_for_rebooted_vm(session, name)
|
|
if name.startswith('i-'):
|
|
log = get_rule_log_for_vm(session, name)
|
|
result.append(log)
|
|
except:
|
|
util.SMlog("Failed to get rule logs, better luck next time!")
|
|
|
|
return ";".join(result)
|
|
|
|
@echo
|
|
def cleanup_rules_for_dead_vms():
|
|
try:
|
|
session = get_xapi_session()
|
|
vms = session.xenapi.VM.get_all()
|
|
cleaned = 0
|
|
for vm_name in [session.xenapi.VM.get_name_label(x) for x in vms]:
|
|
if 1 in [ vm_name.startswith(c) for c in ['r-', 'i-', 's-', 'v-'] ]:
|
|
vm = session.xenapi.VM.get_by_name_label(vm_name)
|
|
if len(vm) != 1:
|
|
continue
|
|
vm_rec = session.xenapi.VM.get_record(vm[0])
|
|
state = vm_rec.get('power_state')
|
|
if state != 'Running' and state != 'Paused':
|
|
util.SMlog("vm " + vm_name + " is not running, cleaning up")
|
|
destroy_network_rules_for_vm(session, {'vmName':vm_name})
|
|
cleaned = cleaned+1
|
|
|
|
util.SMlog("Cleaned up rules for " + str(cleaned) + " vms")
|
|
except:
|
|
util.SMlog("Failed to cleanup rules for dead vms!")
|
|
|
|
|
|
|
|
@echo
|
|
def cleanup_rules():
|
|
try:
|
|
session = get_xapi_session()
|
|
|
|
chainscmd = "iptables-save | grep '^:' | awk '{print $1}' | cut -d':' -f2"
|
|
chains = util.pread2(['/bin/bash', '-c', chainscmd]).split('\n')
|
|
cleaned = 0
|
|
cleanup = []
|
|
for chain in chains:
|
|
if 1 in [ chain.startswith(c) for c in ['r-', 'i-', 's-', 'v-'] ]:
|
|
if chain.startswith('i-') or chain.startswith('r-'):
|
|
vm_name = chain + '-untagged'
|
|
else:
|
|
vm_name = chain
|
|
|
|
vm = session.xenapi.VM.get_by_name_label(vm_name)
|
|
if len(vm) != 1:
|
|
util.SMlog("chain " + chain + " does not correspond to a vm, cleaning up")
|
|
cleanup.append(vm_name)
|
|
continue
|
|
vm_rec = session.xenapi.VM.get_record(vm[0])
|
|
state = vm_rec.get('power_state')
|
|
if state != 'Running' and state != 'Paused':
|
|
util.SMlog("vm " + vm_name + " is not running, cleaning up")
|
|
cleanup.append(vm_name)
|
|
|
|
for vmname in cleanup:
|
|
destroy_network_rules_for_vm(session, {'vmName':vmname})
|
|
|
|
util.SMlog("Cleaned up rules for " + str(len(cleanup)) + " chains")
|
|
except:
|
|
util.SMlog("Failed to cleanup rules !")
|
|
|
|
@echo
|
|
def check_rule_log_for_vm(vmName, vmID, vmIP, domID, signature, seqno):
|
|
vm_name = vmName;
|
|
logfilename = "/var/run/cloud/" + vm_name +".log"
|
|
if not os.path.exists(logfilename):
|
|
return [True, True, True, True, True, True]
|
|
|
|
lines = (line.rstrip() for line in open(logfilename))
|
|
|
|
[_vmName,_vmID,_vmIP,_domID,_signature,_seqno] = ['_', '-1', '_', '-1', '_', '-1']
|
|
try:
|
|
for line in lines:
|
|
[_vmName,_vmID,_vmIP,_domID,_signature,_seqno] = line.split(',')
|
|
break
|
|
except:
|
|
util.SMlog("Failed to parse log file for vm " + vm_name)
|
|
remove_rule_log_for_vm(vm_name)
|
|
return False
|
|
|
|
return [(vm_name != _vmName), \
|
|
(vmID != _vmID), \
|
|
(vmIP != _vmIP), \
|
|
(domID != _domID), \
|
|
(signature != _signature), \
|
|
(seqno != _seqno)]
|
|
|
|
|
|
@echo
|
|
def write_rule_log_for_vm(vmName, vmID, vmIP, domID, signature, seqno):
|
|
vm_name = vmName
|
|
logfilename = "/var/run/cloud/" + vm_name +".log"
|
|
util.SMlog("Writing log to " + logfilename)
|
|
logf = open(logfilename, 'w')
|
|
output = ','.join([vmName, vmID, vmIP, domID, signature, seqno])
|
|
result = True
|
|
try:
|
|
logf.write(output)
|
|
logf.write('\n')
|
|
except:
|
|
util.SMlog("Failed to write to rule log file " + logfilename)
|
|
result = False
|
|
|
|
logf.close()
|
|
|
|
return result
|
|
|
|
@echo
|
|
def remove_rule_log_for_vm(vmName):
|
|
vm_name = vmName
|
|
logfilename = "/var/run/cloud/" + vm_name +".log"
|
|
|
|
result = True
|
|
try:
|
|
os.remove(logfilename)
|
|
except:
|
|
util.SMlog("Failed to delete rule log file " + logfilename)
|
|
result = False
|
|
|
|
return result
|
|
|
|
@echo
|
|
def network_rules(session, args):
|
|
try:
|
|
session = get_xapi_session()
|
|
vm_name = args.get('vmName')
|
|
vmName = vm_name
|
|
vm_ip = args.get('vmIP')
|
|
vm_id = args.get('vmID')
|
|
signature = args.pop('signature')
|
|
seqno = args.pop('seqno')
|
|
try:
|
|
vm = session.xenapi.VM.get_by_name_label(vm_name)
|
|
if len(vm) != 1:
|
|
util.SMlog("### Could not get record for vm ## " + vm_name)
|
|
return 'false'
|
|
vm_rec = session.xenapi.VM.get_record(vm[0])
|
|
domid = vm_rec.get('domid')
|
|
except:
|
|
util.SMlog("### Failed to get domid for vm ## " + vm_name)
|
|
return 'false'
|
|
if domid == '-1':
|
|
util.SMlog("### Failed to get domid for vm (-1): " + vm_name)
|
|
return 'false'
|
|
|
|
vif = "vif" + domid + ".0"
|
|
vm_name = '-'.join(vm_name.split('-')[:-1])
|
|
vmchain = vm_name
|
|
|
|
changes = check_rule_log_for_vm (vmName, vm_id, vm_ip, domid, signature, seqno)
|
|
|
|
if not 1 in changes:
|
|
util.SMlog("Rules already programmed for vm " + vm_name)
|
|
return 'true'
|
|
|
|
if changes[1] or changes[2] or changes[3]:
|
|
util.SMlog("Change detected in vmId or vmIp or domId, resetting default rules")
|
|
default_network_rules(session, args)
|
|
|
|
rules = args.pop('rules')
|
|
lines = rules.split(' ')
|
|
|
|
util.SMlog(" programming network rules for IP: " + vm_ip + " vmname=" + vm_name)
|
|
util.pread2(['iptables', '-F', vmchain])
|
|
|
|
for line in lines:
|
|
tokens = line.split(':')
|
|
if len(tokens) != 4:
|
|
continue
|
|
protocol = tokens[0]
|
|
start = tokens[1]
|
|
end = tokens[2]
|
|
cidrs = tokens.pop();
|
|
ips = cidrs.split(",")
|
|
ips.pop()
|
|
allow_any = False
|
|
if '0.0.0.0/0' in ips:
|
|
i = ips.index('0.0.0.0/0')
|
|
del ips[i]
|
|
allow_any = True
|
|
range = start + ":" + end
|
|
if ips:
|
|
ipsetname = vm_name + "_" + protocol + "_" + start + "_" + end
|
|
if start == "-1":
|
|
ipsetname = vm_name + "_" + protocol + "_any"
|
|
|
|
if ipset(ipsetname, protocol, start, end, ips) == False:
|
|
util.SMlog(" failed to create ipset for rule " + str(tokens))
|
|
|
|
if protocol == 'all':
|
|
iptables = ['iptables', '-I', vmchain, '-m', 'state', '--state', 'NEW', '-m', 'set', '--match-set', ipsetname, 'src', '-j', 'ACCEPT']
|
|
elif protocol != 'icmp':
|
|
iptables = ['iptables', '-I', vmchain, '-p', protocol, '-m', protocol, '--dport', range, '-m', 'state', '--state', 'NEW', '-m', 'set', '--match-set', ipsetname, 'src', '-j', 'ACCEPT']
|
|
else:
|
|
range = start + "/" + end
|
|
if start == "-1":
|
|
range = "any"
|
|
iptables = ['iptables', '-I', vmchain, '-p', 'icmp', '--icmp-type', range, '-m', 'set', '--match-set', ipsetname, 'src', '-j', 'ACCEPT']
|
|
util.pread2(iptables)
|
|
util.SMlog(iptables)
|
|
|
|
if allow_any and protocol != 'all':
|
|
if protocol != 'icmp':
|
|
iptables = ['iptables', '-I', vmchain, '-p', protocol, '-m', protocol, '--dport', range, '-m', 'state', '--state', 'NEW', '-j', 'ACCEPT']
|
|
else:
|
|
range = start + "/" + end
|
|
if start == "-1":
|
|
range = "any"
|
|
iptables = ['iptables', '-I', vmchain, '-p', 'icmp', '--icmp-type', range, '-j', 'ACCEPT']
|
|
util.pread2(iptables)
|
|
util.SMlog(iptables)
|
|
|
|
|
|
util.pread2(['iptables', '-A', vmchain, '-p', 'udp', '--dport', '67:68', '--sport', '67:68', '-j', 'ACCEPT'])
|
|
util.pread2(['iptables', '-I', vmchain, '-m', 'physdev', '--physdev-is-bridged', '--physdev-in', vif, '--source', vm_ip, '-j', 'RETURN'])
|
|
util.pread2(['iptables', '-A', vmchain, '-j', 'DROP'])
|
|
|
|
if write_rule_log_for_vm(vmName, vm_id, vm_ip, domid, signature, seqno) == False:
|
|
return 'false'
|
|
|
|
return 'true'
|
|
except:
|
|
util.SMlog("Failed to network rule !")
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
XenAPIPlugin.dispatch({"pingtest": pingtest, "setup_heartbeat_sr":setup_heartbeat_sr, "check_heartbeat":check_heartbeat, "heartbeat": heartbeat, "setup_iscsi":setup_iscsi, "find_bond": find_bond, "gethostvmstats": gethostvmstats, "getvncport": getvncport, "getgateway": getgateway, "getnetwork": getnetwork, "preparemigration": preparemigration, "setIptables": setIptables, "patchdomr": patchdomr, "pingdomr": pingdomr, "pingxenserver": pingxenserver, "ipassoc": ipassoc, "vm_data": vm_data, "savePassword": savePassword, "saveDhcpEntry": saveDhcpEntry, "setFirewallRule": setFirewallRule, "setLoadBalancerRule": setLoadBalancerRule, "createFile": createFile, "deleteFile": deleteFile, "checkMount": checkMount, "checkIscsi": checkIscsi, "networkUsage": networkUsage, "network_rules":network_rules, "can_bridge_firewall":can_bridge_firewall, "default_network_rules":default_network_rules, "destroy_network_rules_for_vm":destroy_network_rules_for_vm, "default_network_rules_systemvm":default_network_rules_systemvm, "get_rule_logs_for_vms":get_rule_logs_for_vms, "setLinkLocalIP":setLinkLocalIP})
|
|
|