66# to you under the Apache License, Version 2.0 (the
77# "License"); you may not use this file except in compliance
88# with the License. You may obtain a copy of the License at
9- #
109# http://www.apache.org/licenses/LICENSE-2.0
11- #
1210# Unless required by applicable law or agreed to in writing,
1311# software distributed under the License is distributed on an
1412# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
1917import logging
2018import re
2119import sys
20+ import os
21+ import subprocess
22+ from threading import Timer
2223from xml .dom .minidom import parse
2324from cloudutils .configFileOps import configFileOps
2425from cloudutils .networkConfig import networkConfig
@@ -30,19 +31,24 @@ logging.basicConfig(filename='/var/log/libvirt/qemu-hook.log',
3031 level = logging .INFO )
3132logger = logging .getLogger ('qemu-hook' )
3233
34+ customDir = "/etc/libvirt/hooks/custom"
35+ customDirPermissions = 0744
36+ timeoutSeconds = 10 * 60
37+ validQemuActions = ['prepare' , 'start' , 'started' , 'stopped' , 'release' , 'migrate' , 'restore' , 'reconnect' , 'attach' ]
38+
3339def isOldStyleBridge (brName ):
3440 if brName .find ("cloudVirBr" ) == 0 :
35- return True
41+ return True
3642 else :
37- return False
43+ return False
3844
3945def isNewStyleBridge (brName ):
4046 if brName .startswith ('brvx-' ):
4147 return False
4248 if re .match (r"br(\w+)-(\d+)" , brName ) == None :
43- return False
49+ return False
4450 else :
45- return True
51+ return True
4652
4753def getGuestNetworkDevice ():
4854 netlib = networkConfig ()
@@ -71,6 +77,66 @@ def handleMigrateBegin():
7177 pass
7278
7379
80+ def executeCustomScripts (sysArgs ):
81+ createDirectoryIfNotExists (customDir , customDirPermissions )
82+ scripts = getCustomScriptsFromDirectory ()
83+
84+ for scriptName in scripts :
85+ executeScript (scriptName , sysArgs )
86+
87+
88+ def executeScript (scriptName , sysArgs ):
89+ logger .info ('Executing custom script: %s, parameters: %s' % (scriptName , ' ' .join (map (str , sysArgs ))))
90+ path = customDir + os .path .sep + scriptName
91+
92+ if not os .access (path , os .X_OK ):
93+ logger .warning ('Custom script: %s is not executable; skipping execution.' % scriptName )
94+ return
95+
96+ try :
97+ process = subprocess .Popen ([path ] + sysArgs , stdout = subprocess .PIPE ,
98+ stderr = subprocess .PIPE , shell = False )
99+ try :
100+ timer = Timer (timeoutSeconds , terminateProcess , [process , scriptName ])
101+ timer .start ()
102+ output , error = process .communicate ()
103+
104+ if process .returncode == - 15 :
105+ logger .error ('Custom script: %s terminated after timeout of %s second[s].'
106+ % (scriptName , timeoutSeconds ))
107+ return
108+ if process .returncode != 0 :
109+ logger .info ('return code: %s' % str (process .returncode ))
110+ raise Exception (error )
111+ logger .info ('Custom script: %s finished successfully; output: \n %s' %
112+ (scriptName , str (output )))
113+ finally :
114+ timer .cancel ()
115+ except (OSError , Exception ) as e :
116+ logger .exception ("Custom script: %s finished with error: \n %s" % (scriptName , e ))
117+
118+
119+ def terminateProcess (process , scriptName ):
120+ logger .warning ('Custom script: %s taking longer than %s second[s]; terminating..' % (scriptName , str (timeoutSeconds )))
121+ process .terminate ()
122+
123+
124+ def getCustomScriptsFromDirectory ():
125+ return sorted (filter (lambda fileName : (fileName is not None ) & (fileName != "" ) & ('_' in fileName ) &
126+ (fileName .startswith ((action + '_' )) | fileName .startswith (('all' + '_' ))),
127+ os .listdir (customDir )), key = lambda fileName : substringAfter (fileName , '_' ))
128+
129+
130+ def createDirectoryIfNotExists (dir , permissions ):
131+ if not os .path .exists (dir ):
132+ logger .info ('Directory %s does not exist; creating it.' % dir )
133+ os .makedirs (dir , permissions )
134+
135+
136+ def substringAfter (s , delimiter ):
137+ return s .partition (delimiter )[2 ]
138+
139+
74140if __name__ == '__main__' :
75141 if len (sys .argv ) != 5 :
76142 sys .exit (0 )
@@ -79,5 +145,11 @@ if __name__ == '__main__':
79145 logger .debug ("Executing qemu hook with args: %s" % sys .argv )
80146 action , status = sys .argv [2 :4 ]
81147
148+ if action not in validQemuActions :
149+ logger .error ('The given action: %s, is not a valid libvirt qemu operation.' % action )
150+ sys .exit (0 )
151+
82152 if action == "migrate" and status == "begin" :
83153 handleMigrateBegin ()
154+
155+ executeCustomScripts (sys .argv [1 :])
0 commit comments