Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# Copyright (C) Citrix Systems Inc. 

2# 

3# This program is free software; you can redistribute it and/or modify 

4# it under the terms of the GNU Lesser General Public License as published 

5# by the Free Software Foundation; version 2.1 only. 

6# 

7# This program is distributed in the hope that it will be useful, 

8# but WITHOUT ANY WARRANTY; without even the implied warranty of 

9# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

10# GNU Lesser General Public License for more details. 

11# 

12# You should have received a copy of the GNU Lesser General Public License 

13# along with this program; if not, write to the Free Software Foundation, Inc., 

14# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 

15# 

16# A plugin for enabling trim on LVM based SRs to free up storage space 

17# in Storage arrays. 

18 

19import xml 

20import os 

21import time 

22import util 

23import lock 

24import lvhdutil 

25import vhdutil 

26import lvutil 

27 

28TRIM_LV_TAG = "_trim_lv" 

29TRIM_CAP = "SR_TRIM" 

30LOCK_RETRY_ATTEMPTS = 3 

31LOCK_RETRY_INTERVAL = 1 

32ERROR_CODE_KEY = "errcode" 

33ERROR_MSG_KEY = "errmsg" 

34 

35TRIM_LAST_TRIGGERED_KEY = "trim_last_triggered" 

36MASTER_LVM_CONF = '/etc/lvm/master' 

37 

38 

39def _vg_by_sr_uuid(sr_uuid): 

40 return lvhdutil.VG_PREFIX + sr_uuid 

41 

42 

43def _lvpath_by_vg_lv_name(vg_name, lv_name): 

44 return os.path.join(lvhdutil.VG_LOCATION, vg_name, lv_name) 

45 

46 

47def to_xml(d): 

48 

49 dom = xml.dom.minidom.Document() 

50 trim_response = dom.createElement("trim_response") 

51 dom.appendChild(trim_response) 

52 

53 for key, value in sorted(d.items()): 

54 key_value_element = dom.createElement("key_value_pair") 

55 trim_response.appendChild(key_value_element) 

56 

57 key_element = dom.createElement("key") 

58 key_text_node = dom.createTextNode(key) 

59 key_element.appendChild(key_text_node) 

60 key_value_element.appendChild(key_element) 

61 

62 value_element = dom.createElement("value") 

63 value_text_mode = dom.createTextNode(value) 

64 value_element.appendChild(value_text_mode) 

65 key_value_element.appendChild(value_element) 

66 

67 return dom.toxml() 

68 

69 

70def _handle_discard_error(vg_name, sr_uuid, error): 

71 if "BLKDISCARD ioctl failed: Operation not supported" not in error.reason: 

72 err_msg = { 

73 ERROR_CODE_KEY: 'TrimException', 

74 ERROR_MSG_KEY: error.reason 

75 } 

76 return to_xml(err_msg) 

77 else: 

78 return str(True) 

79 

80 

81# Note: This function is expected to be called from a context where 

82# the SR is locked by the thread calling the function; therefore removing 

83# any risk of a race condition updating the LAST_TRIGGERED value. 

84def _log_last_triggered(session, sr_uuid): 

85 try: 

86 sr_ref = session.xenapi.SR.get_by_uuid(sr_uuid) 

87 other_config = session.xenapi.SR.get_other_config(sr_ref) 

88 if TRIM_LAST_TRIGGERED_KEY in other_config: 

89 session.xenapi.SR.remove_from_other_config(sr_ref, TRIM_LAST_TRIGGERED_KEY) 

90 session.xenapi.SR.add_to_other_config(sr_ref, TRIM_LAST_TRIGGERED_KEY, str(time.time())) 

91 except: 

92 util.logException("Unable to set other-config:%s" % TRIM_LAST_TRIGGERED_KEY) 

93 

94 

95def do_trim(session, args): 

96 """Attempt to trim the given LVHDSR""" 

97 util.SMlog("do_trim: %s" % args) 

98 sr_uuid = args["sr_uuid"] 

99 os.environ['LVM_SYSTEM_DIR'] = MASTER_LVM_CONF 

100 

101 if TRIM_CAP not in util.sr_get_capability(sr_uuid): 

102 util.SMlog("Trim command ignored on unsupported SR %s" % sr_uuid) 

103 err_msg = {ERROR_CODE_KEY: 'UnsupportedSRForTrim', 

104 ERROR_MSG_KEY: 'Trim on [%s] not supported' % sr_uuid} 

105 return to_xml(err_msg) 

106 

107 # Lock SR, get vg empty space details 

108 sr_lock = lock.Lock(vhdutil.LOCK_TYPE_SR, sr_uuid) 

109 got_lock = False 

110 for i in range(LOCK_RETRY_ATTEMPTS): 

111 got_lock = sr_lock.acquireNoblock() 

112 if got_lock: 

113 break 

114 time.sleep(LOCK_RETRY_INTERVAL) 

115 

116 if got_lock: 

117 try: 

118 vg_name = _vg_by_sr_uuid(sr_uuid) 

119 lv_name = sr_uuid + TRIM_LV_TAG 

120 lv_path = _lvpath_by_vg_lv_name(vg_name, lv_name) 

121 

122 # Clean trim LV in case the previous trim attemp failed 

123 if lvutil.exists(lv_path): 

124 lvutil.remove(lv_path) 

125 

126 #Check if VG limits are enough for creating LV. 

127 stats = lvutil._getVGstats(vg_name) 

128 if (stats['freespace'] < lvutil.LVM_SIZE_INCREMENT): 

129 util.SMlog("No space to claim on a full SR %s" % sr_uuid) 

130 err_msg = {ERROR_CODE_KEY: 'Trim failed on full SR', 

131 ERROR_MSG_KEY: 'No space to claim on a full SR'} 

132 result = to_xml(err_msg) 

133 else: 

134 # Perform a lvcreate, blkdiscard and lvremove to 

135 # trigger trim on the array 

136 lvutil.create(lv_name, 0, vg_name, size_in_percentage="100%F") 

137 cmd = ["/usr/sbin/blkdiscard", "-v", lv_path] 

138 stdout = util.pread2(cmd) 

139 util.SMlog("Stdout is %s" % stdout) 

140 util.SMlog("Trim on SR: %s complete. " % sr_uuid) 

141 result = str(True) 

142 except util.CommandException as e: 

143 result = _handle_discard_error(vg_name, sr_uuid, e) 

144 except: 

145 err_msg = { 

146 ERROR_CODE_KEY: 'UnknownTrimException', 

147 ERROR_MSG_KEY: 'Unknown Exception: trim failed on SR [%s]' 

148 % sr_uuid 

149 } 

150 result = to_xml(err_msg) 

151 finally: 

152 if lvutil.exists(lv_path): 

153 lvutil.remove(lv_path) 

154 

155 _log_last_triggered(session, sr_uuid) 

156 

157 sr_lock.release() 

158 return result 

159 else: 

160 util.SMlog("Could not complete Trim on %s, Lock unavailable !" \ 

161 % sr_uuid) 

162 err_msg = {ERROR_CODE_KEY: 'SRUnavailable', 

163 ERROR_MSG_KEY: 'Unable to get SR lock [%s]' % sr_uuid} 

164 return to_xml(err_msg)