1515# along with this program; if not, write to the Free Software Foundation, Inc.,
1616# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1717
18- from sm .core import util
1918import os
2019import sys
2120import re
22- from sm .core import xs_errors
23- from sm .core import mpath_cli
2421import json
2522import subprocess
2623
27- supported = ['iscsi' , 'lvmoiscsi' , 'rawhba' , 'lvmohba' , 'ocfsohba' , 'ocfsoiscsi' , 'netapp' , 'gfs2' ]
28-
29- LOCK_TYPE_HOST = "host"
30- LOCK_NS1 = "mpathcount1"
31- LOCK_NS2 = "mpathcount2"
32-
33- MAPPER_DIR = "/dev/mapper"
34- MPATHS_DIR = "/dev/shm"
35- MPATH_FILE_NAME = "/dev/shm/mpath_status"
36- match_bySCSIid = False
37- mpath_enabled = True
38- SCSIid = 'NOTSUPPLIED'
39- XAPI_HEALTH_CHECK = '/opt/xensource/libexec/xapi-health-check'
40-
41- cached_DM_maj = None
42-
43- def get_dm_major ():
44- global cached_DM_maj
45- if not cached_DM_maj :
46- try :
47- line = [x for x in open ('/proc/devices' ).readlines () if x .endswith ('device-mapper\n ' )]
48- cached_DM_maj = int (line [0 ].split ()[0 ])
49- except :
50- pass
51- return cached_DM_maj
52-
53-
54- def mpc_exit (session , code ):
55- if session is not None :
56- try :
57- session .xenapi .session .logout ()
58- except :
59- pass
60- sys .exit (code )
61-
62-
63- def match_host_id (s ):
64- regex = re .compile ("^INSTALLATION_UUID" )
65- return regex .search (s , 0 )
66-
67-
68- def get_localhost_uuid ():
69- filename = '/etc/xensource-inventory'
70- try :
71- f = open (filename , 'r' )
72- except :
73- raise xs_errors .XenError ('EIO' , \
74- opterr = "Unable to open inventory file [%s]" % filename )
75- domid = ''
76- for line in filter (match_host_id , f .readlines ()):
77- domid = line .split ("'" )[1 ]
78- return domid
79-
80-
81- def match_dmpLUN (s ):
82- regex = re .compile ("[0-9]*:[0-9]*:[0-9]*:[0-9]*" )
83- return regex .search (s , 0 )
84-
85-
86- def match_pathup (s ):
87- path_status = None
88- match = re .match (r'.*\d+:\d+:\d+:\d+\s+\S+\s+\S+\s+\S+\s+(\S+)' , s )
89- if match :
90- path_status = match .group (1 )
91- if path_status in ['faulty' , 'shaky' , 'failed' ]:
92- return False
93- return True
94-
95-
96- def _tostring (l ):
97- return str (l )
98-
99-
100- def get_path_count (SCSIid ):
101- count = 0
102- total = 0
103- lines = mpath_cli .get_topology (SCSIid )
104- for line in filter (match_dmpLUN , lines ):
105- total += 1
106- if match_pathup (line ):
107- count += 1
108- return (count , total )
109-
110-
111- def get_root_dev_major ():
112- buf = os .stat ('/' )
113- devno = buf .st_dev
114- return os .major (devno )
115-
116-
117- # @key: key to update
118- # @SCSIid: SCSI id of multipath map
119- # @entry: string representing previous value
120- # @remove: callback to remove key
121- # @add: callback to add key/value pair
122- # @mpath_status: map to record multipath status
123- def update_config (key , SCSIid , entry , remove , add , mpath_status = None ):
124- path = os .path .join (MAPPER_DIR , SCSIid )
125- util .SMlog ("MPATH: Updating entry for [%s], current: %s" % (SCSIid , entry ))
126- if os .path .exists (path ):
127- count , total = get_path_count (SCSIid )
128- max = 0
129- if len (entry ) != 0 :
130- try :
131- p = entry .strip ('[' )
132- p = p .strip (']' )
133- q = p .split (',' )
134- max = int (q [1 ])
135- except :
136- pass
137- if total > max :
138- max = total
139- newentry = [count , max ]
140- if str (newentry ) != entry :
141- remove ('multipathed' )
142- remove (key )
143- add ('multipathed' , 'true' )
144- add (key , str (newentry ))
145- util .SMlog ("MPATH: Set val: %s" % str (newentry ))
146- if mpath_status != None :
147- mpath_status .update ({str (key ): f"{ count } /{ max } " })
148- else :
149- util .SMlog ('MPATH: device %s gone' % (SCSIid ))
150- remove ('multipathed' )
151- remove (key )
152-
153-
154- def get_SCSIidlist (devconfig , sm_config ):
155- SCSIidlist = []
156- if 'SCSIid' in sm_config :
157- SCSIidlist = sm_config ['SCSIid' ].split (',' )
158- elif 'SCSIid' in devconfig :
159- SCSIidlist .append (devconfig ['SCSIid' ])
160- elif 'provider' in devconfig :
161- SCSIidlist .append (devconfig ['ScsiId' ])
162- else :
163- for key in sm_config :
164- if util ._isSCSIid (key ):
165- SCSIidlist .append (re .sub ("^scsi-" , "" , key ))
166- return SCSIidlist
167-
168-
169- def check_root_disk (config , maps , remove , add ):
170- if get_root_dev_major () == get_dm_major ():
171- # Ensure output headers are not in the list
172- if 'name' in maps :
173- maps .remove ('name' )
174- # first map will always correspond to the root dev, dm-0
175- assert (len (maps ) > 0 )
176- i = maps [0 ]
177- if (not match_bySCSIid ) or i == SCSIid :
178- util .SMlog ("Matched SCSIid %s, updating " \
179- " Host.other-config:mpath-boot " % i )
180- key = "mpath-boot"
181- if key not in config :
182- update_config (key , i , "" , remove , add )
183- else :
184- update_config (key , i , config [key ], remove , add )
185-
186-
187- def check_devconfig (devconfig , sm_config , config , remove , add , mpath_status = None ):
188- SCSIidlist = get_SCSIidlist (devconfig , sm_config )
189- if not len (SCSIidlist ):
190- return
191- for i in SCSIidlist :
192- if match_bySCSIid and i != SCSIid :
193- continue
194- util .SMlog ("Matched SCSIid, updating %s" % i )
195- key = "mpath-" + i
196- if not mpath_enabled :
197- remove (key )
198- remove ('multipathed' )
199- else :
200- if key not in config :
201- update_config (key , i , "" , remove , add , mpath_status )
202- else :
203- update_config (key , i , config [key ], remove , add , mpath_status )
204-
205- def check_xapi_is_enabled ():
206- """Check XAPI health status"""
207- def _run_command (command , timeout ):
208- try :
209- process = subprocess .Popen (
210- command ,
211- stdout = subprocess .PIPE ,
212- stderr = subprocess .PIPE ,
213- universal_newlines = True
214- )
215- try :
216- stdout , stderr = process .communicate (timeout = timeout )
217- return process .returncode , stdout , stderr
218- except subprocess .TimeoutExpired :
219- process .kill ()
220- util .SMlog (f"Command execution timeout after { timeout } s: { ' ' .join (command )} " )
221- return - 1 , "" , "Timeout"
222- except Exception as e :
223- util .SMlog (f"Error executing command: { e } " )
224- return - 1 , "" , str (e )
225-
226- returncode , _ , stderr = _run_command ([XAPI_HEALTH_CHECK ], timeout = 120 )
227- if returncode != 0 :
228- util .SMlog (f"XAPI health check failed: { stderr } " )
229- return returncode == 0
24+ from sm .core import util
25+ from sm .core import xs_errors
26+ from sm .core import mpath_cli
27+ from sm import mpathcount
23028
23129if __name__ == '__main__' :
23230 try :
@@ -235,16 +33,16 @@ if __name__ == '__main__':
23533 print ("Unable to open local XAPI session" )
23634 sys .exit (- 1 )
23735
238- localhost = session .xenapi .host .get_by_uuid (get_localhost_uuid ())
239- check_xapi_is_enabled ()
36+ localhost = session .xenapi .host .get_by_uuid (mpathcount . get_localhost_uuid ())
37+ mpathcount . check_xapi_is_enabled ()
24038 # Check whether multipathing is enabled (either for root dev or SRs)
24139 try :
242- if get_root_dev_major () != get_dm_major ():
40+ if mpathcount . get_root_dev_major () != mpathcount . get_dm_major ():
24341 hconf = session .xenapi .host .get_other_config (localhost )
24442 assert (hconf ['multipathing' ] == 'true' )
245- mpath_enabled = True
43+ mpathcount . mpath_enabled = True
24644 except :
247- mpath_enabled = False
45+ mpathcount . mpath_enabled = False
24846
24947 # Check root disk if multipathed
25048 try :
@@ -256,16 +54,16 @@ if __name__ == '__main__':
25654 session .xenapi .host .add_to_other_config (localhost , key , val )
25755 config = session .xenapi .host .get_other_config (localhost )
25856 maps = mpath_cli .list_maps ()
259- check_root_disk (config , maps , _remove , _add )
57+ mpathcount . check_root_disk (config , maps , _remove , _add )
26058
26159 except :
26260 util .SMlog ("MPATH: Failure updating Host.other-config:mpath-boot db" )
263- mpc_exit (session , - 1 )
61+ mpathcount . mpc_exit (session , - 1 )
26462
26563 try :
26664 pbds = session .xenapi .PBD .get_all_records_where ("field \" host\" = \" %s\" " % localhost )
26765 except :
268- mpc_exit (session , - 1 )
66+ mpathcount . mpc_exit (session , - 1 )
26967
27068 try :
27169 mpath_status = {}
@@ -280,17 +78,17 @@ if __name__ == '__main__':
28078 config = record ['other_config' ]
28179 SR = record ['SR' ]
28280 srtype = session .xenapi .SR .get_type (SR )
283- if srtype in supported :
81+ if srtype in mpathcount . supported :
28482 devconfig = record ["device_config" ]
28583 sm_config = session .xenapi .SR .get_sm_config (SR )
286- check_devconfig (devconfig , sm_config , config , remove , add , mpath_status )
287- mpath_status = mpath_status if mpath_enabled else {}
288- util .atomicFileWrite (MPATH_FILE_NAME , MPATHS_DIR , json .dumps (mpath_status ))
289- os .chmod (MPATH_FILE_NAME , 0o0644 )
84+ mpathcount . check_devconfig (devconfig , sm_config , config , remove , add , mpath_status )
85+ mpath_status = mpath_status if mpathcount . mpath_enabled else {}
86+ util .atomicFileWrite (mpathcount . MPATH_FILE_NAME , mpathcount . MPATHS_DIR , json .dumps (mpath_status ))
87+ os .chmod (mpathcount . MPATH_FILE_NAME , 0o0644 )
29088 except :
29189 util .SMlog ("MPATH: Failure updating db. %s" % sys .exc_info ())
292- mpc_exit (session , - 1 )
90+ mpathcount . mpc_exit (session , - 1 )
29391
29492 util .SMlog ("MPATH: Update done" )
29593
296- mpc_exit (session , 0 )
94+ mpathcount . mpc_exit (session , 0 )
0 commit comments